@schilderlabs/pitown 0.1.0 → 0.1.2
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 +8 -0
- package/dist/{config-D67TLZft.mjs → config-Bw-mNdF5.mjs} +27 -3
- package/dist/config-Bw-mNdF5.mjs.map +1 -0
- package/dist/{controller-DJPeTtIy.mjs → controller-D7lezZjg.mjs} +20 -2
- package/dist/controller-D7lezZjg.mjs.map +1 -0
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/run.mjs +3 -3
- package/dist/run.mjs.map +1 -1
- package/dist/status.mjs +2 -2
- package/dist/status.mjs.map +1 -1
- package/dist/watch.mjs +2 -2
- package/dist/watch.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/config-D67TLZft.mjs.map +0 -1
- package/dist/controller-DJPeTtIy.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -12,6 +12,14 @@ For the full project overview, roadmap, and architecture context, see the main r
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
npm install -g @schilderlabs/pitown
|
|
15
|
+
npm install -g @mariozechner/pi-coding-agent
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`pitown run` requires Pi to be installed and authenticated.
|
|
19
|
+
Verify Pi first:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pi -p "hello"
|
|
15
23
|
```
|
|
16
24
|
|
|
17
25
|
## Usage
|
|
@@ -1,9 +1,26 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { homedir } from "node:os";
|
|
4
5
|
import { createHash } from "node:crypto";
|
|
5
6
|
import { spawnSync } from "node:child_process";
|
|
6
7
|
|
|
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
|
|
7
24
|
//#region ../core/src/shell.ts
|
|
8
25
|
function runCommandSync(command, args, options) {
|
|
9
26
|
const result = spawnSync(command, args, {
|
|
@@ -19,6 +36,13 @@ function runCommandSync(command, args, options) {
|
|
|
19
36
|
exitCode: result.status ?? 1
|
|
20
37
|
};
|
|
21
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
|
+
}
|
|
22
46
|
function assertSuccess(result, context) {
|
|
23
47
|
if (result.exitCode === 0) return;
|
|
24
48
|
const details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
|
|
@@ -178,5 +202,5 @@ function resolveRunConfig(argv) {
|
|
|
178
202
|
}
|
|
179
203
|
|
|
180
204
|
//#endregion
|
|
181
|
-
export { getRepoArtifactsDir as a, createRepoSlug as c, getRepoRoot as d,
|
|
182
|
-
//# sourceMappingURL=config-
|
|
205
|
+
export { getRepoArtifactsDir as a, createRepoSlug as c, getRepoRoot as d, assertCommandAvailable as f, getRecommendedPlanDir as i, getCurrentBranch as l, isDirectExecution as m, resolveRunConfig as n, getRepoLatestRunPointerPath as o, runCommandSync as p, getLatestRunPointerPath as r, getTownHomeDir as s, parseCliFlags as t, getRepoIdentity as u };
|
|
206
|
+
//# sourceMappingURL=config-Bw-mNdF5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-Bw-mNdF5.mjs","names":[],"sources":["../src/entrypoint.ts","../../core/src/shell.ts","../../core/src/repo.ts","../src/paths.ts","../src/config.ts"],"sourcesContent":["import { realpathSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nfunction normalizePath(path: string | undefined): string | null {\n\tif (!path) return null\n\ttry {\n\t\treturn realpathSync(path)\n\t} catch {\n\t\treturn resolve(path)\n\t}\n}\n\nexport function isDirectExecution(fileUrl: string, argv1 = process.argv[1]): boolean {\n\tconst modulePath = normalizePath(fileURLToPath(fileUrl))\n\tconst invokedPath = normalizePath(argv1)\n\treturn modulePath !== null && invokedPath !== null && modulePath === invokedPath\n}\n","import { spawnSync } from \"node:child_process\"\n\nexport interface CommandResult {\n\tstdout: string\n\tstderr: string\n\texitCode: number\n}\n\nexport function runCommandSync(\n\tcommand: string,\n\targs: string[],\n\toptions?: { cwd?: string; env?: NodeJS.ProcessEnv },\n): CommandResult {\n\tconst result = spawnSync(command, args, {\n\t\tcwd: options?.cwd,\n\t\tenv: options?.env,\n\t\tencoding: \"utf-8\",\n\t})\n\tconst errorText = result.error instanceof Error ? `${result.error.message}\n` : \"\"\n\n\treturn {\n\t\tstdout: result.stdout ?? \"\",\n\t\tstderr: `${errorText}${result.stderr ?? \"\"}`,\n\t\texitCode: result.status ?? 1,\n\t}\n}\n\nexport function assertCommandAvailable(command: string) {\n\tconst result = spawnSync(command, [\"--help\"], {\n\t\tencoding: \"utf-8\",\n\t\tstdio: \"ignore\",\n\t})\n\n\tif (result.error instanceof Error) {\n\t\tthrow new Error(result.error.message)\n\t}\n}\n\nexport function assertSuccess(result: CommandResult, context: string) {\n\tif (result.exitCode === 0) return\n\tconst details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join(\"\\n\")\n\tthrow new Error(`${context} failed${details ? `\\n${details}` : \"\"}`)\n}\n","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 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\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 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,4 +1,4 @@
|
|
|
1
|
-
import { c as createRepoSlug, d as getRepoRoot, f as
|
|
1
|
+
import { c as createRepoSlug, d as getRepoRoot, f as assertCommandAvailable, l as getCurrentBranch, p as runCommandSync, u as getRepoIdentity } from "./config-Bw-mNdF5.mjs";
|
|
2
2
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { homedir, hostname } from "node:os";
|
|
@@ -144,6 +144,23 @@ function createPiPrompt(input) {
|
|
|
144
144
|
"Continue from the current scaffold state."
|
|
145
145
|
].join("\n");
|
|
146
146
|
}
|
|
147
|
+
function assertPiRuntimeAvailable(piCommand) {
|
|
148
|
+
try {
|
|
149
|
+
assertCommandAvailable(piCommand);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
if (piCommand === "pi") throw new Error([
|
|
152
|
+
"Pi Town requires the `pi` CLI to run `pitown run`.",
|
|
153
|
+
"Install Pi: npm install -g @mariozechner/pi-coding-agent",
|
|
154
|
+
"Then authenticate Pi and verify it works: pi -p \"hello\"",
|
|
155
|
+
`Details: ${error.message}`
|
|
156
|
+
].join("\n"));
|
|
157
|
+
throw new Error([
|
|
158
|
+
`Pi Town could not execute the configured Pi command: ${piCommand}`,
|
|
159
|
+
"Make sure the command exists on PATH or points to an executable file.",
|
|
160
|
+
`Details: ${error.message}`
|
|
161
|
+
].join("\n"));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
147
164
|
function createManifest(input) {
|
|
148
165
|
return {
|
|
149
166
|
runId: input.runId,
|
|
@@ -202,6 +219,7 @@ function runController(options) {
|
|
|
202
219
|
goal,
|
|
203
220
|
recommendedPlanDir
|
|
204
221
|
});
|
|
222
|
+
assertPiRuntimeAvailable(piCommand);
|
|
205
223
|
mkdirSync(runDir, { recursive: true });
|
|
206
224
|
mkdirSync(latestDir, { recursive: true });
|
|
207
225
|
writeText(join(runDir, "questions.jsonl"), "");
|
|
@@ -321,4 +339,4 @@ function runController(options) {
|
|
|
321
339
|
|
|
322
340
|
//#endregion
|
|
323
341
|
export { runController as t };
|
|
324
|
-
//# sourceMappingURL=controller-
|
|
342
|
+
//# sourceMappingURL=controller-D7lezZjg.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-D7lezZjg.mjs","names":[],"sources":["../../core/src/events.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 { 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 { appendJsonl } from \"./events.js\"\nimport { acquireRepoLease } from \"./lease.js\"\nimport { computeMetrics } from \"./metrics.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 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\"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`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\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\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 ? `Pi invocation completed.${recommendation}` : `Pi invocation failed.${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\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\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\tconst piResult = runCommandSync(piCommand, [\"--no-session\", \"-p\", prompt], {\n\t\t\tcwd: repoRoot,\n\t\t\tenv: process.env,\n\t\t})\n\t\tconst piEndedAt = new Date().toISOString()\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\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\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\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/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { m as isDirectExecution } from "./config-Bw-mNdF5.mjs";
|
|
2
3
|
import { runTown } from "./run.mjs";
|
|
3
4
|
import { showTownStatus } from "./status.mjs";
|
|
4
5
|
import { watchTown } from "./watch.mjs";
|
|
@@ -43,7 +44,7 @@ function runCli(argv = process.argv.slice(2)) {
|
|
|
43
44
|
break;
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
|
-
if (import.meta.url
|
|
47
|
+
if (isDirectExecution(import.meta.url)) runCli();
|
|
47
48
|
|
|
48
49
|
//#endregion
|
|
49
50
|
export { printHelp, runCli };
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { runTown } from \"./run.js\"\nimport { showTownStatus } from \"./status.js\"\nimport { watchTown } from \"./watch.js\"\n\nexport function printHelp() {\n\tconsole.log(\n\t\t[\n\t\t\t\"pitown\",\n\t\t\t\"\",\n\t\t\t\"Usage:\",\n\t\t\t\" pitown run [--repo <path>] [--plan <path>] [--goal <text>]\",\n\t\t\t\" pitown status [--repo <path>]\",\n\t\t\t\" pitown watch [--repo <path>]\",\n\t\t\t\" pitown help\",\n\t\t\t\" pitown --help\",\n\t\t].join(\"\\n\"),\n\t)\n}\n\nexport function runCli(argv = process.argv.slice(2)) {\n\tconst [command, ...args] = argv\n\n\tswitch (command) {\n\t\tcase undefined:\n\t\tcase \"help\":\n\t\tcase \"--help\":\n\t\tcase \"-h\":\n\t\t\tprintHelp()\n\t\t\tbreak\n\t\tcase \"run\": {\n\t\t\tconst result = runTown(args)\n\t\t\tif (result.piInvocation.exitCode !== 0) process.exitCode = result.piInvocation.exitCode\n\t\t\tbreak\n\t\t}\n\t\tcase \"status\":\n\t\t\tshowTownStatus(args)\n\t\t\tbreak\n\t\tcase \"watch\":\n\t\t\twatchTown(args)\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.log(`Unknown command: ${command}`)\n\t\t\tprintHelp()\n\t\t\tprocess.exitCode = 1\n\t\t\tbreak\n\t}\n}\n\nif (import.meta.url
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { isDirectExecution } from \"./entrypoint.js\"\nimport { runTown } from \"./run.js\"\nimport { showTownStatus } from \"./status.js\"\nimport { watchTown } from \"./watch.js\"\n\nexport function printHelp() {\n\tconsole.log(\n\t\t[\n\t\t\t\"pitown\",\n\t\t\t\"\",\n\t\t\t\"Usage:\",\n\t\t\t\" pitown run [--repo <path>] [--plan <path>] [--goal <text>]\",\n\t\t\t\" pitown status [--repo <path>]\",\n\t\t\t\" pitown watch [--repo <path>]\",\n\t\t\t\" pitown help\",\n\t\t\t\" pitown --help\",\n\t\t].join(\"\\n\"),\n\t)\n}\n\nexport function runCli(argv = process.argv.slice(2)) {\n\tconst [command, ...args] = argv\n\n\tswitch (command) {\n\t\tcase undefined:\n\t\tcase \"help\":\n\t\tcase \"--help\":\n\t\tcase \"-h\":\n\t\t\tprintHelp()\n\t\t\tbreak\n\t\tcase \"run\": {\n\t\t\tconst result = runTown(args)\n\t\t\tif (result.piInvocation.exitCode !== 0) process.exitCode = result.piInvocation.exitCode\n\t\t\tbreak\n\t\t}\n\t\tcase \"status\":\n\t\t\tshowTownStatus(args)\n\t\t\tbreak\n\t\tcase \"watch\":\n\t\t\twatchTown(args)\n\t\t\tbreak\n\t\tdefault:\n\t\t\tconsole.log(`Unknown command: ${command}`)\n\t\t\tprintHelp()\n\t\t\tprocess.exitCode = 1\n\t\t\tbreak\n\t}\n}\n\nif (isDirectExecution(import.meta.url)) {\n\trunCli()\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,YAAY;AAC3B,SAAQ,IACP;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,CAAC,KAAK,KAAK,CACZ;;AAGF,SAAgB,OAAO,OAAO,QAAQ,KAAK,MAAM,EAAE,EAAE;CACpD,MAAM,CAAC,SAAS,GAAG,QAAQ;AAE3B,SAAQ,SAAR;EACC,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;AACJ,cAAW;AACX;EACD,KAAK,OAAO;GACX,MAAM,SAAS,QAAQ,KAAK;AAC5B,OAAI,OAAO,aAAa,aAAa,EAAG,SAAQ,WAAW,OAAO,aAAa;AAC/E;;EAED,KAAK;AACJ,kBAAe,KAAK;AACpB;EACD,KAAK;AACJ,aAAU,KAAK;AACf;EACD;AACC,WAAQ,IAAI,oBAAoB,UAAU;AAC1C,cAAW;AACX,WAAQ,WAAW;AACnB;;;AAIH,IAAI,kBAAkB,OAAO,KAAK,IAAI,CACrC,SAAQ"}
|
package/dist/run.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { a as getRepoArtifactsDir, c as createRepoSlug, d as getRepoRoot, i as getRecommendedPlanDir, m as isDirectExecution, n as resolveRunConfig, o as getRepoLatestRunPointerPath, r as getLatestRunPointerPath, s as getTownHomeDir, u as getRepoIdentity } from "./config-Bw-mNdF5.mjs";
|
|
2
|
+
import { t as runController } from "./controller-D7lezZjg.mjs";
|
|
3
3
|
import { existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
|
|
@@ -55,7 +55,7 @@ function runTown(argv = process.argv.slice(2)) {
|
|
|
55
55
|
if (result.summary.recommendedPlanDir) console.log(`- recommended plans: ${result.summary.recommendedPlanDir}`);
|
|
56
56
|
return result;
|
|
57
57
|
}
|
|
58
|
-
if (import.meta.url
|
|
58
|
+
if (isDirectExecution(import.meta.url)) {
|
|
59
59
|
const result = runTown();
|
|
60
60
|
if (result.piInvocation.exitCode !== 0) process.exitCode = result.piInvocation.exitCode;
|
|
61
61
|
}
|
package/dist/run.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.mjs","names":[],"sources":["../src/run.ts"],"sourcesContent":["import { existsSync, mkdirSync, statSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport {\n\tcreateRepoSlug,\n\tgetRepoIdentity,\n\tgetRepoRoot,\n\trunController,\n\ttype ControllerRunResult,\n} from \"../../core/src/index.js\"\nimport { resolveRunConfig } from \"./config.js\"\nimport {\n\tgetLatestRunPointerPath,\n\tgetRecommendedPlanDir,\n\tgetRepoArtifactsDir,\n\tgetRepoLatestRunPointerPath,\n\tgetTownHomeDir,\n} from \"./paths.js\"\n\ninterface LatestRunPointer {\n\trepoSlug: string\n\trepoRoot: string\n\trunId: string\n\trunDir: string\n\tlatestDir: string\n\tmanifestPath: string\n\tmetricsPath: string\n\tsummaryPath: string\n\tupdatedAt: string\n}\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 assertDirectory(path: string, label: string) {\n\tif (!existsSync(path)) throw new Error(`${label} does not exist: ${path}`)\n\tif (!statSync(path).isDirectory()) throw new Error(`${label} is not a directory: ${path}`)\n}\n\nfunction createLatestRunPointer(result: ControllerRunResult, repoSlug: string, repoRoot: string): LatestRunPointer {\n\treturn {\n\t\trepoSlug,\n\t\trepoRoot,\n\t\trunId: result.runId,\n\t\trunDir: result.runDir,\n\t\tlatestDir: result.latestDir,\n\t\tmanifestPath: join(result.latestDir, \"manifest.json\"),\n\t\tmetricsPath: join(result.latestDir, \"metrics.json\"),\n\t\tsummaryPath: join(result.latestDir, \"run-summary.json\"),\n\t\tupdatedAt: new Date().toISOString(),\n\t}\n}\n\nexport function runTown(argv = process.argv.slice(2)): ControllerRunResult {\n\tconst config = resolveRunConfig(argv)\n\tassertDirectory(config.repo, \"Target repo\")\n\tif (config.plan) assertDirectory(config.plan, \"Plan path\")\n\n\tconst townHome = getTownHomeDir()\n\tmkdirSync(townHome, { recursive: true })\n\n\tconst repoRoot = getRepoRoot(config.repo)\n\tconst repoId = getRepoIdentity(repoRoot)\n\tconst repoSlug = createRepoSlug(repoId, repoRoot)\n\tconst recommendedPlanDir = config.plan ? null : getRecommendedPlanDir(repoSlug)\n\tconst artifactsDir = getRepoArtifactsDir(repoSlug)\n\n\tconst result = runController({\n\t\tartifactsDir,\n\t\tcwd: repoRoot,\n\t\tgoal: config.goal,\n\t\tmode: \"single-pi\",\n\t\tplanPath: config.plan,\n\t\trecommendedPlanDir,\n\t})\n\n\tconst latestPointer = createLatestRunPointer(result, repoSlug, repoRoot)\n\twriteJson(getLatestRunPointerPath(), latestPointer)\n\twriteJson(getRepoLatestRunPointerPath(repoSlug), latestPointer)\n\n\tconsole.log(\"[pitown] run written\")\n\tconsole.log(`- run id: ${result.runId}`)\n\tconsole.log(`- repo root: ${result.manifest.repoRoot}`)\n\tconsole.log(`- branch: ${result.manifest.branch}`)\n\tconsole.log(`- artifacts: ${result.runDir}`)\n\tconsole.log(`- latest metrics: ${latestPointer.metricsPath}`)\n\tconsole.log(`- pi exit code: ${result.piInvocation.exitCode}`)\n\tif (result.manifest.planPath) console.log(`- plan path: ${result.manifest.planPath}`)\n\tif (result.summary.recommendedPlanDir) console.log(`- recommended plans: ${result.summary.recommendedPlanDir}`)\n\n\treturn result\n}\n\nif (import.meta.url
|
|
1
|
+
{"version":3,"file":"run.mjs","names":[],"sources":["../src/run.ts"],"sourcesContent":["import { existsSync, mkdirSync, statSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport {\n\tcreateRepoSlug,\n\tgetRepoIdentity,\n\tgetRepoRoot,\n\trunController,\n\ttype ControllerRunResult,\n} from \"../../core/src/index.js\"\nimport { isDirectExecution } from \"./entrypoint.js\"\nimport { resolveRunConfig } from \"./config.js\"\nimport {\n\tgetLatestRunPointerPath,\n\tgetRecommendedPlanDir,\n\tgetRepoArtifactsDir,\n\tgetRepoLatestRunPointerPath,\n\tgetTownHomeDir,\n} from \"./paths.js\"\n\ninterface LatestRunPointer {\n\trepoSlug: string\n\trepoRoot: string\n\trunId: string\n\trunDir: string\n\tlatestDir: string\n\tmanifestPath: string\n\tmetricsPath: string\n\tsummaryPath: string\n\tupdatedAt: string\n}\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 assertDirectory(path: string, label: string) {\n\tif (!existsSync(path)) throw new Error(`${label} does not exist: ${path}`)\n\tif (!statSync(path).isDirectory()) throw new Error(`${label} is not a directory: ${path}`)\n}\n\nfunction createLatestRunPointer(result: ControllerRunResult, repoSlug: string, repoRoot: string): LatestRunPointer {\n\treturn {\n\t\trepoSlug,\n\t\trepoRoot,\n\t\trunId: result.runId,\n\t\trunDir: result.runDir,\n\t\tlatestDir: result.latestDir,\n\t\tmanifestPath: join(result.latestDir, \"manifest.json\"),\n\t\tmetricsPath: join(result.latestDir, \"metrics.json\"),\n\t\tsummaryPath: join(result.latestDir, \"run-summary.json\"),\n\t\tupdatedAt: new Date().toISOString(),\n\t}\n}\n\nexport function runTown(argv = process.argv.slice(2)): ControllerRunResult {\n\tconst config = resolveRunConfig(argv)\n\tassertDirectory(config.repo, \"Target repo\")\n\tif (config.plan) assertDirectory(config.plan, \"Plan path\")\n\n\tconst townHome = getTownHomeDir()\n\tmkdirSync(townHome, { recursive: true })\n\n\tconst repoRoot = getRepoRoot(config.repo)\n\tconst repoId = getRepoIdentity(repoRoot)\n\tconst repoSlug = createRepoSlug(repoId, repoRoot)\n\tconst recommendedPlanDir = config.plan ? null : getRecommendedPlanDir(repoSlug)\n\tconst artifactsDir = getRepoArtifactsDir(repoSlug)\n\n\tconst result = runController({\n\t\tartifactsDir,\n\t\tcwd: repoRoot,\n\t\tgoal: config.goal,\n\t\tmode: \"single-pi\",\n\t\tplanPath: config.plan,\n\t\trecommendedPlanDir,\n\t})\n\n\tconst latestPointer = createLatestRunPointer(result, repoSlug, repoRoot)\n\twriteJson(getLatestRunPointerPath(), latestPointer)\n\twriteJson(getRepoLatestRunPointerPath(repoSlug), latestPointer)\n\n\tconsole.log(\"[pitown] run written\")\n\tconsole.log(`- run id: ${result.runId}`)\n\tconsole.log(`- repo root: ${result.manifest.repoRoot}`)\n\tconsole.log(`- branch: ${result.manifest.branch}`)\n\tconsole.log(`- artifacts: ${result.runDir}`)\n\tconsole.log(`- latest metrics: ${latestPointer.metricsPath}`)\n\tconsole.log(`- pi exit code: ${result.piInvocation.exitCode}`)\n\tif (result.manifest.planPath) console.log(`- plan path: ${result.manifest.planPath}`)\n\tif (result.summary.recommendedPlanDir) console.log(`- recommended plans: ${result.summary.recommendedPlanDir}`)\n\n\treturn result\n}\n\nif (isDirectExecution(import.meta.url)) {\n\tconst result = runTown()\n\tif (result.piInvocation.exitCode !== 0) process.exitCode = result.piInvocation.exitCode\n}\n"],"mappings":";;;;;;AA+BA,SAAS,UAAU,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,gBAAgB,MAAc,OAAe;AACrD,KAAI,CAAC,WAAW,KAAK,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,OAAO;AAC1E,KAAI,CAAC,SAAS,KAAK,CAAC,aAAa,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,uBAAuB,OAAO;;AAG3F,SAAS,uBAAuB,QAA6B,UAAkB,UAAoC;AAClH,QAAO;EACN;EACA;EACA,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,cAAc,KAAK,OAAO,WAAW,gBAAgB;EACrD,aAAa,KAAK,OAAO,WAAW,eAAe;EACnD,aAAa,KAAK,OAAO,WAAW,mBAAmB;EACvD,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;;AAGF,SAAgB,QAAQ,OAAO,QAAQ,KAAK,MAAM,EAAE,EAAuB;CAC1E,MAAM,SAAS,iBAAiB,KAAK;AACrC,iBAAgB,OAAO,MAAM,cAAc;AAC3C,KAAI,OAAO,KAAM,iBAAgB,OAAO,MAAM,YAAY;AAG1D,WADiB,gBAAgB,EACb,EAAE,WAAW,MAAM,CAAC;CAExC,MAAM,WAAW,YAAY,OAAO,KAAK;CAEzC,MAAM,WAAW,eADF,gBAAgB,SAAS,EACA,SAAS;CACjD,MAAM,qBAAqB,OAAO,OAAO,OAAO,sBAAsB,SAAS;CAG/E,MAAM,SAAS,cAAc;EAC5B,cAHoB,oBAAoB,SAAS;EAIjD,KAAK;EACL,MAAM,OAAO;EACb,MAAM;EACN,UAAU,OAAO;EACjB;EACA,CAAC;CAEF,MAAM,gBAAgB,uBAAuB,QAAQ,UAAU,SAAS;AACxE,WAAU,yBAAyB,EAAE,cAAc;AACnD,WAAU,4BAA4B,SAAS,EAAE,cAAc;AAE/D,SAAQ,IAAI,uBAAuB;AACnC,SAAQ,IAAI,aAAa,OAAO,QAAQ;AACxC,SAAQ,IAAI,gBAAgB,OAAO,SAAS,WAAW;AACvD,SAAQ,IAAI,aAAa,OAAO,SAAS,SAAS;AAClD,SAAQ,IAAI,gBAAgB,OAAO,SAAS;AAC5C,SAAQ,IAAI,qBAAqB,cAAc,cAAc;AAC7D,SAAQ,IAAI,mBAAmB,OAAO,aAAa,WAAW;AAC9D,KAAI,OAAO,SAAS,SAAU,SAAQ,IAAI,gBAAgB,OAAO,SAAS,WAAW;AACrF,KAAI,OAAO,QAAQ,mBAAoB,SAAQ,IAAI,wBAAwB,OAAO,QAAQ,qBAAqB;AAE/G,QAAO;;AAGR,IAAI,kBAAkB,OAAO,KAAK,IAAI,EAAE;CACvC,MAAM,SAAS,SAAS;AACxB,KAAI,OAAO,aAAa,aAAa,EAAG,SAAQ,WAAW,OAAO,aAAa"}
|
package/dist/status.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as getRepoArtifactsDir, c as createRepoSlug, d as getRepoRoot, o as getRepoLatestRunPointerPath, r as getLatestRunPointerPath, s as getTownHomeDir, t as parseCliFlags, u as getRepoIdentity } from "./config-
|
|
1
|
+
import { a as getRepoArtifactsDir, c as createRepoSlug, d as getRepoRoot, m as isDirectExecution, o as getRepoLatestRunPointerPath, r as getLatestRunPointerPath, s as getTownHomeDir, t as parseCliFlags, u as getRepoIdentity } from "./config-Bw-mNdF5.mjs";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
@@ -63,7 +63,7 @@ function showTownStatus(argv = process.argv.slice(2)) {
|
|
|
63
63
|
console.log(`- metrics file: ${latest.metricsPath}`);
|
|
64
64
|
console.log(readFileSync(latest.metricsPath, "utf-8").trim());
|
|
65
65
|
}
|
|
66
|
-
if (import.meta.url
|
|
66
|
+
if (isDirectExecution(import.meta.url)) showTownStatus();
|
|
67
67
|
|
|
68
68
|
//#endregion
|
|
69
69
|
export { resolveLatestRunPointer, showTownStatus };
|
package/dist/status.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.mjs","names":[],"sources":["../src/status.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { createRepoSlug, getRepoIdentity, getRepoRoot } from \"../../core/src/index.js\"\nimport { parseCliFlags } from \"./config.js\"\nimport {\n\tgetLatestRunPointerPath,\n\tgetRepoArtifactsDir,\n\tgetRepoLatestRunPointerPath,\n\tgetTownHomeDir,\n} from \"./paths.js\"\n\ninterface LatestRunPointer {\n\trepoSlug: string\n\trepoRoot: string\n\trunId?: string\n\trunDir?: string\n\tlatestDir: string\n\tmanifestPath: string\n\tmetricsPath: string\n\tsummaryPath: string\n\tupdatedAt?: string\n}\n\nfunction readJson<T>(path: string): T {\n\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T\n}\n\nfunction createFallbackPointer(repoRoot: string, repoSlug: string): LatestRunPointer {\n\tconst latestDir = join(getRepoArtifactsDir(repoSlug), \"latest\")\n\treturn {\n\t\trepoSlug,\n\t\trepoRoot,\n\t\tlatestDir,\n\t\tmanifestPath: join(latestDir, \"manifest.json\"),\n\t\tmetricsPath: join(latestDir, \"metrics.json\"),\n\t\tsummaryPath: join(latestDir, \"run-summary.json\"),\n\t}\n}\n\nexport function resolveLatestRunPointer(argv = process.argv.slice(2)): LatestRunPointer | null {\n\tconst flags = parseCliFlags(argv)\n\n\tif (flags.repo) {\n\t\tconst repoRoot = getRepoRoot(flags.repo)\n\t\tconst repoSlug = createRepoSlug(getRepoIdentity(repoRoot), repoRoot)\n\t\tconst repoPointerPath = getRepoLatestRunPointerPath(repoSlug)\n\t\tif (existsSync(repoPointerPath)) return readJson<LatestRunPointer>(repoPointerPath)\n\t\treturn createFallbackPointer(repoRoot, repoSlug)\n\t}\n\n\tconst latestPointerPath = getLatestRunPointerPath()\n\tif (!existsSync(latestPointerPath)) return null\n\treturn readJson<LatestRunPointer>(latestPointerPath)\n}\n\nexport function showTownStatus(argv = process.argv.slice(2)) {\n\tconst latest = resolveLatestRunPointer(argv)\n\tconsole.log(\"[pitown] status\")\n\tconsole.log(`- town home: ${getTownHomeDir()}`)\n\n\tif (latest === null) {\n\t\tconsole.log(\"- no local runs found yet\")\n\t\tconsole.log(`- expected pointer: ${getLatestRunPointerPath()}`)\n\t\treturn\n\t}\n\n\tif (existsSync(latest.summaryPath)) {\n\t\tconst summary = readJson<{\n\t\t\trunId?: string\n\t\t\tmode?: string\n\t\t\tmessage?: string\n\t\t\tpiExitCode?: number\n\t\t}>(latest.summaryPath)\n\t\tif (summary.runId) console.log(`- latest run: ${summary.runId}`)\n\t\tif (summary.mode) console.log(`- mode: ${summary.mode}`)\n\t\tif (summary.piExitCode !== undefined) console.log(`- pi exit code: ${summary.piExitCode}`)\n\t\tif (summary.message) console.log(`- note: ${summary.message}`)\n\t}\n\n\tif (existsSync(latest.manifestPath)) {\n\t\tconst manifest = readJson<{\n\t\t\trepoRoot?: string\n\t\t\tbranch?: string\n\t\t\tgoal?: string | null\n\t\t\tplanPath?: string | null\n\t\t\trecommendedPlanDir?: string | null\n\t\t\tstopReason?: string | null\n\t\t}>(latest.manifestPath)\n\t\tif (manifest.repoRoot) console.log(`- repo root: ${manifest.repoRoot}`)\n\t\tif (manifest.branch) console.log(`- branch: ${manifest.branch}`)\n\t\tif (manifest.goal) console.log(`- goal: ${manifest.goal}`)\n\t\tif (manifest.planPath) console.log(`- plan path: ${manifest.planPath}`)\n\t\tif (!manifest.planPath && manifest.recommendedPlanDir) {\n\t\t\tconsole.log(`- recommended plans: ${manifest.recommendedPlanDir}`)\n\t\t}\n\t\tif (manifest.stopReason) console.log(`- stop reason: ${manifest.stopReason}`)\n\t}\n\n\tif (!existsSync(latest.metricsPath)) {\n\t\tconsole.log(\"- no metrics snapshot found yet\")\n\t\tconsole.log(`- expected path: ${latest.metricsPath}`)\n\t\treturn\n\t}\n\n\tconsole.log(`- metrics file: ${latest.metricsPath}`)\n\tconsole.log(readFileSync(latest.metricsPath, \"utf-8\").trim())\n}\n\nif (import.meta.url
|
|
1
|
+
{"version":3,"file":"status.mjs","names":[],"sources":["../src/status.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { createRepoSlug, getRepoIdentity, getRepoRoot } from \"../../core/src/index.js\"\nimport { parseCliFlags } from \"./config.js\"\nimport { isDirectExecution } from \"./entrypoint.js\"\nimport {\n\tgetLatestRunPointerPath,\n\tgetRepoArtifactsDir,\n\tgetRepoLatestRunPointerPath,\n\tgetTownHomeDir,\n} from \"./paths.js\"\n\ninterface LatestRunPointer {\n\trepoSlug: string\n\trepoRoot: string\n\trunId?: string\n\trunDir?: string\n\tlatestDir: string\n\tmanifestPath: string\n\tmetricsPath: string\n\tsummaryPath: string\n\tupdatedAt?: string\n}\n\nfunction readJson<T>(path: string): T {\n\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T\n}\n\nfunction createFallbackPointer(repoRoot: string, repoSlug: string): LatestRunPointer {\n\tconst latestDir = join(getRepoArtifactsDir(repoSlug), \"latest\")\n\treturn {\n\t\trepoSlug,\n\t\trepoRoot,\n\t\tlatestDir,\n\t\tmanifestPath: join(latestDir, \"manifest.json\"),\n\t\tmetricsPath: join(latestDir, \"metrics.json\"),\n\t\tsummaryPath: join(latestDir, \"run-summary.json\"),\n\t}\n}\n\nexport function resolveLatestRunPointer(argv = process.argv.slice(2)): LatestRunPointer | null {\n\tconst flags = parseCliFlags(argv)\n\n\tif (flags.repo) {\n\t\tconst repoRoot = getRepoRoot(flags.repo)\n\t\tconst repoSlug = createRepoSlug(getRepoIdentity(repoRoot), repoRoot)\n\t\tconst repoPointerPath = getRepoLatestRunPointerPath(repoSlug)\n\t\tif (existsSync(repoPointerPath)) return readJson<LatestRunPointer>(repoPointerPath)\n\t\treturn createFallbackPointer(repoRoot, repoSlug)\n\t}\n\n\tconst latestPointerPath = getLatestRunPointerPath()\n\tif (!existsSync(latestPointerPath)) return null\n\treturn readJson<LatestRunPointer>(latestPointerPath)\n}\n\nexport function showTownStatus(argv = process.argv.slice(2)) {\n\tconst latest = resolveLatestRunPointer(argv)\n\tconsole.log(\"[pitown] status\")\n\tconsole.log(`- town home: ${getTownHomeDir()}`)\n\n\tif (latest === null) {\n\t\tconsole.log(\"- no local runs found yet\")\n\t\tconsole.log(`- expected pointer: ${getLatestRunPointerPath()}`)\n\t\treturn\n\t}\n\n\tif (existsSync(latest.summaryPath)) {\n\t\tconst summary = readJson<{\n\t\t\trunId?: string\n\t\t\tmode?: string\n\t\t\tmessage?: string\n\t\t\tpiExitCode?: number\n\t\t}>(latest.summaryPath)\n\t\tif (summary.runId) console.log(`- latest run: ${summary.runId}`)\n\t\tif (summary.mode) console.log(`- mode: ${summary.mode}`)\n\t\tif (summary.piExitCode !== undefined) console.log(`- pi exit code: ${summary.piExitCode}`)\n\t\tif (summary.message) console.log(`- note: ${summary.message}`)\n\t}\n\n\tif (existsSync(latest.manifestPath)) {\n\t\tconst manifest = readJson<{\n\t\t\trepoRoot?: string\n\t\t\tbranch?: string\n\t\t\tgoal?: string | null\n\t\t\tplanPath?: string | null\n\t\t\trecommendedPlanDir?: string | null\n\t\t\tstopReason?: string | null\n\t\t}>(latest.manifestPath)\n\t\tif (manifest.repoRoot) console.log(`- repo root: ${manifest.repoRoot}`)\n\t\tif (manifest.branch) console.log(`- branch: ${manifest.branch}`)\n\t\tif (manifest.goal) console.log(`- goal: ${manifest.goal}`)\n\t\tif (manifest.planPath) console.log(`- plan path: ${manifest.planPath}`)\n\t\tif (!manifest.planPath && manifest.recommendedPlanDir) {\n\t\t\tconsole.log(`- recommended plans: ${manifest.recommendedPlanDir}`)\n\t\t}\n\t\tif (manifest.stopReason) console.log(`- stop reason: ${manifest.stopReason}`)\n\t}\n\n\tif (!existsSync(latest.metricsPath)) {\n\t\tconsole.log(\"- no metrics snapshot found yet\")\n\t\tconsole.log(`- expected path: ${latest.metricsPath}`)\n\t\treturn\n\t}\n\n\tconsole.log(`- metrics file: ${latest.metricsPath}`)\n\tconsole.log(readFileSync(latest.metricsPath, \"utf-8\").trim())\n}\n\nif (isDirectExecution(import.meta.url)) {\n\tshowTownStatus()\n}\n"],"mappings":";;;;;AAwBA,SAAS,SAAY,MAAiB;AACrC,QAAO,KAAK,MAAM,aAAa,MAAM,QAAQ,CAAC;;AAG/C,SAAS,sBAAsB,UAAkB,UAAoC;CACpF,MAAM,YAAY,KAAK,oBAAoB,SAAS,EAAE,SAAS;AAC/D,QAAO;EACN;EACA;EACA;EACA,cAAc,KAAK,WAAW,gBAAgB;EAC9C,aAAa,KAAK,WAAW,eAAe;EAC5C,aAAa,KAAK,WAAW,mBAAmB;EAChD;;AAGF,SAAgB,wBAAwB,OAAO,QAAQ,KAAK,MAAM,EAAE,EAA2B;CAC9F,MAAM,QAAQ,cAAc,KAAK;AAEjC,KAAI,MAAM,MAAM;EACf,MAAM,WAAW,YAAY,MAAM,KAAK;EACxC,MAAM,WAAW,eAAe,gBAAgB,SAAS,EAAE,SAAS;EACpE,MAAM,kBAAkB,4BAA4B,SAAS;AAC7D,MAAI,WAAW,gBAAgB,CAAE,QAAO,SAA2B,gBAAgB;AACnF,SAAO,sBAAsB,UAAU,SAAS;;CAGjD,MAAM,oBAAoB,yBAAyB;AACnD,KAAI,CAAC,WAAW,kBAAkB,CAAE,QAAO;AAC3C,QAAO,SAA2B,kBAAkB;;AAGrD,SAAgB,eAAe,OAAO,QAAQ,KAAK,MAAM,EAAE,EAAE;CAC5D,MAAM,SAAS,wBAAwB,KAAK;AAC5C,SAAQ,IAAI,kBAAkB;AAC9B,SAAQ,IAAI,gBAAgB,gBAAgB,GAAG;AAE/C,KAAI,WAAW,MAAM;AACpB,UAAQ,IAAI,4BAA4B;AACxC,UAAQ,IAAI,uBAAuB,yBAAyB,GAAG;AAC/D;;AAGD,KAAI,WAAW,OAAO,YAAY,EAAE;EACnC,MAAM,UAAU,SAKb,OAAO,YAAY;AACtB,MAAI,QAAQ,MAAO,SAAQ,IAAI,iBAAiB,QAAQ,QAAQ;AAChE,MAAI,QAAQ,KAAM,SAAQ,IAAI,WAAW,QAAQ,OAAO;AACxD,MAAI,QAAQ,eAAe,OAAW,SAAQ,IAAI,mBAAmB,QAAQ,aAAa;AAC1F,MAAI,QAAQ,QAAS,SAAQ,IAAI,WAAW,QAAQ,UAAU;;AAG/D,KAAI,WAAW,OAAO,aAAa,EAAE;EACpC,MAAM,WAAW,SAOd,OAAO,aAAa;AACvB,MAAI,SAAS,SAAU,SAAQ,IAAI,gBAAgB,SAAS,WAAW;AACvE,MAAI,SAAS,OAAQ,SAAQ,IAAI,aAAa,SAAS,SAAS;AAChE,MAAI,SAAS,KAAM,SAAQ,IAAI,WAAW,SAAS,OAAO;AAC1D,MAAI,SAAS,SAAU,SAAQ,IAAI,gBAAgB,SAAS,WAAW;AACvE,MAAI,CAAC,SAAS,YAAY,SAAS,mBAClC,SAAQ,IAAI,wBAAwB,SAAS,qBAAqB;AAEnE,MAAI,SAAS,WAAY,SAAQ,IAAI,kBAAkB,SAAS,aAAa;;AAG9E,KAAI,CAAC,WAAW,OAAO,YAAY,EAAE;AACpC,UAAQ,IAAI,kCAAkC;AAC9C,UAAQ,IAAI,oBAAoB,OAAO,cAAc;AACrD;;AAGD,SAAQ,IAAI,mBAAmB,OAAO,cAAc;AACpD,SAAQ,IAAI,aAAa,OAAO,aAAa,QAAQ,CAAC,MAAM,CAAC;;AAG9D,IAAI,kBAAkB,OAAO,KAAK,IAAI,CACrC,iBAAgB"}
|
package/dist/watch.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as getTownHomeDir } from "./config-
|
|
1
|
+
import { m as isDirectExecution, s as getTownHomeDir } from "./config-Bw-mNdF5.mjs";
|
|
2
2
|
import { resolveLatestRunPointer, showTownStatus } from "./status.mjs";
|
|
3
3
|
import { existsSync, watchFile } from "node:fs";
|
|
4
4
|
|
|
@@ -18,7 +18,7 @@ function watchTown(argv = process.argv.slice(2)) {
|
|
|
18
18
|
showTownStatus(argv);
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
if (import.meta.url
|
|
21
|
+
if (isDirectExecution(import.meta.url)) watchTown();
|
|
22
22
|
|
|
23
23
|
//#endregion
|
|
24
24
|
export { watchTown };
|
package/dist/watch.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.mjs","names":[],"sources":["../src/watch.ts"],"sourcesContent":["import { existsSync, watchFile } from \"node:fs\"\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 (import.meta.url
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schilderlabs/pitown",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Globally installable CLI for Pi Town",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"node": ">=24"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@schilderlabs/pitown-core": "0.1.
|
|
24
|
+
"@schilderlabs/pitown-core": "0.1.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"eslint": "9.39.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-D67TLZft.mjs","names":[],"sources":["../../core/src/shell.ts","../../core/src/repo.ts","../src/paths.ts","../src/config.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\"\n\nexport interface CommandResult {\n\tstdout: string\n\tstderr: string\n\texitCode: number\n}\n\nexport function runCommandSync(\n\tcommand: string,\n\targs: string[],\n\toptions?: { cwd?: string; env?: NodeJS.ProcessEnv },\n): CommandResult {\n\tconst result = spawnSync(command, args, {\n\t\tcwd: options?.cwd,\n\t\tenv: options?.env,\n\t\tencoding: \"utf-8\",\n\t})\n\tconst errorText = result.error instanceof Error ? `${result.error.message}\n` : \"\"\n\n\treturn {\n\t\tstdout: result.stdout ?? \"\",\n\t\tstderr: `${errorText}${result.stderr ?? \"\"}`,\n\t\texitCode: result.status ?? 1,\n\t}\n}\n\nexport function assertSuccess(result: CommandResult, context: string) {\n\tif (result.exitCode === 0) return\n\tconst details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join(\"\\n\")\n\tthrow new Error(`${context} failed${details ? `\\n${details}` : \"\"}`)\n}\n","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 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\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 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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"controller-DJPeTtIy.mjs","names":[],"sources":["../../core/src/events.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 { 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 { appendJsonl } from \"./events.js\"\nimport { acquireRepoLease } from \"./lease.js\"\nimport { computeMetrics } from \"./metrics.js\"\nimport { createRepoSlug, getCurrentBranch, getRepoIdentity, getRepoRoot } from \"./repo.js\"\nimport { 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 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\"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`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 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\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\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 ? `Pi invocation completed.${recommendation}` : `Pi invocation failed.${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\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\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\tconst piResult = runCommandSync(piCommand, [\"--no-session\", \"-p\", prompt], {\n\t\t\tcwd: repoRoot,\n\t\t\tenv: process.env,\n\t\t})\n\t\tconst piEndedAt = new Date().toISOString()\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\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\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\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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"}
|