gsd-pi 2.23.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.
Files changed (121) hide show
  1. package/dist/cli.js +12 -3
  2. package/dist/headless.d.ts +4 -0
  3. package/dist/headless.js +118 -10
  4. package/dist/help-text.js +22 -7
  5. package/dist/resource-loader.js +64 -9
  6. package/dist/resources/extensions/gsd/auto-dispatch.ts +51 -2
  7. package/dist/resources/extensions/gsd/auto-prompts.ts +73 -0
  8. package/dist/resources/extensions/gsd/auto-recovery.ts +41 -2
  9. package/dist/resources/extensions/gsd/auto-worktree.ts +15 -3
  10. package/dist/resources/extensions/gsd/auto.ts +123 -41
  11. package/dist/resources/extensions/gsd/commands.ts +176 -10
  12. package/dist/resources/extensions/gsd/complexity.ts +1 -0
  13. package/dist/resources/extensions/gsd/dashboard-overlay.ts +38 -0
  14. package/dist/resources/extensions/gsd/doctor.ts +56 -11
  15. package/dist/resources/extensions/gsd/exit-command.ts +2 -2
  16. package/dist/resources/extensions/gsd/gitignore.ts +1 -0
  17. package/dist/resources/extensions/gsd/guided-flow.ts +75 -0
  18. package/dist/resources/extensions/gsd/index.ts +34 -1
  19. package/dist/resources/extensions/gsd/parallel-eligibility.ts +233 -0
  20. package/dist/resources/extensions/gsd/parallel-merge.ts +156 -0
  21. package/dist/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
  22. package/dist/resources/extensions/gsd/preferences.ts +65 -1
  23. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
  24. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  25. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
  26. package/dist/resources/extensions/gsd/provider-error-pause.ts +29 -2
  27. package/dist/resources/extensions/gsd/session-status-io.ts +197 -0
  28. package/dist/resources/extensions/gsd/state.ts +72 -30
  29. package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
  30. package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
  31. package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
  32. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +202 -2
  33. package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
  34. package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
  35. package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
  36. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
  37. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
  38. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
  39. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
  40. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
  41. package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
  42. package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
  43. package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
  44. package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
  45. package/dist/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
  46. package/dist/resources/extensions/gsd/types.ts +15 -1
  47. package/dist/resources/extensions/subagent/index.ts +5 -0
  48. package/dist/resources/extensions/subagent/worker-registry.ts +99 -0
  49. package/dist/update-check.d.ts +9 -0
  50. package/dist/update-check.js +97 -0
  51. package/package.json +6 -1
  52. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  53. package/packages/pi-ai/dist/providers/anthropic.js +16 -7
  54. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  55. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  56. package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -4
  57. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  58. package/packages/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
  59. package/packages/pi-ai/dist/providers/google-vertex.js +21 -9
  60. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  61. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  62. package/packages/pi-ai/dist/providers/openai-completions.js +12 -4
  63. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  64. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  65. package/packages/pi-ai/dist/providers/openai-responses.js +12 -4
  66. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  67. package/packages/pi-ai/src/providers/anthropic.ts +21 -8
  68. package/packages/pi-ai/src/providers/azure-openai-responses.ts +16 -4
  69. package/packages/pi-ai/src/providers/google-vertex.ts +32 -17
  70. package/packages/pi-ai/src/providers/openai-completions.ts +16 -4
  71. package/packages/pi-ai/src/providers/openai-responses.ts +16 -4
  72. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  73. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  75. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  76. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  77. package/packages/pi-coding-agent/src/core/settings-manager.ts +2 -2
  78. package/scripts/postinstall.js +7 -109
  79. package/src/resources/extensions/gsd/auto-dispatch.ts +51 -2
  80. package/src/resources/extensions/gsd/auto-prompts.ts +73 -0
  81. package/src/resources/extensions/gsd/auto-recovery.ts +41 -2
  82. package/src/resources/extensions/gsd/auto-worktree.ts +15 -3
  83. package/src/resources/extensions/gsd/auto.ts +123 -41
  84. package/src/resources/extensions/gsd/commands.ts +176 -10
  85. package/src/resources/extensions/gsd/complexity.ts +1 -0
  86. package/src/resources/extensions/gsd/dashboard-overlay.ts +38 -0
  87. package/src/resources/extensions/gsd/doctor.ts +56 -11
  88. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  89. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  90. package/src/resources/extensions/gsd/guided-flow.ts +75 -0
  91. package/src/resources/extensions/gsd/index.ts +34 -1
  92. package/src/resources/extensions/gsd/parallel-eligibility.ts +233 -0
  93. package/src/resources/extensions/gsd/parallel-merge.ts +156 -0
  94. package/src/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
  95. package/src/resources/extensions/gsd/preferences.ts +65 -1
  96. package/src/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
  97. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  98. package/src/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
  99. package/src/resources/extensions/gsd/provider-error-pause.ts +29 -2
  100. package/src/resources/extensions/gsd/session-status-io.ts +197 -0
  101. package/src/resources/extensions/gsd/state.ts +72 -30
  102. package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
  103. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
  104. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
  105. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +202 -2
  106. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
  107. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
  108. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
  109. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
  110. package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
  111. package/src/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
  112. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
  113. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
  114. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
  115. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
  116. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
  117. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
  118. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
  119. package/src/resources/extensions/gsd/types.ts +15 -1
  120. package/src/resources/extensions/subagent/index.ts +5 -0
  121. 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();
@@ -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
+ }
@@ -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
+ }