feat(web): build project loop workspace

This commit is contained in:
-Puter
2026-07-24 02:03:54 +05:30
parent 7974bd5f3e
commit 5d6ba558ab
10 changed files with 1353 additions and 245 deletions

View File

@@ -7,6 +7,7 @@
".": "./src/index.ts",
"./agent-os": "./src/agent-os.ts",
"./project": "./src/project.ts",
"./project-work": "./src/project-work.ts",
"./signal": "./src/signal.ts"
},
"scripts": {

View File

@@ -1,4 +1,5 @@
// oxlint-disable-next-line no-barrel-file -- The package root intentionally exposes its public modules.
export * from "./agent-os";
export * from "./project";
export * from "./project-work";
export * from "./signal";

View File

@@ -0,0 +1,30 @@
import { describe, expect, test } from "vitest";
import { getProjectWorkProgress, getProjectWorkStatus } from "./project-work";
describe("project work state mapping", () => {
test("maps issue and run statuses to the same product language", () => {
expect(getProjectWorkStatus("working")).toEqual({
label: "In progress",
phase: "active",
verification: "running",
});
expect(getProjectWorkStatus("needs-input")).toEqual({
label: "Needs your input",
phase: "waiting",
verification: "blocked",
});
});
test("keeps progress monotonic through the normal loop", () => {
expect(getProjectWorkProgress("open")).toBeLessThan(
getProjectWorkProgress("queued")
);
expect(getProjectWorkProgress("queued")).toBeLessThan(
getProjectWorkProgress("working")
);
expect(getProjectWorkProgress("working")).toBeLessThan(
getProjectWorkProgress("completed")
);
});
});

View File

@@ -0,0 +1,115 @@
export const PROJECT_ISSUE_STATUSES = [
"open",
"queued",
"working",
"needs-input",
"completed",
"failed",
] as const;
export type ProjectIssueStatus = (typeof PROJECT_ISSUE_STATUSES)[number];
export const PROJECT_RUN_STATUSES = [
"ready",
"queued",
"working",
"needs-input",
"completed",
"failed",
] as const;
export type ProjectRunStatus = (typeof PROJECT_RUN_STATUSES)[number];
export type ProjectWorkPhase =
| "captured"
| "queued"
| "active"
| "waiting"
| "completed"
| "failed";
export type ProjectVerificationStatus =
| "not-started"
| "running"
| "blocked"
| "passed"
| "failed";
export interface ProjectWorkStatus {
readonly label: string;
readonly phase: ProjectWorkPhase;
readonly verification: ProjectVerificationStatus;
}
const STATUS_MAP: Readonly<
Record<ProjectIssueStatus | ProjectRunStatus, ProjectWorkStatus>
> = {
completed: {
label: "Completed",
phase: "completed",
verification: "passed",
},
failed: {
label: "Failed",
phase: "failed",
verification: "failed",
},
"needs-input": {
label: "Needs your input",
phase: "waiting",
verification: "blocked",
},
open: {
label: "Ready to start",
phase: "captured",
verification: "not-started",
},
queued: {
label: "Queued",
phase: "queued",
verification: "not-started",
},
ready: {
label: "Ready",
phase: "queued",
verification: "not-started",
},
working: {
label: "In progress",
phase: "active",
verification: "running",
},
};
export const getProjectWorkStatus = (
status: ProjectIssueStatus | ProjectRunStatus
): ProjectWorkStatus => STATUS_MAP[status];
export const getProjectWorkProgress = (
status: ProjectIssueStatus | ProjectRunStatus
): number => {
switch (status) {
case "completed": {
return 100;
}
case "failed": {
return 58;
}
case "needs-input": {
return 78;
}
case "working": {
return 68;
}
case "queued":
case "ready": {
return 24;
}
case "open": {
return 8;
}
default: {
return 0;
}
}
};