gsd-pi 2.22.0 → 2.24.0
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 -1
- package/dist/cli.js +74 -7
- package/dist/headless.d.ts +25 -0
- package/dist/headless.js +454 -0
- package/dist/help-text.js +47 -0
- package/dist/mcp-server.d.ts +20 -3
- package/dist/mcp-server.js +21 -1
- package/dist/models-resolver.d.ts +32 -0
- package/dist/models-resolver.js +50 -0
- package/dist/resource-loader.js +64 -9
- package/dist/resources/extensions/bg-shell/output-formatter.ts +36 -16
- package/dist/resources/extensions/bg-shell/process-manager.ts +6 -4
- package/dist/resources/extensions/bg-shell/types.ts +33 -1
- package/dist/resources/extensions/browser-tools/capture.ts +18 -16
- package/dist/resources/extensions/browser-tools/index.ts +20 -0
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +25 -0
- package/dist/resources/extensions/browser-tools/tools/action-cache.ts +216 -0
- package/dist/resources/extensions/browser-tools/tools/codegen.ts +274 -0
- package/dist/resources/extensions/browser-tools/tools/device.ts +183 -0
- package/dist/resources/extensions/browser-tools/tools/extract.ts +229 -0
- package/dist/resources/extensions/browser-tools/tools/injection-detect.ts +221 -0
- package/dist/resources/extensions/browser-tools/tools/network-mock.ts +244 -0
- package/dist/resources/extensions/browser-tools/tools/pdf.ts +92 -0
- package/dist/resources/extensions/browser-tools/tools/state-persistence.ts +202 -0
- package/dist/resources/extensions/browser-tools/tools/visual-diff.ts +209 -0
- package/dist/resources/extensions/browser-tools/tools/zoom.ts +104 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +2 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +73 -0
- package/dist/resources/extensions/gsd/auto-recovery.ts +51 -2
- package/dist/resources/extensions/gsd/auto-worktree.ts +15 -3
- package/dist/resources/extensions/gsd/auto.ts +560 -52
- package/dist/resources/extensions/gsd/captures.ts +49 -0
- package/dist/resources/extensions/gsd/commands.ts +194 -11
- package/dist/resources/extensions/gsd/complexity.ts +1 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +54 -2
- package/dist/resources/extensions/gsd/diff-context.ts +73 -80
- package/dist/resources/extensions/gsd/doctor.ts +76 -12
- package/dist/resources/extensions/gsd/exit-command.ts +2 -2
- package/dist/resources/extensions/gsd/forensics.ts +95 -52
- package/dist/resources/extensions/gsd/gitignore.ts +1 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +85 -5
- package/dist/resources/extensions/gsd/index.ts +34 -1
- package/dist/resources/extensions/gsd/mcp-server.ts +33 -12
- package/dist/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/dist/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +65 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +104 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -0
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +2 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +70 -0
- package/dist/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/dist/resources/extensions/gsd/roadmap-slices.ts +41 -1
- package/dist/resources/extensions/gsd/session-forensics.ts +36 -2
- package/dist/resources/extensions/gsd/session-status-io.ts +197 -0
- package/dist/resources/extensions/gsd/state.ts +72 -30
- package/dist/resources/extensions/gsd/templates/milestone-validation.md +62 -0
- package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/dist/resources/extensions/gsd/tests/auto-lock-creation.test.ts +186 -0
- package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +264 -0
- package/dist/resources/extensions/gsd/tests/auto-skip-loop.test.ts +123 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/dist/resources/extensions/gsd/tests/doctor.test.ts +58 -0
- package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +17 -6
- package/dist/resources/extensions/gsd/tests/integration/headless-command.ts +534 -0
- package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/roadmap-slices.test.ts +43 -1
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +120 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +203 -2
- package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +8 -3
- package/dist/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +83 -0
- package/dist/resources/extensions/gsd/types.ts +15 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +8 -1
- package/dist/resources/extensions/gsd/workspace-index.ts +34 -6
- package/dist/resources/extensions/subagent/index.ts +5 -0
- package/dist/resources/extensions/subagent/worker-registry.ts +99 -0
- package/dist/update-check.d.ts +9 -0
- package/dist/update-check.js +97 -0
- package/package.json +6 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +16 -7
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.js +21 -9
- package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +12 -4
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic.ts +21 -8
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +16 -4
- package/packages/pi-ai/src/providers/google-vertex.ts +32 -17
- package/packages/pi-ai/src/providers/openai-completions.ts +16 -4
- package/packages/pi-ai/src/providers/openai-responses.ts +16 -4
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash-background.test.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-background.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-background.test.js +79 -0
- package/packages/pi-coding-agent/dist/core/tools/bash-background.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +18 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +77 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
- package/packages/pi-coding-agent/src/core/settings-manager.ts +2 -2
- package/packages/pi-coding-agent/src/core/tools/bash-background.test.ts +91 -0
- package/packages/pi-coding-agent/src/core/tools/bash.ts +83 -1
- package/packages/pi-coding-agent/src/core/tools/index.ts +1 -0
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/scripts/postinstall.js +7 -109
- package/src/resources/extensions/bg-shell/output-formatter.ts +36 -16
- package/src/resources/extensions/bg-shell/process-manager.ts +6 -4
- package/src/resources/extensions/bg-shell/types.ts +33 -1
- package/src/resources/extensions/browser-tools/capture.ts +18 -16
- package/src/resources/extensions/browser-tools/index.ts +20 -0
- package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +25 -0
- package/src/resources/extensions/browser-tools/tools/action-cache.ts +216 -0
- package/src/resources/extensions/browser-tools/tools/codegen.ts +274 -0
- package/src/resources/extensions/browser-tools/tools/device.ts +183 -0
- package/src/resources/extensions/browser-tools/tools/extract.ts +229 -0
- package/src/resources/extensions/browser-tools/tools/injection-detect.ts +221 -0
- package/src/resources/extensions/browser-tools/tools/network-mock.ts +244 -0
- package/src/resources/extensions/browser-tools/tools/pdf.ts +92 -0
- package/src/resources/extensions/browser-tools/tools/state-persistence.ts +202 -0
- package/src/resources/extensions/browser-tools/tools/visual-diff.ts +209 -0
- package/src/resources/extensions/browser-tools/tools/zoom.ts +104 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +2 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +73 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +51 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +15 -3
- package/src/resources/extensions/gsd/auto.ts +560 -52
- package/src/resources/extensions/gsd/captures.ts +49 -0
- package/src/resources/extensions/gsd/commands.ts +194 -11
- package/src/resources/extensions/gsd/complexity.ts +1 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +54 -2
- package/src/resources/extensions/gsd/diff-context.ts +73 -80
- package/src/resources/extensions/gsd/doctor.ts +76 -12
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/forensics.ts +95 -52
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/guided-flow.ts +85 -5
- package/src/resources/extensions/gsd/index.ts +34 -1
- package/src/resources/extensions/gsd/mcp-server.ts +33 -12
- package/src/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/src/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/src/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +65 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +104 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +2 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +70 -0
- package/src/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/src/resources/extensions/gsd/roadmap-slices.ts +41 -1
- package/src/resources/extensions/gsd/session-forensics.ts +36 -2
- package/src/resources/extensions/gsd/session-status-io.ts +197 -0
- package/src/resources/extensions/gsd/state.ts +72 -30
- package/src/resources/extensions/gsd/templates/milestone-validation.md +62 -0
- package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +264 -0
- package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +17 -6
- package/src/resources/extensions/gsd/tests/integration/headless-command.ts +534 -0
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +203 -2
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +8 -3
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +83 -0
- package/src/resources/extensions/gsd/types.ts +15 -1
- package/src/resources/extensions/gsd/visualizer-overlay.ts +8 -1
- package/src/resources/extensions/gsd/workspace-index.ts +34 -6
- package/src/resources/extensions/subagent/index.ts +5 -0
- package/src/resources/extensions/subagent/worker-registry.ts +99 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the parallel worker registry used by the dashboard overlay.
|
|
3
|
+
*
|
|
4
|
+
* Verifies worker lifecycle (register → update → cleanup), batch grouping,
|
|
5
|
+
* and the hasActiveWorkers() status check.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createTestContext } from './test-helpers.ts';
|
|
9
|
+
import {
|
|
10
|
+
registerWorker,
|
|
11
|
+
updateWorker,
|
|
12
|
+
getActiveWorkers,
|
|
13
|
+
getWorkerBatches,
|
|
14
|
+
hasActiveWorkers,
|
|
15
|
+
resetWorkerRegistry,
|
|
16
|
+
} from '../../subagent/worker-registry.ts';
|
|
17
|
+
|
|
18
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
19
|
+
|
|
20
|
+
// ─── Setup ────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
resetWorkerRegistry();
|
|
23
|
+
|
|
24
|
+
// ─── Registration ─────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
console.log("\n=== Worker Registration ===");
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
resetWorkerRegistry();
|
|
30
|
+
const id = registerWorker("scout", "Explore codebase", 0, 3, "batch-1");
|
|
31
|
+
assertTrue(id.startsWith("worker-"), "worker ID has correct prefix");
|
|
32
|
+
const workers = getActiveWorkers();
|
|
33
|
+
assertEq(workers.length, 1, "one worker registered");
|
|
34
|
+
assertEq(workers[0].agent, "scout", "worker agent name correct");
|
|
35
|
+
assertEq(workers[0].task, "Explore codebase", "worker task correct");
|
|
36
|
+
assertEq(workers[0].status, "running", "worker starts as running");
|
|
37
|
+
assertEq(workers[0].index, 0, "worker index correct");
|
|
38
|
+
assertEq(workers[0].batchSize, 3, "worker batch size correct");
|
|
39
|
+
assertEq(workers[0].batchId, "batch-1", "worker batch ID correct");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── Multiple workers in a batch ──────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
console.log("\n=== Multiple Workers in a Batch ===");
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
resetWorkerRegistry();
|
|
48
|
+
const id1 = registerWorker("scout", "Task A", 0, 3, "batch-2");
|
|
49
|
+
const id2 = registerWorker("researcher", "Task B", 1, 3, "batch-2");
|
|
50
|
+
const id3 = registerWorker("worker", "Task C", 2, 3, "batch-2");
|
|
51
|
+
|
|
52
|
+
const workers = getActiveWorkers();
|
|
53
|
+
assertEq(workers.length, 3, "three workers registered");
|
|
54
|
+
assertTrue(hasActiveWorkers(), "has active workers");
|
|
55
|
+
|
|
56
|
+
const batches = getWorkerBatches();
|
|
57
|
+
assertEq(batches.size, 1, "one batch");
|
|
58
|
+
const batch = batches.get("batch-2");
|
|
59
|
+
assertTrue(batch !== undefined, "batch-2 exists");
|
|
60
|
+
assertEq(batch!.length, 3, "batch has 3 workers");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Worker status updates ────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
console.log("\n=== Worker Status Updates ===");
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
resetWorkerRegistry();
|
|
69
|
+
const id1 = registerWorker("scout", "Task A", 0, 2, "batch-3");
|
|
70
|
+
const id2 = registerWorker("worker", "Task B", 1, 2, "batch-3");
|
|
71
|
+
|
|
72
|
+
updateWorker(id1, "completed");
|
|
73
|
+
const workers = getActiveWorkers();
|
|
74
|
+
const w1 = workers.find(w => w.id === id1);
|
|
75
|
+
assertEq(w1?.status, "completed", "worker 1 marked completed");
|
|
76
|
+
|
|
77
|
+
const w2 = workers.find(w => w.id === id2);
|
|
78
|
+
assertEq(w2?.status, "running", "worker 2 still running");
|
|
79
|
+
assertTrue(hasActiveWorkers(), "still has active workers (worker 2 running)");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Failed worker ────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
console.log("\n=== Failed Worker ===");
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
resetWorkerRegistry();
|
|
88
|
+
const id = registerWorker("scout", "Task A", 0, 1, "batch-4");
|
|
89
|
+
updateWorker(id, "failed");
|
|
90
|
+
const workers = getActiveWorkers();
|
|
91
|
+
assertEq(workers[0].status, "failed", "worker marked failed");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Multiple batches ─────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
console.log("\n=== Multiple Batches ===");
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
resetWorkerRegistry();
|
|
100
|
+
registerWorker("scout", "Task A", 0, 2, "batch-5");
|
|
101
|
+
registerWorker("worker", "Task B", 1, 2, "batch-5");
|
|
102
|
+
registerWorker("researcher", "Task C", 0, 1, "batch-6");
|
|
103
|
+
|
|
104
|
+
const batches = getWorkerBatches();
|
|
105
|
+
assertEq(batches.size, 2, "two batches");
|
|
106
|
+
assertEq(batches.get("batch-5")!.length, 2, "batch-5 has 2 workers");
|
|
107
|
+
assertEq(batches.get("batch-6")!.length, 1, "batch-6 has 1 worker");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─── hasActiveWorkers with all completed ──────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
console.log("\n=== hasActiveWorkers — all completed ===");
|
|
113
|
+
|
|
114
|
+
{
|
|
115
|
+
resetWorkerRegistry();
|
|
116
|
+
const id1 = registerWorker("scout", "Task A", 0, 2, "batch-7");
|
|
117
|
+
const id2 = registerWorker("worker", "Task B", 1, 2, "batch-7");
|
|
118
|
+
updateWorker(id1, "completed");
|
|
119
|
+
updateWorker(id2, "completed");
|
|
120
|
+
assertTrue(!hasActiveWorkers(), "no active workers when all completed");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ─── Reset clears everything ─────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
console.log("\n=== Reset ===");
|
|
126
|
+
|
|
127
|
+
{
|
|
128
|
+
registerWorker("scout", "Task", 0, 1, "batch-8");
|
|
129
|
+
assertTrue(getActiveWorkers().length > 0, "workers exist before reset");
|
|
130
|
+
resetWorkerRegistry();
|
|
131
|
+
assertEq(getActiveWorkers().length, 0, "no workers after reset");
|
|
132
|
+
assertTrue(!hasActiveWorkers(), "hasActiveWorkers false after reset");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Update non-existent worker is no-op ──────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
console.log("\n=== Update non-existent worker ===");
|
|
138
|
+
|
|
139
|
+
{
|
|
140
|
+
resetWorkerRegistry();
|
|
141
|
+
// Should not throw
|
|
142
|
+
updateWorker("nonexistent-id", "completed");
|
|
143
|
+
assertEq(getActiveWorkers().length, 0, "no workers created by updating nonexistent");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ─── Summary ──────────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
report();
|
|
@@ -16,7 +16,9 @@ import type { Classification, CaptureEntry } from "./captures.js";
|
|
|
16
16
|
import {
|
|
17
17
|
loadPendingCaptures,
|
|
18
18
|
loadAllCaptures,
|
|
19
|
+
loadActionableCaptures,
|
|
19
20
|
markCaptureResolved,
|
|
21
|
+
markCaptureExecuted,
|
|
20
22
|
} from "./captures.js";
|
|
21
23
|
|
|
22
24
|
// ─── Resolution Executors ─────────────────────────────────────────────────────
|
|
@@ -198,3 +200,84 @@ export function buildQuickTaskPrompt(capture: CaptureEntry): string {
|
|
|
198
200
|
`5. When done, say: "Quick task complete."`,
|
|
199
201
|
].join("\n");
|
|
200
202
|
}
|
|
203
|
+
|
|
204
|
+
// ─── Post-Triage Resolution Executor ─────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Result of executing triage resolutions after a triage-captures unit completes.
|
|
208
|
+
*/
|
|
209
|
+
export interface TriageExecutionResult {
|
|
210
|
+
/** Number of inject resolutions executed (tasks added to plan) */
|
|
211
|
+
injected: number;
|
|
212
|
+
/** Number of replan triggers written */
|
|
213
|
+
replanned: number;
|
|
214
|
+
/** Captures classified as quick-task that need dispatch */
|
|
215
|
+
quickTasks: CaptureEntry[];
|
|
216
|
+
/** Details of each action taken, for logging */
|
|
217
|
+
actions: string[];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Execute pending triage resolutions.
|
|
222
|
+
*
|
|
223
|
+
* Called after a triage-captures unit completes. Reads CAPTURES.md for
|
|
224
|
+
* resolved captures that have actionable classifications (inject, replan,
|
|
225
|
+
* quick-task) but haven't been executed yet, then:
|
|
226
|
+
*
|
|
227
|
+
* - inject: calls executeInject() to add a task to the current slice plan
|
|
228
|
+
* - replan: calls executeReplan() to write the REPLAN-TRIGGER.md marker
|
|
229
|
+
* - quick-task: collects for dispatch (caller handles dispatching quick-task units)
|
|
230
|
+
*
|
|
231
|
+
* Each capture is marked as executed after its resolution action succeeds,
|
|
232
|
+
* preventing double-execution on retries or restarts.
|
|
233
|
+
*/
|
|
234
|
+
export function executeTriageResolutions(
|
|
235
|
+
basePath: string,
|
|
236
|
+
mid: string,
|
|
237
|
+
sid: string,
|
|
238
|
+
): TriageExecutionResult {
|
|
239
|
+
const result: TriageExecutionResult = {
|
|
240
|
+
injected: 0,
|
|
241
|
+
replanned: 0,
|
|
242
|
+
quickTasks: [],
|
|
243
|
+
actions: [],
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const actionable = loadActionableCaptures(basePath);
|
|
247
|
+
if (actionable.length === 0) return result;
|
|
248
|
+
|
|
249
|
+
for (const capture of actionable) {
|
|
250
|
+
switch (capture.classification) {
|
|
251
|
+
case "inject": {
|
|
252
|
+
const newTaskId = executeInject(basePath, mid, sid, capture);
|
|
253
|
+
if (newTaskId) {
|
|
254
|
+
markCaptureExecuted(basePath, capture.id);
|
|
255
|
+
result.injected++;
|
|
256
|
+
result.actions.push(`Injected ${newTaskId} from ${capture.id}: "${capture.text}"`);
|
|
257
|
+
} else {
|
|
258
|
+
result.actions.push(`Failed to inject ${capture.id}: "${capture.text}" (no plan file or parse error)`);
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case "replan": {
|
|
263
|
+
const success = executeReplan(basePath, mid, sid, capture);
|
|
264
|
+
if (success) {
|
|
265
|
+
markCaptureExecuted(basePath, capture.id);
|
|
266
|
+
result.replanned++;
|
|
267
|
+
result.actions.push(`Replan triggered from ${capture.id}: "${capture.text}"`);
|
|
268
|
+
} else {
|
|
269
|
+
result.actions.push(`Failed to trigger replan from ${capture.id}: "${capture.text}"`);
|
|
270
|
+
}
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case "quick-task": {
|
|
274
|
+
// Quick-tasks are collected for dispatch, not executed inline
|
|
275
|
+
result.quickTasks.push(capture);
|
|
276
|
+
result.actions.push(`Quick-task queued from ${capture.id}: "${capture.text}"`);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// ─── Enums & Literal Unions ────────────────────────────────────────────────
|
|
6
6
|
|
|
7
7
|
export type RiskLevel = 'low' | 'medium' | 'high';
|
|
8
|
-
export type Phase = 'pre-planning' | 'needs-discussion' | 'discussing' | 'researching' | 'planning' | 'executing' | 'verifying' | 'summarizing' | 'advancing' | 'completing-milestone' | 'replanning-slice' | 'complete' | 'paused' | 'blocked';
|
|
8
|
+
export type Phase = 'pre-planning' | 'needs-discussion' | 'discussing' | 'researching' | 'planning' | 'executing' | 'verifying' | 'summarizing' | 'advancing' | 'validating-milestone' | 'completing-milestone' | 'replanning-slice' | 'complete' | 'paused' | 'blocked';
|
|
9
9
|
export type ContinueStatus = 'in_progress' | 'interrupted' | 'compacted';
|
|
10
10
|
|
|
11
11
|
// ─── Roadmap (Milestone-level) ─────────────────────────────────────────────
|
|
@@ -264,6 +264,7 @@ export interface PhaseSkipPreferences {
|
|
|
264
264
|
skip_research?: boolean;
|
|
265
265
|
skip_reassess?: boolean;
|
|
266
266
|
skip_slice_research?: boolean;
|
|
267
|
+
skip_milestone_validation?: boolean;
|
|
267
268
|
}
|
|
268
269
|
|
|
269
270
|
export interface NotificationPreferences {
|
|
@@ -363,3 +364,16 @@ export interface Requirement {
|
|
|
363
364
|
full_content: string; // full requirement text
|
|
364
365
|
superseded_by: string | null; // ID of superseding requirement, or null
|
|
365
366
|
}
|
|
367
|
+
|
|
368
|
+
// ─── Parallel Orchestration Types ────────────────────────────────────────
|
|
369
|
+
|
|
370
|
+
export type MergeStrategy = "per-slice" | "per-milestone";
|
|
371
|
+
export type AutoMergeMode = "auto" | "confirm" | "manual";
|
|
372
|
+
|
|
373
|
+
export interface ParallelConfig {
|
|
374
|
+
enabled: boolean;
|
|
375
|
+
max_workers: number;
|
|
376
|
+
budget_ceiling?: number;
|
|
377
|
+
merge_strategy: MergeStrategy;
|
|
378
|
+
auto_merge: AutoMergeMode;
|
|
379
|
+
}
|
|
@@ -112,6 +112,13 @@ export class GSDVisualizerOverlay {
|
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
if (matchesKey(data, Key.shift("tab"))) {
|
|
116
|
+
this.activeTab = (this.activeTab - 1 + TAB_COUNT) % TAB_COUNT;
|
|
117
|
+
this.invalidate();
|
|
118
|
+
this.tui.requestRender();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
if (matchesKey(data, Key.tab)) {
|
|
116
123
|
this.activeTab = (this.activeTab + 1) % TAB_COUNT;
|
|
117
124
|
this.invalidate();
|
|
@@ -300,7 +307,7 @@ export class GSDVisualizerOverlay {
|
|
|
300
307
|
const lines = this.wrapInBox(visibleContent, width);
|
|
301
308
|
|
|
302
309
|
// Footer hint
|
|
303
|
-
const hint = th.fg("dim", "Tab/1-7 switch · / filter · ↑↓ scroll · g/G top/end · esc close");
|
|
310
|
+
const hint = th.fg("dim", "Tab/Shift+Tab/1-7 switch · / filter · ↑↓ scroll · g/G top/end · esc close");
|
|
304
311
|
const hintVis = visibleWidth(hint);
|
|
305
312
|
const hintPad = Math.max(0, Math.floor((width - hintVis) / 2));
|
|
306
313
|
lines.push(" ".repeat(hintPad) + hint);
|
|
@@ -103,10 +103,21 @@ async function indexSlice(basePath: string, milestoneId: string, sliceId: string
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
export
|
|
106
|
+
export interface IndexWorkspaceOptions {
|
|
107
|
+
/**
|
|
108
|
+
* When true, run validatePlanBoundary and validateCompleteBoundary for each slice.
|
|
109
|
+
* Skipped by default — validation is expensive (content analysis) and only needed
|
|
110
|
+
* for explicit doctor/audit flows. The /gsd status dashboard and scope pickers
|
|
111
|
+
* don't need the full issue list.
|
|
112
|
+
*/
|
|
113
|
+
validate?: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function indexWorkspace(basePath: string, opts: IndexWorkspaceOptions = {}): Promise<GSDWorkspaceIndex> {
|
|
107
117
|
const milestoneIds = findMilestoneIds(basePath);
|
|
108
118
|
const milestones: WorkspaceMilestoneTarget[] = [];
|
|
109
119
|
const validationIssues: ValidationIssue[] = [];
|
|
120
|
+
const runValidation = opts.validate === true;
|
|
110
121
|
|
|
111
122
|
for (const milestoneId of milestoneIds) {
|
|
112
123
|
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP") ?? undefined;
|
|
@@ -118,11 +129,27 @@ export async function indexWorkspace(basePath: string): Promise<GSDWorkspaceInde
|
|
|
118
129
|
if (roadmapContent) {
|
|
119
130
|
const roadmap = parseRoadmap(roadmapContent);
|
|
120
131
|
title = titleFromRoadmapHeader(roadmapContent, milestoneId);
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
|
|
133
|
+
// Parallelise all per-slice I/O: indexSlice + (optional) validation calls run concurrently.
|
|
134
|
+
// Order is preserved via Promise.all on an array built from roadmap.slices.
|
|
135
|
+
const sliceResults = await Promise.all(
|
|
136
|
+
roadmap.slices.map(async (slice) => {
|
|
137
|
+
if (runValidation) {
|
|
138
|
+
const [indexedSlice, planIssues, completeIssues] = await Promise.all([
|
|
139
|
+
indexSlice(basePath, milestoneId, slice.id, slice.title, slice.done),
|
|
140
|
+
validatePlanBoundary(basePath, milestoneId, slice.id),
|
|
141
|
+
validateCompleteBoundary(basePath, milestoneId, slice.id),
|
|
142
|
+
]);
|
|
143
|
+
return { indexedSlice, issues: [...planIssues, ...completeIssues] };
|
|
144
|
+
}
|
|
145
|
+
const indexedSlice = await indexSlice(basePath, milestoneId, slice.id, slice.title, slice.done);
|
|
146
|
+
return { indexedSlice, issues: [] as ValidationIssue[] };
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
for (const { indexedSlice, issues } of sliceResults) {
|
|
123
151
|
slices.push(indexedSlice);
|
|
124
|
-
validationIssues.push(...
|
|
125
|
-
validationIssues.push(...await validateCompleteBoundary(basePath, milestoneId, slice.id));
|
|
152
|
+
validationIssues.push(...issues);
|
|
126
153
|
}
|
|
127
154
|
}
|
|
128
155
|
}
|
|
@@ -173,7 +200,8 @@ export async function listDoctorScopeSuggestions(basePath: string): Promise<Arra
|
|
|
173
200
|
}
|
|
174
201
|
|
|
175
202
|
export async function getSuggestedNextCommands(basePath: string): Promise<string[]> {
|
|
176
|
-
|
|
203
|
+
// Run validation here since we surface a /gsd doctor audit hint when issues exist.
|
|
204
|
+
const index = await indexWorkspace(basePath, { validate: true });
|
|
177
205
|
const scope = index.active.milestoneId && index.active.sliceId
|
|
178
206
|
? `${index.active.milestoneId}/${index.active.sliceId}`
|
|
179
207
|
: index.active.milestoneId;
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
mergeDeltaPatches,
|
|
34
34
|
readIsolationMode,
|
|
35
35
|
} from "./isolation.js";
|
|
36
|
+
import { registerWorker, updateWorker } from "./worker-registry.js";
|
|
36
37
|
|
|
37
38
|
const MAX_PARALLEL_TASKS = 8;
|
|
38
39
|
const MAX_CONCURRENCY = 4;
|
|
@@ -626,7 +627,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
626
627
|
};
|
|
627
628
|
|
|
628
629
|
const MAX_RETRIES = 1; // Retry failed tasks once
|
|
630
|
+
const batchId = crypto.randomUUID();
|
|
631
|
+
const batchSize = params.tasks.length;
|
|
629
632
|
const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
|
|
633
|
+
const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
|
|
630
634
|
let result = await runSingleAgent(
|
|
631
635
|
ctx.cwd,
|
|
632
636
|
agents,
|
|
@@ -666,6 +670,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
666
670
|
);
|
|
667
671
|
}
|
|
668
672
|
|
|
673
|
+
updateWorker(workerId, result.exitCode === 0 ? "completed" : "failed");
|
|
669
674
|
allResults[index] = result;
|
|
670
675
|
emitParallelUpdate();
|
|
671
676
|
return result;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Registry — Tracks active subagent sessions for dashboard visibility.
|
|
3
|
+
*
|
|
4
|
+
* Provides a global registry of currently-running parallel workers so the
|
|
5
|
+
* GSD dashboard overlay can display real-time worker status.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface WorkerEntry {
|
|
9
|
+
id: string;
|
|
10
|
+
agent: string;
|
|
11
|
+
task: string;
|
|
12
|
+
startedAt: number;
|
|
13
|
+
status: "running" | "completed" | "failed";
|
|
14
|
+
/** Index within a parallel batch (0-based) */
|
|
15
|
+
index: number;
|
|
16
|
+
/** Total workers in the parallel batch */
|
|
17
|
+
batchSize: number;
|
|
18
|
+
/** Unique batch identifier for grouping parallel runs */
|
|
19
|
+
batchId: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const activeWorkers = new Map<string, WorkerEntry>();
|
|
23
|
+
let workerIdCounter = 0;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register a new worker. Returns the worker ID for later updates.
|
|
27
|
+
*/
|
|
28
|
+
export function registerWorker(
|
|
29
|
+
agent: string,
|
|
30
|
+
task: string,
|
|
31
|
+
index: number,
|
|
32
|
+
batchSize: number,
|
|
33
|
+
batchId: string,
|
|
34
|
+
): string {
|
|
35
|
+
const id = `worker-${++workerIdCounter}`;
|
|
36
|
+
activeWorkers.set(id, {
|
|
37
|
+
id,
|
|
38
|
+
agent,
|
|
39
|
+
task,
|
|
40
|
+
startedAt: Date.now(),
|
|
41
|
+
status: "running",
|
|
42
|
+
index,
|
|
43
|
+
batchSize,
|
|
44
|
+
batchId,
|
|
45
|
+
});
|
|
46
|
+
return id;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Update worker status when it completes or fails.
|
|
51
|
+
*/
|
|
52
|
+
export function updateWorker(id: string, status: "completed" | "failed"): void {
|
|
53
|
+
const entry = activeWorkers.get(id);
|
|
54
|
+
if (entry) {
|
|
55
|
+
entry.status = status;
|
|
56
|
+
// Remove after a brief display window (5 seconds)
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
activeWorkers.delete(id);
|
|
59
|
+
}, 5000);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get all currently-tracked workers (running + recently completed).
|
|
65
|
+
*/
|
|
66
|
+
export function getActiveWorkers(): WorkerEntry[] {
|
|
67
|
+
return Array.from(activeWorkers.values());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get workers grouped by batch.
|
|
72
|
+
*/
|
|
73
|
+
export function getWorkerBatches(): Map<string, WorkerEntry[]> {
|
|
74
|
+
const batches = new Map<string, WorkerEntry[]>();
|
|
75
|
+
for (const worker of activeWorkers.values()) {
|
|
76
|
+
const batch = batches.get(worker.batchId) ?? [];
|
|
77
|
+
batch.push(worker);
|
|
78
|
+
batches.set(worker.batchId, batch);
|
|
79
|
+
}
|
|
80
|
+
return batches;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if any parallel workers are currently running.
|
|
85
|
+
*/
|
|
86
|
+
export function hasActiveWorkers(): boolean {
|
|
87
|
+
for (const worker of activeWorkers.values()) {
|
|
88
|
+
if (worker.status === "running") return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Reset registry state. Used for testing.
|
|
95
|
+
*/
|
|
96
|
+
export function resetWorkerRegistry(): void {
|
|
97
|
+
activeWorkers.clear();
|
|
98
|
+
workerIdCounter = 0;
|
|
99
|
+
}
|