infinity-harness 2.8.9 → 2.8.11
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/CHANGELOG.md +18 -0
- package/dist/extensions/infinity-harness/index.js +3614 -0
- package/dist/extensions/infinity-harness/index.js.map +1 -0
- package/dist/src/approval.js +154 -0
- package/dist/src/approval.js.map +1 -0
- package/dist/src/core/brief.js +309 -0
- package/dist/src/core/brief.js.map +1 -0
- package/dist/src/core/config.js +539 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/exec.js +106 -0
- package/dist/src/core/exec.js.map +1 -0
- package/dist/src/core/featureList.js +365 -0
- package/dist/src/core/featureList.js.map +1 -0
- package/dist/src/core/fsx.js +123 -0
- package/dist/src/core/fsx.js.map +1 -0
- package/dist/src/core/gates.js +490 -0
- package/dist/src/core/gates.js.map +1 -0
- package/dist/src/core/init.js +480 -0
- package/dist/src/core/init.js.map +1 -0
- package/dist/src/core/lock.js +183 -0
- package/dist/src/core/lock.js.map +1 -0
- package/dist/src/core/modelRouter.js +185 -0
- package/dist/src/core/modelRouter.js.map +1 -0
- package/dist/src/core/paths.js +129 -0
- package/dist/src/core/paths.js.map +1 -0
- package/dist/src/core/phases.js +254 -0
- package/dist/src/core/phases.js.map +1 -0
- package/dist/src/core/plan.js +15 -0
- package/dist/src/core/plan.js.map +1 -0
- package/dist/src/core/runState.js +126 -0
- package/dist/src/core/runState.js.map +1 -0
- package/dist/src/core/settings.js +877 -0
- package/dist/src/core/settings.js.map +1 -0
- package/dist/src/core/skills.js +319 -0
- package/dist/src/core/skills.js.map +1 -0
- package/dist/src/core/skillsAudit.js +194 -0
- package/dist/src/core/skillsAudit.js.map +1 -0
- package/dist/src/core/types.js +77 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/daemon/budget.js +85 -0
- package/dist/src/daemon/budget.js.map +1 -0
- package/dist/src/daemon/guard.js +122 -0
- package/dist/src/daemon/guard.js.map +1 -0
- package/dist/src/daemon/index.js +461 -0
- package/dist/src/daemon/index.js.map +1 -0
- package/dist/src/daemon/isolation.js +84 -0
- package/dist/src/daemon/isolation.js.map +1 -0
- package/dist/src/daemon/preflight.js +131 -0
- package/dist/src/daemon/preflight.js.map +1 -0
- package/dist/src/daemon/server.js +174 -0
- package/dist/src/daemon/server.js.map +1 -0
- package/dist/src/daemon/supervisorState.js +42 -0
- package/dist/src/daemon/supervisorState.js.map +1 -0
- package/dist/src/daemon/worker.js +239 -0
- package/dist/src/daemon/worker.js.map +1 -0
- package/dist/src/daemon/worktree.js +102 -0
- package/dist/src/daemon/worktree.js.map +1 -0
- package/dist/src/escalate.js +312 -0
- package/dist/src/escalate.js.map +1 -0
- package/dist/src/exec/piWorker.js +630 -0
- package/dist/src/exec/piWorker.js.map +1 -0
- package/dist/src/goal.js +317 -0
- package/dist/src/goal.js.map +1 -0
- package/dist/src/goalLoop.js +374 -0
- package/dist/src/goalLoop.js.map +1 -0
- package/dist/src/goalSpec.js +397 -0
- package/dist/src/goalSpec.js.map +1 -0
- package/dist/src/goalState.js +255 -0
- package/dist/src/goalState.js.map +1 -0
- package/dist/src/handoff.js +205 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/intake.js +203 -0
- package/dist/src/intake.js.map +1 -0
- package/dist/src/loop.js +592 -0
- package/dist/src/loop.js.map +1 -0
- package/dist/src/modelRouter.js +216 -0
- package/dist/src/modelRouter.js.map +1 -0
- package/dist/src/remote.js +246 -0
- package/dist/src/remote.js.map +1 -0
- package/dist/src/replan.js +262 -0
- package/dist/src/replan.js.map +1 -0
- package/dist/src/review.js +54 -0
- package/dist/src/review.js.map +1 -0
- package/dist/src/rework.js +241 -0
- package/dist/src/rework.js.map +1 -0
- package/dist/src/runState.js +4 -0
- package/dist/src/runState.js.map +1 -0
- package/dist/src/scheduler.js +270 -0
- package/dist/src/scheduler.js.map +1 -0
- package/dist/src/supervisor.js +787 -0
- package/dist/src/supervisor.js.map +1 -0
- package/dist/src/taskList.js +430 -0
- package/dist/src/taskList.js.map +1 -0
- package/dist/src/ui/config.js +268 -0
- package/dist/src/ui/config.js.map +1 -0
- package/dist/src/ui/dashboard.js +1288 -0
- package/dist/src/ui/dashboard.js.map +1 -0
- package/dist/src/ui/display.js +229 -0
- package/dist/src/ui/display.js.map +1 -0
- package/dist/src/ui/planTree.js +219 -0
- package/dist/src/ui/planTree.js.map +1 -0
- package/dist/src/ui/theme.js +298 -0
- package/dist/src/ui/theme.js.map +1 -0
- package/dist/src/ui/viewState.js +65 -0
- package/dist/src/ui/viewState.js.map +1 -0
- package/dist/src/ui/widget.js +721 -0
- package/dist/src/ui/widget.js.map +1 -0
- package/dist/src/ui/wizard.js +540 -0
- package/dist/src/ui/wizard.js.map +1 -0
- package/dist/src/unstuck.js +210 -0
- package/dist/src/unstuck.js.map +1 -0
- package/dist/src/worker.js +267 -0
- package/dist/src/worker.js.map +1 -0
- package/dist/src/workflow.js +250 -0
- package/dist/src/workflow.js.map +1 -0
- package/dist/tests/approval.test.js +144 -0
- package/dist/tests/approval.test.js.map +1 -0
- package/dist/tests/brief.test.js +271 -0
- package/dist/tests/brief.test.js.map +1 -0
- package/dist/tests/config.test.js +320 -0
- package/dist/tests/config.test.js.map +1 -0
- package/dist/tests/dashboard.test.js +277 -0
- package/dist/tests/dashboard.test.js.map +1 -0
- package/dist/tests/display.test.js +229 -0
- package/dist/tests/display.test.js.map +1 -0
- package/dist/tests/escalate.test.js +272 -0
- package/dist/tests/escalate.test.js.map +1 -0
- package/dist/tests/featureList.test.js +429 -0
- package/dist/tests/featureList.test.js.map +1 -0
- package/dist/tests/gates.test.js +333 -0
- package/dist/tests/gates.test.js.map +1 -0
- package/dist/tests/goal.test.js +229 -0
- package/dist/tests/goal.test.js.map +1 -0
- package/dist/tests/goalLoop.test.js +170 -0
- package/dist/tests/goalLoop.test.js.map +1 -0
- package/dist/tests/goalSpec.test.js +146 -0
- package/dist/tests/goalSpec.test.js.map +1 -0
- package/dist/tests/goalState.test.js +202 -0
- package/dist/tests/goalState.test.js.map +1 -0
- package/dist/tests/handoff.test.js +127 -0
- package/dist/tests/handoff.test.js.map +1 -0
- package/dist/tests/init.test.js +264 -0
- package/dist/tests/init.test.js.map +1 -0
- package/dist/tests/intake.test.js +288 -0
- package/dist/tests/intake.test.js.map +1 -0
- package/dist/tests/loop.test.js +453 -0
- package/dist/tests/loop.test.js.map +1 -0
- package/dist/tests/modelRouter.test.js +131 -0
- package/dist/tests/modelRouter.test.js.map +1 -0
- package/dist/tests/phases.test.js +244 -0
- package/dist/tests/phases.test.js.map +1 -0
- package/dist/tests/planTree.test.js +190 -0
- package/dist/tests/planTree.test.js.map +1 -0
- package/dist/tests/remote.test.js +380 -0
- package/dist/tests/remote.test.js.map +1 -0
- package/dist/tests/replan.test.js +113 -0
- package/dist/tests/replan.test.js.map +1 -0
- package/dist/tests/review.test.js +77 -0
- package/dist/tests/review.test.js.map +1 -0
- package/dist/tests/rework.test.js +109 -0
- package/dist/tests/rework.test.js.map +1 -0
- package/dist/tests/routing-live.test.js +121 -0
- package/dist/tests/routing-live.test.js.map +1 -0
- package/dist/tests/runState.test.js +73 -0
- package/dist/tests/runState.test.js.map +1 -0
- package/dist/tests/settings.test.js +311 -0
- package/dist/tests/settings.test.js.map +1 -0
- package/dist/tests/skills.test.js +387 -0
- package/dist/tests/skills.test.js.map +1 -0
- package/dist/tests/supervisor.test.js +437 -0
- package/dist/tests/supervisor.test.js.map +1 -0
- package/dist/tests/surface.test.js +203 -0
- package/dist/tests/surface.test.js.map +1 -0
- package/dist/tests/taskList.test.js +376 -0
- package/dist/tests/taskList.test.js.map +1 -0
- package/dist/tests/theme.test.js +149 -0
- package/dist/tests/theme.test.js.map +1 -0
- package/dist/tests/unstuck.test.js +268 -0
- package/dist/tests/unstuck.test.js.map +1 -0
- package/dist/tests/widget.test.js +424 -0
- package/dist/tests/widget.test.js.map +1 -0
- package/dist/tests/worker.test.js +306 -0
- package/dist/tests/worker.test.js.map +1 -0
- package/dist/tests/workflow.test.js +187 -0
- package/dist/tests/workflow.test.js.map +1 -0
- package/extensions/infinity-harness/index.ts +39 -12
- package/package.json +5 -2
- package/src/core/modelRouter.ts +23 -1
- package/src/daemon/index.ts +3 -3
- package/src/daemon/worker.ts +28 -5
- package/src/ui/config.ts +25 -1
- package/src/ui/wizard.ts +10 -5
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { hashLite, buildFingerprint, getWorkerRoot, getTaskRoot, getAttemptDir, getNextAttemptNumber, createWorkerRunDir, recordAttempt, createAndRecordAttempt, spawnIsolatedWorker, withFeatureListLock, withHarnessLocks, WORKER_ROOT_SEGMENT, PROMPT_FILE, OUTPUT_FILE, FINGERPRINT_FILE, } from "../src/worker.js";
|
|
6
|
+
function tmpProject() {
|
|
7
|
+
const d = mkdtempSync(join(tmpdir(), "pi-worker-"));
|
|
8
|
+
mkdirSync(join(d, "harness", "features"), { recursive: true });
|
|
9
|
+
return d;
|
|
10
|
+
}
|
|
11
|
+
// --- constants & hashLite ---
|
|
12
|
+
assert.equal(WORKER_ROOT_SEGMENT, "tmp/infinity-harness");
|
|
13
|
+
assert.equal(PROMPT_FILE, "prompt.md");
|
|
14
|
+
assert.equal(OUTPUT_FILE, "output.log");
|
|
15
|
+
assert.equal(FINGERPRINT_FILE, "fingerprint.json");
|
|
16
|
+
assert.equal(hashLite(""), 0);
|
|
17
|
+
{
|
|
18
|
+
const h1 = hashLite("hello");
|
|
19
|
+
const h2 = hashLite("hello");
|
|
20
|
+
assert.equal(h1, h2, "deterministic");
|
|
21
|
+
assert.notEqual(hashLite("hello"), hashLite("world"));
|
|
22
|
+
console.log("✓ hashLite deterministic");
|
|
23
|
+
}
|
|
24
|
+
// --- getWorkerRoot / getTaskRoot / getAttemptDir ---
|
|
25
|
+
{
|
|
26
|
+
const root = getWorkerRoot("/tmp/proj");
|
|
27
|
+
assert.equal(root, resolve("/tmp/proj", "tmp/infinity-harness"));
|
|
28
|
+
const taskRoot = getTaskRoot("/tmp/proj", "run-1", "feature-003", "task-005");
|
|
29
|
+
assert.ok(taskRoot.includes("run-1") && taskRoot.includes("feature-003") && taskRoot.includes("task-005"));
|
|
30
|
+
const attemptDir = getAttemptDir("/tmp/proj", "run-1", "feature-003", "task-005", 2);
|
|
31
|
+
assert.ok(attemptDir.endsWith("attempt-2"));
|
|
32
|
+
console.log("✓ worker dir helpers");
|
|
33
|
+
}
|
|
34
|
+
// --- createWorkerRunDir + getNextAttemptNumber increment ---
|
|
35
|
+
{
|
|
36
|
+
const proj = tmpProject();
|
|
37
|
+
try {
|
|
38
|
+
// first attempt
|
|
39
|
+
const d1 = createWorkerRunDir(proj, "run-xyz", "feature-003", "task-005");
|
|
40
|
+
assert.ok(existsSync(d1), "attempt-1 exists");
|
|
41
|
+
assert.ok(d1.endsWith("attempt-1"));
|
|
42
|
+
// second call without explicit attempt -> attempt-2
|
|
43
|
+
const d2 = createWorkerRunDir(proj, "run-xyz", "feature-003", "task-005");
|
|
44
|
+
assert.ok(d2.endsWith("attempt-2"));
|
|
45
|
+
assert.ok(existsSync(d2));
|
|
46
|
+
// explicit attempt
|
|
47
|
+
const d5 = createWorkerRunDir(proj, "run-xyz", "feature-003", "task-005", 5);
|
|
48
|
+
assert.ok(d5.endsWith("attempt-5"));
|
|
49
|
+
// getNextAttemptNumber should be 6 (max 5 +1)
|
|
50
|
+
const taskRoot = getTaskRoot(proj, "run-xyz", "feature-003", "task-005");
|
|
51
|
+
assert.equal(getNextAttemptNumber(taskRoot), 6);
|
|
52
|
+
console.log("✓ createWorkerRunDir increments");
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
rmSync(proj, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// --- recordAttempt writes three files ---
|
|
59
|
+
{
|
|
60
|
+
const proj = tmpProject();
|
|
61
|
+
try {
|
|
62
|
+
const dir = createWorkerRunDir(proj, "run-a", "f-1", "t-1");
|
|
63
|
+
const fp = buildFingerprint({ projectDir: proj, runId: "run-a", featureId: "f-1", taskId: "t-1", attempt: 1, baseRevision: 2 });
|
|
64
|
+
const res = recordAttempt(dir, { prompt: "hello prompt", output: "hello output", fingerprint: fp });
|
|
65
|
+
assert.ok(existsSync(res.promptPath));
|
|
66
|
+
assert.ok(existsSync(res.outputPath));
|
|
67
|
+
assert.ok(existsSync(res.fingerprintPath));
|
|
68
|
+
assert.equal(readFileSync(res.promptPath, "utf-8"), "hello prompt");
|
|
69
|
+
assert.equal(readFileSync(res.outputPath, "utf-8"), "hello output");
|
|
70
|
+
const saved = JSON.parse(readFileSync(res.fingerprintPath, "utf-8"));
|
|
71
|
+
assert.equal(saved.runId, "run-a");
|
|
72
|
+
assert.equal(saved.baseRevision, 2);
|
|
73
|
+
assert.equal(saved.attempt, 1);
|
|
74
|
+
console.log("✓ recordAttempt writes prompt/output/fingerprint");
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
rmSync(proj, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// --- createAndRecordAttempt convenience ---
|
|
81
|
+
{
|
|
82
|
+
const proj = tmpProject();
|
|
83
|
+
try {
|
|
84
|
+
const fp = buildFingerprint({ projectDir: proj, runId: "run-b", featureId: "feat-2", taskId: "task-006", attempt: 1, baseRevision: 0 });
|
|
85
|
+
const { attemptDir, attempt, fingerprint } = createAndRecordAttempt(proj, "run-b", "feat-2", "task-006", {
|
|
86
|
+
prompt: "p",
|
|
87
|
+
output: "o",
|
|
88
|
+
fingerprint: fp,
|
|
89
|
+
});
|
|
90
|
+
assert.equal(attempt, 1);
|
|
91
|
+
assert.ok(existsSync(join(attemptDir, "prompt.md")));
|
|
92
|
+
assert.equal(fingerprint.attempt, 1);
|
|
93
|
+
// second call increments
|
|
94
|
+
const r2 = createAndRecordAttempt(proj, "run-b", "feat-2", "task-006", { prompt: "p2", output: "o2", fingerprint: fp });
|
|
95
|
+
assert.equal(r2.attempt, 2);
|
|
96
|
+
console.log("✓ createAndRecordAttempt increments");
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
rmSync(proj, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// --- buildFingerprint preserves baseRevision & featureListHash ---
|
|
103
|
+
{
|
|
104
|
+
const proj = tmpProject();
|
|
105
|
+
try {
|
|
106
|
+
// write feature-list with baseRevision 7 to test reading
|
|
107
|
+
writeFileSync(join(proj, "harness", "features", "feature-list.json"), JSON.stringify({ version: "0.1", baseRevision: 7, features: [] }, null, 2));
|
|
108
|
+
const fp = buildFingerprint({ projectDir: proj, runId: "run-c", featureId: "f", taskId: "t", attempt: 1 });
|
|
109
|
+
assert.equal(fp.baseRevision, 7, "reads baseRevision from file");
|
|
110
|
+
assert.ok(typeof fp.timestamp === "string" && fp.timestamp.includes("T"), "timestamp iso");
|
|
111
|
+
assert.ok(typeof fp.featureListHash === "number");
|
|
112
|
+
// explicit baseRevision overrides file
|
|
113
|
+
const fp2 = buildFingerprint({ projectDir: proj, runId: "run-c", featureId: "f", taskId: "t", attempt: 2, baseRevision: 42 });
|
|
114
|
+
assert.equal(fp2.baseRevision, 42);
|
|
115
|
+
console.log("✓ buildFingerprint preserves baseRevision");
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
rmSync(proj, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// --- baseRevision preserved through file helpers (integration with taskList) ---
|
|
122
|
+
{
|
|
123
|
+
const proj = tmpProject();
|
|
124
|
+
try {
|
|
125
|
+
// Use the plan editor to create a file, then fingerprint should see its revision
|
|
126
|
+
const { writeTaskList } = await import("../src/taskList.js");
|
|
127
|
+
// initial file rev 0 -> create task
|
|
128
|
+
const r1 = writeTaskList(proj, { tasks: [{ key: "a", subject: "A", status: "pending" }] });
|
|
129
|
+
assert.equal(r1.revision, 1);
|
|
130
|
+
const fp = buildFingerprint({ projectDir: proj, runId: "run-d", featureId: "f", taskId: "t", attempt: 1 });
|
|
131
|
+
assert.equal(fp.baseRevision, 1);
|
|
132
|
+
// worker dir should not corrupt file
|
|
133
|
+
const dir = createWorkerRunDir(proj, "run-d", "f", "t");
|
|
134
|
+
const fp2 = buildFingerprint({ projectDir: proj, runId: "run-d", featureId: "f", taskId: "t", attempt: 1, baseRevision: 1 });
|
|
135
|
+
recordAttempt(dir, { prompt: "p", output: "done", fingerprint: fp2 });
|
|
136
|
+
// file still rev 1
|
|
137
|
+
const after = JSON.parse(readFileSync(join(proj, "harness", "features", "feature-list.json"), "utf-8"));
|
|
138
|
+
assert.equal(after.baseRevision, 1, "worker attempt does not corrupt baseRevision");
|
|
139
|
+
console.log("✓ baseRevision preserved after worker record");
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
rmSync(proj, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// --- proper-lockfile: concurrent workers do not corrupt baseRevision ---
|
|
146
|
+
{
|
|
147
|
+
const proj = tmpProject();
|
|
148
|
+
try {
|
|
149
|
+
const { writeTaskList } = await import("../src/taskList.js");
|
|
150
|
+
writeTaskList(proj, { tasks: [{ key: "a", subject: "A", status: "pending" }] });
|
|
151
|
+
// simulate two workers trying to update concurrently with lock
|
|
152
|
+
const worker = async (id) => {
|
|
153
|
+
return withFeatureListLock(proj, async () => {
|
|
154
|
+
// read, apply, write under lock
|
|
155
|
+
const before = JSON.parse(readFileSync(join(proj, "harness", "features", "feature-list.json"), "utf-8"));
|
|
156
|
+
const rev = before.baseRevision;
|
|
157
|
+
// small delay to increase race window
|
|
158
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
159
|
+
// try to apply with current rev; one will succeed, other may see stale if not re-read
|
|
160
|
+
// For this test, we just verify lock serializes: we do a simple file write under lock
|
|
161
|
+
const p = join(proj, "harness", "features", "feature-list.json");
|
|
162
|
+
const cur = JSON.parse(readFileSync(p, "utf-8"));
|
|
163
|
+
cur.baseRevision = cur.baseRevision + 1;
|
|
164
|
+
cur.features[0].tasks.push({ id: `c${id}`, key: `c${id}`, description: `C${id}`, status: "pending", dependsOn: [] });
|
|
165
|
+
writeFileSync(p, JSON.stringify(cur, null, 2));
|
|
166
|
+
return cur.baseRevision;
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
const [r1, r2] = await Promise.all([worker(1), worker(2)]);
|
|
170
|
+
const final = JSON.parse(readFileSync(join(proj, "harness", "features", "feature-list.json"), "utf-8"));
|
|
171
|
+
// Both should have succeeded sequentially due to lock, final rev = initial 1 + 2 = 3
|
|
172
|
+
assert.equal(final.baseRevision, 3, `expected rev 3 after two locked writes, got ${final.baseRevision} r1=${r1} r2=${r2}`);
|
|
173
|
+
assert.equal(final.features[0].tasks.length, 3, "both tasks present");
|
|
174
|
+
console.log("✓ proper-lockfile concurrent writes serialized");
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
rmSync(proj, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// --- withHarnessLocks also works (locks both files) ---
|
|
181
|
+
{
|
|
182
|
+
const proj = tmpProject();
|
|
183
|
+
try {
|
|
184
|
+
writeFileSync(join(proj, "harness", "config.json"), JSON.stringify({ phase: "build" }, null, 2));
|
|
185
|
+
writeFileSync(join(proj, "harness", "features", "feature-list.json"), JSON.stringify({ version: "0.1", baseRevision: 0, features: [] }, null, 2));
|
|
186
|
+
let ran = false;
|
|
187
|
+
await withHarnessLocks(proj, async () => {
|
|
188
|
+
ran = true;
|
|
189
|
+
assert.ok(existsSync(join(proj, "harness", "features", "feature-list.json")));
|
|
190
|
+
assert.ok(existsSync(join(proj, "harness", "config.json")));
|
|
191
|
+
});
|
|
192
|
+
assert.ok(ran);
|
|
193
|
+
console.log("✓ withHarnessLocks");
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
rmSync(proj, { recursive: true, force: true });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// --- spawnIsolatedWorker: no command just records ---
|
|
200
|
+
{
|
|
201
|
+
const proj = tmpProject();
|
|
202
|
+
try {
|
|
203
|
+
const res = await spawnIsolatedWorker({
|
|
204
|
+
projectDir: proj,
|
|
205
|
+
runId: "run-e",
|
|
206
|
+
featureId: "feature-003",
|
|
207
|
+
taskId: "task-005",
|
|
208
|
+
prompt: "do the thing",
|
|
209
|
+
});
|
|
210
|
+
assert.ok(existsSync(join(res.attemptDir, "prompt.md")));
|
|
211
|
+
assert.equal(readFileSync(join(res.attemptDir, "prompt.md"), "utf-8"), "do the thing");
|
|
212
|
+
assert.equal(res.exitCode, 0);
|
|
213
|
+
assert.equal(res.fingerprint.runId, "run-e");
|
|
214
|
+
assert.equal(res.attempt, 1);
|
|
215
|
+
// second spawn increments to 2
|
|
216
|
+
const res2 = await spawnIsolatedWorker({
|
|
217
|
+
projectDir: proj,
|
|
218
|
+
runId: "run-e",
|
|
219
|
+
featureId: "feature-003",
|
|
220
|
+
taskId: "task-005",
|
|
221
|
+
prompt: "second",
|
|
222
|
+
});
|
|
223
|
+
assert.equal(res2.attempt, 2);
|
|
224
|
+
assert.ok(res2.attemptDir.endsWith("attempt-2"));
|
|
225
|
+
console.log("✓ spawnIsolatedWorker no-command records");
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
rmSync(proj, { recursive: true, force: true });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// --- spawnIsolatedWorker with command writes output.log ---
|
|
232
|
+
{
|
|
233
|
+
const proj = tmpProject();
|
|
234
|
+
try {
|
|
235
|
+
const res = await spawnIsolatedWorker({
|
|
236
|
+
projectDir: proj,
|
|
237
|
+
runId: "run-f",
|
|
238
|
+
featureId: "feature-003",
|
|
239
|
+
taskId: "task-005",
|
|
240
|
+
prompt: "echo test",
|
|
241
|
+
command: "echo hello-worker",
|
|
242
|
+
timeoutMs: 5000,
|
|
243
|
+
});
|
|
244
|
+
assert.ok(existsSync(join(res.attemptDir, "output.log")));
|
|
245
|
+
const out = readFileSync(join(res.attemptDir, "output.log"), "utf-8");
|
|
246
|
+
assert.ok(out.includes("hello-worker"), `output should contain hello-worker, got ${out}`);
|
|
247
|
+
assert.equal(res.exitCode, 0);
|
|
248
|
+
console.log("✓ spawnIsolatedWorker with command writes output.log");
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
rmSync(proj, { recursive: true, force: true });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// --- fingerprint.json is valid JSON with required fields ---
|
|
255
|
+
{
|
|
256
|
+
const proj = tmpProject();
|
|
257
|
+
try {
|
|
258
|
+
const res = await spawnIsolatedWorker({
|
|
259
|
+
projectDir: proj,
|
|
260
|
+
runId: "run-g",
|
|
261
|
+
featureId: "feature-003",
|
|
262
|
+
taskId: "task-005",
|
|
263
|
+
prompt: "p",
|
|
264
|
+
command: "echo ok",
|
|
265
|
+
});
|
|
266
|
+
const fpPath = join(res.attemptDir, "fingerprint.json");
|
|
267
|
+
assert.ok(existsSync(fpPath));
|
|
268
|
+
const fp = JSON.parse(readFileSync(fpPath, "utf-8"));
|
|
269
|
+
assert.equal(fp.runId, "run-g");
|
|
270
|
+
assert.equal(fp.featureId, "feature-003");
|
|
271
|
+
assert.equal(fp.taskId, "task-005");
|
|
272
|
+
assert.equal(typeof fp.baseRevision, "number");
|
|
273
|
+
assert.ok(fp.timestamp);
|
|
274
|
+
// gitHead may be undefined in temp repo without git, thats ok
|
|
275
|
+
console.log("✓ fingerprint.json valid");
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
rmSync(proj, { recursive: true, force: true });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// --- tmp/infinity-harness isolation does not corrupt harness files ---
|
|
282
|
+
{
|
|
283
|
+
const proj = tmpProject();
|
|
284
|
+
try {
|
|
285
|
+
const { writeTaskList } = await import("../src/taskList.js");
|
|
286
|
+
writeTaskList(proj, { tasks: [{ key: "a", subject: "A", status: "pending" }] });
|
|
287
|
+
const before = readFileSync(join(proj, "harness", "features", "feature-list.json"), "utf-8");
|
|
288
|
+
const res = await spawnIsolatedWorker({
|
|
289
|
+
projectDir: proj,
|
|
290
|
+
runId: "run-h",
|
|
291
|
+
featureId: "feature-003",
|
|
292
|
+
taskId: "task-005",
|
|
293
|
+
prompt: "isolated",
|
|
294
|
+
command: "echo isolated",
|
|
295
|
+
});
|
|
296
|
+
const after = readFileSync(join(proj, "harness", "features", "feature-list.json"), "utf-8");
|
|
297
|
+
assert.equal(before, after, "feature-list.json untouched by isolated worker");
|
|
298
|
+
assert.ok(res.attemptDir.replace(/\\/g, "/").includes("tmp/infinity-harness"));
|
|
299
|
+
console.log("✓ tmp/infinity-harness isolated from harness files");
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
rmSync(proj, { recursive: true, force: true });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
console.log("All worker tests PASS");
|
|
306
|
+
//# sourceMappingURL=worker.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.test.js","sourceRoot":"","sources":["../../tests/worker.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAe,MAAM,SAAS,CAAC;AAC/G,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,SAAS,UAAU;IACjB,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;IACpD,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,+BAA+B;AAC/B,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,CAAC;AAC1D,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AACvC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AACxC,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;AACnD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAC9B,CAAC;IACC,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,eAAe,CAAC,CAAC;IACtC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAC1C,CAAC;AAED,sDAAsD;AACtD,CAAC;IACC,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;IAC9E,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3G,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IACrF,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;AACtC,CAAC;AAED,8DAA8D;AAC9D,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,gBAAgB;QAChB,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;QAC1E,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAC9C,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACpC,oDAAoD;QACpD,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;QAC1E,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,mBAAmB;QACnB,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QAC7E,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACpC,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;QACzE,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IACjD,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,2CAA2C;AAC3C,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5D,MAAM,EAAE,GAAG,gBAAgB,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QAChI,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;QACpG,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC;QACpE,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;QACrE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAClE,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,6CAA6C;AAC7C,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,gBAAgB,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QACxI,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE;YACvG,MAAM,EAAE,GAAG;YACX,MAAM,EAAE,GAAG;YACX,WAAW,EAAE,EAAE;SAChB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACzB,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACrC,yBAAyB;QACzB,MAAM,EAAE,GAAG,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;QACxH,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACrD,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,yDAAyD;QACzD,aAAa,CACX,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,EACtD,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAC3E,CAAC;QACF,MAAM,EAAE,GAAG,gBAAgB,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3G,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,8BAA8B,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,SAAS,KAAK,QAAQ,IAAI,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,CAAC;QAC3F,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC;QAClD,uCAAuC;QACvC,MAAM,GAAG,GAAG,gBAAgB,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9H,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAC3D,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,iFAAiF;QACjF,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC7D,oCAAoC;QACpC,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,EAAE,GAAG,gBAAgB,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3G,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QACjC,qCAAqC;QACrC,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,gBAAgB,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7H,aAAa,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;QACtE,mBAAmB;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACxG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,EAAE,8CAA8C,CAAC,CAAC;QACpF,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAC9D,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC7D,aAAa,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAChF,+DAA+D;QAC/D,MAAM,MAAM,GAAG,KAAK,EAAE,EAAU,EAAE,EAAE;YAClC,OAAO,mBAAmB,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;gBAC1C,gCAAgC;gBAChC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;gBACzG,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC;gBAChC,sCAAsC;gBACtC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC5C,sFAAsF;gBACtF,sFAAsF;gBACtF,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC;gBACjE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;gBACjD,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC;gBACxC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;gBACrH,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC/C,OAAO,GAAG,CAAC,YAAY,CAAC;YAC1B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QACF,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACxG,qFAAqF;QACrF,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,EAAE,+CAA+C,KAAK,CAAC,YAAY,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QAC3H,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,oBAAoB,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAChE,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,yDAAyD;AACzD,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACjG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAClJ,IAAI,GAAG,GAAG,KAAK,CAAC;QAChB,MAAM,gBAAgB,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YACtC,GAAG,GAAG,IAAI,CAAC;YACX,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;YAC9E,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACpC,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,uDAAuD;AACvD,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC;YACpC,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,aAAa;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,cAAc;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QACzD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAC;QACvF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC7B,+BAA+B;QAC/B,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC;YACrC,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,aAAa;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;IAC1D,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,6DAA6D;AAC7D,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC;YACpC,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,aAAa;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,mBAAmB;YAC5B,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QACtE,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,2CAA2C,GAAG,EAAE,CAAC,CAAC;QAC1F,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;IACtE,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC;YACpC,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,aAAa;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;QACxD,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC/C,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;QACxB,8DAA8D;QAC9D,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,CAAC;IACC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC7D,aAAa,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAChF,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;QAC7F,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC;YACpC,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,OAAO;YACd,SAAS,EAAE,aAAa;YACxB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,eAAe;SACzB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5F,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,gDAAgD,CAAC,CAAC;QAC9E,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC;QAC/E,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;IACpE,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflows: which phases run, and who signs each one.
|
|
3
|
+
*
|
|
4
|
+
* "copilot" and "autopilot" were a single switch, and a single switch is the
|
|
5
|
+
* wrong shape for the question — it cannot say "let it define and plan on its
|
|
6
|
+
* own but show me the review". The setting is a mode per phase; the two
|
|
7
|
+
* familiar words are two named points in that space.
|
|
8
|
+
*
|
|
9
|
+
* The two rules worth defending here: a saved workflow belongs to the *person*
|
|
10
|
+
* so it is there on their next project, and a built-in name cannot be taken,
|
|
11
|
+
* because "copilot" has to mean the same thing in every conversation about
|
|
12
|
+
* this tool.
|
|
13
|
+
*/
|
|
14
|
+
import assert from "node:assert/strict";
|
|
15
|
+
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
import { BUILTIN_WORKFLOWS, SIGNABLE_PHASES, applyWorkflow, builtInWorkflow, deleteWorkflow, describeModes, findWorkflow, listWorkflows, loadSavedWorkflows, matchWorkflow, modeFor, modesOf, normalizeModes, normalizePhases, renderWorkflow, saveWorkflow, signedPhases, slugify, summarizeWorkflow, } from "../src/workflow.js";
|
|
19
|
+
import { defaultConfig, loadConfig, saveConfig } from "../src/core/config.js";
|
|
20
|
+
import { userWorkflowsPath } from "../src/core/paths.js";
|
|
21
|
+
function sandbox() {
|
|
22
|
+
const dir = mkdtempSync(join(tmpdir(), "workflow-store-"));
|
|
23
|
+
return { env: { PI_CODING_AGENT_DIR: dir }, clean: () => rmSync(dir, { recursive: true, force: true }) };
|
|
24
|
+
}
|
|
25
|
+
// ── the built-ins ──────────────────────────────────────────────────────────
|
|
26
|
+
{
|
|
27
|
+
const ids = BUILTIN_WORKFLOWS.map((w) => w.id);
|
|
28
|
+
assert.ok(ids.includes("copilot") && ids.includes("autopilot"), "the two familiar names survive");
|
|
29
|
+
assert.equal(new Set(ids).size, ids.length, "ids are unique");
|
|
30
|
+
const copilot = builtInWorkflow("copilot");
|
|
31
|
+
assert.deepEqual(signedPhasesOf(copilot), ["define", "plan"], "copilot signs the two thinking phases it runs");
|
|
32
|
+
const autopilot = builtInWorkflow("autopilot");
|
|
33
|
+
assert.deepEqual(signedPhasesOf(autopilot), [], "autopilot signs nothing");
|
|
34
|
+
const every = builtInWorkflow("every-gate");
|
|
35
|
+
assert.deepEqual(signedPhasesOf(every), every.phases, "every gate means every gate");
|
|
36
|
+
const spec = builtInWorkflow("spec-and-ship");
|
|
37
|
+
assert.deepEqual(signedPhasesOf(spec), ["define", "ship"], "the shape a single switch could not express: scope in, release out");
|
|
38
|
+
assert.equal(builtInWorkflow("nope"), null);
|
|
39
|
+
console.log("✓ the built-ins cover the shapes people ask for, including one no switch could");
|
|
40
|
+
}
|
|
41
|
+
function signedPhasesOf(w) {
|
|
42
|
+
return w.phases.filter((p) => w.modes[p] === "copilot");
|
|
43
|
+
}
|
|
44
|
+
// ── saving, reusing, deleting ──────────────────────────────────────────────
|
|
45
|
+
{
|
|
46
|
+
const box = sandbox();
|
|
47
|
+
assert.deepEqual(loadSavedWorkflows(box.env), [], "nothing saved to start with");
|
|
48
|
+
assert.equal(listWorkflows(box.env).length, BUILTIN_WORKFLOWS.length);
|
|
49
|
+
const saved = saveWorkflow({
|
|
50
|
+
name: "Client work",
|
|
51
|
+
phases: ["define", "plan", "build", "verify", "review", "ship"],
|
|
52
|
+
modes: { define: "copilot", review: "copilot", ship: "copilot" },
|
|
53
|
+
}, box.env);
|
|
54
|
+
assert.equal(saved.ok, true, saved.error ?? "");
|
|
55
|
+
assert.equal(saved.workflow?.id, "client-work");
|
|
56
|
+
assert.equal(saved.workflow?.builtIn, false);
|
|
57
|
+
assert.ok(saved.workflow?.savedAt, "a saved workflow is stamped");
|
|
58
|
+
assert.ok(saved.workflow.description.length > 0, "and describes itself if you did not");
|
|
59
|
+
// The point of saving: it is there next time, on any project.
|
|
60
|
+
assert.equal(findWorkflow("client-work", box.env)?.name, "Client work");
|
|
61
|
+
assert.equal(listWorkflows(box.env).length, BUILTIN_WORKFLOWS.length + 1);
|
|
62
|
+
assert.equal(listWorkflows(box.env)[0]?.builtIn, true, "built-ins come first in the menu");
|
|
63
|
+
// Saving the same name again is editing, not a duplicate.
|
|
64
|
+
const edited = saveWorkflow({ name: "Client work", phases: ["define", "plan", "build"], modes: { plan: "copilot" } }, box.env);
|
|
65
|
+
assert.equal(edited.ok, true);
|
|
66
|
+
assert.equal(loadSavedWorkflows(box.env).length, 1, "one entry, not two");
|
|
67
|
+
assert.equal(findWorkflow("client-work", box.env)?.modes.plan, "copilot");
|
|
68
|
+
assert.equal(deleteWorkflow("client-work", box.env).ok, true);
|
|
69
|
+
assert.equal(loadSavedWorkflows(box.env).length, 0);
|
|
70
|
+
box.clean();
|
|
71
|
+
console.log("✓ a saved workflow belongs to the person and is there on the next project");
|
|
72
|
+
}
|
|
73
|
+
// ── built-ins are read-only ────────────────────────────────────────────────
|
|
74
|
+
{
|
|
75
|
+
const box = sandbox();
|
|
76
|
+
for (const id of ["copilot", "autopilot", "Copilot", "AUTOPILOT", "auto pilot"]) {
|
|
77
|
+
const attempt = saveWorkflow({ name: id, phases: ["define"], modes: {} }, box.env);
|
|
78
|
+
if (builtInWorkflow(slugify(id))) {
|
|
79
|
+
assert.equal(attempt.ok, false, `"${id}" should be refused`);
|
|
80
|
+
assert.match(attempt.error ?? "", /built-in/i);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
assert.equal(deleteWorkflow("copilot", box.env).ok, false, "and they cannot be deleted either");
|
|
84
|
+
assert.equal(saveWorkflow({ name: " ", phases: ["define"], modes: {} }, box.env).ok, false);
|
|
85
|
+
assert.equal(saveWorkflow({ name: "!!!", phases: ["define"], modes: {} }, box.env).ok, false);
|
|
86
|
+
box.clean();
|
|
87
|
+
console.log("✓ 'copilot' means the same thing everywhere — built-ins cannot be shadowed or deleted");
|
|
88
|
+
}
|
|
89
|
+
// ── a corrupt store degrades to the built-ins ──────────────────────────────
|
|
90
|
+
{
|
|
91
|
+
const box = sandbox();
|
|
92
|
+
const path = userWorkflowsPath(box.env);
|
|
93
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
94
|
+
writeFileSync(path, "{not json", "utf-8");
|
|
95
|
+
assert.deepEqual(loadSavedWorkflows(box.env), [], "unreadable reads as none, never as a throw");
|
|
96
|
+
writeFileSync(path, JSON.stringify({ version: "1", workflows: [{ nonsense: true }] }), "utf-8");
|
|
97
|
+
assert.deepEqual(loadSavedWorkflows(box.env), [], "an entry with no id and no name is not a workflow");
|
|
98
|
+
writeFileSync(path, JSON.stringify({ version: "1", workflows: [{ id: "x", name: "X", phases: ["nope"], modes: { nope: "wat" } }] }), "utf-8");
|
|
99
|
+
const repaired = loadSavedWorkflows(box.env)[0];
|
|
100
|
+
assert.deepEqual(repaired.phases, normalizePhases(undefined), "a nonsense phase list falls back to the default");
|
|
101
|
+
assert.ok(Object.values(repaired.modes).every((m) => m === "copilot" || m === "autopilot"), "and a nonsense mode becomes a real one");
|
|
102
|
+
box.clean();
|
|
103
|
+
console.log("✓ a corrupt store leaves you with the built-ins rather than a broken session");
|
|
104
|
+
}
|
|
105
|
+
// ── applying one to a config ───────────────────────────────────────────────
|
|
106
|
+
{
|
|
107
|
+
const config = defaultConfig();
|
|
108
|
+
applyWorkflow(config, builtInWorkflow("research-first"));
|
|
109
|
+
assert.ok(config.phases.enabled.includes("research"), "the workflow's phases become the pipeline");
|
|
110
|
+
assert.equal(config.phases.enabled[0], "research", "in pipeline order");
|
|
111
|
+
assert.equal(config.phaseModes.research, "copilot");
|
|
112
|
+
assert.equal(config.phaseModes.build, "autopilot");
|
|
113
|
+
assert.deepEqual(config.workflow, { id: "research-first", name: "research first" });
|
|
114
|
+
assert.equal(modeFor(config, "define"), "copilot");
|
|
115
|
+
assert.equal(modeFor(config, "build"), "autopilot");
|
|
116
|
+
assert.equal(modeFor(config, null), "autopilot", "no phase is nothing to stop for");
|
|
117
|
+
assert.deepEqual(signedPhases(config), ["research", "define", "plan"]);
|
|
118
|
+
// Switching workflow replaces the modes rather than merging them.
|
|
119
|
+
applyWorkflow(config, builtInWorkflow("autopilot"));
|
|
120
|
+
assert.deepEqual(signedPhases(config), [], "the old copilot phases are gone, not left behind");
|
|
121
|
+
assert.ok(!config.phases.enabled.includes("research"), "and so is the phase it added");
|
|
122
|
+
console.log("✓ applying a workflow replaces the pipeline and the modes, cleanly");
|
|
123
|
+
}
|
|
124
|
+
// ── knowing when the settings have drifted ─────────────────────────────────
|
|
125
|
+
{
|
|
126
|
+
const box = sandbox();
|
|
127
|
+
const config = defaultConfig();
|
|
128
|
+
applyWorkflow(config, builtInWorkflow("copilot"));
|
|
129
|
+
assert.equal(matchWorkflow(config, box.env)?.id, "copilot");
|
|
130
|
+
assert.match(summarizeWorkflow(config, box.env), /^copilot ·/);
|
|
131
|
+
// One setting changed by hand, and it is no longer copilot. Saying so is the
|
|
132
|
+
// difference between a word that means something and a label.
|
|
133
|
+
config.phaseModes = { ...config.phaseModes, review: "copilot" };
|
|
134
|
+
assert.equal(matchWorkflow(config, box.env), null);
|
|
135
|
+
assert.match(summarizeWorkflow(config, box.env), /copilot \(edited\)/);
|
|
136
|
+
box.clean();
|
|
137
|
+
console.log("✓ a config edited off its preset says so instead of still claiming the name");
|
|
138
|
+
}
|
|
139
|
+
// ── an older config comes forward ──────────────────────────────────────────
|
|
140
|
+
{
|
|
141
|
+
const dir = mkdtempSync(join(tmpdir(), "workflow-migrate-"));
|
|
142
|
+
mkdirSync(join(dir, "harness"), { recursive: true });
|
|
143
|
+
// Exactly what 2.3 wrote: a three-phase `approvals` and no `phaseModes`.
|
|
144
|
+
const legacy = {
|
|
145
|
+
version: "2.0",
|
|
146
|
+
mode: "copilot",
|
|
147
|
+
phases: { enabled: ["define", "plan", "build", "verify", "review", "ship"] },
|
|
148
|
+
approvals: { research: false, define: true, plan: true },
|
|
149
|
+
gates: {},
|
|
150
|
+
git: {},
|
|
151
|
+
maxRetries: 10,
|
|
152
|
+
};
|
|
153
|
+
writeFileSync(join(dir, "harness", "config.json"), JSON.stringify(legacy), "utf-8");
|
|
154
|
+
const { config, ok } = loadConfig(dir);
|
|
155
|
+
assert.equal(ok, true);
|
|
156
|
+
assert.deepEqual(signedPhases(config), ["define", "plan"], "a project mid-run keeps the approvals it was configured with");
|
|
157
|
+
assert.equal(config.phaseModes.build, "autopilot");
|
|
158
|
+
assert.equal(config.workflow?.id, "copilot", "and is labelled with the workflow it amounts to");
|
|
159
|
+
// A 2.3 config that approved nothing lands on autopilot, not on a mystery.
|
|
160
|
+
writeFileSync(join(dir, "harness", "config.json"), JSON.stringify({ ...legacy, approvals: { research: false, define: false, plan: false } }), "utf-8");
|
|
161
|
+
assert.deepEqual(signedPhases(loadConfig(dir).config), []);
|
|
162
|
+
assert.equal(loadConfig(dir).config.workflow?.id, "autopilot");
|
|
163
|
+
// And the migration does not clobber a config that already has modes.
|
|
164
|
+
const modern = defaultConfig();
|
|
165
|
+
applyWorkflow(modern, builtInWorkflow("spec-and-ship"));
|
|
166
|
+
saveConfig(dir, modern);
|
|
167
|
+
assert.deepEqual(signedPhases(loadConfig(dir).config), ["define", "ship"]);
|
|
168
|
+
rmSync(dir, { recursive: true, force: true });
|
|
169
|
+
console.log("✓ a 2.3 config keeps its approvals without anyone editing JSON");
|
|
170
|
+
}
|
|
171
|
+
// ── describing ─────────────────────────────────────────────────────────────
|
|
172
|
+
{
|
|
173
|
+
assert.match(describeModes(["define", "build"], { define: "copilot", build: "autopilot" }), /DEFINE/);
|
|
174
|
+
assert.match(describeModes(["define"], { define: "autopilot" }), /without stopping/);
|
|
175
|
+
assert.match(describeModes(["define"], { define: "copilot" }), /every phase/);
|
|
176
|
+
const rendered = renderWorkflow(builtInWorkflow("copilot"));
|
|
177
|
+
assert.match(rendered, /\[define\]/, "a phase that stops is bracketed");
|
|
178
|
+
assert.match(rendered, /brackets/, "and the notation is explained rather than assumed");
|
|
179
|
+
assert.deepEqual(modesOf(defaultConfig()).init, undefined, "INIT is not a phase with a mode");
|
|
180
|
+
assert.ok(!SIGNABLE_PHASES.includes("init"));
|
|
181
|
+
assert.deepEqual(normalizeModes({ define: "copilot" }, ["define", "build"]), {
|
|
182
|
+
define: "copilot",
|
|
183
|
+
build: "autopilot",
|
|
184
|
+
});
|
|
185
|
+
console.log("✓ a workflow can be read at a glance, and unset phases default to autopilot");
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=workflow.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.test.js","sourceRoot":"","sources":["../../tests/workflow.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -540,6 +540,27 @@ export default function (pi: ExtensionAPI): void {
|
|
|
540
540
|
* would produce a tier that fails at the first task rather than at setup.
|
|
541
541
|
*/
|
|
542
542
|
const availableModels = (ctx: ExtensionContext): ModelChoice[] => {
|
|
543
|
+
// Helper: get pi-default thinking levels for a model (the truth — not our THINKING_LEVELS)
|
|
544
|
+
const supportedOf = (m: unknown): readonly string[] | null => {
|
|
545
|
+
try {
|
|
546
|
+
const mod = m as { reasoning?: boolean; thinkingLevelMap?: Record<string, string | null> };
|
|
547
|
+
if (!mod) return null;
|
|
548
|
+
// pi-ai getSupportedThinkingLevels: reasoning false => ["off"]
|
|
549
|
+
if (mod.reasoning === false) return ["off"] as const;
|
|
550
|
+
if (mod.reasoning === true) {
|
|
551
|
+
const map = mod.thinkingLevelMap ?? {};
|
|
552
|
+
const all: readonly string[] = ["off","minimal","low","medium","high","xhigh","max"];
|
|
553
|
+
const supported = all.filter(l => {
|
|
554
|
+
const v = (map as Record<string, string | null | undefined>)[l];
|
|
555
|
+
if (v === null) return false;
|
|
556
|
+
if ((l === "xhigh" || l === "max") && v === undefined) return false;
|
|
557
|
+
return true;
|
|
558
|
+
});
|
|
559
|
+
return supported.length ? supported : (["off"] as const);
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
} catch { return null; }
|
|
563
|
+
};
|
|
543
564
|
try {
|
|
544
565
|
const scoped = ctx.scopedModels ?? [];
|
|
545
566
|
const models =
|
|
@@ -558,7 +579,8 @@ export default function (pi: ExtensionAPI): void {
|
|
|
558
579
|
if (m.name && m.name !== m.id) bits.push(`· ${m.name}`);
|
|
559
580
|
if (m.contextWindow) bits.push(`· ${Math.round(m.contextWindow / 1000)}k ctx`);
|
|
560
581
|
if (m.reasoning) bits.push("· reasoning");
|
|
561
|
-
|
|
582
|
+
const levels = supportedOf(m);
|
|
583
|
+
out.push({ ref, label: bits.join(" "), ...(levels ? { supportedThinkingLevels: levels } : {}) } as unknown as ModelChoice & { supportedThinkingLevels?: readonly string[] });
|
|
562
584
|
}
|
|
563
585
|
out.sort((a, b) => a.ref.localeCompare(b.ref));
|
|
564
586
|
return out;
|
|
@@ -2030,14 +2052,16 @@ export default function (pi: ExtensionAPI): void {
|
|
|
2030
2052
|
const { spawn: _sp } = await import("node:child_process");
|
|
2031
2053
|
const { existsSync: _ex, openSync: _op, closeSync: _cl } = await import("node:fs");
|
|
2032
2054
|
const { resolve: _re } = await import("node:path");
|
|
2033
|
-
//
|
|
2034
|
-
// Prefer the package's own dist (works in
|
|
2035
|
-
//
|
|
2055
|
+
// Daemon lives in the *installed package*, not the target project.
|
|
2056
|
+
// Prefer the package's own dist (works in every install, including Node 26 where
|
|
2057
|
+
// stripping .ts under node_modules is ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING).
|
|
2058
|
+
// The .ts src entries are kept only for unpacked dev checkouts without a build.
|
|
2036
2059
|
let pkgDir: string | null = null;
|
|
2037
2060
|
try { pkgDir = _re(dirname(fileURLToPath(import.meta.url)), "../.."); } catch {}
|
|
2038
|
-
const
|
|
2039
|
-
const
|
|
2040
|
-
const
|
|
2061
|
+
const pkgJs = pkgDir ? _re(pkgDir, "dist/src/daemon/index.js") : null;
|
|
2062
|
+
const pkgTs = pkgDir ? _re(pkgDir, "src/daemon/index.ts") : null;
|
|
2063
|
+
const altJs = pkgDir ? _re(pkgDir, "dist/daemon/index.js") : null;
|
|
2064
|
+
const cands = [pkgJs, altJs, _re(dir, "dist/daemon/index.js"), _re(dir, "dist/src/daemon/index.js"), pkgTs, _re(dir, "src/daemon/index.ts")].filter(Boolean) as string[];
|
|
2041
2065
|
let entry: string | null = null;
|
|
2042
2066
|
for (const c of cands) if (_ex(c)) { entry = c; break; }
|
|
2043
2067
|
if (!entry) return { spawned: false };
|
|
@@ -2900,15 +2924,18 @@ export default function (pi: ExtensionAPI): void {
|
|
|
2900
2924
|
const { existsSync, openSync, closeSync } = await import("node:fs");
|
|
2901
2925
|
const { resolve: _resolve } = await import("node:path");
|
|
2902
2926
|
// Daemon entry must exist; when not built (dev) fall back to supervisor.
|
|
2903
|
-
//
|
|
2927
|
+
// Package dist first — Node 26 bans stripping .ts under node_modules.
|
|
2904
2928
|
let _pkgDir: string | null = null;
|
|
2905
2929
|
try { _pkgDir = dirname(fileURLToPath(import.meta.url)); _pkgDir = _resolve(_pkgDir, "../.."); } catch {}
|
|
2906
|
-
const
|
|
2907
|
-
const
|
|
2930
|
+
const _pkgJs = _pkgDir ? _resolve(_pkgDir, "dist/src/daemon/index.js") : null;
|
|
2931
|
+
const _altJs = _pkgDir ? _resolve(_pkgDir, "dist/daemon/index.js") : null;
|
|
2932
|
+
const _pkgTs = _pkgDir ? _resolve(_pkgDir, "src/daemon/index.ts") : null;
|
|
2908
2933
|
const candidates = [
|
|
2909
|
-
|
|
2934
|
+
_pkgJs,
|
|
2935
|
+
_altJs,
|
|
2936
|
+
_resolve(dir, "dist/src/daemon/index.js"),
|
|
2910
2937
|
_resolve(dir, "dist/daemon/index.js"),
|
|
2911
|
-
|
|
2938
|
+
_pkgTs,
|
|
2912
2939
|
_resolve(dir, "src/daemon/index.ts"),
|
|
2913
2940
|
].filter(Boolean) as string[];
|
|
2914
2941
|
let entry: string | null = null;
|