@schilderlabs/pitown 0.1.2 → 0.2.1
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.
- package/README.md +19 -0
- package/dist/{config-Bw-mNdF5.mjs → config-CUpe9o0x.mjs} +30 -50
- package/dist/config-CUpe9o0x.mjs.map +1 -0
- package/dist/{controller-D7lezZjg.mjs → controller-9ihAZj3V.mjs} +193 -10
- package/dist/controller-9ihAZj3V.mjs.map +1 -0
- package/dist/doctor.d.mts +8 -0
- package/dist/doctor.mjs +42 -0
- package/dist/doctor.mjs.map +1 -0
- package/dist/entrypoint-CyJDLudQ.mjs +61 -0
- package/dist/entrypoint-CyJDLudQ.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1235 -6
- package/dist/index.mjs.map +1 -1
- package/dist/pi-C0fURZj7.mjs +12 -0
- package/dist/pi-C0fURZj7.mjs.map +1 -0
- package/dist/run.d.mts +3 -0
- package/dist/run.mjs +8 -3
- package/dist/run.mjs.map +1 -1
- package/dist/status.mjs +2 -1
- package/dist/status.mjs.map +1 -1
- package/dist/watch.mjs +2 -1
- package/dist/watch.mjs.map +1 -1
- package/package.json +7 -5
- package/dist/config-Bw-mNdF5.mjs.map +0 -1
- package/dist/controller-D7lezZjg.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ npm install -g @mariozechner/pi-coding-agent
|
|
|
19
19
|
Verify Pi first:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
pitown doctor
|
|
22
23
|
pi -p "hello"
|
|
23
24
|
```
|
|
24
25
|
|
|
@@ -26,10 +27,28 @@ pi -p "hello"
|
|
|
26
27
|
|
|
27
28
|
```bash
|
|
28
29
|
pitown --help
|
|
30
|
+
pitown
|
|
31
|
+
pitown mayor
|
|
32
|
+
pitown mayor "plan the next milestones"
|
|
29
33
|
pitown run --repo /path/to/repo --plan /path/to/private/plans --goal "continue from current scaffold state"
|
|
30
34
|
pitown status
|
|
31
35
|
```
|
|
32
36
|
|
|
37
|
+
If you are already inside a repo, `pitown` and `pitown mayor` use the current working repo by default.
|
|
38
|
+
|
|
39
|
+
The main workflow is:
|
|
40
|
+
|
|
41
|
+
1. `cd` into a repo
|
|
42
|
+
2. run `pitown` or `pitown mayor`
|
|
43
|
+
3. use `/plan` inside the mayor session when you want a read-only plan first
|
|
44
|
+
4. use `pitown board`, `pitown peek mayor`, or `pitown msg mayor "..."` as needed
|
|
45
|
+
|
|
46
|
+
Inside the mayor session:
|
|
47
|
+
|
|
48
|
+
- `/plan` toggles read-only planning mode
|
|
49
|
+
- `/todos` shows the captured numbered plan
|
|
50
|
+
- leaving `/plan` returns the mayor to normal execution and delegation mode
|
|
51
|
+
|
|
33
52
|
## Runtime storage
|
|
34
53
|
|
|
35
54
|
By default, Pi Town stores local runtime state under `~/.pi-town` and keeps private plans outside the target repo.
|
|
@@ -1,55 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as runCommandSync, r as assertSuccess } from "./entrypoint-CyJDLudQ.mjs";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
3
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
|
-
import { spawnSync } from "node:child_process";
|
|
7
6
|
|
|
8
|
-
//#region src/entrypoint.ts
|
|
9
|
-
function normalizePath(path) {
|
|
10
|
-
if (!path) return null;
|
|
11
|
-
try {
|
|
12
|
-
return realpathSync(path);
|
|
13
|
-
} catch {
|
|
14
|
-
return resolve(path);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function isDirectExecution(fileUrl, argv1 = process.argv[1]) {
|
|
18
|
-
const modulePath = normalizePath(fileURLToPath(fileUrl));
|
|
19
|
-
const invokedPath = normalizePath(argv1);
|
|
20
|
-
return modulePath !== null && invokedPath !== null && modulePath === invokedPath;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region ../core/src/shell.ts
|
|
25
|
-
function runCommandSync(command, args, options) {
|
|
26
|
-
const result = spawnSync(command, args, {
|
|
27
|
-
cwd: options?.cwd,
|
|
28
|
-
env: options?.env,
|
|
29
|
-
encoding: "utf-8"
|
|
30
|
-
});
|
|
31
|
-
const errorText = result.error instanceof Error ? `${result.error.message}
|
|
32
|
-
` : "";
|
|
33
|
-
return {
|
|
34
|
-
stdout: result.stdout ?? "",
|
|
35
|
-
stderr: `${errorText}${result.stderr ?? ""}`,
|
|
36
|
-
exitCode: result.status ?? 1
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function assertCommandAvailable(command) {
|
|
40
|
-
const result = spawnSync(command, ["--help"], {
|
|
41
|
-
encoding: "utf-8",
|
|
42
|
-
stdio: "ignore"
|
|
43
|
-
});
|
|
44
|
-
if (result.error instanceof Error) throw new Error(result.error.message);
|
|
45
|
-
}
|
|
46
|
-
function assertSuccess(result, context) {
|
|
47
|
-
if (result.exitCode === 0) return;
|
|
48
|
-
const details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
|
|
49
|
-
throw new Error(`${context} failed${details ? `\n${details}` : ""}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
//#endregion
|
|
53
7
|
//#region ../core/src/repo.ts
|
|
54
8
|
function gitResult(cwd, args) {
|
|
55
9
|
return runCommandSync("git", args, { cwd });
|
|
@@ -115,6 +69,9 @@ function getReposRootDir() {
|
|
|
115
69
|
function getRepoArtifactsDir(repoSlug) {
|
|
116
70
|
return join(getReposRootDir(), repoSlug);
|
|
117
71
|
}
|
|
72
|
+
function getRepoAgentsDir(repoSlug) {
|
|
73
|
+
return join(getRepoArtifactsDir(repoSlug), "agents");
|
|
74
|
+
}
|
|
118
75
|
function getLatestRunPointerPath() {
|
|
119
76
|
return join(getTownHomeDir(), "latest-run.json");
|
|
120
77
|
}
|
|
@@ -183,6 +140,29 @@ function parseCliFlags(argv) {
|
|
|
183
140
|
}
|
|
184
141
|
return flags;
|
|
185
142
|
}
|
|
143
|
+
function parseOptionalRepoFlag(argv) {
|
|
144
|
+
const rest = [];
|
|
145
|
+
let repo;
|
|
146
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
147
|
+
const arg = argv[index];
|
|
148
|
+
if (arg.startsWith("--repo=")) {
|
|
149
|
+
repo = arg.slice(7);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (arg === "--repo") {
|
|
153
|
+
const value = argv[index + 1];
|
|
154
|
+
if (!value) throw new Error("Missing value for --repo");
|
|
155
|
+
repo = value;
|
|
156
|
+
index += 1;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
rest.push(arg);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
repo,
|
|
163
|
+
rest
|
|
164
|
+
};
|
|
165
|
+
}
|
|
186
166
|
function loadUserConfig() {
|
|
187
167
|
const configPath = getUserConfigPath();
|
|
188
168
|
if (!existsSync(configPath)) return {};
|
|
@@ -202,5 +182,5 @@ function resolveRunConfig(argv) {
|
|
|
202
182
|
}
|
|
203
183
|
|
|
204
184
|
//#endregion
|
|
205
|
-
export {
|
|
206
|
-
//# sourceMappingURL=config-
|
|
185
|
+
export { getRecommendedPlanDir as a, getRepoLatestRunPointerPath as c, createRepoSlug as d, getCurrentBranch as f, isGitRepo as h, getLatestRunPointerPath as i, getTownHomeDir as l, getRepoRoot as m, parseOptionalRepoFlag as n, getRepoAgentsDir as o, getRepoIdentity as p, resolveRunConfig as r, getRepoArtifactsDir as s, parseCliFlags as t, getUserConfigPath as u };
|
|
186
|
+
//# sourceMappingURL=config-CUpe9o0x.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { n as detectPiAuthFailure, t as createPiAuthHelpMessage } from "./pi-C0fURZj7.mjs";
|
|
2
|
+
import { a as runCommandSync, n as assertCommandAvailable } from "./entrypoint-CyJDLudQ.mjs";
|
|
3
|
+
import { d as createRepoSlug, f as getCurrentBranch, m as getRepoRoot, p as getRepoIdentity } from "./config-CUpe9o0x.mjs";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
5
|
import { dirname, join } from "node:path";
|
|
4
6
|
import { homedir, hostname } from "node:os";
|
|
5
7
|
|
|
@@ -11,6 +13,127 @@ function appendJsonl(filePath, value) {
|
|
|
11
13
|
flag: "a"
|
|
12
14
|
});
|
|
13
15
|
}
|
|
16
|
+
function readJsonl(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
return readFileSync(filePath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
19
|
+
} catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region ../core/src/agents.ts
|
|
26
|
+
function writeJson$1(path, value) {
|
|
27
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
28
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
29
|
+
}
|
|
30
|
+
function ensureMailbox(path) {
|
|
31
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
32
|
+
if (!existsSync(path)) writeFileSync(path, "", "utf-8");
|
|
33
|
+
}
|
|
34
|
+
function getAgentsDir(artifactsDir) {
|
|
35
|
+
return join(artifactsDir, "agents");
|
|
36
|
+
}
|
|
37
|
+
function getAgentDir(artifactsDir, agentId) {
|
|
38
|
+
return join(getAgentsDir(artifactsDir), agentId);
|
|
39
|
+
}
|
|
40
|
+
function getAgentStatePath(artifactsDir, agentId) {
|
|
41
|
+
return join(getAgentDir(artifactsDir, agentId), "state.json");
|
|
42
|
+
}
|
|
43
|
+
function getAgentSessionPath(artifactsDir, agentId) {
|
|
44
|
+
return join(getAgentDir(artifactsDir, agentId), "session.json");
|
|
45
|
+
}
|
|
46
|
+
function getAgentMailboxPath(artifactsDir, agentId, box) {
|
|
47
|
+
return join(getAgentDir(artifactsDir, agentId), `${box}.jsonl`);
|
|
48
|
+
}
|
|
49
|
+
function getAgentSessionsDir(artifactsDir, agentId) {
|
|
50
|
+
return join(getAgentDir(artifactsDir, agentId), "sessions");
|
|
51
|
+
}
|
|
52
|
+
function getSessionIdFromPath(sessionPath) {
|
|
53
|
+
if (!sessionPath) return null;
|
|
54
|
+
return /_([0-9a-f-]+)\.jsonl$/i.exec(sessionPath)?.[1] ?? null;
|
|
55
|
+
}
|
|
56
|
+
function createAgentSessionRecord(input) {
|
|
57
|
+
return {
|
|
58
|
+
runtime: "pi",
|
|
59
|
+
persisted: true,
|
|
60
|
+
sessionDir: input?.sessionDir ?? null,
|
|
61
|
+
sessionId: input?.sessionId ?? null,
|
|
62
|
+
sessionPath: input?.sessionPath ?? null,
|
|
63
|
+
lastAttachedAt: input?.lastAttachedAt ?? null
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function createAgentState(input) {
|
|
67
|
+
return {
|
|
68
|
+
agentId: input.agentId,
|
|
69
|
+
role: input.role,
|
|
70
|
+
status: input.status,
|
|
71
|
+
taskId: input.taskId ?? null,
|
|
72
|
+
task: input.task ?? null,
|
|
73
|
+
branch: input.branch ?? null,
|
|
74
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
75
|
+
lastMessage: input.lastMessage ?? null,
|
|
76
|
+
waitingOn: input.waitingOn ?? null,
|
|
77
|
+
blocked: input.blocked ?? false,
|
|
78
|
+
runId: input.runId ?? null,
|
|
79
|
+
session: input.session ?? createAgentSessionRecord()
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function writeAgentState(artifactsDir, state) {
|
|
83
|
+
mkdirSync(getAgentDir(artifactsDir, state.agentId), { recursive: true });
|
|
84
|
+
ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "inbox"));
|
|
85
|
+
ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "outbox"));
|
|
86
|
+
writeJson$1(getAgentStatePath(artifactsDir, state.agentId), state);
|
|
87
|
+
writeJson$1(getAgentSessionPath(artifactsDir, state.agentId), state.session);
|
|
88
|
+
}
|
|
89
|
+
function readAgentState(artifactsDir, agentId) {
|
|
90
|
+
const statePath = getAgentStatePath(artifactsDir, agentId);
|
|
91
|
+
try {
|
|
92
|
+
return JSON.parse(readFileSync(statePath, "utf-8"));
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function listAgentStates(artifactsDir) {
|
|
98
|
+
const agentsDir = getAgentsDir(artifactsDir);
|
|
99
|
+
let entries;
|
|
100
|
+
try {
|
|
101
|
+
entries = readdirSync(agentsDir);
|
|
102
|
+
} catch {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
return entries.map((entry) => readAgentState(artifactsDir, entry)).filter((state) => state !== null).sort((left, right) => left.agentId.localeCompare(right.agentId));
|
|
106
|
+
}
|
|
107
|
+
function appendAgentMessage(input) {
|
|
108
|
+
const record = {
|
|
109
|
+
box: input.box,
|
|
110
|
+
from: input.from,
|
|
111
|
+
body: input.body,
|
|
112
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
113
|
+
};
|
|
114
|
+
appendJsonl(getAgentMailboxPath(input.artifactsDir, input.agentId, input.box), record);
|
|
115
|
+
return record;
|
|
116
|
+
}
|
|
117
|
+
function readAgentMessages(artifactsDir, agentId, box) {
|
|
118
|
+
return readJsonl(getAgentMailboxPath(artifactsDir, agentId, box));
|
|
119
|
+
}
|
|
120
|
+
function getLatestAgentSession(artifactsDir, agentId) {
|
|
121
|
+
const sessionDir = getAgentSessionsDir(artifactsDir, agentId);
|
|
122
|
+
let entries;
|
|
123
|
+
try {
|
|
124
|
+
entries = readdirSync(sessionDir);
|
|
125
|
+
} catch {
|
|
126
|
+
return createAgentSessionRecord({ sessionDir });
|
|
127
|
+
}
|
|
128
|
+
const latestSessionPath = entries.filter((entry) => entry.endsWith(".jsonl")).sort().at(-1) ?? null;
|
|
129
|
+
if (latestSessionPath === null) return createAgentSessionRecord({ sessionDir });
|
|
130
|
+
const sessionPath = join(sessionDir, latestSessionPath);
|
|
131
|
+
return createAgentSessionRecord({
|
|
132
|
+
sessionDir,
|
|
133
|
+
sessionPath,
|
|
134
|
+
sessionId: getSessionIdFromPath(sessionPath)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
14
137
|
|
|
15
138
|
//#endregion
|
|
16
139
|
//#region ../core/src/lease.ts
|
|
@@ -117,6 +240,16 @@ function computeMetrics(input) {
|
|
|
117
240
|
function createRunId() {
|
|
118
241
|
return `run-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
119
242
|
}
|
|
243
|
+
function createPiInvocationArgs(input) {
|
|
244
|
+
const args = [];
|
|
245
|
+
if (input.extensionPath) args.push("--extension", input.extensionPath);
|
|
246
|
+
if (input.appendedSystemPrompt) args.push("--append-system-prompt", input.appendedSystemPrompt);
|
|
247
|
+
if (input.sessionPath) args.push("--session", input.sessionPath);
|
|
248
|
+
else if (input.sessionDir) args.push("--session-dir", input.sessionDir);
|
|
249
|
+
else throw new Error("Pi invocation requires a session path or session directory");
|
|
250
|
+
args.push("-p", input.prompt);
|
|
251
|
+
return args;
|
|
252
|
+
}
|
|
120
253
|
function writeJson(path, value) {
|
|
121
254
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
122
255
|
}
|
|
@@ -126,6 +259,9 @@ function writeText(path, value) {
|
|
|
126
259
|
function createPiPrompt(input) {
|
|
127
260
|
const goal = input.goal ?? "continue from current scaffold state";
|
|
128
261
|
if (input.planPath) return [
|
|
262
|
+
"You are the Pi Town leader agent for this repository.",
|
|
263
|
+
"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.",
|
|
264
|
+
"",
|
|
129
265
|
"Read the private plans in:",
|
|
130
266
|
`- ${input.planPath}`,
|
|
131
267
|
"",
|
|
@@ -137,6 +273,9 @@ function createPiPrompt(input) {
|
|
|
137
273
|
"Keep any persisted run artifacts high-signal and avoid copying private plan contents into them."
|
|
138
274
|
].join("\n");
|
|
139
275
|
return [
|
|
276
|
+
"You are the Pi Town leader agent for this repository.",
|
|
277
|
+
"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.",
|
|
278
|
+
"",
|
|
140
279
|
`Work in the repository at: ${input.repoRoot}`,
|
|
141
280
|
`Goal: ${goal}`,
|
|
142
281
|
"No private plan path is configured for this run.",
|
|
@@ -186,12 +325,13 @@ function createManifest(input) {
|
|
|
186
325
|
function createSummary(input) {
|
|
187
326
|
const success = input.exitCode === 0;
|
|
188
327
|
const recommendation = input.recommendedPlanDir === null ? "" : ` No plan path was configured. Recommended private plans location: ${input.recommendedPlanDir}.`;
|
|
328
|
+
const authHelp = success || !detectPiAuthFailure(input.stderr, input.stdout) ? "" : ` ${createPiAuthHelpMessage()}`;
|
|
189
329
|
return {
|
|
190
330
|
runId: input.runId,
|
|
191
331
|
mode: input.mode,
|
|
192
332
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
193
333
|
success,
|
|
194
|
-
message: success ? `Pi invocation completed.${recommendation}` : `Pi invocation failed.${recommendation}`,
|
|
334
|
+
message: success ? `Pi invocation completed.${recommendation}` : `Pi invocation failed.${authHelp}${recommendation}`,
|
|
195
335
|
piExitCode: input.exitCode,
|
|
196
336
|
recommendedPlanDir: input.recommendedPlanDir
|
|
197
337
|
};
|
|
@@ -219,6 +359,23 @@ function runController(options) {
|
|
|
219
359
|
goal,
|
|
220
360
|
recommendedPlanDir
|
|
221
361
|
});
|
|
362
|
+
const existingLeaderState = readAgentState(artifactsDir, "leader");
|
|
363
|
+
const existingLeaderSession = existingLeaderState?.session.sessionPath || existingLeaderState?.session.sessionDir ? existingLeaderState.session : getLatestAgentSession(artifactsDir, "leader");
|
|
364
|
+
const leaderSessionDir = existingLeaderSession.sessionDir ?? getAgentSessionsDir(artifactsDir, "leader");
|
|
365
|
+
const leaderState = createAgentState({
|
|
366
|
+
agentId: "leader",
|
|
367
|
+
role: "leader",
|
|
368
|
+
status: "starting",
|
|
369
|
+
task: goal,
|
|
370
|
+
branch,
|
|
371
|
+
lastMessage: goal ? `Starting leader run for goal: ${goal}` : "Starting leader run",
|
|
372
|
+
runId,
|
|
373
|
+
session: createAgentSessionRecord({
|
|
374
|
+
sessionDir: leaderSessionDir,
|
|
375
|
+
sessionId: existingLeaderSession.sessionId,
|
|
376
|
+
sessionPath: existingLeaderSession.sessionPath
|
|
377
|
+
})
|
|
378
|
+
});
|
|
222
379
|
assertPiRuntimeAvailable(piCommand);
|
|
223
380
|
mkdirSync(runDir, { recursive: true });
|
|
224
381
|
mkdirSync(latestDir, { recursive: true });
|
|
@@ -228,6 +385,7 @@ function runController(options) {
|
|
|
228
385
|
status: "starting",
|
|
229
386
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
230
387
|
});
|
|
388
|
+
writeAgentState(artifactsDir, leaderState);
|
|
231
389
|
const lease = acquireRepoLease(runId, repoId, branch);
|
|
232
390
|
try {
|
|
233
391
|
const manifest = createManifest({
|
|
@@ -257,15 +415,23 @@ function runController(options) {
|
|
|
257
415
|
command: piCommand,
|
|
258
416
|
createdAt: piStartedAt
|
|
259
417
|
});
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
"
|
|
263
|
-
|
|
264
|
-
|
|
418
|
+
writeAgentState(artifactsDir, createAgentState({
|
|
419
|
+
...leaderState,
|
|
420
|
+
status: "running",
|
|
421
|
+
lastMessage: goal ? `Leader working on: ${goal}` : "Leader working"
|
|
422
|
+
}));
|
|
423
|
+
const piResult = runCommandSync(piCommand, createPiInvocationArgs({
|
|
424
|
+
sessionDir: leaderState.session.sessionPath === null ? leaderSessionDir : null,
|
|
425
|
+
sessionPath: leaderState.session.sessionPath,
|
|
426
|
+
prompt,
|
|
427
|
+
appendedSystemPrompt: options.appendedSystemPrompt,
|
|
428
|
+
extensionPath: options.extensionPath
|
|
429
|
+
}), {
|
|
265
430
|
cwd: repoRoot,
|
|
266
431
|
env: process.env
|
|
267
432
|
});
|
|
268
433
|
const piEndedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
434
|
+
const latestLeaderSession = getLatestAgentSession(artifactsDir, "leader");
|
|
269
435
|
writeText(stdoutPath, piResult.stdout);
|
|
270
436
|
writeText(stderrPath, piResult.stderr);
|
|
271
437
|
const piInvocation = {
|
|
@@ -274,6 +440,9 @@ function runController(options) {
|
|
|
274
440
|
repoRoot,
|
|
275
441
|
planPath,
|
|
276
442
|
goal,
|
|
443
|
+
sessionDir: latestLeaderSession.sessionDir,
|
|
444
|
+
sessionId: latestLeaderSession.sessionId,
|
|
445
|
+
sessionPath: latestLeaderSession.sessionPath,
|
|
277
446
|
startedAt: piStartedAt,
|
|
278
447
|
endedAt: piEndedAt,
|
|
279
448
|
exitCode: piResult.exitCode,
|
|
@@ -297,6 +466,8 @@ function runController(options) {
|
|
|
297
466
|
runId,
|
|
298
467
|
mode,
|
|
299
468
|
exitCode: piInvocation.exitCode,
|
|
469
|
+
stdout: piResult.stdout,
|
|
470
|
+
stderr: piResult.stderr,
|
|
300
471
|
recommendedPlanDir
|
|
301
472
|
});
|
|
302
473
|
const finalManifest = {
|
|
@@ -316,6 +487,18 @@ function runController(options) {
|
|
|
316
487
|
writeJson(join(latestDir, "manifest.json"), finalManifest);
|
|
317
488
|
writeJson(join(latestDir, "metrics.json"), metrics);
|
|
318
489
|
writeJson(join(latestDir, "run-summary.json"), summary);
|
|
490
|
+
writeAgentState(artifactsDir, createAgentState({
|
|
491
|
+
...leaderState,
|
|
492
|
+
status: piInvocation.exitCode === 0 ? "idle" : "blocked",
|
|
493
|
+
lastMessage: piInvocation.exitCode === 0 ? "Leader run completed and is ready for the next instruction" : `Leader run stopped with exit code ${piInvocation.exitCode}`,
|
|
494
|
+
blocked: piInvocation.exitCode !== 0,
|
|
495
|
+
waitingOn: piInvocation.exitCode === 0 ? null : "human-or-follow-up-run",
|
|
496
|
+
session: createAgentSessionRecord({
|
|
497
|
+
sessionDir: latestLeaderSession.sessionDir,
|
|
498
|
+
sessionId: latestLeaderSession.sessionId,
|
|
499
|
+
sessionPath: latestLeaderSession.sessionPath
|
|
500
|
+
})
|
|
501
|
+
}));
|
|
319
502
|
appendJsonl(join(runDir, "events.jsonl"), {
|
|
320
503
|
type: "run_finished",
|
|
321
504
|
runId,
|
|
@@ -338,5 +521,5 @@ function runController(options) {
|
|
|
338
521
|
}
|
|
339
522
|
|
|
340
523
|
//#endregion
|
|
341
|
-
export { runController as t };
|
|
342
|
-
//# sourceMappingURL=controller-
|
|
524
|
+
export { createAgentState as a, getLatestAgentSession as c, readAgentState as d, writeAgentState as f, createAgentSessionRecord as i, listAgentStates as l, computeMetrics as n, getAgentDir as o, appendJsonl as p, appendAgentMessage as r, getAgentSessionsDir as s, runController as t, readAgentMessages as u };
|
|
525
|
+
//# sourceMappingURL=controller-9ihAZj3V.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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":"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"}
|
package/dist/doctor.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { n as detectPiAuthFailure, t as createPiAuthHelpMessage } from "./pi-C0fURZj7.mjs";
|
|
2
|
+
import { a as runCommandSync, t as isDirectExecution } from "./entrypoint-CyJDLudQ.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/doctor.ts
|
|
5
|
+
function runDoctor() {
|
|
6
|
+
console.log("[pitown] doctor");
|
|
7
|
+
if (runCommandSync("pi", ["--help"]).exitCode !== 0) {
|
|
8
|
+
console.log("- pi cli: missing");
|
|
9
|
+
console.log("- install: npm install -g @mariozechner/pi-coding-agent");
|
|
10
|
+
console.log("- verify: pi -p \"hello\"");
|
|
11
|
+
return { ok: false };
|
|
12
|
+
}
|
|
13
|
+
const check = runCommandSync("pi", [
|
|
14
|
+
"--no-session",
|
|
15
|
+
"-p",
|
|
16
|
+
"hello"
|
|
17
|
+
]);
|
|
18
|
+
if (check.exitCode === 0) {
|
|
19
|
+
console.log("- pi cli: installed");
|
|
20
|
+
console.log("- pi auth: ready");
|
|
21
|
+
console.log("- status: ok");
|
|
22
|
+
return { ok: true };
|
|
23
|
+
}
|
|
24
|
+
if (detectPiAuthFailure(check.stderr, check.stdout)) {
|
|
25
|
+
console.log("- pi cli: installed");
|
|
26
|
+
console.log("- pi auth: not ready");
|
|
27
|
+
console.log(`- note: ${createPiAuthHelpMessage()}`);
|
|
28
|
+
return { ok: false };
|
|
29
|
+
}
|
|
30
|
+
console.log("- pi cli: installed");
|
|
31
|
+
console.log("- pi check: failed");
|
|
32
|
+
if (check.stderr.trim()) console.log(`- stderr: ${check.stderr.trim()}`);
|
|
33
|
+
else if (check.stdout.trim()) console.log(`- stdout: ${check.stdout.trim()}`);
|
|
34
|
+
return { ok: false };
|
|
35
|
+
}
|
|
36
|
+
if (isDirectExecution(import.meta.url)) {
|
|
37
|
+
if (!runDoctor().ok) process.exitCode = 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { runDoctor };
|
|
42
|
+
//# sourceMappingURL=doctor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.mjs","names":[],"sources":["../src/doctor.ts"],"sourcesContent":["import { createPiAuthHelpMessage, detectPiAuthFailure, runCommandSync } from \"../../core/src/index.js\"\nimport { isDirectExecution } from \"./entrypoint.js\"\n\nexport interface DoctorResult {\n\tok: boolean\n}\n\nexport function runDoctor(): DoctorResult {\n\tconsole.log(\"[pitown] doctor\")\n\n\tconst availability = runCommandSync(\"pi\", [\"--help\"])\n\tif (availability.exitCode !== 0) {\n\t\tconsole.log(\"- pi cli: missing\")\n\t\tconsole.log(\"- install: npm install -g @mariozechner/pi-coding-agent\")\n\t\tconsole.log('- verify: pi -p \"hello\"')\n\t\treturn { ok: false }\n\t}\n\n\tconst check = runCommandSync(\"pi\", [\"--no-session\", \"-p\", \"hello\"])\n\tif (check.exitCode === 0) {\n\t\tconsole.log(\"- pi cli: installed\")\n\t\tconsole.log(\"- pi auth: ready\")\n\t\tconsole.log(\"- status: ok\")\n\t\treturn { ok: true }\n\t}\n\n\tif (detectPiAuthFailure(check.stderr, check.stdout)) {\n\t\tconsole.log(\"- pi cli: installed\")\n\t\tconsole.log(\"- pi auth: not ready\")\n\t\tconsole.log(`- note: ${createPiAuthHelpMessage()}`)\n\t\treturn { ok: false }\n\t}\n\n\tconsole.log(\"- pi cli: installed\")\n\tconsole.log(\"- pi check: failed\")\n\tif (check.stderr.trim()) console.log(`- stderr: ${check.stderr.trim()}`)\n\telse if (check.stdout.trim()) console.log(`- stdout: ${check.stdout.trim()}`)\n\treturn { ok: false }\n}\n\nif (isDirectExecution(import.meta.url)) {\n\tconst result = runDoctor()\n\tif (!result.ok) process.exitCode = 1\n}\n"],"mappings":";;;;AAOA,SAAgB,YAA0B;AACzC,SAAQ,IAAI,kBAAkB;AAG9B,KADqB,eAAe,MAAM,CAAC,SAAS,CAAC,CACpC,aAAa,GAAG;AAChC,UAAQ,IAAI,oBAAoB;AAChC,UAAQ,IAAI,0DAA0D;AACtE,UAAQ,IAAI,4BAA0B;AACtC,SAAO,EAAE,IAAI,OAAO;;CAGrB,MAAM,QAAQ,eAAe,MAAM;EAAC;EAAgB;EAAM;EAAQ,CAAC;AACnE,KAAI,MAAM,aAAa,GAAG;AACzB,UAAQ,IAAI,sBAAsB;AAClC,UAAQ,IAAI,mBAAmB;AAC/B,UAAQ,IAAI,eAAe;AAC3B,SAAO,EAAE,IAAI,MAAM;;AAGpB,KAAI,oBAAoB,MAAM,QAAQ,MAAM,OAAO,EAAE;AACpD,UAAQ,IAAI,sBAAsB;AAClC,UAAQ,IAAI,uBAAuB;AACnC,UAAQ,IAAI,WAAW,yBAAyB,GAAG;AACnD,SAAO,EAAE,IAAI,OAAO;;AAGrB,SAAQ,IAAI,sBAAsB;AAClC,SAAQ,IAAI,qBAAqB;AACjC,KAAI,MAAM,OAAO,MAAM,CAAE,SAAQ,IAAI,aAAa,MAAM,OAAO,MAAM,GAAG;UAC/D,MAAM,OAAO,MAAM,CAAE,SAAQ,IAAI,aAAa,MAAM,OAAO,MAAM,GAAG;AAC7E,QAAO,EAAE,IAAI,OAAO;;AAGrB,IAAI,kBAAkB,OAAO,KAAK,IAAI,EAErC;KAAI,CADW,WAAW,CACd,GAAI,SAAQ,WAAW"}
|