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
|
@@ -67,7 +67,7 @@ export interface SidecarItem {
|
|
|
67
67
|
export const MAX_UNIT_DISPATCHES = 3;
|
|
68
68
|
export const STUB_RECOVERY_THRESHOLD = 2;
|
|
69
69
|
export const MAX_LIFETIME_DISPATCHES = 6;
|
|
70
|
-
export const NEW_SESSION_TIMEOUT_MS =
|
|
70
|
+
export const NEW_SESSION_TIMEOUT_MS = 120_000;
|
|
71
71
|
|
|
72
72
|
// ─── AutoSession ─────────────────────────────────────────────────────────────
|
|
73
73
|
|
|
@@ -11,6 +11,7 @@ import type { GSDState } from "./types.js";
|
|
|
11
11
|
import { getCurrentBranch } from "./worktree.js";
|
|
12
12
|
import { getActiveHook } from "./post-unit-hooks.js";
|
|
13
13
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
14
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
14
15
|
import {
|
|
15
16
|
resolveMilestoneFile,
|
|
16
17
|
resolveSliceFile,
|
|
@@ -24,7 +25,11 @@ import { makeUI } from "../shared/tui.js";
|
|
|
24
25
|
import { GLYPH, INDENT } from "../shared/mod.js";
|
|
25
26
|
import { computeProgressScore } from "./progress-score.js";
|
|
26
27
|
import { getActiveWorktreeName } from "./worktree-command.js";
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
getGlobalGSDPreferencesPath,
|
|
30
|
+
getProjectGSDPreferencesPath,
|
|
31
|
+
parsePreferencesMarkdown,
|
|
32
|
+
} from "./preferences.js";
|
|
28
33
|
import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
|
|
29
34
|
import { parseUnitId } from "./unit-id.js";
|
|
30
35
|
import {
|
|
@@ -370,26 +375,74 @@ export type WidgetMode = "full" | "small" | "min" | "off";
|
|
|
370
375
|
const WIDGET_MODES: WidgetMode[] = ["full", "small", "min", "off"];
|
|
371
376
|
let widgetMode: WidgetMode = "full";
|
|
372
377
|
let widgetModeInitialized = false;
|
|
378
|
+
let widgetModePreferencePath: string | null = null;
|
|
379
|
+
|
|
380
|
+
function safeReadTextFile(path: string): string | null {
|
|
381
|
+
try {
|
|
382
|
+
if (!existsSync(path)) return null;
|
|
383
|
+
return readFileSync(path, "utf-8");
|
|
384
|
+
} catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function readWidgetModeFromFile(path: string): WidgetMode | undefined {
|
|
390
|
+
const raw = safeReadTextFile(path);
|
|
391
|
+
if (!raw) return undefined;
|
|
392
|
+
const prefs = parsePreferencesMarkdown(raw);
|
|
393
|
+
const saved = prefs?.widget_mode;
|
|
394
|
+
if (saved && WIDGET_MODES.includes(saved as WidgetMode)) {
|
|
395
|
+
return saved as WidgetMode;
|
|
396
|
+
}
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function resolveWidgetModePreferencePath(
|
|
401
|
+
projectPath = getProjectGSDPreferencesPath(),
|
|
402
|
+
globalPath = getGlobalGSDPreferencesPath(),
|
|
403
|
+
): string {
|
|
404
|
+
if (readWidgetModeFromFile(projectPath)) {
|
|
405
|
+
return projectPath;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (readWidgetModeFromFile(globalPath)) {
|
|
409
|
+
return globalPath;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (safeReadTextFile(projectPath) !== null) return projectPath;
|
|
413
|
+
if (safeReadTextFile(globalPath) !== null) return globalPath;
|
|
414
|
+
return getGlobalGSDPreferencesPath();
|
|
415
|
+
}
|
|
373
416
|
|
|
374
417
|
/** Load widget mode from preferences (once). */
|
|
375
|
-
function ensureWidgetModeLoaded(): void {
|
|
418
|
+
function ensureWidgetModeLoaded(projectPath?: string, globalPath?: string): void {
|
|
376
419
|
if (widgetModeInitialized) return;
|
|
377
420
|
widgetModeInitialized = true;
|
|
378
421
|
try {
|
|
379
|
-
const
|
|
380
|
-
const
|
|
422
|
+
const resolvedProjectPath = projectPath ?? getProjectGSDPreferencesPath();
|
|
423
|
+
const resolvedGlobalPath = globalPath ?? getGlobalGSDPreferencesPath();
|
|
424
|
+
const saved = readWidgetModeFromFile(resolvedProjectPath) ?? readWidgetModeFromFile(resolvedGlobalPath);
|
|
381
425
|
if (saved && WIDGET_MODES.includes(saved as WidgetMode)) {
|
|
382
426
|
widgetMode = saved as WidgetMode;
|
|
383
427
|
}
|
|
428
|
+
widgetModePreferencePath = resolveWidgetModePreferencePath(resolvedProjectPath, resolvedGlobalPath);
|
|
384
429
|
} catch (err) { /* non-fatal — use default */
|
|
385
|
-
logWarning("dashboard", `operation failed: ${
|
|
430
|
+
logWarning("dashboard", `operation failed: ${getErrorMessage(err)}`);
|
|
431
|
+
widgetModePreferencePath = getGlobalGSDPreferencesPath();
|
|
386
432
|
}
|
|
387
433
|
}
|
|
388
434
|
|
|
389
|
-
/**
|
|
390
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Persist widget mode to the preference file that owns the effective value.
|
|
437
|
+
* Project-scoped widget_mode wins over global; if neither scope defines it,
|
|
438
|
+
* we prefer an existing project preferences file and otherwise fall back to
|
|
439
|
+
* the global preferences file.
|
|
440
|
+
*/
|
|
441
|
+
function persistWidgetMode(
|
|
442
|
+
mode: WidgetMode,
|
|
443
|
+
prefsPath = widgetModePreferencePath ?? resolveWidgetModePreferencePath(),
|
|
444
|
+
): void {
|
|
391
445
|
try {
|
|
392
|
-
const prefsPath = getGlobalGSDPreferencesPath();
|
|
393
446
|
let content = "";
|
|
394
447
|
if (existsSync(prefsPath)) {
|
|
395
448
|
content = readFileSync(prefsPath, "utf-8");
|
|
@@ -408,26 +461,34 @@ function persistWidgetMode(mode: WidgetMode): void {
|
|
|
408
461
|
}
|
|
409
462
|
|
|
410
463
|
/** Cycle to the next widget mode. Returns the new mode. */
|
|
411
|
-
export function cycleWidgetMode(): WidgetMode {
|
|
412
|
-
ensureWidgetModeLoaded();
|
|
464
|
+
export function cycleWidgetMode(projectPath?: string, globalPath?: string): WidgetMode {
|
|
465
|
+
ensureWidgetModeLoaded(projectPath, globalPath);
|
|
413
466
|
const idx = WIDGET_MODES.indexOf(widgetMode);
|
|
414
467
|
widgetMode = WIDGET_MODES[(idx + 1) % WIDGET_MODES.length];
|
|
415
|
-
persistWidgetMode(widgetMode);
|
|
468
|
+
persistWidgetMode(widgetMode, widgetModePreferencePath ?? resolveWidgetModePreferencePath(projectPath, globalPath));
|
|
416
469
|
return widgetMode;
|
|
417
470
|
}
|
|
418
471
|
|
|
419
472
|
/** Set widget mode directly. */
|
|
420
|
-
export function setWidgetMode(mode: WidgetMode): void {
|
|
473
|
+
export function setWidgetMode(mode: WidgetMode, projectPath?: string, globalPath?: string): void {
|
|
474
|
+
ensureWidgetModeLoaded(projectPath, globalPath);
|
|
421
475
|
widgetMode = mode;
|
|
422
|
-
persistWidgetMode(widgetMode);
|
|
476
|
+
persistWidgetMode(widgetMode, widgetModePreferencePath ?? resolveWidgetModePreferencePath(projectPath, globalPath));
|
|
423
477
|
}
|
|
424
478
|
|
|
425
479
|
/** Get current widget mode. */
|
|
426
|
-
export function getWidgetMode(): WidgetMode {
|
|
427
|
-
ensureWidgetModeLoaded();
|
|
480
|
+
export function getWidgetMode(projectPath?: string, globalPath?: string): WidgetMode {
|
|
481
|
+
ensureWidgetModeLoaded(projectPath, globalPath);
|
|
428
482
|
return widgetMode;
|
|
429
483
|
}
|
|
430
484
|
|
|
485
|
+
/** Test-only reset for widget mode caching. */
|
|
486
|
+
export function _resetWidgetModeForTests(): void {
|
|
487
|
+
widgetMode = "full";
|
|
488
|
+
widgetModeInitialized = false;
|
|
489
|
+
widgetModePreferencePath = null;
|
|
490
|
+
}
|
|
491
|
+
|
|
431
492
|
// ─── Progress Widget ──────────────────────────────────────────────────────────
|
|
432
493
|
|
|
433
494
|
/** State accessors passed to updateProgressWidget to avoid direct global access */
|
|
@@ -901,4 +962,3 @@ function padToWidth(s: string, colWidth: number): string {
|
|
|
901
962
|
if (vis >= colWidth) return truncateToWidth(s, colWidth, "…");
|
|
902
963
|
return s + " ".repeat(colWidth - vis);
|
|
903
964
|
}
|
|
904
|
-
|
|
@@ -379,40 +379,8 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
379
379
|
},
|
|
380
380
|
},
|
|
381
381
|
{
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (state.phase !== "planning") return null;
|
|
385
|
-
// Phase skip: skip research when preference or profile says so
|
|
386
|
-
if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
|
|
387
|
-
return null;
|
|
388
|
-
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
389
|
-
const sid = state.activeSlice!.id;
|
|
390
|
-
const sTitle = state.activeSlice!.title;
|
|
391
|
-
const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
|
|
392
|
-
if (researchFile) return null; // has research, fall through
|
|
393
|
-
// Skip slice research for S01 when milestone research already exists —
|
|
394
|
-
// the milestone research already covers the same ground for the first slice.
|
|
395
|
-
const milestoneResearchFile = resolveMilestoneFile(
|
|
396
|
-
basePath,
|
|
397
|
-
mid,
|
|
398
|
-
"RESEARCH",
|
|
399
|
-
);
|
|
400
|
-
if (milestoneResearchFile && sid === "S01") return null; // fall through to plan-slice
|
|
401
|
-
return {
|
|
402
|
-
action: "dispatch",
|
|
403
|
-
unitType: "research-slice",
|
|
404
|
-
unitId: `${mid}/${sid}`,
|
|
405
|
-
prompt: await buildResearchSlicePrompt(
|
|
406
|
-
mid,
|
|
407
|
-
midTitle,
|
|
408
|
-
sid,
|
|
409
|
-
sTitle,
|
|
410
|
-
basePath,
|
|
411
|
-
),
|
|
412
|
-
};
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
{
|
|
382
|
+
// Keep this rule before the single-slice research rule so the multi-slice
|
|
383
|
+
// path wins whenever 2+ slices are ready.
|
|
416
384
|
name: "planning (multiple slices need research) → parallel-research-slices",
|
|
417
385
|
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
418
386
|
if (state.phase !== "planning") return null;
|
|
@@ -459,6 +427,40 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
459
427
|
};
|
|
460
428
|
},
|
|
461
429
|
},
|
|
430
|
+
{
|
|
431
|
+
name: "planning (no research, not S01) → research-slice",
|
|
432
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
433
|
+
if (state.phase !== "planning") return null;
|
|
434
|
+
// Phase skip: skip research when preference or profile says so
|
|
435
|
+
if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
|
|
436
|
+
return null;
|
|
437
|
+
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
438
|
+
const sid = state.activeSlice!.id;
|
|
439
|
+
const sTitle = state.activeSlice!.title;
|
|
440
|
+
const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
|
|
441
|
+
if (researchFile) return null; // has research, fall through
|
|
442
|
+
// Skip slice research for S01 when milestone research already exists —
|
|
443
|
+
// the milestone research already covers the same ground for the first slice.
|
|
444
|
+
const milestoneResearchFile = resolveMilestoneFile(
|
|
445
|
+
basePath,
|
|
446
|
+
mid,
|
|
447
|
+
"RESEARCH",
|
|
448
|
+
);
|
|
449
|
+
if (milestoneResearchFile && sid === "S01") return null; // fall through to plan-slice
|
|
450
|
+
return {
|
|
451
|
+
action: "dispatch",
|
|
452
|
+
unitType: "research-slice",
|
|
453
|
+
unitId: `${mid}/${sid}`,
|
|
454
|
+
prompt: await buildResearchSlicePrompt(
|
|
455
|
+
mid,
|
|
456
|
+
midTitle,
|
|
457
|
+
sid,
|
|
458
|
+
sTitle,
|
|
459
|
+
basePath,
|
|
460
|
+
),
|
|
461
|
+
};
|
|
462
|
+
},
|
|
463
|
+
},
|
|
462
464
|
{
|
|
463
465
|
name: "planning → plan-slice",
|
|
464
466
|
match: async ({ state, mid, midTitle, basePath }) => {
|
|
@@ -883,4 +885,3 @@ export async function resolveDispatch(
|
|
|
883
885
|
export function getDispatchRuleNames(): string[] {
|
|
884
886
|
return DISPATCH_RULES.map((r) => r.name);
|
|
885
887
|
}
|
|
886
|
-
|
|
@@ -329,8 +329,17 @@ export function resolveModelId<T extends { id: string; provider: string }>(
|
|
|
329
329
|
if (candidates.length === 0) return undefined;
|
|
330
330
|
if (candidates.length === 1) return candidates[0];
|
|
331
331
|
|
|
332
|
-
//
|
|
333
|
-
//
|
|
332
|
+
// When the user's current provider is claude-code (set by startup migration
|
|
333
|
+
// or explicit selection), honour it for bare IDs. Routing back to anthropic
|
|
334
|
+
// would undo the migration and hit the third-party subscription block (#3772).
|
|
335
|
+
if (currentProvider === "claude-code") {
|
|
336
|
+
const ccMatch = candidates.find(m => m.provider === "claude-code");
|
|
337
|
+
if (ccMatch) return ccMatch;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Extension / CLI-wrapper providers that should not win bare-ID resolution
|
|
341
|
+
// when a first-class API provider also offers the same model AND the user
|
|
342
|
+
// has not explicitly chosen the extension provider.
|
|
334
343
|
const EXTENSION_PROVIDERS = new Set(["claude-code"]);
|
|
335
344
|
|
|
336
345
|
// Prefer currentProvider only when it is a first-class API provider
|
|
@@ -353,7 +362,7 @@ export function resolveModelId<T extends { id: string; provider: string }>(
|
|
|
353
362
|
* Uses case-insensitive matching with alias support to prevent fail-open on
|
|
354
363
|
* provider naming variations (e.g. "copilot" vs "github-copilot").
|
|
355
364
|
*/
|
|
356
|
-
const FLAT_RATE_PROVIDERS = new Set(["github-copilot", "copilot"]);
|
|
365
|
+
const FLAT_RATE_PROVIDERS = new Set(["github-copilot", "copilot", "claude-code"]);
|
|
357
366
|
|
|
358
367
|
export function isFlatRateProvider(provider: string): boolean {
|
|
359
368
|
return FLAT_RATE_PROVIDERS.has(provider.toLowerCase());
|
|
@@ -261,7 +261,12 @@ export async function inlineGsdRootFile(
|
|
|
261
261
|
|
|
262
262
|
/**
|
|
263
263
|
* Inline decisions with optional milestone scoping from the DB.
|
|
264
|
-
* Falls back to filesystem via inlineGsdRootFile when DB unavailable
|
|
264
|
+
* Falls back to filesystem via inlineGsdRootFile only when DB is unavailable.
|
|
265
|
+
*
|
|
266
|
+
* Cascade logic (R005):
|
|
267
|
+
* 1. Query with { milestoneId, scope } if scope provided
|
|
268
|
+
* 2. If empty AND scope was provided, retry with { milestoneId } only (drop scope)
|
|
269
|
+
* 3. If still empty, return null (intentional per D020)
|
|
265
270
|
*/
|
|
266
271
|
export async function inlineDecisionsFromDb(
|
|
267
272
|
base: string, milestoneId?: string, scope?: string, level?: InlineLevel,
|
|
@@ -271,7 +276,15 @@ export async function inlineDecisionsFromDb(
|
|
|
271
276
|
const { isDbAvailable } = await import("./gsd-db.js");
|
|
272
277
|
if (isDbAvailable()) {
|
|
273
278
|
const { queryDecisions, formatDecisionsForPrompt } = await import("./context-store.js");
|
|
274
|
-
|
|
279
|
+
|
|
280
|
+
// First query: try with both milestoneId and scope (if scope provided)
|
|
281
|
+
let decisions = queryDecisions({ milestoneId, scope });
|
|
282
|
+
|
|
283
|
+
// Cascade: if empty AND scope was provided, retry without scope
|
|
284
|
+
if (decisions.length === 0 && scope) {
|
|
285
|
+
decisions = queryDecisions({ milestoneId });
|
|
286
|
+
}
|
|
287
|
+
|
|
275
288
|
if (decisions.length > 0) {
|
|
276
289
|
// Use compact format for non-full levels to save ~35% tokens
|
|
277
290
|
const formatted = inlineLevel !== "full"
|
|
@@ -279,26 +292,29 @@ export async function inlineDecisionsFromDb(
|
|
|
279
292
|
: formatDecisionsForPrompt(decisions);
|
|
280
293
|
return `### Decisions\nSource: \`.gsd/DECISIONS.md\`\n\n${formatted}`;
|
|
281
294
|
}
|
|
295
|
+
// DB available but cascade returned empty — intentional per D020, don't fall back to file
|
|
296
|
+
return null;
|
|
282
297
|
}
|
|
283
298
|
} catch (err) {
|
|
284
299
|
logWarning("prompt", `inlineDecisionsFromDb failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
285
300
|
}
|
|
301
|
+
// DB unavailable — fall back to filesystem
|
|
286
302
|
return inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
287
303
|
}
|
|
288
304
|
|
|
289
305
|
/**
|
|
290
|
-
* Inline requirements with optional slice scoping from the DB.
|
|
306
|
+
* Inline requirements with optional milestone and slice scoping from the DB.
|
|
291
307
|
* Falls back to filesystem via inlineGsdRootFile when DB unavailable or empty.
|
|
292
308
|
*/
|
|
293
309
|
export async function inlineRequirementsFromDb(
|
|
294
|
-
base: string, sliceId?: string, level?: InlineLevel,
|
|
310
|
+
base: string, milestoneId?: string, sliceId?: string, level?: InlineLevel,
|
|
295
311
|
): Promise<string | null> {
|
|
296
312
|
const inlineLevel = level ?? resolveInlineLevel();
|
|
297
313
|
try {
|
|
298
314
|
const { isDbAvailable } = await import("./gsd-db.js");
|
|
299
315
|
if (isDbAvailable()) {
|
|
300
316
|
const { queryRequirements, formatRequirementsForPrompt } = await import("./context-store.js");
|
|
301
|
-
const requirements = queryRequirements({ sliceId });
|
|
317
|
+
const requirements = queryRequirements({ milestoneId, sliceId });
|
|
302
318
|
if (requirements.length > 0) {
|
|
303
319
|
// Use compact format for non-full levels to save ~40% tokens
|
|
304
320
|
const formatted = inlineLevel !== "full"
|
|
@@ -335,6 +351,131 @@ export async function inlineProjectFromDb(
|
|
|
335
351
|
return inlineGsdRootFile(base, "project.md", "Project");
|
|
336
352
|
}
|
|
337
353
|
|
|
354
|
+
// ─── Stopwords for keyword extraction ─────────────────────────────────────
|
|
355
|
+
const STOPWORDS = new Set(['of', 'the', 'and', 'a', 'for', '+', '-', 'to', 'in', 'on', 'with', 'is', 'as', 'by']);
|
|
356
|
+
|
|
357
|
+
// Generic words that don't provide meaningful scope differentiation
|
|
358
|
+
const GENERIC_WORDS = new Set([
|
|
359
|
+
'setup', 'integration', 'implementation', 'testing', 'test', 'tests',
|
|
360
|
+
'config', 'configuration', 'init', 'initial', 'basic', 'core',
|
|
361
|
+
'main', 'primary', 'final', 'complete', 'finish', 'end',
|
|
362
|
+
'start', 'begin', 'first', 'last', 'update', 'updates',
|
|
363
|
+
'fix', 'fixes', 'add', 'adds', 'remove', 'removes',
|
|
364
|
+
'create', 'creates', 'build', 'builds', 'deploy', 'deployment',
|
|
365
|
+
'refactor', 'refactoring', 'cleanup', 'polish', 'review',
|
|
366
|
+
// Process/activity words that describe what you're doing, not what domain
|
|
367
|
+
'hardening', 'validation', 'verification', 'optimization',
|
|
368
|
+
'improvement', 'enhancement', 'infrastructure',
|
|
369
|
+
]);
|
|
370
|
+
|
|
371
|
+
// Pattern to match slice/milestone/task IDs (e.g., S01, M001, T03)
|
|
372
|
+
const UNIT_ID_PATTERN = /^[smt]\d+$/i;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Derive a scope keyword from slice title and optional description.
|
|
376
|
+
* Returns the most specific noun (first non-generic keyword) for decision scoping.
|
|
377
|
+
*
|
|
378
|
+
* Examples:
|
|
379
|
+
* - "Auth Middleware & Protected Route" → "auth"
|
|
380
|
+
* - "Database & User Model Setup" → "database"
|
|
381
|
+
* - "Integration Testing" → undefined (too generic)
|
|
382
|
+
* - "API Rate Limiting" → "api"
|
|
383
|
+
*
|
|
384
|
+
* @param sliceTitle - The slice title
|
|
385
|
+
* @param sliceDescription - Optional roadmap description (demo text)
|
|
386
|
+
* @returns A single lowercase keyword or undefined if no meaningful scope
|
|
387
|
+
*/
|
|
388
|
+
export function deriveSliceScope(sliceTitle: string, sliceDescription?: string): string | undefined {
|
|
389
|
+
// Combine title and description for keyword extraction
|
|
390
|
+
const combinedText = sliceDescription
|
|
391
|
+
? `${sliceTitle} ${sliceDescription}`
|
|
392
|
+
: sliceTitle;
|
|
393
|
+
|
|
394
|
+
// Extract all words, lowercase, remove punctuation
|
|
395
|
+
const words = combinedText
|
|
396
|
+
.split(/[\s&+,;:|/\\()-]+/)
|
|
397
|
+
.map(w => w.toLowerCase().replace(/[^a-z0-9]/g, ''))
|
|
398
|
+
.filter(w => w.length >= 2);
|
|
399
|
+
|
|
400
|
+
// Find the first word that is:
|
|
401
|
+
// 1. Not a stopword
|
|
402
|
+
// 2. Not a generic word
|
|
403
|
+
// 3. Not a unit ID (S01, M001, T03)
|
|
404
|
+
// 4. At least 3 characters (meaningful scope)
|
|
405
|
+
for (const word of words) {
|
|
406
|
+
if (STOPWORDS.has(word)) continue;
|
|
407
|
+
if (GENERIC_WORDS.has(word)) continue;
|
|
408
|
+
if (UNIT_ID_PATTERN.test(word)) continue;
|
|
409
|
+
if (word.length < 3) continue;
|
|
410
|
+
return word;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Extract keywords from a slice title for scoped knowledge queries.
|
|
417
|
+
* Splits on whitespace, filters stopwords, lowercases.
|
|
418
|
+
* Example: 'KNOWLEDGE scoping + roadmap excerpt' → ['knowledge', 'scoping', 'roadmap', 'excerpt']
|
|
419
|
+
*/
|
|
420
|
+
function extractKeywords(title: string): string[] {
|
|
421
|
+
return title
|
|
422
|
+
.split(/\s+/)
|
|
423
|
+
.map(w => w.toLowerCase().replace(/[^a-z0-9]/g, ''))
|
|
424
|
+
.filter(w => w.length > 0 && !STOPWORDS.has(w));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Inline scoped KNOWLEDGE.md content based on keywords from slice title.
|
|
429
|
+
* Reads KNOWLEDGE.md, filters to sections matching keywords, formats with header.
|
|
430
|
+
* Returns null if no KNOWLEDGE.md exists or no sections match.
|
|
431
|
+
*/
|
|
432
|
+
export async function inlineKnowledgeScoped(
|
|
433
|
+
base: string,
|
|
434
|
+
keywords: string[],
|
|
435
|
+
): Promise<string | null> {
|
|
436
|
+
const knowledgePath = resolveGsdRootFile(base, "KNOWLEDGE");
|
|
437
|
+
if (!existsSync(knowledgePath)) return null;
|
|
438
|
+
|
|
439
|
+
const content = await loadFile(knowledgePath);
|
|
440
|
+
if (!content) return null;
|
|
441
|
+
|
|
442
|
+
// Import queryKnowledge from context-store
|
|
443
|
+
const { queryKnowledge } = await import("./context-store.js");
|
|
444
|
+
const scoped = await queryKnowledge(content, keywords);
|
|
445
|
+
|
|
446
|
+
// Return null if no sections matched (empty string from queryKnowledge)
|
|
447
|
+
if (!scoped) return null;
|
|
448
|
+
|
|
449
|
+
return `### Project Knowledge (scoped)\nSource: \`${relGsdRootFile("KNOWLEDGE")}\`\n\n${scoped.trim()}`;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Inline a roadmap excerpt for a specific slice.
|
|
454
|
+
* Reads full roadmap, extracts minimal excerpt with header + predecessor + target row.
|
|
455
|
+
* Returns null if roadmap doesn't exist or slice not found.
|
|
456
|
+
*/
|
|
457
|
+
export async function inlineRoadmapExcerpt(
|
|
458
|
+
base: string,
|
|
459
|
+
mid: string,
|
|
460
|
+
sid: string,
|
|
461
|
+
): Promise<string | null> {
|
|
462
|
+
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
463
|
+
if (!roadmapPath || !existsSync(roadmapPath)) return null;
|
|
464
|
+
|
|
465
|
+
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
466
|
+
const content = await loadFile(roadmapPath);
|
|
467
|
+
if (!content) return null;
|
|
468
|
+
|
|
469
|
+
// Import formatRoadmapExcerpt from context-store
|
|
470
|
+
const { formatRoadmapExcerpt } = await import("./context-store.js");
|
|
471
|
+
const excerpt = formatRoadmapExcerpt(content, sid, roadmapRel);
|
|
472
|
+
|
|
473
|
+
// Return null if slice not found in roadmap
|
|
474
|
+
if (!excerpt) return null;
|
|
475
|
+
|
|
476
|
+
return `### Milestone Roadmap (excerpt)\nSource: \`${roadmapRel}\`\n\n${excerpt}`;
|
|
477
|
+
}
|
|
478
|
+
|
|
338
479
|
// ─── Skill Activation & Discovery ─────────────────────────────────────────
|
|
339
480
|
|
|
340
481
|
function normalizeSkillReference(ref: string): string {
|
|
@@ -880,7 +1021,7 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
880
1021
|
inlined.push(await inlineFile(contextPath, contextRel, "Milestone Context"));
|
|
881
1022
|
const projectInline = await inlineProjectFromDb(base);
|
|
882
1023
|
if (projectInline) inlined.push(projectInline);
|
|
883
|
-
const requirementsInline = await inlineRequirementsFromDb(base);
|
|
1024
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid);
|
|
884
1025
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
885
1026
|
const decisionsInline = await inlineDecisionsFromDb(base, mid);
|
|
886
1027
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
@@ -930,7 +1071,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
930
1071
|
if (inlineLevel !== "minimal") {
|
|
931
1072
|
const projectInline = await inlineProjectFromDb(base);
|
|
932
1073
|
if (projectInline) inlined.push(projectInline);
|
|
933
|
-
const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
|
|
1074
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
|
|
934
1075
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
935
1076
|
const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
|
|
936
1077
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
@@ -999,19 +1140,35 @@ export async function buildResearchSlicePrompt(
|
|
|
999
1140
|
const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
|
|
1000
1141
|
|
|
1001
1142
|
const inlined: string[] = [];
|
|
1002
|
-
|
|
1143
|
+
|
|
1144
|
+
// Use roadmap excerpt instead of full roadmap for context reduction
|
|
1145
|
+
const roadmapExcerptRS = await inlineRoadmapExcerpt(base, mid, sid);
|
|
1146
|
+
if (roadmapExcerptRS) {
|
|
1147
|
+
inlined.push(roadmapExcerptRS);
|
|
1148
|
+
} else {
|
|
1149
|
+
// Fall back to full roadmap if excerpt fails
|
|
1150
|
+
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1003
1153
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
1004
1154
|
if (contextInline) inlined.push(contextInline);
|
|
1005
1155
|
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1006
1156
|
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1007
1157
|
const researchInline = await inlineFileOptional(milestoneResearchPath, milestoneResearchRel, "Milestone Research");
|
|
1008
1158
|
if (researchInline) inlined.push(researchInline);
|
|
1009
|
-
|
|
1159
|
+
|
|
1160
|
+
// Derive scope from slice title for decision filtering (R005)
|
|
1161
|
+
const derivedScope = deriveSliceScope(sTitle);
|
|
1162
|
+
const decisionsInline = await inlineDecisionsFromDb(base, mid, derivedScope);
|
|
1010
1163
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
1011
|
-
const requirementsInline = await inlineRequirementsFromDb(base, sid);
|
|
1164
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid, sid);
|
|
1012
1165
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
1013
|
-
|
|
1166
|
+
|
|
1167
|
+
// Use scoped knowledge based on slice title keywords
|
|
1168
|
+
const keywords = extractKeywords(sTitle);
|
|
1169
|
+
const knowledgeInlineRS = await inlineKnowledgeScoped(base, keywords);
|
|
1014
1170
|
if (knowledgeInlineRS) inlined.push(knowledgeInlineRS);
|
|
1171
|
+
|
|
1015
1172
|
inlined.push(inlineTemplate("research", "Research"));
|
|
1016
1173
|
|
|
1017
1174
|
const depContent = await inlineDependencySummaries(mid, sid, base);
|
|
@@ -1060,19 +1217,33 @@ export async function buildPlanSlicePrompt(
|
|
|
1060
1217
|
const researchSliceAnchor = readPhaseAnchor(base, mid, "research-slice");
|
|
1061
1218
|
if (researchSliceAnchor) inlined.push(formatAnchorForPrompt(researchSliceAnchor));
|
|
1062
1219
|
|
|
1063
|
-
|
|
1220
|
+
// Use roadmap excerpt instead of full roadmap for context reduction
|
|
1221
|
+
const roadmapExcerptPS = await inlineRoadmapExcerpt(base, mid, sid);
|
|
1222
|
+
if (roadmapExcerptPS) {
|
|
1223
|
+
inlined.push(roadmapExcerptPS);
|
|
1224
|
+
} else {
|
|
1225
|
+
// Fall back to full roadmap if excerpt fails
|
|
1226
|
+
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1064
1229
|
const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
|
|
1065
1230
|
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1066
1231
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
|
|
1067
1232
|
if (researchInline) inlined.push(researchInline);
|
|
1068
1233
|
if (inlineLevel !== "minimal") {
|
|
1069
|
-
|
|
1234
|
+
// Derive scope from slice title for decision filtering (R005)
|
|
1235
|
+
const derivedScopePS = deriveSliceScope(sTitle);
|
|
1236
|
+
const decisionsInline = await inlineDecisionsFromDb(base, mid, derivedScopePS, inlineLevel);
|
|
1070
1237
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
1071
|
-
const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
|
|
1238
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid, sid, inlineLevel);
|
|
1072
1239
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
1073
1240
|
}
|
|
1074
|
-
|
|
1241
|
+
|
|
1242
|
+
// Use scoped knowledge based on slice title keywords
|
|
1243
|
+
const keywordsPS = extractKeywords(sTitle);
|
|
1244
|
+
const knowledgeInlinePS = await inlineKnowledgeScoped(base, keywordsPS);
|
|
1075
1245
|
if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
|
|
1246
|
+
|
|
1076
1247
|
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
1077
1248
|
if (inlineLevel === "full") {
|
|
1078
1249
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
@@ -1272,7 +1443,7 @@ export async function buildCompleteSlicePrompt(
|
|
|
1272
1443
|
if (sliceCtxInline) inlined.push(sliceCtxInline);
|
|
1273
1444
|
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
|
|
1274
1445
|
if (inlineLevel !== "minimal") {
|
|
1275
|
-
const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
|
|
1446
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid, sid, inlineLevel);
|
|
1276
1447
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
1277
1448
|
}
|
|
1278
1449
|
const knowledgeInlineCS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
@@ -1355,7 +1526,7 @@ export async function buildCompleteMilestonePrompt(
|
|
|
1355
1526
|
|
|
1356
1527
|
// Inline root GSD files (skip for minimal — completion can read these if needed)
|
|
1357
1528
|
if (inlineLevel !== "minimal") {
|
|
1358
|
-
const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
|
|
1529
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
|
|
1359
1530
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
1360
1531
|
const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
|
|
1361
1532
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
@@ -1421,7 +1592,7 @@ export async function buildValidateMilestonePrompt(
|
|
|
1421
1592
|
logWarning("prompt", `buildValidateMilestonePrompt verification classes lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1422
1593
|
}
|
|
1423
1594
|
|
|
1424
|
-
// Inline all slice summaries and
|
|
1595
|
+
// Inline all slice summaries and assessment results
|
|
1425
1596
|
let valSliceIds: string[] = [];
|
|
1426
1597
|
try {
|
|
1427
1598
|
const { isDbAvailable, getMilestoneSlices } = await import("./gsd-db.js");
|
|
@@ -1446,10 +1617,10 @@ export async function buildValidateMilestonePrompt(
|
|
|
1446
1617
|
const summaryRel = relSliceFile(base, mid, sid, "SUMMARY");
|
|
1447
1618
|
inlined.push(await inlineFile(summaryPath, summaryRel, `${sid} Summary`));
|
|
1448
1619
|
|
|
1449
|
-
const
|
|
1450
|
-
const
|
|
1451
|
-
const
|
|
1452
|
-
if (
|
|
1620
|
+
const assessmentPath = resolveSliceFile(base, mid, sid, "ASSESSMENT");
|
|
1621
|
+
const assessmentRel = relSliceFile(base, mid, sid, "ASSESSMENT");
|
|
1622
|
+
const assessmentInline = await inlineFileOptional(assessmentPath, assessmentRel, `${sid} Assessment`);
|
|
1623
|
+
if (assessmentInline) inlined.push(assessmentInline);
|
|
1453
1624
|
}
|
|
1454
1625
|
|
|
1455
1626
|
// Aggregate unresolved follow-ups and known limitations across slices
|
|
@@ -1480,7 +1651,7 @@ export async function buildValidateMilestonePrompt(
|
|
|
1480
1651
|
|
|
1481
1652
|
// Inline root GSD files
|
|
1482
1653
|
if (inlineLevel !== "minimal") {
|
|
1483
|
-
const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
|
|
1654
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
|
|
1484
1655
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
1485
1656
|
const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
|
|
1486
1657
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
@@ -1656,7 +1827,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1656
1827
|
if (inlineLevel !== "minimal") {
|
|
1657
1828
|
const projectInline = await inlineProjectFromDb(base);
|
|
1658
1829
|
if (projectInline) inlined.push(projectInline);
|
|
1659
|
-
const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
|
|
1830
|
+
const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
|
|
1660
1831
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
1661
1832
|
const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
|
|
1662
1833
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
@@ -1979,4 +2150,3 @@ export async function buildRewriteDocsPrompt(
|
|
|
1979
2150
|
overridesPath: relGsdRootFile("OVERRIDES"),
|
|
1980
2151
|
});
|
|
1981
2152
|
}
|
|
1982
|
-
|