@schilderlabs/pitown 0.1.2 → 0.2.6
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 +25 -0
- package/dist/{config-Bw-mNdF5.mjs → config-BG1v4iIi.mjs} +29 -50
- package/dist/config-BG1v4iIi.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-WBAQmFbT.mjs +61 -0
- package/dist/entrypoint-WBAQmFbT.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1265 -8
- package/dist/index.mjs.map +1 -1
- package/dist/loop-CocC9qO1.mjs +678 -0
- package/dist/loop-CocC9qO1.mjs.map +1 -0
- package/dist/pi-C7HRNjBG.mjs +12 -0
- package/dist/pi-C7HRNjBG.mjs.map +1 -0
- package/dist/repo-context-BuA2JqPm.mjs +45 -0
- package/dist/repo-context-BuA2JqPm.mjs.map +1 -0
- package/dist/run.d.mts +3 -69
- package/dist/run.mjs +39 -19
- package/dist/run.mjs.map +1 -1
- package/dist/status.mjs +2 -1
- package/dist/status.mjs.map +1 -1
- package/dist/tasks-De4IAy3x.mjs +195 -0
- package/dist/tasks-De4IAy3x.mjs.map +1 -0
- package/dist/types-COGNGvsY.d.mts +142 -0
- package/dist/watch.d.mts +35 -1
- package/dist/watch.mjs +129 -16
- package/dist/watch.mjs.map +1 -1
- package/package.json +21 -23
- package/dist/config-Bw-mNdF5.mjs.map +0 -1
- package/dist/controller-D7lezZjg.mjs +0 -342
- package/dist/controller-D7lezZjg.mjs.map +0 -1
|
@@ -1,342 +0,0 @@
|
|
|
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
|
-
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { homedir, hostname } from "node:os";
|
|
5
|
-
|
|
6
|
-
//#region ../core/src/events.ts
|
|
7
|
-
function appendJsonl(filePath, value) {
|
|
8
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
9
|
-
writeFileSync(filePath, `${JSON.stringify(value)}\n`, {
|
|
10
|
-
encoding: "utf-8",
|
|
11
|
-
flag: "a"
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region ../core/src/lease.ts
|
|
17
|
-
function sanitize(value) {
|
|
18
|
-
return value.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
19
|
-
}
|
|
20
|
-
function processAlive(pid) {
|
|
21
|
-
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
22
|
-
try {
|
|
23
|
-
process.kill(pid, 0);
|
|
24
|
-
return true;
|
|
25
|
-
} catch {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function acquireRepoLease(runId, repoId, branch) {
|
|
30
|
-
const locksDir = join(homedir(), ".pi-town", "locks");
|
|
31
|
-
mkdirSync(locksDir, { recursive: true });
|
|
32
|
-
const leasePath = join(locksDir, `pi-town-${sanitize(repoId)}-${sanitize(branch)}.json`);
|
|
33
|
-
const nextData = {
|
|
34
|
-
runId,
|
|
35
|
-
repoId,
|
|
36
|
-
branch,
|
|
37
|
-
pid: process.pid,
|
|
38
|
-
hostname: hostname(),
|
|
39
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
40
|
-
};
|
|
41
|
-
try {
|
|
42
|
-
const current = JSON.parse(readFileSync(leasePath, "utf-8"));
|
|
43
|
-
if (processAlive(current.pid)) throw new Error(`Pi Town lease already held by pid ${current.pid} on ${current.hostname} for run ${current.runId}.`);
|
|
44
|
-
rmSync(leasePath, { force: true });
|
|
45
|
-
} catch (error) {
|
|
46
|
-
if (error.code !== "ENOENT") {
|
|
47
|
-
if (error instanceof Error && error.message.startsWith("Pi Town lease already held")) throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
writeFileSync(leasePath, `${JSON.stringify(nextData, null, 2)}\n`, "utf-8");
|
|
51
|
-
return {
|
|
52
|
-
path: leasePath,
|
|
53
|
-
release: () => {
|
|
54
|
-
try {
|
|
55
|
-
if (JSON.parse(readFileSync(leasePath, "utf-8")).runId === runId) rmSync(leasePath, { force: true });
|
|
56
|
-
} catch {}
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region ../core/src/metrics.ts
|
|
63
|
-
function round(value) {
|
|
64
|
-
return Math.round(value * 1e3) / 1e3;
|
|
65
|
-
}
|
|
66
|
-
function diffHours(start, end) {
|
|
67
|
-
return (Date.parse(end) - Date.parse(start)) / 36e5;
|
|
68
|
-
}
|
|
69
|
-
function average(values) {
|
|
70
|
-
if (values.length === 0) return null;
|
|
71
|
-
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
72
|
-
}
|
|
73
|
-
function computeInterruptRate(interrupts, taskAttempts) {
|
|
74
|
-
if (taskAttempts.length === 0) return 0;
|
|
75
|
-
return round(interrupts.length / taskAttempts.length);
|
|
76
|
-
}
|
|
77
|
-
function computeAutonomousCompletionRate(taskAttempts) {
|
|
78
|
-
const completed = taskAttempts.filter((task) => task.status === "completed");
|
|
79
|
-
if (completed.length === 0) return 0;
|
|
80
|
-
return round(completed.filter((task) => !task.interrupted).length / completed.length);
|
|
81
|
-
}
|
|
82
|
-
function computeContextCoverageScore(interrupts) {
|
|
83
|
-
const observed = new Set(interrupts.map((interrupt) => interrupt.category));
|
|
84
|
-
if (observed.size === 0) return 0;
|
|
85
|
-
return round(new Set(interrupts.filter((interrupt) => interrupt.fixType).map((interrupt) => interrupt.category)).size / observed.size);
|
|
86
|
-
}
|
|
87
|
-
function computeMeanTimeToCorrect(interrupts) {
|
|
88
|
-
const value = average(interrupts.filter((interrupt) => interrupt.resolvedAt).map((interrupt) => diffHours(interrupt.createdAt, interrupt.resolvedAt)));
|
|
89
|
-
return value === null ? null : round(value);
|
|
90
|
-
}
|
|
91
|
-
function computeFeedbackToDemoCycleTime(feedbackCycles) {
|
|
92
|
-
const value = average(feedbackCycles.map((cycle) => diffHours(cycle.feedbackAt, cycle.demoReadyAt)));
|
|
93
|
-
return value === null ? null : round(value);
|
|
94
|
-
}
|
|
95
|
-
function computeMetrics(input) {
|
|
96
|
-
const observedCategories = new Set(input.interrupts.map((interrupt) => interrupt.category));
|
|
97
|
-
const coveredCategories = new Set(input.interrupts.filter((interrupt) => interrupt.fixType).map((interrupt) => interrupt.category));
|
|
98
|
-
const completedTasks = input.taskAttempts.filter((task) => task.status === "completed").length;
|
|
99
|
-
return {
|
|
100
|
-
interruptRate: computeInterruptRate(input.interrupts, input.taskAttempts),
|
|
101
|
-
autonomousCompletionRate: computeAutonomousCompletionRate(input.taskAttempts),
|
|
102
|
-
contextCoverageScore: computeContextCoverageScore(input.interrupts),
|
|
103
|
-
meanTimeToCorrectHours: computeMeanTimeToCorrect(input.interrupts),
|
|
104
|
-
feedbackToDemoCycleTimeHours: computeFeedbackToDemoCycleTime(input.feedbackCycles ?? []),
|
|
105
|
-
totals: {
|
|
106
|
-
taskAttempts: input.taskAttempts.length,
|
|
107
|
-
completedTasks,
|
|
108
|
-
interrupts: input.interrupts.length,
|
|
109
|
-
observedInterruptCategories: observedCategories.size,
|
|
110
|
-
coveredInterruptCategories: coveredCategories.size
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
//#endregion
|
|
116
|
-
//#region ../core/src/controller.ts
|
|
117
|
-
function createRunId() {
|
|
118
|
-
return `run-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
119
|
-
}
|
|
120
|
-
function writeJson(path, value) {
|
|
121
|
-
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
|
|
122
|
-
}
|
|
123
|
-
function writeText(path, value) {
|
|
124
|
-
writeFileSync(path, value, "utf-8");
|
|
125
|
-
}
|
|
126
|
-
function createPiPrompt(input) {
|
|
127
|
-
const goal = input.goal ?? "continue from current scaffold state";
|
|
128
|
-
if (input.planPath) return [
|
|
129
|
-
"Read the private plans in:",
|
|
130
|
-
`- ${input.planPath}`,
|
|
131
|
-
"",
|
|
132
|
-
"and the current code in:",
|
|
133
|
-
`- ${input.repoRoot}`,
|
|
134
|
-
"",
|
|
135
|
-
`Goal: ${goal}`,
|
|
136
|
-
"Continue from the current scaffold state.",
|
|
137
|
-
"Keep any persisted run artifacts high-signal and avoid copying private plan contents into them."
|
|
138
|
-
].join("\n");
|
|
139
|
-
return [
|
|
140
|
-
`Work in the repository at: ${input.repoRoot}`,
|
|
141
|
-
`Goal: ${goal}`,
|
|
142
|
-
"No private plan path is configured for this run.",
|
|
143
|
-
input.recommendedPlanDir ? `If you need private plans, use a user-owned location such as: ${input.recommendedPlanDir}` : "If you need private plans, keep them in a user-owned location outside the repo.",
|
|
144
|
-
"Continue from the current scaffold state."
|
|
145
|
-
].join("\n");
|
|
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
|
-
}
|
|
164
|
-
function createManifest(input) {
|
|
165
|
-
return {
|
|
166
|
-
runId: input.runId,
|
|
167
|
-
repoId: input.repoId,
|
|
168
|
-
repoSlug: input.repoSlug,
|
|
169
|
-
repoRoot: input.repoRoot,
|
|
170
|
-
branch: input.branch,
|
|
171
|
-
goal: input.goal,
|
|
172
|
-
planPath: input.planPath,
|
|
173
|
-
recommendedPlanDir: input.recommendedPlanDir,
|
|
174
|
-
mode: input.mode,
|
|
175
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
176
|
-
endedAt: null,
|
|
177
|
-
stopReason: null,
|
|
178
|
-
leasePath: input.leasePath,
|
|
179
|
-
piExitCode: null,
|
|
180
|
-
completedTaskCount: 0,
|
|
181
|
-
blockedTaskCount: 0,
|
|
182
|
-
skippedTaskCount: 0,
|
|
183
|
-
totalCostUsd: 0
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
function createSummary(input) {
|
|
187
|
-
const success = input.exitCode === 0;
|
|
188
|
-
const recommendation = input.recommendedPlanDir === null ? "" : ` No plan path was configured. Recommended private plans location: ${input.recommendedPlanDir}.`;
|
|
189
|
-
return {
|
|
190
|
-
runId: input.runId,
|
|
191
|
-
mode: input.mode,
|
|
192
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
193
|
-
success,
|
|
194
|
-
message: success ? `Pi invocation completed.${recommendation}` : `Pi invocation failed.${recommendation}`,
|
|
195
|
-
piExitCode: input.exitCode,
|
|
196
|
-
recommendedPlanDir: input.recommendedPlanDir
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
function runController(options) {
|
|
200
|
-
const cwd = options.cwd ?? process.cwd();
|
|
201
|
-
const artifactsDir = options.artifactsDir;
|
|
202
|
-
const repoRoot = getRepoRoot(cwd);
|
|
203
|
-
const repoId = getRepoIdentity(repoRoot);
|
|
204
|
-
const repoSlug = createRepoSlug(repoId, repoRoot);
|
|
205
|
-
const branch = options.branch ?? getCurrentBranch(repoRoot) ?? "workspace";
|
|
206
|
-
const goal = options.goal ?? null;
|
|
207
|
-
const planPath = options.planPath ?? null;
|
|
208
|
-
const recommendedPlanDir = planPath ? null : options.recommendedPlanDir ?? null;
|
|
209
|
-
const mode = options.mode ?? "single-pi";
|
|
210
|
-
const piCommand = options.piCommand ?? "pi";
|
|
211
|
-
const runId = createRunId();
|
|
212
|
-
const runDir = join(artifactsDir, "runs", runId);
|
|
213
|
-
const latestDir = join(artifactsDir, "latest");
|
|
214
|
-
const stdoutPath = join(runDir, "stdout.txt");
|
|
215
|
-
const stderrPath = join(runDir, "stderr.txt");
|
|
216
|
-
const prompt = createPiPrompt({
|
|
217
|
-
repoRoot,
|
|
218
|
-
planPath,
|
|
219
|
-
goal,
|
|
220
|
-
recommendedPlanDir
|
|
221
|
-
});
|
|
222
|
-
assertPiRuntimeAvailable(piCommand);
|
|
223
|
-
mkdirSync(runDir, { recursive: true });
|
|
224
|
-
mkdirSync(latestDir, { recursive: true });
|
|
225
|
-
writeText(join(runDir, "questions.jsonl"), "");
|
|
226
|
-
writeText(join(runDir, "interventions.jsonl"), "");
|
|
227
|
-
writeJson(join(runDir, "agent-state.json"), {
|
|
228
|
-
status: "starting",
|
|
229
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
230
|
-
});
|
|
231
|
-
const lease = acquireRepoLease(runId, repoId, branch);
|
|
232
|
-
try {
|
|
233
|
-
const manifest = createManifest({
|
|
234
|
-
runId,
|
|
235
|
-
repoId,
|
|
236
|
-
repoSlug,
|
|
237
|
-
repoRoot,
|
|
238
|
-
branch,
|
|
239
|
-
goal,
|
|
240
|
-
planPath,
|
|
241
|
-
recommendedPlanDir,
|
|
242
|
-
mode,
|
|
243
|
-
leasePath: lease.path
|
|
244
|
-
});
|
|
245
|
-
appendJsonl(join(runDir, "events.jsonl"), {
|
|
246
|
-
type: "run_started",
|
|
247
|
-
runId,
|
|
248
|
-
repoId,
|
|
249
|
-
repoSlug,
|
|
250
|
-
branch,
|
|
251
|
-
createdAt: manifest.startedAt
|
|
252
|
-
});
|
|
253
|
-
const piStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
254
|
-
appendJsonl(join(runDir, "events.jsonl"), {
|
|
255
|
-
type: "pi_invocation_started",
|
|
256
|
-
runId,
|
|
257
|
-
command: piCommand,
|
|
258
|
-
createdAt: piStartedAt
|
|
259
|
-
});
|
|
260
|
-
const piResult = runCommandSync(piCommand, [
|
|
261
|
-
"--no-session",
|
|
262
|
-
"-p",
|
|
263
|
-
prompt
|
|
264
|
-
], {
|
|
265
|
-
cwd: repoRoot,
|
|
266
|
-
env: process.env
|
|
267
|
-
});
|
|
268
|
-
const piEndedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
269
|
-
writeText(stdoutPath, piResult.stdout);
|
|
270
|
-
writeText(stderrPath, piResult.stderr);
|
|
271
|
-
const piInvocation = {
|
|
272
|
-
command: piCommand,
|
|
273
|
-
cwd: repoRoot,
|
|
274
|
-
repoRoot,
|
|
275
|
-
planPath,
|
|
276
|
-
goal,
|
|
277
|
-
startedAt: piStartedAt,
|
|
278
|
-
endedAt: piEndedAt,
|
|
279
|
-
exitCode: piResult.exitCode,
|
|
280
|
-
stdoutPath,
|
|
281
|
-
stderrPath,
|
|
282
|
-
promptSummary: planPath ? "Read private plan path and continue from current scaffold state." : "Continue from current scaffold state without a configured private plan path."
|
|
283
|
-
};
|
|
284
|
-
writeJson(join(runDir, "pi-invocation.json"), piInvocation);
|
|
285
|
-
appendJsonl(join(runDir, "events.jsonl"), {
|
|
286
|
-
type: "pi_invocation_finished",
|
|
287
|
-
runId,
|
|
288
|
-
command: piCommand,
|
|
289
|
-
exitCode: piInvocation.exitCode,
|
|
290
|
-
createdAt: piEndedAt
|
|
291
|
-
});
|
|
292
|
-
const metrics = computeMetrics({
|
|
293
|
-
taskAttempts: [],
|
|
294
|
-
interrupts: []
|
|
295
|
-
});
|
|
296
|
-
const summary = createSummary({
|
|
297
|
-
runId,
|
|
298
|
-
mode,
|
|
299
|
-
exitCode: piInvocation.exitCode,
|
|
300
|
-
recommendedPlanDir
|
|
301
|
-
});
|
|
302
|
-
const finalManifest = {
|
|
303
|
-
...manifest,
|
|
304
|
-
endedAt: piEndedAt,
|
|
305
|
-
stopReason: piInvocation.exitCode === 0 ? "pi invocation completed" : `pi invocation exited with code ${piInvocation.exitCode}`,
|
|
306
|
-
piExitCode: piInvocation.exitCode
|
|
307
|
-
};
|
|
308
|
-
writeJson(join(runDir, "manifest.json"), finalManifest);
|
|
309
|
-
writeJson(join(runDir, "metrics.json"), metrics);
|
|
310
|
-
writeJson(join(runDir, "run-summary.json"), summary);
|
|
311
|
-
writeJson(join(runDir, "agent-state.json"), {
|
|
312
|
-
status: summary.success ? "completed" : "failed",
|
|
313
|
-
updatedAt: piEndedAt,
|
|
314
|
-
exitCode: piInvocation.exitCode
|
|
315
|
-
});
|
|
316
|
-
writeJson(join(latestDir, "manifest.json"), finalManifest);
|
|
317
|
-
writeJson(join(latestDir, "metrics.json"), metrics);
|
|
318
|
-
writeJson(join(latestDir, "run-summary.json"), summary);
|
|
319
|
-
appendJsonl(join(runDir, "events.jsonl"), {
|
|
320
|
-
type: "run_finished",
|
|
321
|
-
runId,
|
|
322
|
-
createdAt: finalManifest.endedAt,
|
|
323
|
-
stopReason: finalManifest.stopReason,
|
|
324
|
-
metrics
|
|
325
|
-
});
|
|
326
|
-
return {
|
|
327
|
-
runId,
|
|
328
|
-
runDir,
|
|
329
|
-
latestDir,
|
|
330
|
-
manifest: finalManifest,
|
|
331
|
-
metrics,
|
|
332
|
-
summary,
|
|
333
|
-
piInvocation
|
|
334
|
-
};
|
|
335
|
-
} finally {
|
|
336
|
-
lease.release();
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
//#endregion
|
|
341
|
-
export { runController as t };
|
|
342
|
-
//# sourceMappingURL=controller-D7lezZjg.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
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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"}
|