@schilderlabs/pitown 0.2.1 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,142 @@
1
+ //#region ../core/src/types.d.ts
2
+ type RunMode = "single-pi";
3
+ type AgentStatus = "queued" | "starting" | "running" | "idle" | "blocked" | "completed" | "failed" | "stopped";
4
+ type TaskStatus = "queued" | "running" | "blocked" | "completed" | "aborted";
5
+ interface AgentSessionRecord {
6
+ runtime: "pi";
7
+ persisted: boolean;
8
+ sessionDir: string | null;
9
+ sessionId: string | null;
10
+ sessionPath: string | null;
11
+ processId: number | null;
12
+ lastAttachedAt: string | null;
13
+ }
14
+ interface AgentStateSnapshot {
15
+ agentId: string;
16
+ role: string;
17
+ status: AgentStatus;
18
+ taskId: string | null;
19
+ task: string | null;
20
+ branch: string | null;
21
+ updatedAt: string;
22
+ lastMessage: string | null;
23
+ waitingOn: string | null;
24
+ blocked: boolean;
25
+ runId: string | null;
26
+ session: AgentSessionRecord;
27
+ }
28
+ interface TaskRecord {
29
+ taskId: string;
30
+ title: string;
31
+ status: TaskStatus;
32
+ role: string;
33
+ assignedAgentId: string;
34
+ createdBy: string;
35
+ createdAt: string;
36
+ updatedAt: string;
37
+ }
38
+ interface MetricsSnapshot {
39
+ interruptRate: number;
40
+ autonomousCompletionRate: number;
41
+ contextCoverageScore: number;
42
+ meanTimeToCorrectHours: number | null;
43
+ feedbackToDemoCycleTimeHours: number | null;
44
+ totals: {
45
+ taskAttempts: number;
46
+ completedTasks: number;
47
+ interrupts: number;
48
+ observedInterruptCategories: number;
49
+ coveredInterruptCategories: number;
50
+ };
51
+ }
52
+ interface RunManifest {
53
+ runId: string;
54
+ repoId: string;
55
+ repoSlug: string;
56
+ repoRoot: string;
57
+ branch: string;
58
+ goal: string | null;
59
+ planPath: string | null;
60
+ recommendedPlanDir: string | null;
61
+ mode: RunMode;
62
+ startedAt: string;
63
+ endedAt: string | null;
64
+ stopReason: string | null;
65
+ leasePath: string;
66
+ piExitCode: number | null;
67
+ completedTaskCount: number;
68
+ blockedTaskCount: number;
69
+ skippedTaskCount: number;
70
+ totalCostUsd: number;
71
+ }
72
+ interface PiInvocationRecord {
73
+ command: string;
74
+ cwd: string;
75
+ repoRoot: string;
76
+ planPath: string | null;
77
+ goal: string | null;
78
+ sessionDir: string | null;
79
+ sessionId: string | null;
80
+ sessionPath: string | null;
81
+ startedAt: string;
82
+ endedAt: string;
83
+ exitCode: number;
84
+ stdoutPath: string;
85
+ stderrPath: string;
86
+ promptSummary: string;
87
+ }
88
+ interface RunSummary {
89
+ runId: string;
90
+ mode: RunMode;
91
+ createdAt: string;
92
+ success: boolean;
93
+ message: string;
94
+ piExitCode: number;
95
+ recommendedPlanDir: string | null;
96
+ }
97
+ interface ControllerRunResult {
98
+ runId: string;
99
+ runDir: string;
100
+ latestDir: string;
101
+ manifest: RunManifest;
102
+ metrics: MetricsSnapshot;
103
+ summary: RunSummary;
104
+ piInvocation: PiInvocationRecord;
105
+ }
106
+ type LoopStopReason = "all-tasks-completed" | "all-remaining-tasks-blocked" | "mayor-blocked" | "mayor-idle-no-work" | "max-iterations-reached" | "max-wall-time-reached" | "pi-exit-nonzero" | "high-interrupt-rate";
107
+ interface BoardSnapshot {
108
+ tasks: Array<{
109
+ taskId: string;
110
+ status: TaskStatus;
111
+ }>;
112
+ agents: Array<{
113
+ agentId: string;
114
+ status: AgentStatus;
115
+ blocked: boolean;
116
+ }>;
117
+ allTasksCompleted: boolean;
118
+ allRemainingTasksBlocked: boolean;
119
+ mayorBlocked: boolean;
120
+ hasQueuedOrRunningWork: boolean;
121
+ }
122
+ interface LoopIterationResult {
123
+ iteration: number;
124
+ controllerResult: ControllerRunResult;
125
+ boardSnapshot: BoardSnapshot;
126
+ metrics: MetricsSnapshot;
127
+ elapsedMs: number;
128
+ continueReason: string | null;
129
+ stopReason: LoopStopReason | null;
130
+ }
131
+ interface LoopRunResult {
132
+ loopId: string;
133
+ iterations: LoopIterationResult[];
134
+ stopReason: LoopStopReason;
135
+ totalIterations: number;
136
+ totalElapsedMs: number;
137
+ finalBoardSnapshot: BoardSnapshot;
138
+ aggregateMetrics: MetricsSnapshot;
139
+ }
140
+ //#endregion
141
+ export { LoopRunResult as n, TaskRecord as r, AgentStateSnapshot as t };
142
+ //# sourceMappingURL=types-COGNGvsY.d.mts.map
package/dist/watch.d.mts CHANGED
@@ -1,5 +1,39 @@
1
+ import { r as TaskRecord, t as AgentStateSnapshot } from "./types-COGNGvsY.mjs";
2
+
3
+ //#region ../core/src/agents.d.ts
4
+ declare function listAgentStates(artifactsDir: string): AgentStateSnapshot[];
5
+ //#endregion
6
+ //#region ../core/src/tasks.d.ts
7
+ declare function listTaskRecords(artifactsDir: string): TaskRecord[];
8
+ //#endregion
1
9
  //#region src/watch.d.ts
10
+ interface WatchSummarySnapshot {
11
+ runId: string | null;
12
+ piExitCode: number | null;
13
+ mode: string | null;
14
+ message: string | null;
15
+ stopReason: string | null;
16
+ }
17
+ interface WatchManifestSnapshot {
18
+ branch: string | null;
19
+ goal: string | null;
20
+ planPath: string | null;
21
+ recommendedPlanDir: string | null;
22
+ }
23
+ interface WatchSnapshot {
24
+ repoRoot: string;
25
+ repoSlug: string;
26
+ repoName: string;
27
+ branch: string | null;
28
+ summary: WatchSummarySnapshot;
29
+ manifest: WatchManifestSnapshot;
30
+ agents: ReturnType<typeof listAgentStates>;
31
+ tasks: ReturnType<typeof listTaskRecords>;
32
+ }
33
+ declare function buildWatchSnapshot(argv?: string[]): WatchSnapshot;
34
+ declare function createWatchFingerprint(snapshot: WatchSnapshot): string;
35
+ declare function renderWatchSnapshot(snapshot: WatchSnapshot): string[];
2
36
  declare function watchTown(argv?: string[]): void;
3
37
  //#endregion
4
- export { watchTown };
38
+ export { WatchSnapshot, buildWatchSnapshot, createWatchFingerprint, renderWatchSnapshot, watchTown };
5
39
  //# sourceMappingURL=watch.d.mts.map
package/dist/watch.mjs CHANGED
@@ -1,26 +1,138 @@
1
- import { t as isDirectExecution } from "./entrypoint-CyJDLudQ.mjs";
2
- import { l as getTownHomeDir } from "./config-CUpe9o0x.mjs";
3
- import { resolveLatestRunPointer, showTownStatus } from "./status.mjs";
4
- import { existsSync, watchFile } from "node:fs";
1
+ import { d as listAgentStates, n as listTaskRecords } from "./tasks-De4IAy3x.mjs";
2
+ import { t as isDirectExecution } from "./entrypoint-WBAQmFbT.mjs";
3
+ import { f as getCurrentBranch, l as getTownHomeDir } from "./config-BG1v4iIi.mjs";
4
+ import { t as resolveRepoContext } from "./repo-context-BuA2JqPm.mjs";
5
+ import { resolveLatestRunPointer } from "./status.mjs";
6
+ import { existsSync, readFileSync } from "node:fs";
7
+ import { basename } from "node:path";
5
8
 
6
9
  //#region src/watch.ts
