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
|
@@ -26,6 +26,7 @@ export interface CaptureEntry {
|
|
|
26
26
|
resolution?: string;
|
|
27
27
|
rationale?: string;
|
|
28
28
|
resolvedAt?: string;
|
|
29
|
+
executed?: boolean;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export interface TriageResult {
|
|
@@ -211,6 +212,52 @@ export function markCaptureResolved(
|
|
|
211
212
|
writeFileSync(filePath, updated, "utf-8");
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Mark a resolved capture as executed — its resolution action was carried out.
|
|
217
|
+
* Appends `**Executed:** <timestamp>` to the capture's section in CAPTURES.md.
|
|
218
|
+
*/
|
|
219
|
+
export function markCaptureExecuted(basePath: string, captureId: string): void {
|
|
220
|
+
const filePath = resolveCapturesPath(basePath);
|
|
221
|
+
if (!existsSync(filePath)) return;
|
|
222
|
+
|
|
223
|
+
const content = readFileSync(filePath, "utf-8");
|
|
224
|
+
const executedAt = new Date().toISOString();
|
|
225
|
+
|
|
226
|
+
const sectionRegex = new RegExp(
|
|
227
|
+
`(### ${escapeRegex(captureId)}\\n(?:(?!### ).)*?)(?=### |$)`,
|
|
228
|
+
"s",
|
|
229
|
+
);
|
|
230
|
+
const match = sectionRegex.exec(content);
|
|
231
|
+
if (!match) return;
|
|
232
|
+
|
|
233
|
+
let section = match[1];
|
|
234
|
+
|
|
235
|
+
// Remove any existing Executed field (in case of re-execution)
|
|
236
|
+
section = section.replace(/\*\*Executed:\*\*\s*.+\n?/g, "");
|
|
237
|
+
|
|
238
|
+
// Append Executed timestamp
|
|
239
|
+
section = section.trimEnd() + "\n" + `**Executed:** ${executedAt}` + "\n";
|
|
240
|
+
|
|
241
|
+
const updated = content.replace(sectionRegex, section);
|
|
242
|
+
writeFileSync(filePath, updated, "utf-8");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Load resolved captures that have actionable classifications (inject, replan,
|
|
247
|
+
* quick-task) but have NOT yet been executed.
|
|
248
|
+
* These are captures whose resolutions need to be carried out.
|
|
249
|
+
*/
|
|
250
|
+
export function loadActionableCaptures(basePath: string): CaptureEntry[] {
|
|
251
|
+
return loadAllCaptures(basePath).filter(
|
|
252
|
+
c =>
|
|
253
|
+
c.status === "resolved" &&
|
|
254
|
+
!c.executed &&
|
|
255
|
+
(c.classification === "inject" ||
|
|
256
|
+
c.classification === "replan" ||
|
|
257
|
+
c.classification === "quick-task"),
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
214
261
|
// ─── Parser ───────────────────────────────────────────────────────────────────
|
|
215
262
|
|
|
216
263
|
/**
|
|
@@ -235,6 +282,7 @@ function parseCapturesContent(content: string): CaptureEntry[] {
|
|
|
235
282
|
const resolution = extractBoldField(body, "Resolution");
|
|
236
283
|
const rationale = extractBoldField(body, "Rationale");
|
|
237
284
|
const resolvedAt = extractBoldField(body, "Resolved");
|
|
285
|
+
const executedAt = extractBoldField(body, "Executed");
|
|
238
286
|
|
|
239
287
|
if (!text || !timestamp) continue;
|
|
240
288
|
|
|
@@ -251,6 +299,7 @@ function parseCapturesContent(content: string): CaptureEntry[] {
|
|
|
251
299
|
...(resolution ? { resolution } : {}),
|
|
252
300
|
...(rationale ? { rationale } : {}),
|
|
253
301
|
...(resolvedAt ? { resolvedAt } : {}),
|
|
302
|
+
...(executedAt ? { executed: true } : {}),
|
|
254
303
|
});
|
|
255
304
|
}
|
|
256
305
|
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
8
8
|
import { AuthStorage } from "@gsd/pi-coding-agent";
|
|
9
|
-
import { existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync, mkdirSync, unlinkSync } from "node:fs";
|
|
10
10
|
import { join, dirname } from "node:path";
|
|
11
11
|
import { enableDebug, isDebugEnabled } from "./debug-logger.js";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { deriveState } from "./state.js";
|
|
14
14
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
15
15
|
import { GSDVisualizerOverlay } from "./visualizer-overlay.js";
|
|
16
|
-
import { showQueue, showDiscuss } from "./guided-flow.js";
|
|
17
|
-
import { startAuto, stopAuto, pauseAuto, isAutoActive, isAutoPaused, isStepMode, stopAutoRemote } from "./auto.js";
|
|
16
|
+
import { showQueue, showDiscuss, showHeadlessMilestoneCreation } from "./guided-flow.js";
|
|
17
|
+
import { startAuto, stopAuto, pauseAuto, isAutoActive, isAutoPaused, isStepMode, stopAutoRemote, dispatchDirectPhase } from "./auto.js";
|
|
18
18
|
import { resolveProjectRoot } from "./worktree.js";
|
|
19
19
|
import { appendCapture, hasPendingCaptures, loadPendingCaptures } from "./captures.js";
|
|
20
20
|
import {
|
|
@@ -42,6 +42,14 @@ import { handleQuick } from "./quick.js";
|
|
|
42
42
|
import { handleHistory } from "./history.js";
|
|
43
43
|
import { handleUndo } from "./undo.js";
|
|
44
44
|
import { handleExport } from "./export.js";
|
|
45
|
+
import {
|
|
46
|
+
isParallelActive, getOrchestratorState, getWorkerStatuses,
|
|
47
|
+
prepareParallelStart, startParallel, stopParallel,
|
|
48
|
+
pauseWorker, resumeWorker,
|
|
49
|
+
} from "./parallel-orchestrator.js";
|
|
50
|
+
import { formatEligibilityReport } from "./parallel-eligibility.js";
|
|
51
|
+
import { mergeAllCompleted, mergeCompletedMilestone, formatMergeResults } from "./parallel-merge.js";
|
|
52
|
+
import { resolveParallelConfig } from "./preferences.js";
|
|
45
53
|
import { nativeBranchList, nativeDetectMainBranch, nativeBranchListMerged, nativeBranchDelete, nativeForEachRef, nativeUpdateRef } from "./native-git-bridge.js";
|
|
46
54
|
|
|
47
55
|
export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined, reportText: string, structuredIssues: string): void {
|
|
@@ -69,20 +77,53 @@ function projectRoot(): string {
|
|
|
69
77
|
|
|
70
78
|
export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
71
79
|
pi.registerCommand("gsd", {
|
|
72
|
-
description: "GSD — Get Shit Done: /gsd help|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|history|undo|skip|export|cleanup|mode|prefs|config|hooks|run-hook|skill-health|doctor|forensics|migrate|remote|steer|knowledge",
|
|
80
|
+
description: "GSD — Get Shit Done: /gsd help|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|hooks|run-hook|skill-health|doctor|forensics|migrate|remote|steer|knowledge|new-milestone|parallel",
|
|
73
81
|
getArgumentCompletions: (prefix: string) => {
|
|
74
82
|
const subcommands = [
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
|
|
83
|
+
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
84
|
+
{ cmd: "next", desc: "Explicit step mode (same as /gsd)" },
|
|
85
|
+
{ cmd: "auto", desc: "Autonomous mode — research, plan, execute, commit, repeat" },
|
|
86
|
+
{ cmd: "stop", desc: "Stop auto mode gracefully" },
|
|
87
|
+
{ cmd: "pause", desc: "Pause auto-mode (preserves state, /gsd auto to resume)" },
|
|
88
|
+
{ cmd: "status", desc: "Progress dashboard" },
|
|
89
|
+
{ cmd: "visualize", desc: "Open workflow visualizer (progress, deps, metrics, timeline)" },
|
|
90
|
+
{ cmd: "queue", desc: "Queue and reorder future milestones" },
|
|
91
|
+
{ cmd: "quick", desc: "Execute a quick task without full planning overhead" },
|
|
92
|
+
{ cmd: "discuss", desc: "Discuss architecture and decisions" },
|
|
93
|
+
{ cmd: "capture", desc: "Fire-and-forget thought capture" },
|
|
94
|
+
{ cmd: "triage", desc: "Manually trigger triage of pending captures" },
|
|
95
|
+
{ cmd: "dispatch", desc: "Dispatch a specific phase directly" },
|
|
96
|
+
{ cmd: "history", desc: "View execution history" },
|
|
97
|
+
{ cmd: "undo", desc: "Revert last completed unit" },
|
|
98
|
+
{ cmd: "skip", desc: "Prevent a unit from auto-mode dispatch" },
|
|
99
|
+
{ cmd: "export", desc: "Export milestone/slice results" },
|
|
100
|
+
{ cmd: "cleanup", desc: "Remove merged branches or snapshots" },
|
|
101
|
+
{ cmd: "mode", desc: "Switch workflow mode (solo/team)" },
|
|
102
|
+
{ cmd: "prefs", desc: "Manage preferences (model selection, timeouts, etc.)" },
|
|
103
|
+
{ cmd: "config", desc: "Set API keys for external tools" },
|
|
104
|
+
{ cmd: "hooks", desc: "Show configured post-unit and pre-dispatch hooks" },
|
|
105
|
+
{ cmd: "run-hook", desc: "Manually trigger a specific hook" },
|
|
106
|
+
{ cmd: "skill-health", desc: "Skill lifecycle dashboard" },
|
|
107
|
+
{ cmd: "doctor", desc: "Runtime health checks with auto-fix" },
|
|
108
|
+
{ cmd: "forensics", desc: "Examine execution logs" },
|
|
109
|
+
{ cmd: "migrate", desc: "Migrate a v1 .planning directory to .gsd format" },
|
|
110
|
+
{ cmd: "remote", desc: "Control remote auto-mode" },
|
|
111
|
+
{ cmd: "steer", desc: "Hard-steer plan documents during execution" },
|
|
112
|
+
{ cmd: "inspect", desc: "Show SQLite DB diagnostics" },
|
|
113
|
+
{ cmd: "knowledge", desc: "Add persistent project knowledge (rule, pattern, or lesson)" },
|
|
114
|
+
{ cmd: "new-milestone", desc: "Create a milestone from a specification document (headless)" },
|
|
115
|
+
{ cmd: "parallel", desc: "Parallel milestone orchestration (start, status, stop, merge)" },
|
|
79
116
|
];
|
|
80
117
|
const parts = prefix.trim().split(/\s+/);
|
|
81
118
|
|
|
82
119
|
if (parts.length <= 1) {
|
|
83
120
|
return subcommands
|
|
84
|
-
.filter((
|
|
85
|
-
.map((
|
|
121
|
+
.filter((item) => item.cmd.startsWith(parts[0] ?? ""))
|
|
122
|
+
.map((item) => ({
|
|
123
|
+
value: item.cmd,
|
|
124
|
+
label: item.cmd,
|
|
125
|
+
description: item.desc
|
|
126
|
+
}));
|
|
86
127
|
}
|
|
87
128
|
|
|
88
129
|
if (parts[0] === "auto" && parts.length <= 2) {
|
|
@@ -99,6 +140,13 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
99
140
|
.map((cmd) => ({ value: `mode ${cmd}`, label: cmd }));
|
|
100
141
|
}
|
|
101
142
|
|
|
143
|
+
if (parts[0] === "parallel" && parts.length <= 2) {
|
|
144
|
+
const subPrefix = parts[1] ?? "";
|
|
145
|
+
return ["start", "status", "stop", "pause", "resume", "merge"]
|
|
146
|
+
.filter((cmd) => cmd.startsWith(subPrefix))
|
|
147
|
+
.map((cmd) => ({ value: `parallel ${cmd}`, label: cmd }));
|
|
148
|
+
}
|
|
149
|
+
|
|
102
150
|
if (parts[0] === "prefs" && parts.length <= 2) {
|
|
103
151
|
const subPrefix = parts[1] ?? "";
|
|
104
152
|
return ["global", "project", "status", "wizard", "setup", "import-claude"]
|
|
@@ -165,6 +213,13 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
165
213
|
return [];
|
|
166
214
|
}
|
|
167
215
|
|
|
216
|
+
if (parts[0] === "dispatch" && parts.length <= 2) {
|
|
217
|
+
const phasePrefix = parts[1] ?? "";
|
|
218
|
+
return ["research", "plan", "execute", "complete", "reassess", "uat", "replan"]
|
|
219
|
+
.filter((cmd) => cmd.startsWith(phasePrefix))
|
|
220
|
+
.map((cmd) => ({ value: `dispatch ${cmd}`, label: cmd }));
|
|
221
|
+
}
|
|
222
|
+
|
|
168
223
|
return [];
|
|
169
224
|
},
|
|
170
225
|
|
|
@@ -244,7 +299,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
244
299
|
}
|
|
245
300
|
return;
|
|
246
301
|
}
|
|
247
|
-
await stopAuto(ctx, pi);
|
|
302
|
+
await stopAuto(ctx, pi, "User requested stop");
|
|
248
303
|
return;
|
|
249
304
|
}
|
|
250
305
|
|
|
@@ -281,6 +336,108 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
281
336
|
return;
|
|
282
337
|
}
|
|
283
338
|
|
|
339
|
+
// ─── Parallel Orchestration ────────────────────────────────────────
|
|
340
|
+
if (trimmed.startsWith("parallel")) {
|
|
341
|
+
const parallelArgs = trimmed.slice("parallel".length).trim();
|
|
342
|
+
const [subCmd = "", ...restParts] = parallelArgs.split(/\s+/);
|
|
343
|
+
const rest = restParts.join(" ");
|
|
344
|
+
|
|
345
|
+
if (subCmd === "start" || subCmd === "") {
|
|
346
|
+
const loaded = loadEffectiveGSDPreferences();
|
|
347
|
+
const config = resolveParallelConfig(loaded?.preferences);
|
|
348
|
+
if (!config.enabled) {
|
|
349
|
+
pi.sendMessage({
|
|
350
|
+
customType: "gsd-parallel",
|
|
351
|
+
content: "Parallel mode is not enabled. Set `parallel.enabled: true` in your preferences.",
|
|
352
|
+
display: false,
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const candidates = await prepareParallelStart(projectRoot(), loaded?.preferences);
|
|
357
|
+
const report = formatEligibilityReport(candidates);
|
|
358
|
+
if (candidates.eligible.length === 0) {
|
|
359
|
+
pi.sendMessage({ customType: "gsd-parallel", content: report + "\n\nNo milestones are eligible for parallel execution.", display: false });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const result = await startParallel(
|
|
363
|
+
projectRoot(),
|
|
364
|
+
candidates.eligible.map(e => e.milestoneId),
|
|
365
|
+
loaded?.preferences,
|
|
366
|
+
);
|
|
367
|
+
const lines = [`Parallel orchestration started.`, `Workers: ${result.started.join(", ")}`];
|
|
368
|
+
if (result.errors.length > 0) {
|
|
369
|
+
lines.push(`Errors: ${result.errors.map(e => `${e.mid}: ${e.error}`).join("; ")}`);
|
|
370
|
+
}
|
|
371
|
+
pi.sendMessage({ customType: "gsd-parallel", content: report + "\n\n" + lines.join("\n"), display: false });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (subCmd === "status") {
|
|
376
|
+
if (!isParallelActive()) {
|
|
377
|
+
pi.sendMessage({ customType: "gsd-parallel", content: "No parallel orchestration is currently active.", display: false });
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const workers = getWorkerStatuses();
|
|
381
|
+
const lines = ["# Parallel Workers\n"];
|
|
382
|
+
for (const w of workers) {
|
|
383
|
+
lines.push(`- **${w.milestoneId}** (${w.title}) — ${w.state} — ${w.completedUnits} units — $${w.cost.toFixed(2)}`);
|
|
384
|
+
}
|
|
385
|
+
const orchState = getOrchestratorState();
|
|
386
|
+
if (orchState) {
|
|
387
|
+
lines.push(`\nTotal cost: $${orchState.totalCost.toFixed(2)}`);
|
|
388
|
+
}
|
|
389
|
+
pi.sendMessage({ customType: "gsd-parallel", content: lines.join("\n"), display: false });
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (subCmd === "stop") {
|
|
394
|
+
const mid = rest.trim() || undefined;
|
|
395
|
+
await stopParallel(projectRoot(), mid);
|
|
396
|
+
pi.sendMessage({ customType: "gsd-parallel", content: mid ? `Stopped worker for ${mid}.` : "All parallel workers stopped.", display: false });
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (subCmd === "pause") {
|
|
401
|
+
const mid = rest.trim() || undefined;
|
|
402
|
+
pauseWorker(projectRoot(), mid);
|
|
403
|
+
pi.sendMessage({ customType: "gsd-parallel", content: mid ? `Paused worker for ${mid}.` : "All parallel workers paused.", display: false });
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (subCmd === "resume") {
|
|
408
|
+
const mid = rest.trim() || undefined;
|
|
409
|
+
resumeWorker(projectRoot(), mid);
|
|
410
|
+
pi.sendMessage({ customType: "gsd-parallel", content: mid ? `Resumed worker for ${mid}.` : "All parallel workers resumed.", display: false });
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (subCmd === "merge") {
|
|
415
|
+
const mid = rest.trim() || undefined;
|
|
416
|
+
if (mid) {
|
|
417
|
+
// Merge a specific milestone
|
|
418
|
+
const result = await mergeCompletedMilestone(projectRoot(), mid);
|
|
419
|
+
pi.sendMessage({ customType: "gsd-parallel", content: formatMergeResults([result]), display: false });
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
// Merge all completed milestones
|
|
423
|
+
const workers = getWorkerStatuses();
|
|
424
|
+
if (workers.length === 0) {
|
|
425
|
+
pi.sendMessage({ customType: "gsd-parallel", content: "No parallel workers to merge.", display: false });
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const results = await mergeAllCompleted(projectRoot(), workers);
|
|
429
|
+
pi.sendMessage({ customType: "gsd-parallel", content: formatMergeResults(results), display: false });
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
pi.sendMessage({
|
|
434
|
+
customType: "gsd-parallel",
|
|
435
|
+
content: `Unknown parallel subcommand "${subCmd}". Usage: /gsd parallel [start|status|stop|pause|resume|merge]`,
|
|
436
|
+
display: false,
|
|
437
|
+
});
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
284
441
|
if (trimmed === "cleanup") {
|
|
285
442
|
await handleCleanupBranches(ctx, projectRoot());
|
|
286
443
|
await handleCleanupSnapshots(ctx, projectRoot());
|
|
@@ -307,6 +464,21 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
307
464
|
return;
|
|
308
465
|
}
|
|
309
466
|
|
|
467
|
+
if (trimmed === "new-milestone") {
|
|
468
|
+
const basePath = projectRoot();
|
|
469
|
+
const headlessContextPath = join(basePath, ".gsd", "runtime", "headless-context.md");
|
|
470
|
+
if (existsSync(headlessContextPath)) {
|
|
471
|
+
const seedContext = readFileSync(headlessContextPath, "utf-8");
|
|
472
|
+
try { unlinkSync(headlessContextPath); } catch { /* non-fatal */ }
|
|
473
|
+
await showHeadlessMilestoneCreation(ctx, pi, basePath, seedContext);
|
|
474
|
+
} else {
|
|
475
|
+
// No headless context — fall back to interactive smart entry
|
|
476
|
+
const { showSmartEntry } = await import("./guided-flow.js");
|
|
477
|
+
await showSmartEntry(ctx, pi, basePath);
|
|
478
|
+
}
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
310
482
|
if (trimmed.startsWith("capture ") || trimmed === "capture") {
|
|
311
483
|
await handleCapture(trimmed.replace(/^capture\s*/, "").trim(), ctx);
|
|
312
484
|
return;
|
|
@@ -388,6 +560,16 @@ Examples:
|
|
|
388
560
|
return;
|
|
389
561
|
}
|
|
390
562
|
|
|
563
|
+
if (trimmed === "dispatch" || trimmed.startsWith("dispatch ")) {
|
|
564
|
+
const phase = trimmed.replace(/^dispatch\s*/, "").trim();
|
|
565
|
+
if (!phase) {
|
|
566
|
+
ctx.ui.notify("Usage: /gsd dispatch <phase> (research|plan|execute|complete|reassess|uat|replan)", "warning");
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
await dispatchDirectPhase(ctx, pi, phase, projectRoot());
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
391
573
|
if (trimmed === "inspect") {
|
|
392
574
|
await handleInspect(ctx);
|
|
393
575
|
return;
|
|
@@ -417,6 +599,7 @@ function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
417
599
|
" /gsd stop Stop auto-mode gracefully",
|
|
418
600
|
" /gsd pause Pause auto-mode (preserves state, /gsd auto to resume)",
|
|
419
601
|
" /gsd discuss Start guided milestone/slice discussion",
|
|
602
|
+
" /gsd new-milestone Create milestone from headless context (used by gsd headless)",
|
|
420
603
|
"",
|
|
421
604
|
"VISIBILITY",
|
|
422
605
|
" /gsd status Show progress dashboard (Ctrl+Alt+G)",
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "./metrics.js";
|
|
20
20
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
21
21
|
import { getActiveWorktreeName } from "./worktree-command.js";
|
|
22
|
+
import { getWorkerBatches, hasActiveWorkers, type WorkerEntry } from "../subagent/worker-registry.js";
|
|
22
23
|
|
|
23
24
|
function formatDuration(ms: number): string {
|
|
24
25
|
const s = Math.floor(ms / 1000);
|
|
@@ -319,16 +320,23 @@ export class GSDDashboardOverlay {
|
|
|
319
320
|
const centered = (content: string) => row(centerLine(content, contentWidth));
|
|
320
321
|
|
|
321
322
|
const title = th.fg("accent", th.bold("GSD Dashboard"));
|
|
323
|
+
const isRemote = !!this.dashData.remoteSession;
|
|
322
324
|
const status = this.dashData.active
|
|
323
325
|
? `${Date.now() % 2000 < 1000 ? th.fg("success", "●") : th.fg("dim", "○")} ${th.fg("success", "AUTO")}`
|
|
324
326
|
: this.dashData.paused
|
|
325
327
|
? th.fg("warning", "⏸ PAUSED")
|
|
326
|
-
:
|
|
328
|
+
: isRemote
|
|
329
|
+
? `${Date.now() % 2000 < 1000 ? th.fg("success", "●") : th.fg("dim", "○")} ${th.fg("success", "AUTO")} ${th.fg("dim", `(PID ${this.dashData.remoteSession!.pid})`)}`
|
|
330
|
+
: th.fg("dim", "idle");
|
|
327
331
|
const worktreeName = getActiveWorktreeName();
|
|
328
332
|
const worktreeTag = worktreeName
|
|
329
333
|
? ` ${th.fg("warning", `⎇ ${worktreeName}`)}`
|
|
330
334
|
: "";
|
|
331
|
-
const elapsed =
|
|
335
|
+
const elapsed = this.dashData.active || this.dashData.paused
|
|
336
|
+
? th.fg("dim", formatDuration(this.dashData.elapsed))
|
|
337
|
+
: isRemote
|
|
338
|
+
? th.fg("dim", `since ${this.dashData.remoteSession!.startedAt.replace("T", " ").slice(0, 19)}`)
|
|
339
|
+
: "";
|
|
332
340
|
lines.push(row(joinColumns(`${title} ${status}${worktreeTag}`, elapsed, contentWidth)));
|
|
333
341
|
lines.push(blank());
|
|
334
342
|
|
|
@@ -344,11 +352,55 @@ export class GSDDashboardOverlay {
|
|
|
344
352
|
} else if (this.dashData.paused) {
|
|
345
353
|
lines.push(row(th.fg("dim", "/gsd auto to resume")));
|
|
346
354
|
lines.push(blank());
|
|
355
|
+
} else if (isRemote) {
|
|
356
|
+
const rs = this.dashData.remoteSession!;
|
|
357
|
+
const unitDisplay = rs.unitType === "starting" || rs.unitType === "resuming"
|
|
358
|
+
? rs.unitType
|
|
359
|
+
: `${unitLabel(rs.unitType)} ${rs.unitId}`;
|
|
360
|
+
lines.push(row(th.fg("text", `Remote session: ${unitDisplay}`)));
|
|
361
|
+
lines.push(blank());
|
|
347
362
|
} else {
|
|
348
363
|
lines.push(row(th.fg("dim", "No unit running · /gsd auto to start")));
|
|
349
364
|
lines.push(blank());
|
|
350
365
|
}
|
|
351
366
|
|
|
367
|
+
// Parallel workers section — shows active subagent sessions
|
|
368
|
+
if (hasActiveWorkers()) {
|
|
369
|
+
lines.push(hr());
|
|
370
|
+
lines.push(row(th.fg("text", th.bold("Parallel Workers"))));
|
|
371
|
+
lines.push(blank());
|
|
372
|
+
|
|
373
|
+
const batches = getWorkerBatches();
|
|
374
|
+
for (const [batchId, workers] of batches) {
|
|
375
|
+
const running = workers.filter(w => w.status === "running").length;
|
|
376
|
+
const done = workers.filter(w => w.status === "completed").length;
|
|
377
|
+
const failed = workers.filter(w => w.status === "failed").length;
|
|
378
|
+
const total = workers[0]?.batchSize ?? workers.length;
|
|
379
|
+
|
|
380
|
+
lines.push(row(joinColumns(
|
|
381
|
+
` ${th.fg("accent", "⟐")} ${th.fg("text", `Batch ${batchId.slice(0, 8)}`)}`,
|
|
382
|
+
th.fg("dim", `${done + failed}/${total} done`),
|
|
383
|
+
contentWidth,
|
|
384
|
+
)));
|
|
385
|
+
|
|
386
|
+
for (const w of workers) {
|
|
387
|
+
const icon = w.status === "running"
|
|
388
|
+
? th.fg("accent", "▸")
|
|
389
|
+
: w.status === "completed"
|
|
390
|
+
? th.fg("success", "✓")
|
|
391
|
+
: th.fg("error", "✗");
|
|
392
|
+
const elapsed = th.fg("dim", formatDuration(Date.now() - w.startedAt));
|
|
393
|
+
const taskPreview = truncateToWidth(w.task, Math.max(20, contentWidth - 30));
|
|
394
|
+
lines.push(row(joinColumns(
|
|
395
|
+
` ${icon} ${th.fg("text", w.agent)} ${th.fg("dim", taskPreview)}`,
|
|
396
|
+
elapsed,
|
|
397
|
+
contentWidth,
|
|
398
|
+
)));
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
lines.push(blank());
|
|
402
|
+
}
|
|
403
|
+
|
|
352
404
|
// Pending captures badge — only shown when captures are waiting for triage
|
|
353
405
|
if (this.dashData.pendingCaptureCount > 0) {
|
|
354
406
|
const count = this.dashData.pendingCaptureCount;
|