gsd-pi 2.66.1-dev.ed243f2 → 2.67.0-dev.43b0159
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/dist/claude-cli-check.d.ts +8 -0
- package/dist/claude-cli-check.js +36 -0
- package/dist/cli.js +40 -0
- package/dist/onboarding.js +19 -2
- package/dist/resources/extensions/ask-user-questions.js +79 -11
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +4 -3
- package/dist/resources/extensions/claude-code-cli/readiness.js +63 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -3
- package/dist/resources/extensions/gsd/auto/loop.js +13 -1
- package/dist/resources/extensions/gsd/auto/phases.js +22 -3
- package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
- package/dist/resources/extensions/gsd/auto/session.js +1 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +65 -15
- package/dist/resources/extensions/gsd/auto-dispatch.js +30 -28
- package/dist/resources/extensions/gsd/auto-model-selection.js +12 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +173 -25
- package/dist/resources/extensions/gsd/auto-recovery.js +11 -12
- package/dist/resources/extensions/gsd/auto.js +13 -1
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +18 -6
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +59 -5
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +8 -5
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +186 -14
- package/dist/resources/extensions/gsd/codebase-generator.js +4 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
- package/dist/resources/extensions/gsd/commands/dispatcher.js +1 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +94 -4
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +49 -9
- package/dist/resources/extensions/gsd/context-store.js +134 -2
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -1
- package/dist/resources/extensions/gsd/detection.js +6 -0
- package/dist/resources/extensions/gsd/files.js +19 -2
- package/dist/resources/extensions/gsd/guided-flow.js +12 -8
- package/dist/resources/extensions/gsd/index.js +1 -1
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +2 -0
- package/dist/resources/extensions/gsd/parsers-legacy.js +3 -1
- package/dist/resources/extensions/gsd/preferences.js +6 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
- package/dist/resources/extensions/gsd/prompts/discuss.md +3 -3
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/rethink.md +6 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +11 -9
- package/dist/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +2 -1
- package/dist/resources/extensions/gsd/state.js +2 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.js +27 -26
- package/dist/resources/extensions/gsd/workflow-reconcile.js +46 -7
- package/dist/resources/extensions/remote-questions/manager.js +8 -0
- package/dist/resources/extensions/shared/interview-ui.js +10 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/{6502.8874bcae249c02e1.js → 6502.b804e48b7919f55e.js} +3 -3
- package/dist/web/standalone/.next/static/chunks/{webpack-9fed74684e1c5bb1.js → webpack-65f0501b197d1c49.js} +1 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -3
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/json-parse.js +11 -1
- package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +60 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.js +14 -0
- package/packages/pi-ai/dist/utils/tests/json-parse.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +10 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +4 -3
- package/packages/pi-ai/src/utils/json-parse.ts +11 -1
- package/packages/pi-ai/src/utils/repair-tool-json.ts +69 -1
- package/packages/pi-ai/src/utils/tests/json-parse.test.ts +17 -0
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +13 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +16 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +58 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +4 -0
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +69 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +66 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +5 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +18 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +11 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +2 -2
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +13 -0
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +35 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/tui.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +43 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -0
- package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/packages/pi-tui/dist/autocomplete.js +9 -7
- package/packages/pi-tui/dist/autocomplete.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js +54 -0
- package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -0
- package/packages/pi-tui/dist/components/editor.d.ts +3 -1
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +14 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.js +6 -0
- package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +8 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +15 -0
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +43 -0
- package/packages/pi-tui/src/__tests__/tui.test.ts +50 -0
- package/packages/pi-tui/src/autocomplete.ts +9 -7
- package/packages/pi-tui/src/components/__tests__/editor.test.ts +64 -0
- package/packages/pi-tui/src/components/editor.ts +14 -3
- package/packages/pi-tui/src/stdin-buffer.ts +7 -0
- package/packages/pi-tui/src/tui.ts +9 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +103 -11
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +4 -3
- package/src/resources/extensions/claude-code-cli/readiness.ts +67 -12
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -3
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +17 -0
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +18 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -1
- package/src/resources/extensions/gsd/auto/loop.ts +14 -1
- package/src/resources/extensions/gsd/auto/phases.ts +27 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +14 -2
- package/src/resources/extensions/gsd/auto/session.ts +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +76 -16
- package/src/resources/extensions/gsd/auto-dispatch.ts +36 -35
- package/src/resources/extensions/gsd/auto-model-selection.ts +12 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +195 -25
- package/src/resources/extensions/gsd/auto-recovery.ts +15 -15
- package/src/resources/extensions/gsd/auto.ts +12 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +27 -6
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +67 -6
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +11 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +209 -16
- package/src/resources/extensions/gsd/codebase-generator.ts +4 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
- package/src/resources/extensions/gsd/commands/dispatcher.ts +1 -2
- package/src/resources/extensions/gsd/commands/handlers/core.ts +113 -8
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +49 -11
- package/src/resources/extensions/gsd/context-store.ts +167 -2
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +3 -1
- package/src/resources/extensions/gsd/detection.ts +6 -0
- package/src/resources/extensions/gsd/files.ts +21 -2
- package/src/resources/extensions/gsd/guided-flow.ts +15 -8
- package/src/resources/extensions/gsd/index.ts +6 -0
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +2 -0
- package/src/resources/extensions/gsd/parsers-legacy.ts +3 -1
- package/src/resources/extensions/gsd/preferences.ts +6 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
- package/src/resources/extensions/gsd/prompts/discuss.md +3 -3
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/rethink.md +6 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +11 -9
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +4 -1
- package/src/resources/extensions/gsd/state.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +52 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +50 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +21 -7
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/decision-scope-cascade.test.ts +370 -0
- package/src/resources/extensions/gsd/tests/detection.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +53 -13
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/measurement.test.ts +531 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +71 -2
- package/src/resources/extensions/gsd/tests/parsers.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -4
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +210 -35
- package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -27
- package/src/resources/extensions/gsd/workflow-reconcile.ts +59 -8
- package/src/resources/extensions/remote-questions/manager.ts +9 -0
- package/src/resources/extensions/shared/interview-ui.ts +13 -0
- /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → CrKrzIIxk55witDF1eS0L}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → CrKrzIIxk55witDF1eS0L}/_ssgManifest.js +0 -0
|
@@ -14,7 +14,7 @@ This is remediation round {{remediationRound}}. If this is round 0, this is the
|
|
|
14
14
|
|
|
15
15
|
## Context
|
|
16
16
|
|
|
17
|
-
All relevant context has been preloaded below — the roadmap, all slice summaries,
|
|
17
|
+
All relevant context has been preloaded below — the roadmap, all slice summaries, assessment results, requirements, decisions, and project context are inlined. Start working immediately without re-reading these files.
|
|
18
18
|
|
|
19
19
|
{{inlinedContext}}
|
|
20
20
|
|
|
@@ -30,8 +30,8 @@ Prompt: "Review milestone {{milestoneId}} requirements coverage. Working directo
|
|
|
30
30
|
**Reviewer B — Cross-Slice Integration**
|
|
31
31
|
Prompt: "Review milestone {{milestoneId}} cross-slice integration. Working directory: {{workingDirectory}}. Read `{{roadmapPath}}` and find the boundary map (produces/consumes contracts). For each boundary, check that the producing slice's SUMMARY confirms it produced the artifact, and the consuming slice's SUMMARY confirms it consumed it. Output a markdown table: Boundary | Producer Summary | Consumer Summary | Status. End with a one-line verdict: PASS if all boundaries honored, NEEDS-ATTENTION if any gaps."
|
|
32
32
|
|
|
33
|
-
**Reviewer C —
|
|
34
|
-
Prompt: "Review milestone {{milestoneId}}
|
|
33
|
+
**Reviewer C — Assessment & Acceptance Criteria**
|
|
34
|
+
Prompt: "Review milestone {{milestoneId}} assessment evidence and acceptance criteria. Working directory: {{workingDirectory}}. Read `.gsd/{{milestoneId}}/CONTEXT.md` for acceptance criteria. Check for ASSESSMENT files in each slice directory. Verify each acceptance criterion maps to either a passing assessment result or clear SUMMARY evidence. Output a checklist: [ ] Criterion | Evidence. End with a one-line verdict: PASS if all criteria met, NEEDS-ATTENTION if gaps exist."
|
|
35
35
|
|
|
36
36
|
### Step 2 — Synthesize Findings
|
|
37
37
|
|
|
@@ -40,9 +40,9 @@ After all reviewers complete, aggregate their verdicts:
|
|
|
40
40
|
- If any reviewer says NEEDS-ATTENTION → overall verdict: `needs-attention`
|
|
41
41
|
- If any reviewer says FAIL → overall verdict: `needs-remediation`
|
|
42
42
|
|
|
43
|
-
### Step 3 —
|
|
43
|
+
### Step 3 — Persist Validation
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Prepare the validation content you will pass to `gsd_validate_milestone`. Do **not** manually write `{{validationPath}}` — the DB-backed tool is the canonical write path and renders the validation file for you.
|
|
46
46
|
|
|
47
47
|
```markdown
|
|
48
48
|
---
|
|
@@ -59,7 +59,7 @@ reviewers: 3
|
|
|
59
59
|
## Reviewer B — Cross-Slice Integration
|
|
60
60
|
<paste Reviewer B output>
|
|
61
61
|
|
|
62
|
-
## Reviewer C —
|
|
62
|
+
## Reviewer C — Assessment & Acceptance Criteria
|
|
63
63
|
<paste Reviewer C output>
|
|
64
64
|
|
|
65
65
|
## Synthesis
|
|
@@ -69,13 +69,15 @@ reviewers: 3
|
|
|
69
69
|
<if verdict is not pass: specific actions required>
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
Call `gsd_validate_milestone` with the camelCase fields `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verdictRationale`, and `remediationPlan` when needed. If you include verification-class analysis, pass it in `verificationClasses`.
|
|
73
|
+
|
|
72
74
|
**DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')` — the engine owns the WAL connection. Use `gsd_milestone_status` to read milestone and slice state. All data you need is already inlined in the context above or accessible via the `gsd_*` tools. Direct DB access corrupts the WAL and bypasses tool-level validation.
|
|
73
75
|
|
|
74
76
|
If verdict is `needs-remediation`:
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
+
- Use `gsd_reassess_roadmap` to add the remediation slices instead of editing `{{roadmapPath}}` manually
|
|
78
|
+
- Those slices will be planned and executed before validation re-runs
|
|
77
79
|
|
|
78
|
-
**You MUST write `{{validationPath}}
|
|
80
|
+
**You MUST call `gsd_validate_milestone` before finishing. Do not manually write `{{validationPath}}`.**
|
|
79
81
|
|
|
80
82
|
**File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories.
|
|
81
83
|
|
|
@@ -90,9 +90,11 @@ Present a merge plan to the user:
|
|
|
90
90
|
|
|
91
91
|
Ask the user to confirm the merge plan before proceeding.
|
|
92
92
|
|
|
93
|
+
**CRITICAL — Non-bypassable gate:** Do NOT execute any merge commands until the user explicitly approves the merge plan. If `ask_user_questions` fails, errors, returns no response, or the user's response is ambiguous, you MUST re-ask — never rationalize past the block. "No response, I'll proceed with the clean merges," "the plan looks safe, merging," or any other self-authorization is **forbidden**. The gate exists to protect the user's branches; treat a block as an instruction to wait, not an obstacle to work around.
|
|
94
|
+
|
|
93
95
|
### Step 4: Execute Merge
|
|
94
96
|
|
|
95
|
-
Once confirmed, run all commands from `{{mainTreePath}}` (your CWD):
|
|
97
|
+
Once the user has explicitly confirmed, run all commands from `{{mainTreePath}}` (your CWD):
|
|
96
98
|
|
|
97
99
|
1. Ensure you are on the target branch: `git checkout {{mainBranch}}`
|
|
98
100
|
2. If there are conflicts requiring manual reconciliation, apply the reconciled versions first
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { execFileSync } from "node:child_process";
|
|
13
|
+
import { normalizePlannedFileReference } from "../files.js";
|
|
13
14
|
import { logWarning } from "../workflow-logger.js";
|
|
14
15
|
|
|
15
16
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
@@ -57,7 +58,9 @@ export function validateFileChanges(
|
|
|
57
58
|
|
|
58
59
|
// Normalize expected paths (strip leading ./ or /)
|
|
59
60
|
const normalizedExpected = new Set(
|
|
60
|
-
[...allExpected].map(f =>
|
|
61
|
+
[...allExpected].map((f) =>
|
|
62
|
+
normalizePlannedFileReference(f).replace(/^\.\//, "").replace(/^\//, ""),
|
|
63
|
+
),
|
|
61
64
|
);
|
|
62
65
|
|
|
63
66
|
// Compute symmetric difference
|
|
@@ -1317,7 +1317,8 @@ export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
|
|
|
1317
1317
|
? `All milestones complete. ${activeReqs} active requirement${activeReqs === 1 ? '' : 's'} in REQUIREMENTS.md ${activeReqs === 1 ? 'has' : 'have'} not been mapped to a milestone.`
|
|
1318
1318
|
: 'All milestones complete.';
|
|
1319
1319
|
return {
|
|
1320
|
-
activeMilestone:
|
|
1320
|
+
activeMilestone: null,
|
|
1321
|
+
lastCompletedMilestone: lastEntry ? { id: lastEntry.id, title: lastEntry.title } : null,
|
|
1321
1322
|
activeSlice: null,
|
|
1322
1323
|
activeTask: null,
|
|
1323
1324
|
phase: 'complete',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
3
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
8
|
unitVerb,
|
|
@@ -11,11 +12,29 @@ import {
|
|
|
11
12
|
formatWidgetTokens,
|
|
12
13
|
estimateTimeRemaining,
|
|
13
14
|
extractUatSliceId,
|
|
15
|
+
getWidgetMode,
|
|
16
|
+
cycleWidgetMode,
|
|
17
|
+
_resetWidgetModeForTests,
|
|
14
18
|
} from "../auto-dashboard.ts";
|
|
15
19
|
|
|
16
20
|
const autoSource = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "auto.ts"), "utf-8");
|
|
17
21
|
const dashboardSource = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "auto-dashboard.ts"), "utf-8");
|
|
18
22
|
|
|
23
|
+
function makeTempDir(prefix: string): string {
|
|
24
|
+
return join(
|
|
25
|
+
tmpdir(),
|
|
26
|
+
`gsd-auto-dashboard-test-${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function cleanup(dir: string): void {
|
|
31
|
+
try {
|
|
32
|
+
rmSync(dir, { recursive: true, force: true });
|
|
33
|
+
} catch {
|
|
34
|
+
// best-effort
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
19
38
|
// ─── unitVerb ─────────────────────────────────────────────────────────────
|
|
20
39
|
|
|
21
40
|
test("unitVerb maps known unit types to verbs", () => {
|
|
@@ -209,3 +228,35 @@ test("extractUatSliceId returns null for invalid formats", () => {
|
|
|
209
228
|
assert.equal(extractUatSliceId(""), null);
|
|
210
229
|
assert.equal(extractUatSliceId("M001/T01"), null);
|
|
211
230
|
});
|
|
231
|
+
|
|
232
|
+
test("widget mode respects project preference precedence and persists there", (t) => {
|
|
233
|
+
const homeDir = makeTempDir("home");
|
|
234
|
+
const projectDir = makeTempDir("project");
|
|
235
|
+
const globalPrefsPath = join(homeDir, ".gsd", "preferences.md");
|
|
236
|
+
const projectPrefsPath = join(projectDir, ".gsd", "preferences.md");
|
|
237
|
+
|
|
238
|
+
mkdirSync(join(homeDir, ".gsd"), { recursive: true });
|
|
239
|
+
mkdirSync(join(projectDir, ".gsd"), { recursive: true });
|
|
240
|
+
writeFileSync(globalPrefsPath, "---\nversion: 1\nwidget_mode: off\n---\n", "utf-8");
|
|
241
|
+
writeFileSync(projectPrefsPath, "---\nversion: 1\nwidget_mode: small\n---\n", "utf-8");
|
|
242
|
+
|
|
243
|
+
t.after(() => {
|
|
244
|
+
cleanup(homeDir);
|
|
245
|
+
cleanup(projectDir);
|
|
246
|
+
_resetWidgetModeForTests();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
_resetWidgetModeForTests();
|
|
250
|
+
|
|
251
|
+
assert.equal(getWidgetMode(projectPrefsPath, globalPrefsPath), "small", "project widget_mode overrides global");
|
|
252
|
+
assert.equal(
|
|
253
|
+
cycleWidgetMode(projectPrefsPath, globalPrefsPath),
|
|
254
|
+
"min",
|
|
255
|
+
"cycling advances from the project-owned mode",
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const projectPrefs = readFileSync(projectPrefsPath, "utf-8");
|
|
259
|
+
const globalPrefs = readFileSync(globalPrefsPath, "utf-8");
|
|
260
|
+
assert.match(projectPrefs, /widget_mode:\s*min/);
|
|
261
|
+
assert.match(globalPrefs, /widget_mode:\s*off/);
|
|
262
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import test from "node:test";
|
|
1
|
+
import test, { mock } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
@@ -191,6 +191,54 @@ test("runUnit returns cancelled when session creation times out", async () => {
|
|
|
191
191
|
assert.equal(pi.calls.length, 0);
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
+
test("runUnit keeps the session-switch guard across a late newSession settlement", async () => {
|
|
195
|
+
_resetPendingResolve();
|
|
196
|
+
mock.timers.enable();
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const ctx = makeMockCtx();
|
|
200
|
+
const pi = makeMockPi();
|
|
201
|
+
// Use delays longer than NEW_SESSION_TIMEOUT_MS (120s) so the timeout fires
|
|
202
|
+
const firstSession = makeMockSession({ newSessionDelayMs: 200_000 });
|
|
203
|
+
const secondSession = makeMockSession({ newSessionDelayMs: 200_000 });
|
|
204
|
+
|
|
205
|
+
const firstRun = runUnit(ctx, pi, firstSession, "task", "T01", "prompt");
|
|
206
|
+
|
|
207
|
+
// Tick past the 120s session timeout
|
|
208
|
+
mock.timers.tick(121_000);
|
|
209
|
+
await Promise.resolve();
|
|
210
|
+
|
|
211
|
+
const firstResult = await firstRun;
|
|
212
|
+
assert.equal(firstResult.status, "cancelled");
|
|
213
|
+
assert.equal(isSessionSwitchInFlight(), true, "guard should remain set after the timed-out session");
|
|
214
|
+
|
|
215
|
+
mock.timers.tick(1);
|
|
216
|
+
const secondRun = runUnit(ctx, pi, secondSession, "task", "T02", "prompt");
|
|
217
|
+
|
|
218
|
+
mock.timers.tick(100_000);
|
|
219
|
+
await Promise.resolve();
|
|
220
|
+
assert.equal(
|
|
221
|
+
isSessionSwitchInFlight(),
|
|
222
|
+
true,
|
|
223
|
+
"late settlement from the first session must not clear the newer session guard",
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Tick past the second session's timeout (121s total > 120s NEW_SESSION_TIMEOUT_MS)
|
|
227
|
+
mock.timers.tick(21_001);
|
|
228
|
+
await Promise.resolve();
|
|
229
|
+
|
|
230
|
+
const secondResult = await secondRun;
|
|
231
|
+
assert.equal(secondResult.status, "cancelled");
|
|
232
|
+
|
|
233
|
+
// Tick past the second session's delayed promise (200s) so .finally() fires
|
|
234
|
+
mock.timers.tick(80_000);
|
|
235
|
+
await Promise.resolve();
|
|
236
|
+
assert.equal(isSessionSwitchInFlight(), false, "guard should clear after the newer session settles");
|
|
237
|
+
} finally {
|
|
238
|
+
mock.timers.reset();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
194
242
|
test("runUnit returns cancelled when s.active is false before sendMessage", async () => {
|
|
195
243
|
_resetPendingResolve();
|
|
196
244
|
|
|
@@ -412,7 +460,7 @@ function makeMockDeps(
|
|
|
412
460
|
getCurrentBranch: () => "main",
|
|
413
461
|
autoWorktreeBranch: () => "auto/M001",
|
|
414
462
|
resolveMilestoneFile: () => null,
|
|
415
|
-
reconcileMergeState: () =>
|
|
463
|
+
reconcileMergeState: () => "clean",
|
|
416
464
|
getLedger: () => null,
|
|
417
465
|
getProjectTotals: () => ({ cost: 0 }),
|
|
418
466
|
formatCost: (c: number) => `$${c.toFixed(2)}`,
|
|
@@ -143,17 +143,17 @@ test("resolvePreferredModelConfig keeps explicit phase models as the ceiling", (
|
|
|
143
143
|
|
|
144
144
|
// ─── resolveModelId tests ─────────────────────────────────────────────────
|
|
145
145
|
|
|
146
|
-
test("resolveModelId: bare ID resolves to
|
|
146
|
+
test("resolveModelId: bare ID resolves to claude-code when session is claude-code (#3772)", () => {
|
|
147
147
|
const availableModels = [
|
|
148
148
|
{ id: "claude-sonnet-4-6", provider: "anthropic" },
|
|
149
149
|
{ id: "claude-sonnet-4-6", provider: "claude-code" },
|
|
150
150
|
];
|
|
151
151
|
|
|
152
|
-
//
|
|
153
|
-
//
|
|
152
|
+
// When currentProvider is "claude-code" (set by startup migration for subscription
|
|
153
|
+
// users), bare IDs must resolve to claude-code to avoid the third-party block (#3772).
|
|
154
154
|
const result = resolveModelId("claude-sonnet-4-6", availableModels, "claude-code");
|
|
155
155
|
assert.ok(result, "should resolve a model");
|
|
156
|
-
assert.equal(result.provider, "
|
|
156
|
+
assert.equal(result.provider, "claude-code", "bare ID must resolve to claude-code when session provider is claude-code");
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
test("resolveModelId: bare ID still prefers current provider when it is a first-class API provider", () => {
|
|
@@ -227,14 +227,28 @@ test("model change notify in selectAndApplyModel is gated behind verbose flag",
|
|
|
227
227
|
);
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
-
test("resolveModelId: anthropic wins over claude-code
|
|
230
|
+
test("resolveModelId: anthropic wins over claude-code when session provider is not claude-code", () => {
|
|
231
231
|
const availableModels = [
|
|
232
232
|
{ id: "claude-sonnet-4-6", provider: "claude-code" },
|
|
233
233
|
{ id: "claude-sonnet-4-6", provider: "anthropic" },
|
|
234
234
|
];
|
|
235
235
|
|
|
236
|
-
//
|
|
236
|
+
// When the session is NOT on claude-code, bare IDs should resolve to
|
|
237
|
+
// the canonical anthropic provider (original #2905 behavior preserved).
|
|
238
|
+
const result = resolveModelId("claude-sonnet-4-6", availableModels, undefined);
|
|
239
|
+
assert.ok(result, "should resolve a model");
|
|
240
|
+
assert.equal(result.provider, "anthropic", "anthropic must win when session is not claude-code");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("resolveModelId: claude-code wins when session is claude-code regardless of list order", () => {
|
|
244
|
+
const availableModels = [
|
|
245
|
+
{ id: "claude-sonnet-4-6", provider: "claude-code" },
|
|
246
|
+
{ id: "claude-sonnet-4-6", provider: "anthropic" },
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
// When session provider is claude-code (subscription user migration), it must
|
|
250
|
+
// win regardless of candidate ordering to avoid the third-party block (#3772).
|
|
237
251
|
const result = resolveModelId("claude-sonnet-4-6", availableModels, "claude-code");
|
|
238
252
|
assert.ok(result, "should resolve a model");
|
|
239
|
-
assert.equal(result.provider, "
|
|
253
|
+
assert.equal(result.provider, "claude-code", "claude-code must win when it is the session provider");
|
|
240
254
|
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import test, { afterEach } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import { verifyExpectedArtifact } from "../auto-recovery.ts";
|
|
8
|
+
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertGateRow } from "../gsd-db.ts";
|
|
9
|
+
|
|
10
|
+
const tmpDirs: string[] = [];
|
|
11
|
+
|
|
12
|
+
function makeTmpProject(): string {
|
|
13
|
+
const dir = mkdtempSync(join(tmpdir(), "auto-recovery-"));
|
|
14
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
15
|
+
openDatabase(join(dir, ".gsd", "gsd.db"));
|
|
16
|
+
insertMilestone({ id: "M001", title: "Test Milestone", status: "active" });
|
|
17
|
+
insertSlice({
|
|
18
|
+
milestoneId: "M001",
|
|
19
|
+
id: "S01",
|
|
20
|
+
title: "Test Slice",
|
|
21
|
+
status: "pending",
|
|
22
|
+
risk: "low",
|
|
23
|
+
depends: [],
|
|
24
|
+
});
|
|
25
|
+
insertGateRow({ milestoneId: "M001", sliceId: "S01", gateId: "Q3", scope: "slice" });
|
|
26
|
+
tmpDirs.push(dir);
|
|
27
|
+
return dir;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
closeDatabase();
|
|
32
|
+
for (const dir of tmpDirs) {
|
|
33
|
+
try {
|
|
34
|
+
rmSync(dir, { recursive: true, force: true });
|
|
35
|
+
} catch {
|
|
36
|
+
// Best-effort cleanup only.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
tmpDirs.length = 0;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("verifyExpectedArtifact checks pending gate-evaluate artifacts without ESM require failures", () => {
|
|
43
|
+
const base = makeTmpProject();
|
|
44
|
+
|
|
45
|
+
const verified = verifyExpectedArtifact("gate-evaluate", "M001/S01/gates+Q3", base);
|
|
46
|
+
|
|
47
|
+
assert.equal(verified, false, "pending gates should keep gate-evaluate unverified");
|
|
48
|
+
});
|
|
@@ -138,6 +138,28 @@ test("generateCodebaseMap: excludes .gsd/ files", () => {
|
|
|
138
138
|
}
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
+
test("generateCodebaseMap: excludes .claude/ and other tool directories", () => {
|
|
142
|
+
const base = makeTmpRepo();
|
|
143
|
+
try {
|
|
144
|
+
addFile(base, "src/main.ts");
|
|
145
|
+
addFile(base, ".claude/CLAUDE.md");
|
|
146
|
+
addFile(base, ".claude/memory/user.md");
|
|
147
|
+
addFile(base, ".plans/plan.md");
|
|
148
|
+
addFile(base, ".cursor/settings.json");
|
|
149
|
+
addFile(base, ".vscode/settings.json");
|
|
150
|
+
|
|
151
|
+
const result = generateCodebaseMap(base);
|
|
152
|
+
assert.ok(result.content.includes("`src/main.ts`"), "should include src/main.ts");
|
|
153
|
+
assert.ok(!result.content.includes("CLAUDE.md"), "should exclude .claude/ files");
|
|
154
|
+
assert.ok(!result.content.includes("user.md"), "should exclude .claude/memory/ files");
|
|
155
|
+
assert.ok(!result.content.includes(".plans"), "should exclude .plans/ files");
|
|
156
|
+
assert.ok(!result.content.includes(".cursor"), "should exclude .cursor/ files");
|
|
157
|
+
assert.ok(!result.content.includes(".vscode"), "should exclude .vscode/ files");
|
|
158
|
+
} finally {
|
|
159
|
+
cleanup(base);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
141
163
|
test("generateCodebaseMap: excludes binary and lock files", () => {
|
|
142
164
|
const base = makeTmpRepo();
|
|
143
165
|
try {
|
|
@@ -100,6 +100,18 @@ steps: []
|
|
|
100
100
|
// ─── Catalog Registration ────────────────────────────────────────────────
|
|
101
101
|
|
|
102
102
|
describe("workflow catalog registration", () => {
|
|
103
|
+
it("model appears in TOP_LEVEL_SUBCOMMANDS", () => {
|
|
104
|
+
const entry = TOP_LEVEL_SUBCOMMANDS.find((c) => c.cmd === "model");
|
|
105
|
+
assert.ok(entry, "model should be in TOP_LEVEL_SUBCOMMANDS");
|
|
106
|
+
assert.match(entry!.desc, /session model/i);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("getGsdArgumentCompletions('m') includes model", () => {
|
|
110
|
+
const completions = getGsdArgumentCompletions("m");
|
|
111
|
+
const labels = completions.map((c: any) => c.label);
|
|
112
|
+
assert.ok(labels.includes("model"), "should include model completion");
|
|
113
|
+
});
|
|
114
|
+
|
|
103
115
|
it("workflow appears in TOP_LEVEL_SUBCOMMANDS", () => {
|
|
104
116
|
const entry = TOP_LEVEL_SUBCOMMANDS.find((c) => c.cmd === "workflow");
|
|
105
117
|
assert.ok(entry, "workflow should be in TOP_LEVEL_SUBCOMMANDS");
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
formatRequirementsForPrompt,
|
|
16
16
|
queryArtifact,
|
|
17
17
|
queryProject,
|
|
18
|
+
formatRoadmapExcerpt,
|
|
19
|
+
queryKnowledge,
|
|
18
20
|
} from '../context-store.ts';
|
|
19
21
|
|
|
20
22
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -452,3 +454,177 @@ describe("context-store: queryProject", () => {
|
|
|
452
454
|
assert.strictEqual(content, null, 'queryProject returns null when DB closed');
|
|
453
455
|
});
|
|
454
456
|
});
|
|
457
|
+
|
|
458
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
459
|
+
// context-store: formatRoadmapExcerpt
|
|
460
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
461
|
+
|
|
462
|
+
describe("context-store: formatRoadmapExcerpt", () => {
|
|
463
|
+
// Sample roadmap content matching actual M005-ROADMAP.md format
|
|
464
|
+
const sampleRoadmap = `# M005: Tiered Context Injection
|
|
465
|
+
|
|
466
|
+
## Vision
|
|
467
|
+
Refactor prompt builders to inject relevance-scoped context.
|
|
468
|
+
|
|
469
|
+
## Slice Overview
|
|
470
|
+
| ID | Slice | Risk | Depends | Done | After this |
|
|
471
|
+
|----|-------|------|---------|------|------------|
|
|
472
|
+
| S01 | Scope existing queries | low | — | ✅ | planSlice prompt scoped. |
|
|
473
|
+
| S02 | KNOWLEDGE scoping | medium | S01 | ⬜ | KNOWLEDGE sections filtered. |
|
|
474
|
+
| S03 | Measurement test | low | S02 | ⬜ | 40% reduction confirmed. |
|
|
475
|
+
`;
|
|
476
|
+
|
|
477
|
+
test("S02 with S01 predecessor includes both rows", () => {
|
|
478
|
+
const result = formatRoadmapExcerpt(sampleRoadmap, 'S02', '.gsd/milestones/M005/M005-ROADMAP.md');
|
|
479
|
+
|
|
480
|
+
// Should have header
|
|
481
|
+
assert.match(result, /\| ID \| Slice \| Risk \| Depends \| Done \| After this \|/, 'has header row');
|
|
482
|
+
// Should have separator
|
|
483
|
+
assert.match(result, /\|----\|/, 'has separator row');
|
|
484
|
+
// Should have S01 predecessor
|
|
485
|
+
assert.match(result, /\| S01 \|/, 'has predecessor S01 row');
|
|
486
|
+
// Should have S02 target
|
|
487
|
+
assert.match(result, /\| S02 \|/, 'has target S02 row');
|
|
488
|
+
// Should have reference directive
|
|
489
|
+
assert.match(result, /See full roadmap:.*M005-ROADMAP\.md/, 'has reference directive');
|
|
490
|
+
// Should NOT have S03 (not relevant)
|
|
491
|
+
assert.ok(!result.includes('| S03 |'), 'does not include unrelated S03');
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test("S01 with no predecessor includes only target row", () => {
|
|
495
|
+
const result = formatRoadmapExcerpt(sampleRoadmap, 'S01');
|
|
496
|
+
|
|
497
|
+
// Should have header + separator + S01 only
|
|
498
|
+
assert.match(result, /\| ID \| Slice \|/, 'has header row');
|
|
499
|
+
assert.match(result, /\| S01 \|/, 'has target S01 row');
|
|
500
|
+
// Should NOT have S02 or S03
|
|
501
|
+
assert.ok(!result.includes('| S02 |'), 'does not include S02');
|
|
502
|
+
assert.ok(!result.includes('| S03 |'), 'does not include S03');
|
|
503
|
+
// Should have reference
|
|
504
|
+
assert.match(result, /See full roadmap:/, 'has reference directive');
|
|
505
|
+
|
|
506
|
+
// Count rows: header + separator + S01 + blank + directive = 5 lines
|
|
507
|
+
const lines = result.split('\n');
|
|
508
|
+
assert.strictEqual(lines.length, 5, 'correct number of lines (no predecessor)');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test("missing slice returns empty string", () => {
|
|
512
|
+
const result = formatRoadmapExcerpt(sampleRoadmap, 'S99');
|
|
513
|
+
|
|
514
|
+
assert.strictEqual(result, '', 'missing slice returns empty string');
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test("empty input returns empty string", () => {
|
|
518
|
+
assert.strictEqual(formatRoadmapExcerpt('', 'S01'), '', 'empty content returns empty');
|
|
519
|
+
assert.strictEqual(formatRoadmapExcerpt(sampleRoadmap, ''), '', 'empty sliceId returns empty');
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
test("handles table with various column formats", () => {
|
|
523
|
+
// Table with different spacing and content
|
|
524
|
+
const variantRoadmap = `# Milestone
|
|
525
|
+
|
|
526
|
+
| ID | Slice | Risk | Depends | Done | After this |
|
|
527
|
+
|:---|:------|:-----|:--------|:-----|:-----------|
|
|
528
|
+
| S01 | First slice title | low | — | ✅ | First complete. |
|
|
529
|
+
| S02 | Second longer slice title here | medium | S01 | ⬜ | Second working. |
|
|
530
|
+
`;
|
|
531
|
+
|
|
532
|
+
const result = formatRoadmapExcerpt(variantRoadmap, 'S02');
|
|
533
|
+
|
|
534
|
+
assert.match(result, /\| S01 \|/, 'has predecessor with different spacing');
|
|
535
|
+
assert.match(result, /\| S02 \|/, 'has target with different spacing');
|
|
536
|
+
assert.match(result, /Second longer slice title/, 'preserves full slice title');
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test("handles multiple dependencies by using first one", () => {
|
|
540
|
+
const multiDepRoadmap = `| ID | Slice | Risk | Depends | Done | After this |
|
|
541
|
+
|----|-------|------|---------|------|------------|
|
|
542
|
+
| S01 | First | low | — | ✅ | Done. |
|
|
543
|
+
| S02 | Second | low | — | ✅ | Done. |
|
|
544
|
+
| S03 | Third | medium | S01, S02 | ⬜ | Working. |
|
|
545
|
+
`;
|
|
546
|
+
|
|
547
|
+
const result = formatRoadmapExcerpt(multiDepRoadmap, 'S03');
|
|
548
|
+
|
|
549
|
+
// Should include S01 (first dependency) and S03
|
|
550
|
+
assert.match(result, /\| S01 \|/, 'has first dependency S01');
|
|
551
|
+
assert.match(result, /\| S03 \|/, 'has target S03');
|
|
552
|
+
// S02 is also a dependency but we only include the first one
|
|
553
|
+
// (This is intentional to keep excerpts minimal)
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
558
|
+
// context-store: queryKnowledge
|
|
559
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
560
|
+
|
|
561
|
+
describe("context-store: queryKnowledge", () => {
|
|
562
|
+
// Sample KNOWLEDGE.md content
|
|
563
|
+
const sampleKnowledge = `# Project Knowledge
|
|
564
|
+
|
|
565
|
+
## Database Patterns
|
|
566
|
+
SQLite is used with WAL mode for concurrent reads.
|
|
567
|
+
Always use prepared statements.
|
|
568
|
+
|
|
569
|
+
More database details here.
|
|
570
|
+
|
|
571
|
+
## API Design
|
|
572
|
+
REST endpoints follow OpenAPI spec.
|
|
573
|
+
Use versioned paths like /v1/resource.
|
|
574
|
+
|
|
575
|
+
## Testing Guidelines
|
|
576
|
+
Unit tests use node:test.
|
|
577
|
+
Integration tests mock external services.
|
|
578
|
+
`;
|
|
579
|
+
|
|
580
|
+
test("single keyword matches header", async () => {
|
|
581
|
+
const result = await queryKnowledge(sampleKnowledge, ['database']);
|
|
582
|
+
|
|
583
|
+
assert.match(result, /## Database Patterns/, 'includes matching section header');
|
|
584
|
+
assert.match(result, /SQLite is used with WAL mode/, 'includes section content');
|
|
585
|
+
// Should NOT include other sections
|
|
586
|
+
assert.ok(!result.includes('## API Design'), 'does not include non-matching API section');
|
|
587
|
+
assert.ok(!result.includes('## Testing Guidelines'), 'does not include non-matching Testing section');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("multiple keywords match multiple sections", async () => {
|
|
591
|
+
const result = await queryKnowledge(sampleKnowledge, ['database', 'testing']);
|
|
592
|
+
|
|
593
|
+
assert.match(result, /## Database Patterns/, 'includes Database section');
|
|
594
|
+
assert.match(result, /## Testing Guidelines/, 'includes Testing section');
|
|
595
|
+
assert.ok(!result.includes('## API Design'), 'does not include API section');
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test("no matches returns empty string", async () => {
|
|
599
|
+
const result = await queryKnowledge(sampleKnowledge, ['nonexistent']);
|
|
600
|
+
|
|
601
|
+
assert.strictEqual(result, '', 'no matches returns empty string per D020');
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
test("keyword in first paragraph matches", async () => {
|
|
605
|
+
const result = await queryKnowledge(sampleKnowledge, ['sqlite']);
|
|
606
|
+
|
|
607
|
+
// 'sqlite' appears in first paragraph of Database Patterns
|
|
608
|
+
assert.match(result, /## Database Patterns/, 'matches keyword in first paragraph');
|
|
609
|
+
assert.match(result, /SQLite is used/, 'includes the section with matching paragraph');
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
test("case-insensitive matching", async () => {
|
|
613
|
+
const result = await queryKnowledge(sampleKnowledge, ['DATABASE', 'API']);
|
|
614
|
+
|
|
615
|
+
assert.match(result, /## Database Patterns/, 'case-insensitive header match');
|
|
616
|
+
assert.match(result, /## API Design/, 'case-insensitive header match for API');
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test("empty keywords returns empty string", async () => {
|
|
620
|
+
const result = await queryKnowledge(sampleKnowledge, []);
|
|
621
|
+
|
|
622
|
+
assert.strictEqual(result, '', 'empty keywords returns empty string');
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test("empty content returns empty string", async () => {
|
|
626
|
+
const result = await queryKnowledge('', ['database']);
|
|
627
|
+
|
|
628
|
+
assert.strictEqual(result, '', 'empty content returns empty string');
|
|
629
|
+
});
|
|
630
|
+
});
|