7
- function watchTown(argv = process.argv.slice(2)) {
8
- const metricsPath = resolveLatestRunPointer(argv)?.metricsPath;
9
- console.log("[pitown] watch");
10
- console.log(`- town home: ${getTownHomeDir()}`);
11
- console.log("- press Ctrl+C to stop");
12
- showTownStatus(argv);
13
- if (!metricsPath || !existsSync(metricsPath)) {
14
- console.log("- metrics file does not exist yet; watch will activate after the first run");
15
- return;
10
+ function readJsonIfExists(path) {
11
+ if (!existsSync(path)) return null;
12
+ try {
13
+ return JSON.parse(readFileSync(path, "utf-8"));
14
+ } catch {
15
+ return null;
16
16
  }
17
- watchFile(metricsPath, { interval: 1e3 }, () => {
18
- console.log("\n[pitown] metrics updated");
19
- showTownStatus(argv);
17
+ }
18
+ function truncate(text, max) {
19
+ if (!text) return "—";
20
+ const single = text.replace(/\n/g, " ").trim();
21
+ if (single.length <= max) return single;
22
+ return `${single.slice(0, max - 1)}...`;
23
+ }
24
+ function buildWatchSnapshot(argv = process.argv.slice(2)) {
25
+ const repo = resolveRepoContext(argv);
26
+ const latest = resolveLatestRunPointer(["--repo", repo.repoRoot]);
27
+ const summary = latest ? readJsonIfExists(latest.summaryPath) ?? null : null;
28
+ const manifest = latest ? readJsonIfExists(latest.manifestPath) ?? null : null;
29
+ return {
30
+ repoRoot: repo.repoRoot,
31
+ repoSlug: repo.repoSlug,
32
+ repoName: basename(repo.repoRoot),
33
+ branch: getCurrentBranch(repo.repoRoot),
34
+ summary: {
35
+ runId: summary?.runId ?? null,
36
+ piExitCode: summary?.piExitCode ?? null,
37
+ mode: summary?.mode ?? null,
38
+ message: summary?.message ?? null,
39
+ stopReason: manifest?.stopReason ?? null
40
+ },
41
+ manifest: {
42
+ branch: manifest?.branch ?? null,
43
+ goal: manifest?.goal ?? null,
44
+ planPath: manifest?.planPath ?? null,
45
+ recommendedPlanDir: manifest?.recommendedPlanDir ?? null
46
+ },
47
+ agents: listAgentStates(repo.artifactsDir),
48
+ tasks: listTaskRecords(repo.artifactsDir)
49
+ };
50
+ }
51
+ function createWatchFingerprint(snapshot) {
52
+ return JSON.stringify({
53
+ summary: snapshot.summary,
54
+ manifest: snapshot.manifest,
55
+ agents: snapshot.agents.map((agent) => ({
56
+ agentId: agent.agentId,
57
+ role: agent.role,
58
+ status: agent.status,
59
+ taskId: agent.taskId,
60
+ task: agent.task,
61
+ lastMessage: agent.lastMessage,
62
+ waitingOn: agent.waitingOn,
63
+ blocked: agent.blocked,
64
+ updatedAt: agent.updatedAt
65
+ })),
66
+ tasks: snapshot.tasks.map((task) => ({
67
+ taskId: task.taskId,
68
+ title: task.title,
69
+ status: task.status,
70
+ assignedAgentId: task.assignedAgentId,
71
+ updatedAt: task.updatedAt
72
+ }))
20
73
  });
21
74
  }
75
+ function renderWatchSnapshot(snapshot) {
76
+ const branchLabel = snapshot.branch ? ` (${snapshot.branch})` : "";
77
+ const lines = [`[pitown] watch - ${snapshot.repoName}${branchLabel}`];
78
+ lines.push(`- town home: ${getTownHomeDir()}`);
79
+ lines.push(`- repo root: ${snapshot.repoRoot}`);
80
+ if (snapshot.summary.runId) lines.push(`- latest run: ${snapshot.summary.runId}`);
81
+ if (snapshot.summary.mode) lines.push(`- mode: ${snapshot.summary.mode}`);
82
+ if (snapshot.summary.piExitCode !== null) lines.push(`- latest pi exit code: ${snapshot.summary.piExitCode}`);
83
+ if (snapshot.summary.stopReason) lines.push(`- stop reason: ${snapshot.summary.stopReason}`);
84
+ if (snapshot.manifest.goal) lines.push(`- goal: ${snapshot.manifest.goal}`);
85
+ if (snapshot.manifest.planPath) lines.push(`- plan path: ${snapshot.manifest.planPath}`);
86
+ if (!snapshot.manifest.planPath && snapshot.manifest.recommendedPlanDir) lines.push(`- recommended plans: ${snapshot.manifest.recommendedPlanDir}`);
87
+ if (snapshot.summary.message) lines.push(`- note: ${snapshot.summary.message}`);
88
+ lines.push("");
89
+ lines.push("Agents:");
90
+ if (snapshot.agents.length === 0) lines.push(" (no agents)");
91
+ else for (const agent of snapshot.agents) {
92
+ const id = agent.agentId.padEnd(14);
93
+ const role = agent.role.padEnd(10);
94
+ const status = agent.status.padEnd(10);
95
+ const task = truncate(agent.task, 56);
96
+ const msg = agent.lastMessage ? ` | ${truncate(agent.lastMessage, 36)}` : "";
97
+ const waiting = agent.waitingOn ? ` | waiting on: ${agent.waitingOn}` : "";
98
+ lines.push(` ${id}${role}${status}${task}${msg}${waiting}`);
99
+ }
100
+ lines.push("");
101
+ lines.push("Tasks:");
102
+ if (snapshot.tasks.length === 0) lines.push(" (no tasks)");
103
+ else for (const task of snapshot.tasks) {
104
+ const id = task.taskId.padEnd(14);
105
+ const status = task.status.padEnd(12);
106
+ const assignee = task.assignedAgentId.padEnd(14);
107
+ lines.push(` ${id}${status}${assignee}${truncate(task.title, 56)}`);
108
+ }
109
+ return lines;
110
+ }
111
+ function printSnapshot(snapshot) {
112
+ console.log(renderWatchSnapshot(snapshot).join("\n"));
113
+ }
114
+ function watchTown(argv = process.argv.slice(2)) {
115
+ const initialSnapshot = buildWatchSnapshot(argv);
116
+ let previousFingerprint = createWatchFingerprint(initialSnapshot);
117
+ printSnapshot(initialSnapshot);
118
+ console.log("- press Ctrl+C to stop");
119
+ const interval = setInterval(() => {
120
+ const nextSnapshot = buildWatchSnapshot(argv);
121
+ const nextFingerprint = createWatchFingerprint(nextSnapshot);
122
+ if (nextFingerprint === previousFingerprint) return;
123
+ previousFingerprint = nextFingerprint;
124
+ console.log("");
125
+ console.log(`[pitown] watch update - ${(/* @__PURE__ */ new Date()).toISOString()}`);
126
+ printSnapshot(nextSnapshot);
127
+ }, 1e3);
128
+ const stopWatching = () => {
129
+ clearInterval(interval);
130
+ };
131
+ process.once("SIGINT", stopWatching);
132
+ process.once("SIGTERM", stopWatching);
133
+ }
22
134
  if (isDirectExecution(import.meta.url)) watchTown();
23
135
 
24
136
  //#endregion
25
- export { watchTown };
137
+ export { buildWatchSnapshot, createWatchFingerprint, renderWatchSnapshot, watchTown };
26
138
  //# sourceMappingURL=watch.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"watch.mjs","names":[],"sources":["../src/watch.ts"],"sourcesContent":["import { existsSync, watchFile } from \"node:fs\"\nimport { isDirectExecution } from \"./entrypoint.js\"\nimport { getTownHomeDir } from \"./paths.js\"\nimport { resolveLatestRunPointer, showTownStatus } from \"./status.js\"\n\nexport function watchTown(argv = process.argv.slice(2)) {\n\tconst latest = resolveLatestRunPointer(argv)\n\tconst metricsPath = latest?.metricsPath\n\n\tconsole.log(\"[pitown] watch\")\n\tconsole.log(`- town home: ${getTownHomeDir()}`)\n\tconsole.log(\"- press Ctrl+C to stop\")\n\tshowTownStatus(argv)\n\n\tif (!metricsPath || !existsSync(metricsPath)) {\n\t\tconsole.log(\"- metrics file does not exist yet; watch will activate after the first run\")\n\t\treturn\n\t}\n\n\twatchFile(metricsPath, { interval: 1000 }, () => {\n\t\tconsole.log(\"\\n[pitown] metrics updated\")\n\t\tshowTownStatus(argv)\n\t})\n}\n\nif (isDirectExecution(import.meta.url)) {\n\twatchTown()\n}\n"],"mappings":";;;;;;AAKA,SAAgB,UAAU,OAAO,QAAQ,KAAK,MAAM,EAAE,EAAE;CAEvD,MAAM,cADS,wBAAwB,KAAK,EAChB;AAE5B,SAAQ,IAAI,iBAAiB;AAC7B,SAAQ,IAAI,gBAAgB,gBAAgB,GAAG;AAC/C,SAAQ,IAAI,yBAAyB;AACrC,gBAAe,KAAK;AAEpB,KAAI,CAAC,eAAe,CAAC,WAAW,YAAY,EAAE;AAC7C,UAAQ,IAAI,6EAA6E;AACzF;;AAGD,WAAU,aAAa,EAAE,UAAU,KAAM,QAAQ;AAChD,UAAQ,IAAI,6BAA6B;AACzC,iBAAe,KAAK;GACnB;;AAGH,IAAI,kBAAkB,OAAO,KAAK,IAAI,CACrC,YAAW"}
1
+ {"version":3,"file":"watch.mjs","names":[],"sources":["../src/watch.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\"\nimport { basename } from \"node:path\"\nimport { getCurrentBranch, listAgentStates, listTaskRecords } from \"../../core/src/index.js\"\nimport { isDirectExecution } from \"./entrypoint.js\"\nimport { getTownHomeDir } from \"./paths.js\"\nimport { resolveRepoContext } from \"./repo-context.js\"\nimport { resolveLatestRunPointer } from \"./status.js\"\n\ninterface WatchSummarySnapshot {\n\trunId: string | null\n\tpiExitCode: number | null\n\tmode: string | null\n\tmessage: string | null\n\tstopReason: string | null\n}\n\ninterface WatchManifestSnapshot {\n\tbranch: string | null\n\tgoal: string | null\n\tplanPath: string | null\n\trecommendedPlanDir: string | null\n}\n\nexport interface WatchSnapshot {\n\trepoRoot: string\n\trepoSlug: string\n\trepoName: string\n\tbranch: string | null\n\tsummary: WatchSummarySnapshot\n\tmanifest: WatchManifestSnapshot\n\tagents: ReturnType<typeof listAgentStates>\n\ttasks: ReturnType<typeof listTaskRecords>\n}\n\nfunction readJsonIfExists<T>(path: string): T | null {\n\tif (!existsSync(path)) return null\n\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T\n\t} catch {\n\t\treturn null\n\t}\n}\n\nfunction truncate(text: string | null | undefined, max: number): string {\n\tif (!text) return \"—\"\n\tconst single = text.replace(/\\n/g, \" \").trim()\n\tif (single.length <= max) return single\n\treturn `${single.slice(0, max - 1)}...`\n}\n\nexport function buildWatchSnapshot(argv = process.argv.slice(2)): WatchSnapshot {\n\tconst repo = resolveRepoContext(argv)\n\tconst latest = resolveLatestRunPointer([\"--repo\", repo.repoRoot])\n\tconst summary = latest\n\t\t? (readJsonIfExists<{ runId?: string; piExitCode?: number; mode?: string; message?: string }>(latest.summaryPath) ?? null)\n\t\t: null\n\tconst manifest = latest\n\t\t? (readJsonIfExists<{ branch?: string; goal?: string | null; planPath?: string | null; recommendedPlanDir?: string | null; stopReason?: string | null }>(\n\t\t\t\tlatest.manifestPath,\n\t\t\t) ?? null)\n\t\t: null\n\n\treturn {\n\t\trepoRoot: repo.repoRoot,\n\t\trepoSlug: repo.repoSlug,\n\t\trepoName: basename(repo.repoRoot),\n\t\tbranch: getCurrentBranch(repo.repoRoot),\n\t\tsummary: {\n\t\t\trunId: summary?.runId ?? null,\n\t\t\tpiExitCode: summary?.piExitCode ?? null,\n\t\t\tmode: summary?.mode ?? null,\n\t\t\tmessage: summary?.message ?? null,\n\t\t\tstopReason: manifest?.stopReason ?? null,\n\t\t},\n\t\tmanifest: {\n\t\t\tbranch: manifest?.branch ?? null,\n\t\t\tgoal: manifest?.goal ?? null,\n\t\t\tplanPath: manifest?.planPath ?? null,\n\t\t\trecommendedPlanDir: manifest?.recommendedPlanDir ?? null,\n\t\t},\n\t\tagents: listAgentStates(repo.artifactsDir),\n\t\ttasks: listTaskRecords(repo.artifactsDir),\n\t}\n}\n\nexport function createWatchFingerprint(snapshot: WatchSnapshot): string {\n\treturn JSON.stringify({\n\t\tsummary: snapshot.summary,\n\t\tmanifest: snapshot.manifest,\n\t\tagents: snapshot.agents.map((agent) => ({\n\t\t\tagentId: agent.agentId,\n\t\t\trole: agent.role,\n\t\t\tstatus: agent.status,\n\t\t\ttaskId: agent.taskId,\n\t\t\ttask: agent.task,\n\t\t\tlastMessage: agent.lastMessage,\n\t\t\twaitingOn: agent.waitingOn,\n\t\t\tblocked: agent.blocked,\n\t\t\tupdatedAt: agent.updatedAt,\n\t\t})),\n\t\ttasks: snapshot.tasks.map((task) => ({\n\t\t\ttaskId: task.taskId,\n\t\t\ttitle: task.title,\n\t\t\tstatus: task.status,\n\t\t\tassignedAgentId: task.assignedAgentId,\n\t\t\tupdatedAt: task.updatedAt,\n\t\t})),\n\t})\n}\n\nexport function renderWatchSnapshot(snapshot: WatchSnapshot): string[] {\n\tconst branchLabel = snapshot.branch ? ` (${snapshot.branch})` : \"\"\n\tconst lines = [`[pitown] watch - ${snapshot.repoName}${branchLabel}`]\n\n\tlines.push(`- town home: ${getTownHomeDir()}`)\n\tlines.push(`- repo root: ${snapshot.repoRoot}`)\n\tif (snapshot.summary.runId) lines.push(`- latest run: ${snapshot.summary.runId}`)\n\tif (snapshot.summary.mode) lines.push(`- mode: ${snapshot.summary.mode}`)\n\tif (snapshot.summary.piExitCode !== null) lines.push(`- latest pi exit code: ${snapshot.summary.piExitCode}`)\n\tif (snapshot.summary.stopReason) lines.push(`- stop reason: ${snapshot.summary.stopReason}`)\n\tif (snapshot.manifest.goal) lines.push(`- goal: ${snapshot.manifest.goal}`)\n\tif (snapshot.manifest.planPath) lines.push(`- plan path: ${snapshot.manifest.planPath}`)\n\tif (!snapshot.manifest.planPath && snapshot.manifest.recommendedPlanDir) {\n\t\tlines.push(`- recommended plans: ${snapshot.manifest.recommendedPlanDir}`)\n\t}\n\tif (snapshot.summary.message) lines.push(`- note: ${snapshot.summary.message}`)\n\n\tlines.push(\"\")\n\tlines.push(\"Agents:\")\n\tif (snapshot.agents.length === 0) {\n\t\tlines.push(\" (no agents)\")\n\t} else {\n\t\tfor (const agent of snapshot.agents) {\n\t\t\tconst id = agent.agentId.padEnd(14)\n\t\t\tconst role = agent.role.padEnd(10)\n\t\t\tconst status = agent.status.padEnd(10)\n\t\t\tconst task = truncate(agent.task, 56)\n\t\t\tconst msg = agent.lastMessage ? ` | ${truncate(agent.lastMessage, 36)}` : \"\"\n\t\t\tconst waiting = agent.waitingOn ? ` | waiting on: ${agent.waitingOn}` : \"\"\n\t\t\tlines.push(` ${id}${role}${status}${task}${msg}${waiting}`)\n\t\t}\n\t}\n\n\tlines.push(\"\")\n\tlines.push(\"Tasks:\")\n\tif (snapshot.tasks.length === 0) {\n\t\tlines.push(\" (no tasks)\")\n\t} else {\n\t\tfor (const task of snapshot.tasks) {\n\t\t\tconst id = task.taskId.padEnd(14)\n\t\t\tconst status = task.status.padEnd(12)\n\t\t\tconst assignee = task.assignedAgentId.padEnd(14)\n\t\t\tlines.push(` ${id}${status}${assignee}${truncate(task.title, 56)}`)\n\t\t}\n\t}\n\n\treturn lines\n}\n\nfunction printSnapshot(snapshot: WatchSnapshot) {\n\tconsole.log(renderWatchSnapshot(snapshot).join(\"\\n\"))\n}\n\nexport function watchTown(argv = process.argv.slice(2)) {\n\tconst initialSnapshot = buildWatchSnapshot(argv)\n\tlet previousFingerprint = createWatchFingerprint(initialSnapshot)\n\n\tprintSnapshot(initialSnapshot)\n\tconsole.log(\"- press Ctrl+C to stop\")\n\n\tconst interval = setInterval(() => {\n\t\tconst nextSnapshot = buildWatchSnapshot(argv)\n\t\tconst nextFingerprint = createWatchFingerprint(nextSnapshot)\n\t\tif (nextFingerprint === previousFingerprint) return\n\n\t\tpreviousFingerprint = nextFingerprint\n\t\tconsole.log(\"\")\n\t\tconsole.log(`[pitown] watch update - ${new Date().toISOString()}`)\n\t\tprintSnapshot(nextSnapshot)\n\t}, 1000)\n\n\tconst stopWatching = () => {\n\t\tclearInterval(interval)\n\t}\n\n\tprocess.once(\"SIGINT\", stopWatching)\n\tprocess.once(\"SIGTERM\", stopWatching)\n}\n\nif (isDirectExecution(import.meta.url)) {\n\twatchTown()\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,28 +1,20 @@
1
1
  {
2
2
  "name": "@schilderlabs/pitown",
3
- "version": "0.2.1",
4
- "description": "Globally installable CLI for Pi Town",
3
+ "version": "0.2.7",
5
4
  "type": "module",
6
- "bin": {
7
- "pitown": "./dist/index.mjs"
8
- },
5
+ "types": "./dist/index.d.mts",
9
6
  "exports": {
10
7
  ".": {
11
8
  "import": "./dist/index.mjs",
12
9
  "types": "./dist/index.d.mts"
13
10
  }
14
11
  },
15
- "types": "./dist/index.d.mts",
16
- "files": [
17
- "dist",
18
- "README.md"
19
- ],
20
12
  "engines": {
21
13
  "node": ">=24"
22
14
  },
23
15
  "dependencies": {
24
- "@schilderlabs/pitown-core": "0.2.1",
25
- "@schilderlabs/pitown-package": "0.2.1"
16
+ "@schilderlabs/pitown-core": "0.2.7",
17
+ "@schilderlabs/pitown-package": "0.2.7"
26
18
  },
27
19
  "devDependencies": {
28
20
  "eslint": "9.39.3",
@@ -31,25 +23,29 @@
31
23
  "typescript": "5.9.2",
32
24
  "vitest": "4.0.18"
33
25
  },
26
+ "bin": {
27
+ "pitown": "./dist/index.mjs"
28
+ },
29
+ "bugs": "https://github.com/RobSchilderr/pitown/issues",
30
+ "description": "Globally installable CLI for Pi Town",
31
+ "files": [
32
+ "dist",
33
+ "README.md"
34
+ ],
35
+ "homepage": "https://github.com/RobSchilderr/pitown#readme",
34
36
  "keywords": [
35
- "pitown",
36
- "pi-town",
37
+ "automation",
37
38
  "cli",
38
39
  "orchestration",
39
- "automation"
40
+ "pi-package",
41
+ "pi-town",
42
+ "pitown"
40
43
  ],
41
- "repository": {
42
- "type": "git",
43
- "url": "git+https://github.com/RobSchilderr/pitown.git"
44
- },
45
- "homepage": "https://github.com/RobSchilderr/pitown#readme",
46
- "bugs": {
47
- "url": "https://github.com/RobSchilderr/pitown/issues"
48
- },
44
+ "license": "MIT",
49
45
  "publishConfig": {
50
46
  "access": "public"
51
47
  },
52
- "license": "MIT",
48
+ "repository": "RobSchilderr/pitown.git",
53
49
  "scripts": {
54
50
  "build": "tsdown",
55
51
  "lint": "eslint .",
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-CUpe9o0x.mjs","names":[],"sources":["../../core/src/repo.ts","../src/paths.ts","../src/config.ts"],"sourcesContent":["import { createHash } from \"node:crypto\"\nimport { existsSync } from \"node:fs\"\nimport { basename, resolve } from \"node:path\"\nimport { assertSuccess, runCommandSync } from \"./shell.js\"\n\nfunction gitResult(cwd: string, args: string[]) {\n\treturn runCommandSync(\"git\", args, { cwd })\n}\n\nfunction sanitize(value: string): string {\n\treturn value.replace(/[^a-zA-Z0-9._-]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"repo\"\n}\n\nexport function isGitRepo(cwd: string): boolean {\n\tconst result = gitResult(cwd, [\"rev-parse\", \"--is-inside-work-tree\"])\n\treturn result.exitCode === 0 && result.stdout.trim() === \"true\"\n}\n\nexport function getRepoRoot(cwd: string): string {\n\tif (!isGitRepo(cwd)) return resolve(cwd)\n\tconst result = gitResult(cwd, [\"rev-parse\", \"--show-toplevel\"])\n\tassertSuccess(result, \"git rev-parse --show-toplevel\")\n\treturn resolve(result.stdout.trim())\n}\n\nexport function getCurrentBranch(cwd: string): string | null {\n\tif (!isGitRepo(cwd)) return null\n\tconst result = gitResult(cwd, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"])\n\tif (result.exitCode !== 0) return null\n\tconst branch = result.stdout.trim()\n\treturn branch || null\n}\n\nexport function getRepoIdentity(cwd: string): string {\n\tif (!isGitRepo(cwd)) return resolve(cwd)\n\n\tconst remote = gitResult(cwd, [\"config\", \"--get\", \"remote.origin.url\"])\n\tconst remoteValue = remote.stdout.trim()\n\tif (remote.exitCode === 0 && remoteValue) return remoteValue\n\n\tconst root = gitResult(cwd, [\"rev-parse\", \"--show-toplevel\"])\n\tassertSuccess(root, \"git rev-parse --show-toplevel\")\n\tconst commonDir = gitResult(cwd, [\"rev-parse\", \"--git-common-dir\"])\n\tassertSuccess(commonDir, \"git rev-parse --git-common-dir\")\n\n\tconst rootPath = resolve(root.stdout.trim())\n\tconst commonDirPath = commonDir.stdout.trim()\n\treturn `${basename(rootPath)}:${rootPath}:${existsSync(commonDirPath) ? resolve(commonDirPath) : commonDirPath}`\n}\n\nexport function createRepoSlug(repoId: string, repoRoot: string): string {\n\tconst name = sanitize(basename(repoRoot))\n\tconst digest = createHash(\"sha1\").update(repoId).digest(\"hex\").slice(0, 8)\n\treturn `${name}-${digest}`\n}\n","import { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nexport function getTownHomeDir(): string {\n\treturn join(homedir(), \".pi-town\")\n}\n\nexport function getUserConfigPath(): string {\n\treturn join(getTownHomeDir(), \"config.json\")\n}\n\nexport function getPlansRootDir(): string {\n\treturn join(getTownHomeDir(), \"plans\")\n}\n\nexport function getReposRootDir(): string {\n\treturn join(getTownHomeDir(), \"repos\")\n}\n\nexport function getRepoArtifactsDir(repoSlug: string): string {\n\treturn join(getReposRootDir(), repoSlug)\n}\n\nexport function getRepoAgentsDir(repoSlug: string): string {\n\treturn join(getRepoArtifactsDir(repoSlug), \"agents\")\n}\n\nexport function getLatestRunPointerPath(): string {\n\treturn join(getTownHomeDir(), \"latest-run.json\")\n}\n\nexport function getRepoLatestRunPointerPath(repoSlug: string): string {\n\treturn join(getRepoArtifactsDir(repoSlug), \"latest-run.json\")\n}\n\nexport function getRecommendedPlanDir(repoSlug: string): string {\n\treturn join(getPlansRootDir(), repoSlug)\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, isAbsolute, resolve } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { getUserConfigPath } from \"./paths.js\"\n\nconst DEFAULT_GOAL = \"continue from current scaffold state\"\n\nexport interface CliFlags {\n\trepo?: string\n\tplan?: string\n\tgoal?: string\n\thelp: boolean\n}\n\nexport interface OptionalRepoFlagResult {\n\trepo?: string\n\trest: string[]\n}\n\ninterface UserConfig {\n\trepo?: string\n\tplan?: string\n\tgoal?: string\n}\n\nexport interface ResolvedRunConfig {\n\trepo: string\n\tplan: string | null\n\tgoal: string\n\tconfigPath: string\n}\n\nfunction expandHome(value: string): string {\n\tif (value === \"~\") return homedir()\n\tif (value.startsWith(\"~/\")) return resolve(homedir(), value.slice(2))\n\treturn value\n}\n\nfunction resolvePathValue(value: string | undefined, baseDir: string): string | undefined {\n\tif (!value) return undefined\n\tconst expanded = expandHome(value)\n\treturn isAbsolute(expanded) ? resolve(expanded) : resolve(baseDir, expanded)\n}\n\nexport function parseCliFlags(argv: string[]): CliFlags {\n\tconst flags: CliFlags = { help: false }\n\n\tfor (let index = 0; index < argv.length; index += 1) {\n\t\tconst arg = argv[index]\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tflags.help = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg.startsWith(\"--repo=\")) {\n\t\t\tflags.repo = arg.slice(\"--repo=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--repo\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --repo\")\n\t\t\tflags.repo = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg.startsWith(\"--plan=\")) {\n\t\t\tflags.plan = arg.slice(\"--plan=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--plan\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --plan\")\n\t\t\tflags.plan = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg.startsWith(\"--goal=\")) {\n\t\t\tflags.goal = arg.slice(\"--goal=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--goal\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --goal\")\n\t\t\tflags.goal = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tthrow new Error(`Unknown argument: ${arg}`)\n\t}\n\n\treturn flags\n}\n\nexport function parseOptionalRepoFlag(argv: string[]): OptionalRepoFlagResult {\n\tconst rest: string[] = []\n\tlet repo: string | undefined\n\n\tfor (let index = 0; index < argv.length; index += 1) {\n\t\tconst arg = argv[index]\n\n\t\tif (arg.startsWith(\"--repo=\")) {\n\t\t\trepo = arg.slice(\"--repo=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--repo\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --repo\")\n\t\t\trepo = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\trest.push(arg)\n\t}\n\n\treturn { repo, rest }\n}\n\nexport function loadUserConfig(): UserConfig {\n\tconst configPath = getUserConfigPath()\n\tif (!existsSync(configPath)) return {}\n\treturn JSON.parse(readFileSync(configPath, \"utf-8\")) as UserConfig\n}\n\nexport function resolveRunConfig(argv: string[]): ResolvedRunConfig {\n\tconst flags = parseCliFlags(argv)\n\tconst configPath = getUserConfigPath()\n\tconst userConfig = loadUserConfig()\n\tconst configDir = dirname(configPath)\n\n\tconst repo =\n\t\tresolvePathValue(flags.repo, process.cwd()) ??\n\t\tresolvePathValue(userConfig.repo, configDir) ??\n\t\tresolve(process.cwd())\n\tconst plan = resolvePathValue(flags.plan, process.cwd()) ?? resolvePathValue(userConfig.plan, configDir) ?? null\n\tconst goal = flags.goal ?? userConfig.goal ?? DEFAULT_GOAL\n\n\treturn {\n\t\trepo,\n\t\tplan,\n\t\tgoal,\n\t\tconfigPath,\n\t}\n}\n"],"mappings":";;;;;;;AAKA,SAAS,UAAU,KAAa,MAAgB;AAC/C,QAAO,eAAe,OAAO,MAAM,EAAE,KAAK,CAAC;;AAG5C,SAAS,SAAS,OAAuB;AACxC,QAAO,MAAM,QAAQ,qBAAqB,IAAI,CAAC,QAAQ,YAAY,GAAG,IAAI;;AAG3E,SAAgB,UAAU,KAAsB;CAC/C,MAAM,SAAS,UAAU,KAAK,CAAC,aAAa,wBAAwB,CAAC;AACrE,QAAO,OAAO,aAAa,KAAK,OAAO,OAAO,MAAM,KAAK;;AAG1D,SAAgB,YAAY,KAAqB;AAChD,KAAI,CAAC,UAAU,IAAI,CAAE,QAAO,QAAQ,IAAI;CACxC,MAAM,SAAS,UAAU,KAAK,CAAC,aAAa,kBAAkB,CAAC;AAC/D,eAAc,QAAQ,gCAAgC;AACtD,QAAO,QAAQ,OAAO,OAAO,MAAM,CAAC;;AAGrC,SAAgB,iBAAiB,KAA4B;AAC5D,KAAI,CAAC,UAAU,IAAI,CAAE,QAAO;CAC5B,MAAM,SAAS,UAAU,KAAK;EAAC;EAAa;EAAgB;EAAO,CAAC;AACpE,KAAI,OAAO,aAAa,EAAG,QAAO;AAElC,QADe,OAAO,OAAO,MAAM,IAClB;;AAGlB,SAAgB,gBAAgB,KAAqB;AACpD,KAAI,CAAC,UAAU,IAAI,CAAE,QAAO,QAAQ,IAAI;CAExC,MAAM,SAAS,UAAU,KAAK;EAAC;EAAU;EAAS;EAAoB,CAAC;CACvE,MAAM,cAAc,OAAO,OAAO,MAAM;AACxC,KAAI,OAAO,aAAa,KAAK,YAAa,QAAO;CAEjD,MAAM,OAAO,UAAU,KAAK,CAAC,aAAa,kBAAkB,CAAC;AAC7D,eAAc,MAAM,gCAAgC;CACpD,MAAM,YAAY,UAAU,KAAK,CAAC,aAAa,mBAAmB,CAAC;AACnE,eAAc,WAAW,iCAAiC;CAE1D,MAAM,WAAW,QAAQ,KAAK,OAAO,MAAM,CAAC;CAC5C,MAAM,gBAAgB,UAAU,OAAO,MAAM;AAC7C,QAAO,GAAG,SAAS,SAAS,CAAC,GAAG,SAAS,GAAG,WAAW,cAAc,GAAG,QAAQ,cAAc,GAAG;;AAGlG,SAAgB,eAAe,QAAgB,UAA0B;AAGxE,QAAO,GAFM,SAAS,SAAS,SAAS,CAAC,CAE1B,GADA,WAAW,OAAO,CAAC,OAAO,OAAO,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,EAAE;;;;;ACjD3E,SAAgB,iBAAyB;AACxC,QAAO,KAAK,SAAS,EAAE,WAAW;;AAGnC,SAAgB,oBAA4B;AAC3C,QAAO,KAAK,gBAAgB,EAAE,cAAc;;AAG7C,SAAgB,kBAA0B;AACzC,QAAO,KAAK,gBAAgB,EAAE,QAAQ;;AAGvC,SAAgB,kBAA0B;AACzC,QAAO,KAAK,gBAAgB,EAAE,QAAQ;;AAGvC,SAAgB,oBAAoB,UAA0B;AAC7D,QAAO,KAAK,iBAAiB,EAAE,SAAS;;AAGzC,SAAgB,iBAAiB,UAA0B;AAC1D,QAAO,KAAK,oBAAoB,SAAS,EAAE,SAAS;;AAGrD,SAAgB,0BAAkC;AACjD,QAAO,KAAK,gBAAgB,EAAE,kBAAkB;;AAGjD,SAAgB,4BAA4B,UAA0B;AACrE,QAAO,KAAK,oBAAoB,SAAS,EAAE,kBAAkB;;AAG9D,SAAgB,sBAAsB,UAA0B;AAC/D,QAAO,KAAK,iBAAiB,EAAE,SAAS;;;;;AC/BzC,MAAM,eAAe;AA2BrB,SAAS,WAAW,OAAuB;AAC1C,KAAI,UAAU,IAAK,QAAO,SAAS;AACnC,KAAI,MAAM,WAAW,KAAK,CAAE,QAAO,QAAQ,SAAS,EAAE,MAAM,MAAM,EAAE,CAAC;AACrE,QAAO;;AAGR,SAAS,iBAAiB,OAA2B,SAAqC;AACzF,KAAI,CAAC,MAAO,QAAO;CACnB,MAAM,WAAW,WAAW,MAAM;AAClC,QAAO,WAAW,SAAS,GAAG,QAAQ,SAAS,GAAG,QAAQ,SAAS,SAAS;;AAG7E,SAAgB,cAAc,MAA0B;CACvD,MAAM,QAAkB,EAAE,MAAM,OAAO;AAEvC,MAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACpD,MAAM,MAAM,KAAK;AAEjB,MAAI,QAAQ,YAAY,QAAQ,MAAM;AACrC,SAAM,OAAO;AACb;;AAGD,MAAI,IAAI,WAAW,UAAU,EAAE;AAC9B,SAAM,OAAO,IAAI,MAAM,EAAiB;AACxC;;AAGD,MAAI,QAAQ,UAAU;GACrB,MAAM,QAAQ,KAAK,QAAQ;AAC3B,OAAI,CAAC,MAAO,OAAM,IAAI,MAAM,2BAA2B;AACvD,SAAM,OAAO;AACb,YAAS;AACT;;AAGD,MAAI,IAAI,WAAW,UAAU,EAAE;AAC9B,SAAM,OAAO,IAAI,MAAM,EAAiB;AACxC;;AAGD,MAAI,QAAQ,UAAU;GACrB,MAAM,QAAQ,KAAK,QAAQ;AAC3B,OAAI,CAAC,MAAO,OAAM,IAAI,MAAM,2BAA2B;AACvD,SAAM,OAAO;AACb,YAAS;AACT;;AAGD,MAAI,IAAI,WAAW,UAAU,EAAE;AAC9B,SAAM,OAAO,IAAI,MAAM,EAAiB;AACxC;;AAGD,MAAI,QAAQ,UAAU;GACrB,MAAM,QAAQ,KAAK,QAAQ;AAC3B,OAAI,CAAC,MAAO,OAAM,IAAI,MAAM,2BAA2B;AACvD,SAAM,OAAO;AACb,YAAS;AACT;;AAGD,QAAM,IAAI,MAAM,qBAAqB,MAAM;;AAG5C,QAAO;;AAGR,SAAgB,sBAAsB,MAAwC;CAC7E,MAAM,OAAiB,EAAE;CACzB,IAAI;AAEJ,MAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACpD,MAAM,MAAM,KAAK;AAEjB,MAAI,IAAI,WAAW,UAAU,EAAE;AAC9B,UAAO,IAAI,MAAM,EAAiB;AAClC;;AAGD,MAAI,QAAQ,UAAU;GACrB,MAAM,QAAQ,KAAK,QAAQ;AAC3B,OAAI,CAAC,MAAO,OAAM,IAAI,MAAM,2BAA2B;AACvD,UAAO;AACP,YAAS;AACT;;AAGD,OAAK,KAAK,IAAI;;AAGf,QAAO;EAAE;EAAM;EAAM;;AAGtB,SAAgB,iBAA6B;CAC5C,MAAM,aAAa,mBAAmB;AACtC,KAAI,CAAC,WAAW,WAAW,CAAE,QAAO,EAAE;AACtC,QAAO,KAAK,MAAM,aAAa,YAAY,QAAQ,CAAC;;AAGrD,SAAgB,iBAAiB,MAAmC;CACnE,MAAM,QAAQ,cAAc,KAAK;CACjC,MAAM,aAAa,mBAAmB;CACtC,MAAM,aAAa,gBAAgB;CACnC,MAAM,YAAY,QAAQ,WAAW;AASrC,QAAO;EACN,MAPA,iBAAiB,MAAM,MAAM,QAAQ,KAAK,CAAC,IAC3C,iBAAiB,WAAW,MAAM,UAAU,IAC5C,QAAQ,QAAQ,KAAK,CAAC;EAMtB,MALY,iBAAiB,MAAM,MAAM,QAAQ,KAAK,CAAC,IAAI,iBAAiB,WAAW,MAAM,UAAU,IAAI;EAM3G,MALY,MAAM,QAAQ,WAAW,QAAQ;EAM7C;EACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"controller-9ihAZj3V.mjs","names":["writeJson"],"sources":["../../core/src/events.ts","../../core/src/agents.ts","../../core/src/lease.ts","../../core/src/metrics.ts","../../core/src/controller.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname } from \"node:path\"\n\nexport function appendJsonl(filePath: string, value: unknown) {\n\tmkdirSync(dirname(filePath), { recursive: true })\n\twriteFileSync(filePath, `${JSON.stringify(value)}\\n`, { encoding: \"utf-8\", flag: \"a\" })\n}\n\nexport function readJsonl<T>(filePath: string): T[] {\n\ttry {\n\t\tconst raw = readFileSync(filePath, \"utf-8\")\n\t\treturn raw\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((line) => JSON.parse(line) as T)\n\t} catch {\n\t\treturn []\n\t}\n}\n","import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { appendJsonl, readJsonl } from \"./events.js\"\nimport type {\n\tAgentMailbox,\n\tAgentMessageRecord,\n\tAgentSessionRecord,\n\tAgentStateSnapshot,\n\tAgentStatus,\n} from \"./types.js\"\n\nfunction writeJson(path: string, value: unknown) {\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nfunction ensureMailbox(path: string) {\n\tmkdirSync(dirname(path), { recursive: true })\n\tif (!existsSync(path)) writeFileSync(path, \"\", \"utf-8\")\n}\n\nexport function getAgentsDir(artifactsDir: string): string {\n\treturn join(artifactsDir, \"agents\")\n}\n\nexport function getAgentDir(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentsDir(artifactsDir), agentId)\n}\n\nexport function getAgentStatePath(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentDir(artifactsDir, agentId), \"state.json\")\n}\n\nexport function getAgentSessionPath(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentDir(artifactsDir, agentId), \"session.json\")\n}\n\nexport function getAgentMailboxPath(artifactsDir: string, agentId: string, box: AgentMailbox): string {\n\treturn join(getAgentDir(artifactsDir, agentId), `${box}.jsonl`)\n}\n\nexport function getAgentSessionsDir(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentDir(artifactsDir, agentId), \"sessions\")\n}\n\nexport function getSessionIdFromPath(sessionPath: string | null | undefined): string | null {\n\tif (!sessionPath) return null\n\tconst match = /_([0-9a-f-]+)\\.jsonl$/i.exec(sessionPath)\n\treturn match?.[1] ?? null\n}\n\nexport function createAgentSessionRecord(\n\tinput?: Partial<Pick<AgentSessionRecord, \"sessionDir\" | \"sessionId\" | \"sessionPath\" | \"lastAttachedAt\">>,\n): AgentSessionRecord {\n\treturn {\n\t\truntime: \"pi\",\n\t\tpersisted: true,\n\t\tsessionDir: input?.sessionDir ?? null,\n\t\tsessionId: input?.sessionId ?? null,\n\t\tsessionPath: input?.sessionPath ?? null,\n\t\tlastAttachedAt: input?.lastAttachedAt ?? null,\n\t}\n}\n\nexport function createAgentState(input: {\n\tagentId: string\n\trole: string\n\tstatus: AgentStatus\n\ttaskId?: string | null\n\ttask?: string | null\n\tbranch?: string | null\n\tlastMessage?: string | null\n\twaitingOn?: string | null\n\tblocked?: boolean\n\trunId?: string | null\n\tsession?: AgentSessionRecord\n}): AgentStateSnapshot {\n\treturn {\n\t\tagentId: input.agentId,\n\t\trole: input.role,\n\t\tstatus: input.status,\n\t\ttaskId: input.taskId ?? null,\n\t\ttask: input.task ?? null,\n\t\tbranch: input.branch ?? null,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tlastMessage: input.lastMessage ?? null,\n\t\twaitingOn: input.waitingOn ?? null,\n\t\tblocked: input.blocked ?? false,\n\t\trunId: input.runId ?? null,\n\t\tsession: input.session ?? createAgentSessionRecord(),\n\t}\n}\n\nexport function writeAgentState(artifactsDir: string, state: AgentStateSnapshot) {\n\tmkdirSync(getAgentDir(artifactsDir, state.agentId), { recursive: true })\n\tensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, \"inbox\"))\n\tensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, \"outbox\"))\n\twriteJson(getAgentStatePath(artifactsDir, state.agentId), state)\n\twriteJson(getAgentSessionPath(artifactsDir, state.agentId), state.session)\n}\n\nexport function readAgentState(artifactsDir: string, agentId: string): AgentStateSnapshot | null {\n\tconst statePath = getAgentStatePath(artifactsDir, agentId)\n\ttry {\n\t\treturn JSON.parse(readFileSync(statePath, \"utf-8\")) as AgentStateSnapshot\n\t} catch {\n\t\treturn null\n\t}\n}\n\nexport function listAgentStates(artifactsDir: string): AgentStateSnapshot[] {\n\tconst agentsDir = getAgentsDir(artifactsDir)\n\tlet entries: string[]\n\ttry {\n\t\tentries = readdirSync(agentsDir)\n\t} catch {\n\t\treturn []\n\t}\n\n\treturn entries\n\t\t.map((entry) => readAgentState(artifactsDir, entry))\n\t\t.filter((state): state is AgentStateSnapshot => state !== null)\n\t\t.sort((left, right) => left.agentId.localeCompare(right.agentId))\n}\n\nexport function appendAgentMessage(input: {\n\tartifactsDir: string\n\tagentId: string\n\tbox: AgentMailbox\n\tfrom: string\n\tbody: string\n}): AgentMessageRecord {\n\tconst record: AgentMessageRecord = {\n\t\tbox: input.box,\n\t\tfrom: input.from,\n\t\tbody: input.body,\n\t\tcreatedAt: new Date().toISOString(),\n\t}\n\tappendJsonl(getAgentMailboxPath(input.artifactsDir, input.agentId, input.box), record)\n\treturn record\n}\n\nexport function readAgentMessages(artifactsDir: string, agentId: string, box: AgentMailbox): AgentMessageRecord[] {\n\treturn readJsonl<AgentMessageRecord>(getAgentMailboxPath(artifactsDir, agentId, box))\n}\n\nexport function getLatestAgentSession(artifactsDir: string, agentId: string): AgentSessionRecord {\n\tconst sessionDir = getAgentSessionsDir(artifactsDir, agentId)\n\tlet entries: string[]\n\ttry {\n\t\tentries = readdirSync(sessionDir)\n\t} catch {\n\t\treturn createAgentSessionRecord({ sessionDir })\n\t}\n\n\tconst latestSessionPath =\n\t\tentries\n\t\t\t.filter((entry) => entry.endsWith(\".jsonl\"))\n\t\t\t.sort()\n\t\t\t.at(-1) ?? null\n\n\tif (latestSessionPath === null) return createAgentSessionRecord({ sessionDir })\n\n\tconst sessionPath = join(sessionDir, latestSessionPath)\n\treturn createAgentSessionRecord({\n\t\tsessionDir,\n\t\tsessionPath,\n\t\tsessionId: getSessionIdFromPath(sessionPath),\n\t})\n}\n","import { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\"\nimport { homedir, hostname } from \"node:os\"\nimport { join } from \"node:path\"\n\ninterface LeaseData {\n\trunId: string\n\trepoId: string\n\tbranch: string\n\tpid: number\n\thostname: string\n\tstartedAt: string\n}\n\nfunction sanitize(value: string): string {\n\treturn value.replace(/[^a-zA-Z0-9._-]+/g, \"_\")\n}\n\nfunction processAlive(pid: number): boolean {\n\tif (!Number.isFinite(pid) || pid <= 0) return false\n\ttry {\n\t\tprocess.kill(pid, 0)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport function acquireRepoLease(runId: string, repoId: string, branch: string): { path: string; release: () => void } {\n\tconst locksDir = join(homedir(), \".pi-town\", \"locks\")\n\tmkdirSync(locksDir, { recursive: true })\n\n\tconst leasePath = join(locksDir, `pi-town-${sanitize(repoId)}-${sanitize(branch)}.json`)\n\tconst nextData: LeaseData = {\n\t\trunId,\n\t\trepoId,\n\t\tbranch,\n\t\tpid: process.pid,\n\t\thostname: hostname(),\n\t\tstartedAt: new Date().toISOString(),\n\t}\n\n\ttry {\n\t\tconst current = JSON.parse(readFileSync(leasePath, \"utf-8\")) as LeaseData\n\t\tif (processAlive(current.pid)) {\n\t\t\tthrow new Error(`Pi Town lease already held by pid ${current.pid} on ${current.hostname} for run ${current.runId}.`)\n\t\t}\n\t\trmSync(leasePath, { force: true })\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n\t\t\tif (error instanceof Error && error.message.startsWith(\"Pi Town lease already held\")) throw error\n\t\t}\n\t}\n\n\twriteFileSync(leasePath, `${JSON.stringify(nextData, null, 2)}\\n`, \"utf-8\")\n\n\treturn {\n\t\tpath: leasePath,\n\t\trelease: () => {\n\t\t\ttry {\n\t\t\t\tconst current = JSON.parse(readFileSync(leasePath, \"utf-8\")) as LeaseData\n\t\t\t\tif (current.runId === runId) rmSync(leasePath, { force: true })\n\t\t\t} catch {\n\t\t\t\t// ignore cleanup failures\n\t\t\t}\n\t\t},\n\t}\n}\n","import type { FeedbackCycle, InterruptRecord, MetricsSnapshot, TaskAttempt } from \"./types.js\"\n\nfunction round(value: number): number {\n\treturn Math.round(value * 1000) / 1000\n}\n\nfunction diffHours(start: string, end: string): number {\n\treturn (Date.parse(end) - Date.parse(start)) / 3_600_000\n}\n\nfunction average(values: number[]): number | null {\n\tif (values.length === 0) return null\n\treturn values.reduce((sum, value) => sum + value, 0) / values.length\n}\n\nexport function computeInterruptRate(interrupts: InterruptRecord[], taskAttempts: TaskAttempt[]): number {\n\tif (taskAttempts.length === 0) return 0\n\treturn round(interrupts.length / taskAttempts.length)\n}\n\nexport function computeAutonomousCompletionRate(taskAttempts: TaskAttempt[]): number {\n\tconst completed = taskAttempts.filter((task) => task.status === \"completed\")\n\tif (completed.length === 0) return 0\n\tconst autonomous = completed.filter((task) => !task.interrupted)\n\treturn round(autonomous.length / completed.length)\n}\n\nexport function computeContextCoverageScore(interrupts: InterruptRecord[]): number {\n\tconst observed = new Set(interrupts.map((interrupt) => interrupt.category))\n\tif (observed.size === 0) return 0\n\n\tconst covered = new Set(\n\t\tinterrupts.filter((interrupt) => interrupt.fixType).map((interrupt) => interrupt.category),\n\t)\n\n\treturn round(covered.size / observed.size)\n}\n\nexport function computeMeanTimeToCorrect(interrupts: InterruptRecord[]): number | null {\n\tconst resolved = interrupts.filter((interrupt) => interrupt.resolvedAt)\n\tconst hours = resolved.map((interrupt) => diffHours(interrupt.createdAt, interrupt.resolvedAt!))\n\tconst value = average(hours)\n\treturn value === null ? null : round(value)\n}\n\nexport function computeFeedbackToDemoCycleTime(feedbackCycles: FeedbackCycle[]): number | null {\n\tconst hours = feedbackCycles.map((cycle) => diffHours(cycle.feedbackAt, cycle.demoReadyAt))\n\tconst value = average(hours)\n\treturn value === null ? null : round(value)\n}\n\nexport function computeMetrics(input: {\n\ttaskAttempts: TaskAttempt[]\n\tinterrupts: InterruptRecord[]\n\tfeedbackCycles?: FeedbackCycle[]\n}): MetricsSnapshot {\n\tconst observedCategories = new Set(input.interrupts.map((interrupt) => interrupt.category))\n\tconst coveredCategories = new Set(\n\t\tinput.interrupts.filter((interrupt) => interrupt.fixType).map((interrupt) => interrupt.category),\n\t)\n\tconst completedTasks = input.taskAttempts.filter((task) => task.status === \"completed\").length\n\n\treturn {\n\t\tinterruptRate: computeInterruptRate(input.interrupts, input.taskAttempts),\n\t\tautonomousCompletionRate: computeAutonomousCompletionRate(input.taskAttempts),\n\t\tcontextCoverageScore: computeContextCoverageScore(input.interrupts),\n\t\tmeanTimeToCorrectHours: computeMeanTimeToCorrect(input.interrupts),\n\t\tfeedbackToDemoCycleTimeHours: computeFeedbackToDemoCycleTime(input.feedbackCycles ?? []),\n\t\ttotals: {\n\t\t\ttaskAttempts: input.taskAttempts.length,\n\t\t\tcompletedTasks,\n\t\t\tinterrupts: input.interrupts.length,\n\t\t\tobservedInterruptCategories: observedCategories.size,\n\t\t\tcoveredInterruptCategories: coveredCategories.size,\n\t\t},\n\t}\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport {\n\tcreateAgentSessionRecord,\n\tcreateAgentState,\n\tgetAgentSessionsDir,\n\tgetLatestAgentSession,\n\treadAgentState,\n\twriteAgentState,\n} from \"./agents.js\"\nimport { appendJsonl } from \"./events.js\"\nimport { acquireRepoLease } from \"./lease.js\"\nimport { computeMetrics } from \"./metrics.js\"\nimport { createPiAuthHelpMessage, detectPiAuthFailure } from \"./pi.js\"\nimport { createRepoSlug, getCurrentBranch, getRepoIdentity, getRepoRoot } from \"./repo.js\"\nimport { assertCommandAvailable, runCommandSync } from \"./shell.js\"\nimport type { ControllerRunResult, PiInvocationRecord, RunManifest, RunOptions, RunSummary } from \"./types.js\"\n\nfunction createRunId(): string {\n\treturn `run-${new Date().toISOString().replace(/[:.]/g, \"-\")}`\n}\n\nfunction createPiInvocationArgs(input: {\n\tsessionDir?: string | null\n\tsessionPath?: string | null\n\tprompt: string\n\tappendedSystemPrompt?: string | null\n\textensionPath?: string | null\n}) {\n\tconst args: string[] = []\n\n\tif (input.extensionPath) args.push(\"--extension\", input.extensionPath)\n\tif (input.appendedSystemPrompt) args.push(\"--append-system-prompt\", input.appendedSystemPrompt)\n\tif (input.sessionPath) args.push(\"--session\", input.sessionPath)\n\telse if (input.sessionDir) args.push(\"--session-dir\", input.sessionDir)\n\telse throw new Error(\"Pi invocation requires a session path or session directory\")\n\targs.push(\"-p\", input.prompt)\n\n\treturn args\n}\n\nfunction writeJson(path: string, value: unknown) {\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nfunction writeText(path: string, value: string) {\n\twriteFileSync(path, value, \"utf-8\")\n}\n\nfunction createPiPrompt(input: {\n\trepoRoot: string\n\tplanPath: string | null\n\tgoal: string | null\n\trecommendedPlanDir: string | null\n}): string {\n\tconst goal = input.goal ?? \"continue from current scaffold state\"\n\n\tif (input.planPath) {\n\t\treturn [\n\t\t\t\"You are the Pi Town leader agent for this repository.\",\n\t\t\t\"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.\",\n\t\t\t\"\",\n\t\t\t\"Read the private plans in:\",\n\t\t\t`- ${input.planPath}`,\n\t\t\t\"\",\n\t\t\t\"and the current code in:\",\n\t\t\t`- ${input.repoRoot}`,\n\t\t\t\"\",\n\t\t\t`Goal: ${goal}`,\n\t\t\t\"Continue from the current scaffold state.\",\n\t\t\t\"Keep any persisted run artifacts high-signal and avoid copying private plan contents into them.\",\n\t\t].join(\"\\n\")\n\t}\n\n\treturn [\n\t\t\"You are the Pi Town leader agent for this repository.\",\n\t\t\"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.\",\n\t\t\"\",\n\t\t`Work in the repository at: ${input.repoRoot}`,\n\t\t`Goal: ${goal}`,\n\t\t\"No private plan path is configured for this run.\",\n\t\tinput.recommendedPlanDir\n\t\t\t? `If you need private plans, use a user-owned location such as: ${input.recommendedPlanDir}`\n\t\t\t: \"If you need private plans, keep them in a user-owned location outside the repo.\",\n\t\t\"Continue from the current scaffold state.\",\n\t].join(\"\\n\")\n}\n\nfunction assertPiRuntimeAvailable(piCommand: string) {\n\ttry {\n\t\tassertCommandAvailable(piCommand)\n\t} catch (error) {\n\t\tif (piCommand === \"pi\") {\n\t\t\tthrow new Error(\n\t\t\t\t[\n\t\t\t\t\t\"Pi Town requires the `pi` CLI to run `pitown run`.\",\n\t\t\t\t\t\"Install Pi: npm install -g @mariozechner/pi-coding-agent\",\n\t\t\t\t\t\"Then authenticate Pi and verify it works: pi -p \\\"hello\\\"\",\n\t\t\t\t\t`Details: ${(error as Error).message}`,\n\t\t\t\t].join(\"\\n\"),\n\t\t\t)\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t[\n\t\t\t\t`Pi Town could not execute the configured Pi command: ${piCommand}`,\n\t\t\t\t\"Make sure the command exists on PATH or points to an executable file.\",\n\t\t\t\t`Details: ${(error as Error).message}`,\n\t\t\t].join(\"\\n\"),\n\t\t)\n\t}\n}\n\nfunction createManifest(input: {\n\trunId: string\n\trepoId: string\n\trepoSlug: string\n\trepoRoot: string\n\tbranch: string\n\tgoal: string | null\n\tplanPath: string | null\n\trecommendedPlanDir: string | null\n\tmode: \"single-pi\"\n\tleasePath: string\n}): RunManifest {\n\treturn {\n\t\trunId: input.runId,\n\t\trepoId: input.repoId,\n\t\trepoSlug: input.repoSlug,\n\t\trepoRoot: input.repoRoot,\n\t\tbranch: input.branch,\n\t\tgoal: input.goal,\n\t\tplanPath: input.planPath,\n\t\trecommendedPlanDir: input.recommendedPlanDir,\n\t\tmode: input.mode,\n\t\tstartedAt: new Date().toISOString(),\n\t\tendedAt: null,\n\t\tstopReason: null,\n\t\tleasePath: input.leasePath,\n\t\tpiExitCode: null,\n\t\tcompletedTaskCount: 0,\n\t\tblockedTaskCount: 0,\n\t\tskippedTaskCount: 0,\n\t\ttotalCostUsd: 0,\n\t}\n}\n\nfunction createSummary(input: {\n\trunId: string\n\tmode: \"single-pi\"\n\texitCode: number\n\tstdout: string\n\tstderr: string\n\trecommendedPlanDir: string | null\n}): RunSummary {\n\tconst success = input.exitCode === 0\n\tconst recommendation =\n\t\tinput.recommendedPlanDir === null\n\t\t\t? \"\"\n\t\t\t: ` No plan path was configured. Recommended private plans location: ${input.recommendedPlanDir}.`\n\tconst authHelp =\n\t\tsuccess || !detectPiAuthFailure(input.stderr, input.stdout) ? \"\" : ` ${createPiAuthHelpMessage()}`\n\n\treturn {\n\t\trunId: input.runId,\n\t\tmode: input.mode,\n\t\tcreatedAt: new Date().toISOString(),\n\t\tsuccess,\n\t\tmessage: success\n\t\t\t? `Pi invocation completed.${recommendation}`\n\t\t\t: `Pi invocation failed.${authHelp}${recommendation}`,\n\t\tpiExitCode: input.exitCode,\n\t\trecommendedPlanDir: input.recommendedPlanDir,\n\t}\n}\n\nexport function runController(options: RunOptions): ControllerRunResult {\n\tconst cwd = options.cwd ?? process.cwd()\n\tconst artifactsDir = options.artifactsDir\n\tconst repoRoot = getRepoRoot(cwd)\n\tconst repoId = getRepoIdentity(repoRoot)\n\tconst repoSlug = createRepoSlug(repoId, repoRoot)\n\tconst branch = options.branch ?? getCurrentBranch(repoRoot) ?? \"workspace\"\n\tconst goal = options.goal ?? null\n\tconst planPath = options.planPath ?? null\n\tconst recommendedPlanDir = planPath ? null : (options.recommendedPlanDir ?? null)\n\tconst mode = options.mode ?? \"single-pi\"\n\tconst piCommand = options.piCommand ?? \"pi\"\n\tconst runId = createRunId()\n\tconst runDir = join(artifactsDir, \"runs\", runId)\n\tconst latestDir = join(artifactsDir, \"latest\")\n\tconst stdoutPath = join(runDir, \"stdout.txt\")\n\tconst stderrPath = join(runDir, \"stderr.txt\")\n\tconst prompt = createPiPrompt({ repoRoot, planPath, goal, recommendedPlanDir })\n\tconst existingLeaderState = readAgentState(artifactsDir, \"leader\")\n\tconst existingLeaderSession =\n\t\texistingLeaderState?.session.sessionPath || existingLeaderState?.session.sessionDir\n\t\t\t? existingLeaderState.session\n\t\t\t: getLatestAgentSession(artifactsDir, \"leader\")\n\tconst leaderSessionDir = existingLeaderSession.sessionDir ?? getAgentSessionsDir(artifactsDir, \"leader\")\n\tconst leaderState = createAgentState({\n\t\tagentId: \"leader\",\n\t\trole: \"leader\",\n\t\tstatus: \"starting\",\n\t\ttask: goal,\n\t\tbranch,\n\t\tlastMessage: goal ? `Starting leader run for goal: ${goal}` : \"Starting leader run\",\n\t\trunId,\n\t\tsession: createAgentSessionRecord({\n\t\t\tsessionDir: leaderSessionDir,\n\t\t\tsessionId: existingLeaderSession.sessionId,\n\t\t\tsessionPath: existingLeaderSession.sessionPath,\n\t\t}),\n\t})\n\n\tassertPiRuntimeAvailable(piCommand)\n\n\tmkdirSync(runDir, { recursive: true })\n\tmkdirSync(latestDir, { recursive: true })\n\n\twriteText(join(runDir, \"questions.jsonl\"), \"\")\n\twriteText(join(runDir, \"interventions.jsonl\"), \"\")\n\twriteJson(join(runDir, \"agent-state.json\"), {\n\t\tstatus: \"starting\",\n\t\tupdatedAt: new Date().toISOString(),\n\t})\n\twriteAgentState(artifactsDir, leaderState)\n\n\tconst lease = acquireRepoLease(runId, repoId, branch)\n\n\ttry {\n\t\tconst manifest = createManifest({\n\t\t\trunId,\n\t\t\trepoId,\n\t\t\trepoSlug,\n\t\t\trepoRoot,\n\t\t\tbranch,\n\t\t\tgoal,\n\t\t\tplanPath,\n\t\t\trecommendedPlanDir,\n\t\t\tmode,\n\t\t\tleasePath: lease.path,\n\t\t})\n\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"run_started\",\n\t\t\trunId,\n\t\t\trepoId,\n\t\t\trepoSlug,\n\t\t\tbranch,\n\t\t\tcreatedAt: manifest.startedAt,\n\t\t})\n\n\t\tconst piStartedAt = new Date().toISOString()\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"pi_invocation_started\",\n\t\t\trunId,\n\t\t\tcommand: piCommand,\n\t\t\tcreatedAt: piStartedAt,\n\t\t})\n\n\t\twriteAgentState(\n\t\t\tartifactsDir,\n\t\t\tcreateAgentState({\n\t\t\t\t...leaderState,\n\t\t\t\tstatus: \"running\",\n\t\t\t\tlastMessage: goal ? `Leader working on: ${goal}` : \"Leader working\",\n\t\t\t}),\n\t\t)\n\n\t\tconst piArgs = createPiInvocationArgs({\n\t\t\tsessionDir: leaderState.session.sessionPath === null ? leaderSessionDir : null,\n\t\t\tsessionPath: leaderState.session.sessionPath,\n\t\t\tprompt,\n\t\t\tappendedSystemPrompt: options.appendedSystemPrompt,\n\t\t\textensionPath: options.extensionPath,\n\t\t})\n\t\tconst piResult = runCommandSync(piCommand, piArgs, {\n\t\t\tcwd: repoRoot,\n\t\t\tenv: process.env,\n\t\t})\n\t\tconst piEndedAt = new Date().toISOString()\n\t\tconst latestLeaderSession = getLatestAgentSession(artifactsDir, \"leader\")\n\n\t\twriteText(stdoutPath, piResult.stdout)\n\t\twriteText(stderrPath, piResult.stderr)\n\n\t\tconst piInvocation: PiInvocationRecord = {\n\t\t\tcommand: piCommand,\n\t\t\tcwd: repoRoot,\n\t\t\trepoRoot,\n\t\t\tplanPath,\n\t\t\tgoal,\n\t\t\tsessionDir: latestLeaderSession.sessionDir,\n\t\t\tsessionId: latestLeaderSession.sessionId,\n\t\t\tsessionPath: latestLeaderSession.sessionPath,\n\t\t\tstartedAt: piStartedAt,\n\t\t\tendedAt: piEndedAt,\n\t\t\texitCode: piResult.exitCode,\n\t\t\tstdoutPath,\n\t\t\tstderrPath,\n\t\t\tpromptSummary: planPath\n\t\t\t\t? \"Read private plan path and continue from current scaffold state.\"\n\t\t\t\t: \"Continue from current scaffold state without a configured private plan path.\",\n\t\t}\n\t\twriteJson(join(runDir, \"pi-invocation.json\"), piInvocation)\n\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"pi_invocation_finished\",\n\t\t\trunId,\n\t\t\tcommand: piCommand,\n\t\t\texitCode: piInvocation.exitCode,\n\t\t\tcreatedAt: piEndedAt,\n\t\t})\n\n\t\tconst metrics = computeMetrics({\n\t\t\ttaskAttempts: [],\n\t\t\tinterrupts: [],\n\t\t})\n\t\tconst summary = createSummary({\n\t\t\trunId,\n\t\t\tmode,\n\t\t\texitCode: piInvocation.exitCode,\n\t\t\tstdout: piResult.stdout,\n\t\t\tstderr: piResult.stderr,\n\t\t\trecommendedPlanDir,\n\t\t})\n\t\tconst finalManifest: RunManifest = {\n\t\t\t...manifest,\n\t\t\tendedAt: piEndedAt,\n\t\t\tstopReason:\n\t\t\t\tpiInvocation.exitCode === 0\n\t\t\t\t\t? \"pi invocation completed\"\n\t\t\t\t\t: `pi invocation exited with code ${piInvocation.exitCode}`,\n\t\t\tpiExitCode: piInvocation.exitCode,\n\t\t}\n\n\t\twriteJson(join(runDir, \"manifest.json\"), finalManifest)\n\t\twriteJson(join(runDir, \"metrics.json\"), metrics)\n\t\twriteJson(join(runDir, \"run-summary.json\"), summary)\n\t\twriteJson(join(runDir, \"agent-state.json\"), {\n\t\t\tstatus: summary.success ? \"completed\" : \"failed\",\n\t\t\tupdatedAt: piEndedAt,\n\t\t\texitCode: piInvocation.exitCode,\n\t\t})\n\t\twriteJson(join(latestDir, \"manifest.json\"), finalManifest)\n\t\twriteJson(join(latestDir, \"metrics.json\"), metrics)\n\t\twriteJson(join(latestDir, \"run-summary.json\"), summary)\n\t\twriteAgentState(\n\t\t\tartifactsDir,\n\t\t\tcreateAgentState({\n\t\t\t\t...leaderState,\n\t\t\t\t\tstatus: piInvocation.exitCode === 0 ? \"idle\" : \"blocked\",\n\t\t\t\t\tlastMessage:\n\t\t\t\t\t\tpiInvocation.exitCode === 0\n\t\t\t\t\t\t\t? \"Leader run completed and is ready for the next instruction\"\n\t\t\t\t\t\t\t: `Leader run stopped with exit code ${piInvocation.exitCode}`,\n\t\t\t\t\tblocked: piInvocation.exitCode !== 0,\n\t\t\t\t\twaitingOn: piInvocation.exitCode === 0 ? null : \"human-or-follow-up-run\",\n\t\t\t\t\tsession: createAgentSessionRecord({\n\t\t\t\t\t\tsessionDir: latestLeaderSession.sessionDir,\n\t\t\t\t\t\tsessionId: latestLeaderSession.sessionId,\n\t\t\t\t\t\tsessionPath: latestLeaderSession.sessionPath,\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t)\n\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"run_finished\",\n\t\t\trunId,\n\t\t\tcreatedAt: finalManifest.endedAt,\n\t\t\tstopReason: finalManifest.stopReason,\n\t\t\tmetrics,\n\t\t})\n\n\t\treturn {\n\t\t\trunId,\n\t\t\trunDir,\n\t\t\tlatestDir,\n\t\t\tmanifest: finalManifest,\n\t\t\tmetrics,\n\t\t\tsummary,\n\t\t\tpiInvocation,\n\t\t}\n\t} finally {\n\t\tlease.release()\n\t}\n}\n"],"mappings":";;;;;;;;AAGA,SAAgB,YAAY,UAAkB,OAAgB;AAC7D,WAAU,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,eAAc,UAAU,GAAG,KAAK,UAAU,MAAM,CAAC,KAAK;EAAE,UAAU;EAAS,MAAM;EAAK,CAAC;;AAGxF,SAAgB,UAAa,UAAuB;AACnD,KAAI;AAEH,SADY,aAAa,UAAU,QAAQ,CAEzC,MAAM,QAAQ,CACd,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,CACf,KAAK,SAAS,KAAK,MAAM,KAAK,CAAM;SAC/B;AACP,SAAO,EAAE;;;;;;ACNX,SAASA,YAAU,MAAc,OAAgB;AAChD,WAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,eAAc,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAAK,QAAQ;;AAGpE,SAAS,cAAc,MAAc;AACpC,WAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,KAAI,CAAC,WAAW,KAAK,CAAE,eAAc,MAAM,IAAI,QAAQ;;AAGxD,SAAgB,aAAa,cAA8B;AAC1D,QAAO,KAAK,cAAc,SAAS;;AAGpC,SAAgB,YAAY,cAAsB,SAAyB;AAC1E,QAAO,KAAK,aAAa,aAAa,EAAE,QAAQ;;AAGjD,SAAgB,kBAAkB,cAAsB,SAAyB;AAChF,QAAO,KAAK,YAAY,cAAc,QAAQ,EAAE,aAAa;;AAG9D,SAAgB,oBAAoB,cAAsB,SAAyB;AAClF,QAAO,KAAK,YAAY,cAAc,QAAQ,EAAE,eAAe;;AAGhE,SAAgB,oBAAoB,cAAsB,SAAiB,KAA2B;AACrG,QAAO,KAAK,YAAY,cAAc,QAAQ,EAAE,GAAG,IAAI,QAAQ;;AAGhE,SAAgB,oBAAoB,cAAsB,SAAyB;AAClF,QAAO,KAAK,YAAY,cAAc,QAAQ,EAAE,WAAW;;AAG5D,SAAgB,qBAAqB,aAAuD;AAC3F,KAAI,CAAC,YAAa,QAAO;AAEzB,QADc,yBAAyB,KAAK,YAAY,GACzC,MAAM;;AAGtB,SAAgB,yBACf,OACqB;AACrB,QAAO;EACN,SAAS;EACT,WAAW;EACX,YAAY,OAAO,cAAc;EACjC,WAAW,OAAO,aAAa;EAC/B,aAAa,OAAO,eAAe;EACnC,gBAAgB,OAAO,kBAAkB;EACzC;;AAGF,SAAgB,iBAAiB,OAYV;AACtB,QAAO;EACN,SAAS,MAAM;EACf,MAAM,MAAM;EACZ,QAAQ,MAAM;EACd,QAAQ,MAAM,UAAU;EACxB,MAAM,MAAM,QAAQ;EACpB,QAAQ,MAAM,UAAU;EACxB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,aAAa,MAAM,eAAe;EAClC,WAAW,MAAM,aAAa;EAC9B,SAAS,MAAM,WAAW;EAC1B,OAAO,MAAM,SAAS;EACtB,SAAS,MAAM,WAAW,0BAA0B;EACpD;;AAGF,SAAgB,gBAAgB,cAAsB,OAA2B;AAChF,WAAU,YAAY,cAAc,MAAM,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;AACxE,eAAc,oBAAoB,cAAc,MAAM,SAAS,QAAQ,CAAC;AACxE,eAAc,oBAAoB,cAAc,MAAM,SAAS,SAAS,CAAC;AACzE,aAAU,kBAAkB,cAAc,MAAM,QAAQ,EAAE,MAAM;AAChE,aAAU,oBAAoB,cAAc,MAAM,QAAQ,EAAE,MAAM,QAAQ;;AAG3E,SAAgB,eAAe,cAAsB,SAA4C;CAChG,MAAM,YAAY,kBAAkB,cAAc,QAAQ;AAC1D,KAAI;AACH,SAAO,KAAK,MAAM,aAAa,WAAW,QAAQ,CAAC;SAC5C;AACP,SAAO;;;AAIT,SAAgB,gBAAgB,cAA4C;CAC3E,MAAM,YAAY,aAAa,aAAa;CAC5C,IAAI;AACJ,KAAI;AACH,YAAU,YAAY,UAAU;SACzB;AACP,SAAO,EAAE;;AAGV,QAAO,QACL,KAAK,UAAU,eAAe,cAAc,MAAM,CAAC,CACnD,QAAQ,UAAuC,UAAU,KAAK,CAC9D,MAAM,MAAM,UAAU,KAAK,QAAQ,cAAc,MAAM,QAAQ,CAAC;;AAGnE,SAAgB,mBAAmB,OAMZ;CACtB,MAAM,SAA6B;EAClC,KAAK,MAAM;EACX,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;AACD,aAAY,oBAAoB,MAAM,cAAc,MAAM,SAAS,MAAM,IAAI,EAAE,OAAO;AACtF,QAAO;;AAGR,SAAgB,kBAAkB,cAAsB,SAAiB,KAAyC;AACjH,QAAO,UAA8B,oBAAoB,cAAc,SAAS,IAAI,CAAC;;AAGtF,SAAgB,sBAAsB,cAAsB,SAAqC;CAChG,MAAM,aAAa,oBAAoB,cAAc,QAAQ;CAC7D,IAAI;AACJ,KAAI;AACH,YAAU,YAAY,WAAW;SAC1B;AACP,SAAO,yBAAyB,EAAE,YAAY,CAAC;;CAGhD,MAAM,oBACL,QACE,QAAQ,UAAU,MAAM,SAAS,SAAS,CAAC,CAC3C,MAAM,CACN,GAAG,GAAG,IAAI;AAEb,KAAI,sBAAsB,KAAM,QAAO,yBAAyB,EAAE,YAAY,CAAC;CAE/E,MAAM,cAAc,KAAK,YAAY,kBAAkB;AACvD,QAAO,yBAAyB;EAC/B;EACA;EACA,WAAW,qBAAqB,YAAY;EAC5C,CAAC;;;;;AC3JH,SAAS,SAAS,OAAuB;AACxC,QAAO,MAAM,QAAQ,qBAAqB,IAAI;;AAG/C,SAAS,aAAa,KAAsB;AAC3C,KAAI,CAAC,OAAO,SAAS,IAAI,IAAI,OAAO,EAAG,QAAO;AAC9C,KAAI;AACH,UAAQ,KAAK,KAAK,EAAE;AACpB,SAAO;SACA;AACP,SAAO;;;AAIT,SAAgB,iBAAiB,OAAe,QAAgB,QAAuD;CACtH,MAAM,WAAW,KAAK,SAAS,EAAE,YAAY,QAAQ;AACrD,WAAU,UAAU,EAAE,WAAW,MAAM,CAAC;CAExC,MAAM,YAAY,KAAK,UAAU,WAAW,SAAS,OAAO,CAAC,GAAG,SAAS,OAAO,CAAC,OAAO;CACxF,MAAM,WAAsB;EAC3B;EACA;EACA;EACA,KAAK,QAAQ;EACb,UAAU,UAAU;EACpB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;AAED,KAAI;EACH,MAAM,UAAU,KAAK,MAAM,aAAa,WAAW,QAAQ,CAAC;AAC5D,MAAI,aAAa,QAAQ,IAAI,CAC5B,OAAM,IAAI,MAAM,qCAAqC,QAAQ,IAAI,MAAM,QAAQ,SAAS,WAAW,QAAQ,MAAM,GAAG;AAErH,SAAO,WAAW,EAAE,OAAO,MAAM,CAAC;UAC1B,OAAO;AACf,MAAK,MAAgC,SAAS,UAC7C;OAAI,iBAAiB,SAAS,MAAM,QAAQ,WAAW,6BAA6B,CAAE,OAAM;;;AAI9F,eAAc,WAAW,GAAG,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC,KAAK,QAAQ;AAE3E,QAAO;EACN,MAAM;EACN,eAAe;AACd,OAAI;AAEH,QADgB,KAAK,MAAM,aAAa,WAAW,QAAQ,CAAC,CAChD,UAAU,MAAO,QAAO,WAAW,EAAE,OAAO,MAAM,CAAC;WACxD;;EAIT;;;;;AC/DF,SAAS,MAAM,OAAuB;AACrC,QAAO,KAAK,MAAM,QAAQ,IAAK,GAAG;;AAGnC,SAAS,UAAU,OAAe,KAAqB;AACtD,SAAQ,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM,MAAM,IAAI;;AAGhD,SAAS,QAAQ,QAAiC;AACjD,KAAI,OAAO,WAAW,EAAG,QAAO;AAChC,QAAO,OAAO,QAAQ,KAAK,UAAU,MAAM,OAAO,EAAE,GAAG,OAAO;;AAG/D,SAAgB,qBAAqB,YAA+B,cAAqC;AACxG,KAAI,aAAa,WAAW,EAAG,QAAO;AACtC,QAAO,MAAM,WAAW,SAAS,aAAa,OAAO;;AAGtD,SAAgB,gCAAgC,cAAqC;CACpF,MAAM,YAAY,aAAa,QAAQ,SAAS,KAAK,WAAW,YAAY;AAC5E,KAAI,UAAU,WAAW,EAAG,QAAO;AAEnC,QAAO,MADY,UAAU,QAAQ,SAAS,CAAC,KAAK,YAAY,CACxC,SAAS,UAAU,OAAO;;AAGnD,SAAgB,4BAA4B,YAAuC;CAClF,MAAM,WAAW,IAAI,IAAI,WAAW,KAAK,cAAc,UAAU,SAAS,CAAC;AAC3E,KAAI,SAAS,SAAS,EAAG,QAAO;AAMhC,QAAO,MAJS,IAAI,IACnB,WAAW,QAAQ,cAAc,UAAU,QAAQ,CAAC,KAAK,cAAc,UAAU,SAAS,CAC1F,CAEoB,OAAO,SAAS,KAAK;;AAG3C,SAAgB,yBAAyB,YAA8C;CAGtF,MAAM,QAAQ,QAFG,WAAW,QAAQ,cAAc,UAAU,WAAW,CAChD,KAAK,cAAc,UAAU,UAAU,WAAW,UAAU,WAAY,CAAC,CACpE;AAC5B,QAAO,UAAU,OAAO,OAAO,MAAM,MAAM;;AAG5C,SAAgB,+BAA+B,gBAAgD;CAE9F,MAAM,QAAQ,QADA,eAAe,KAAK,UAAU,UAAU,MAAM,YAAY,MAAM,YAAY,CAAC,CAC/D;AAC5B,QAAO,UAAU,OAAO,OAAO,MAAM,MAAM;;AAG5C,SAAgB,eAAe,OAIX;CACnB,MAAM,qBAAqB,IAAI,IAAI,MAAM,WAAW,KAAK,cAAc,UAAU,SAAS,CAAC;CAC3F,MAAM,oBAAoB,IAAI,IAC7B,MAAM,WAAW,QAAQ,cAAc,UAAU,QAAQ,CAAC,KAAK,cAAc,UAAU,SAAS,CAChG;CACD,MAAM,iBAAiB,MAAM,aAAa,QAAQ,SAAS,KAAK,WAAW,YAAY,CAAC;AAExF,QAAO;EACN,eAAe,qBAAqB,MAAM,YAAY,MAAM,aAAa;EACzE,0BAA0B,gCAAgC,MAAM,aAAa;EAC7E,sBAAsB,4BAA4B,MAAM,WAAW;EACnE,wBAAwB,yBAAyB,MAAM,WAAW;EAClE,8BAA8B,+BAA+B,MAAM,kBAAkB,EAAE,CAAC;EACxF,QAAQ;GACP,cAAc,MAAM,aAAa;GACjC;GACA,YAAY,MAAM,WAAW;GAC7B,6BAA6B,mBAAmB;GAChD,4BAA4B,kBAAkB;GAC9C;EACD;;;;;ACzDF,SAAS,cAAsB;AAC9B,QAAO,wBAAO,IAAI,MAAM,EAAC,aAAa,CAAC,QAAQ,SAAS,IAAI;;AAG7D,SAAS,uBAAuB,OAM7B;CACF,MAAM,OAAiB,EAAE;AAEzB,KAAI,MAAM,cAAe,MAAK,KAAK,eAAe,MAAM,cAAc;AACtE,KAAI,MAAM,qBAAsB,MAAK,KAAK,0BAA0B,MAAM,qBAAqB;AAC/F,KAAI,MAAM,YAAa,MAAK,KAAK,aAAa,MAAM,YAAY;UACvD,MAAM,WAAY,MAAK,KAAK,iBAAiB,MAAM,WAAW;KAClE,OAAM,IAAI,MAAM,6DAA6D;AAClF,MAAK,KAAK,MAAM,MAAM,OAAO;AAE7B,QAAO;;AAGR,SAAS,UAAU,MAAc,OAAgB;AAChD,eAAc,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAAK,QAAQ;;AAGpE,SAAS,UAAU,MAAc,OAAe;AAC/C,eAAc,MAAM,OAAO,QAAQ;;AAGpC,SAAS,eAAe,OAKb;CACV,MAAM,OAAO,MAAM,QAAQ;AAE3B,KAAI,MAAM,SACT,QAAO;EACN;EACA;EACA;EACA;EACA,KAAK,MAAM;EACX;EACA;EACA,KAAK,MAAM;EACX;EACA,SAAS;EACT;EACA;EACA,CAAC,KAAK,KAAK;AAGb,QAAO;EACN;EACA;EACA;EACA,8BAA8B,MAAM;EACpC,SAAS;EACT;EACA,MAAM,qBACH,iEAAiE,MAAM,uBACvE;EACH;EACA,CAAC,KAAK,KAAK;;AAGb,SAAS,yBAAyB,WAAmB;AACpD,KAAI;AACH,yBAAuB,UAAU;UACzB,OAAO;AACf,MAAI,cAAc,KACjB,OAAM,IAAI,MACT;GACC;GACA;GACA;GACA,YAAa,MAAgB;GAC7B,CAAC,KAAK,KAAK,CACZ;AAGF,QAAM,IAAI,MACT;GACC,wDAAwD;GACxD;GACA,YAAa,MAAgB;GAC7B,CAAC,KAAK,KAAK,CACZ;;;AAIH,SAAS,eAAe,OAWR;AACf,QAAO;EACN,OAAO,MAAM;EACb,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,UAAU,MAAM;EAChB,QAAQ,MAAM;EACd,MAAM,MAAM;EACZ,UAAU,MAAM;EAChB,oBAAoB,MAAM;EAC1B,MAAM,MAAM;EACZ,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,SAAS;EACT,YAAY;EACZ,WAAW,MAAM;EACjB,YAAY;EACZ,oBAAoB;EACpB,kBAAkB;EAClB,kBAAkB;EAClB,cAAc;EACd;;AAGF,SAAS,cAAc,OAOR;CACd,MAAM,UAAU,MAAM,aAAa;CACnC,MAAM,iBACL,MAAM,uBAAuB,OAC1B,KACA,qEAAqE,MAAM,mBAAmB;CAClG,MAAM,WACL,WAAW,CAAC,oBAAoB,MAAM,QAAQ,MAAM,OAAO,GAAG,KAAK,IAAI,yBAAyB;AAEjG,QAAO;EACN,OAAO,MAAM;EACb,MAAM,MAAM;EACZ,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;EACA,SAAS,UACN,2BAA2B,mBAC3B,wBAAwB,WAAW;EACtC,YAAY,MAAM;EAClB,oBAAoB,MAAM;EAC1B;;AAGF,SAAgB,cAAc,SAA0C;CACvE,MAAM,MAAM,QAAQ,OAAO,QAAQ,KAAK;CACxC,MAAM,eAAe,QAAQ;CAC7B,MAAM,WAAW,YAAY,IAAI;CACjC,MAAM,SAAS,gBAAgB,SAAS;CACxC,MAAM,WAAW,eAAe,QAAQ,SAAS;CACjD,MAAM,SAAS,QAAQ,UAAU,iBAAiB,SAAS,IAAI;CAC/D,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,qBAAqB,WAAW,OAAQ,QAAQ,sBAAsB;CAC5E,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,QAAQ,aAAa;CAC3B,MAAM,SAAS,KAAK,cAAc,QAAQ,MAAM;CAChD,MAAM,YAAY,KAAK,cAAc,SAAS;CAC9C,MAAM,aAAa,KAAK,QAAQ,aAAa;CAC7C,MAAM,aAAa,KAAK,QAAQ,aAAa;CAC7C,MAAM,SAAS,eAAe;EAAE;EAAU;EAAU;EAAM;EAAoB,CAAC;CAC/E,MAAM,sBAAsB,eAAe,cAAc,SAAS;CAClE,MAAM,wBACL,qBAAqB,QAAQ,eAAe,qBAAqB,QAAQ,aACtE,oBAAoB,UACpB,sBAAsB,cAAc,SAAS;CACjD,MAAM,mBAAmB,sBAAsB,cAAc,oBAAoB,cAAc,SAAS;CACxG,MAAM,cAAc,iBAAiB;EACpC,SAAS;EACT,MAAM;EACN,QAAQ;EACR,MAAM;EACN;EACA,aAAa,OAAO,iCAAiC,SAAS;EAC9D;EACA,SAAS,yBAAyB;GACjC,YAAY;GACZ,WAAW,sBAAsB;GACjC,aAAa,sBAAsB;GACnC,CAAC;EACF,CAAC;AAEF,0BAAyB,UAAU;AAEnC,WAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;AACtC,WAAU,WAAW,EAAE,WAAW,MAAM,CAAC;AAEzC,WAAU,KAAK,QAAQ,kBAAkB,EAAE,GAAG;AAC9C,WAAU,KAAK,QAAQ,sBAAsB,EAAE,GAAG;AAClD,WAAU,KAAK,QAAQ,mBAAmB,EAAE;EAC3C,QAAQ;EACR,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,CAAC;AACF,iBAAgB,cAAc,YAAY;CAE1C,MAAM,QAAQ,iBAAiB,OAAO,QAAQ,OAAO;AAErD,KAAI;EACH,MAAM,WAAW,eAAe;GAC/B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,WAAW,MAAM;GACjB,CAAC;AAEF,cAAY,KAAK,QAAQ,eAAe,EAAE;GACzC,MAAM;GACN;GACA;GACA;GACA;GACA,WAAW,SAAS;GACpB,CAAC;EAEF,MAAM,+BAAc,IAAI,MAAM,EAAC,aAAa;AAC5C,cAAY,KAAK,QAAQ,eAAe,EAAE;GACzC,MAAM;GACN;GACA,SAAS;GACT,WAAW;GACX,CAAC;AAEF,kBACC,cACA,iBAAiB;GAChB,GAAG;GACH,QAAQ;GACR,aAAa,OAAO,sBAAsB,SAAS;GACnD,CAAC,CACF;EASD,MAAM,WAAW,eAAe,WAPjB,uBAAuB;GACrC,YAAY,YAAY,QAAQ,gBAAgB,OAAO,mBAAmB;GAC1E,aAAa,YAAY,QAAQ;GACjC;GACA,sBAAsB,QAAQ;GAC9B,eAAe,QAAQ;GACvB,CAAC,EACiD;GAClD,KAAK;GACL,KAAK,QAAQ;GACb,CAAC;EACF,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa;EAC1C,MAAM,sBAAsB,sBAAsB,cAAc,SAAS;AAEzE,YAAU,YAAY,SAAS,OAAO;AACtC,YAAU,YAAY,SAAS,OAAO;EAEtC,MAAM,eAAmC;GACxC,SAAS;GACT,KAAK;GACL;GACA;GACA;GACA,YAAY,oBAAoB;GAChC,WAAW,oBAAoB;GAC/B,aAAa,oBAAoB;GACjC,WAAW;GACX,SAAS;GACT,UAAU,SAAS;GACnB;GACA;GACA,eAAe,WACZ,qEACA;GACH;AACD,YAAU,KAAK,QAAQ,qBAAqB,EAAE,aAAa;AAE3D,cAAY,KAAK,QAAQ,eAAe,EAAE;GACzC,MAAM;GACN;GACA,SAAS;GACT,UAAU,aAAa;GACvB,WAAW;GACX,CAAC;EAEF,MAAM,UAAU,eAAe;GAC9B,cAAc,EAAE;GAChB,YAAY,EAAE;GACd,CAAC;EACF,MAAM,UAAU,cAAc;GAC7B;GACA;GACA,UAAU,aAAa;GACvB,QAAQ,SAAS;GACjB,QAAQ,SAAS;GACjB;GACA,CAAC;EACF,MAAM,gBAA6B;GAClC,GAAG;GACH,SAAS;GACT,YACC,aAAa,aAAa,IACvB,4BACA,kCAAkC,aAAa;GACnD,YAAY,aAAa;GACzB;AAED,YAAU,KAAK,QAAQ,gBAAgB,EAAE,cAAc;AACvD,YAAU,KAAK,QAAQ,eAAe,EAAE,QAAQ;AAChD,YAAU,KAAK,QAAQ,mBAAmB,EAAE,QAAQ;AACpD,YAAU,KAAK,QAAQ,mBAAmB,EAAE;GAC3C,QAAQ,QAAQ,UAAU,cAAc;GACxC,WAAW;GACX,UAAU,aAAa;GACvB,CAAC;AACF,YAAU,KAAK,WAAW,gBAAgB,EAAE,cAAc;AAC1D,YAAU,KAAK,WAAW,eAAe,EAAE,QAAQ;AACnD,YAAU,KAAK,WAAW,mBAAmB,EAAE,QAAQ;AACvD,kBACC,cACA,iBAAiB;GAChB,GAAG;GACF,QAAQ,aAAa,aAAa,IAAI,SAAS;GAC/C,aACC,aAAa,aAAa,IACvB,+DACA,qCAAqC,aAAa;GACtD,SAAS,aAAa,aAAa;GACnC,WAAW,aAAa,aAAa,IAAI,OAAO;GAChD,SAAS,yBAAyB;IACjC,YAAY,oBAAoB;IAChC,WAAW,oBAAoB;IAC/B,aAAa,oBAAoB;IACjC,CAAC;GACF,CAAC,CACF;AAEF,cAAY,KAAK,QAAQ,eAAe,EAAE;GACzC,MAAM;GACN;GACA,WAAW,cAAc;GACzB,YAAY,cAAc;GAC1B;GACA,CAAC;AAEF,SAAO;GACN;GACA;GACA;GACA,UAAU;GACV;GACA;GACA;GACA;WACQ;AACT,QAAM,SAAS"}