gsd-pi 2.76.0-dev.4100bd590 → 2.76.0-dev.479ad0e78
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.js +32 -3
- package/dist/mcp-server.d.ts +7 -0
- package/dist/mcp-server.js +35 -1
- package/dist/onboarding.js +45 -0
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +2 -8
- package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
- package/dist/resources/extensions/gsd/auto/loop.js +9 -0
- package/dist/resources/extensions/gsd/auto/phases.js +58 -5
- package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
- package/dist/resources/extensions/gsd/auto/session.js +22 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +14 -3
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
- package/dist/resources/extensions/gsd/auto-start.js +58 -57
- package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
- package/dist/resources/extensions/gsd/auto.js +70 -28
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
- package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
- package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
- package/dist/resources/extensions/gsd/error-classifier.js +10 -3
- package/dist/resources/extensions/gsd/exec-history.js +120 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +149 -31
- package/dist/resources/extensions/gsd/guided-flow.js +190 -1
- package/dist/resources/extensions/gsd/health-widget.js +4 -1
- package/dist/resources/extensions/gsd/init-wizard.js +15 -1
- package/dist/resources/extensions/gsd/key-manager.js +28 -0
- package/dist/resources/extensions/gsd/model-router.js +36 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
- package/dist/resources/extensions/gsd/preferences-types.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
- package/dist/resources/extensions/gsd/preferences.js +17 -17
- package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
- package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
- package/dist/resources/extensions/gsd/token-counter.js +22 -5
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
- package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
- package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/dist/resources/skills/write-docs/SKILL.md +2 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- 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 +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- 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/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/chunks/6897.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/dist/welcome-screen.js +6 -1
- package/dist/wizard.js +2 -0
- package/package.json +1 -1
- package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
- package/packages/mcp-server/dist/remote-questions.js +732 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +7 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +70 -8
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts +14 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +49 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +64 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -1
- package/packages/mcp-server/src/mcp-server.test.ts +67 -0
- package/packages/mcp-server/src/remote-questions.test.ts +294 -0
- package/packages/mcp-server/src/remote-questions.ts +916 -0
- package/packages/mcp-server/src/server.ts +89 -14
- package/packages/mcp-server/src/session-manager.ts +43 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
- package/packages/mcp-server/src/workflow-tools.ts +84 -43
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/custom.d.ts +38 -0
- package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/custom.js +41 -0
- package/packages/pi-ai/dist/models/custom.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +8 -3
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +16 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
- package/packages/pi-ai/src/models/custom.ts +42 -0
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
- package/packages/pi-ai/src/providers/anthropic.ts +9 -3
- package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
- package/packages/pi-ai/src/providers/simple-options.ts +17 -1
- package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
- package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -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 +7 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.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 +13 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
- package/packages/pi-coding-agent/src/core/agent-session.ts +11 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
- package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
- package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
- package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/scripts/link-workspace-packages.cjs +1 -0
- package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
- package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
- package/src/resources/extensions/gsd/auto/loop.ts +9 -0
- package/src/resources/extensions/gsd/auto/phases.ts +82 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
- package/src/resources/extensions/gsd/auto/session.ts +35 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +17 -2
- package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
- package/src/resources/extensions/gsd/auto-start.ts +60 -68
- package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
- package/src/resources/extensions/gsd/auto.ts +73 -28
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
- package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
- package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
- package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
- package/src/resources/extensions/gsd/error-classifier.ts +10 -3
- package/src/resources/extensions/gsd/exec-history.ts +153 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- package/src/resources/extensions/gsd/gsd-db.ts +157 -33
- package/src/resources/extensions/gsd/guided-flow.ts +222 -1
- package/src/resources/extensions/gsd/health-widget.ts +3 -1
- package/src/resources/extensions/gsd/init-wizard.ts +15 -1
- package/src/resources/extensions/gsd/journal.ts +2 -1
- package/src/resources/extensions/gsd/key-manager.ts +28 -0
- package/src/resources/extensions/gsd/model-router.ts +42 -1
- package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
- package/src/resources/extensions/gsd/preferences-types.ts +46 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
- package/src/resources/extensions/gsd/preferences.ts +17 -17
- package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
- package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
- package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
- package/src/resources/extensions/gsd/token-counter.ts +22 -5
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
- package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
- package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/src/resources/skills/write-docs/SKILL.md +2 -1
- /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_ssgManifest.js +0 -0
|
@@ -440,6 +440,20 @@ function getModelCost(modelId) {
|
|
|
440
440
|
function bareModelId(modelId) {
|
|
441
441
|
return modelId.includes("/") ? modelId.split("/").pop() : modelId;
|
|
442
442
|
}
|
|
443
|
+
// ─── Provider-specific Tool Limits ─────────────────────────────────────────
|
|
444
|
+
/**
|
|
445
|
+
* Groq enforces a hard limit of 128 tools per request.
|
|
446
|
+
* Requests exceeding this limit receive a 400 error:
|
|
447
|
+
* "maximum number of items is 128"
|
|
448
|
+
* @see https://console.groq.com/docs/tool-use
|
|
449
|
+
*/
|
|
450
|
+
export const GROQ_MAX_TOOLS = 128;
|
|
451
|
+
/**
|
|
452
|
+
* Provider IDs that map to the Groq API backend.
|
|
453
|
+
* Used to detect Groq at the GSD routing layer where only the provider string
|
|
454
|
+
* is available (the pi-ai openai-completions adapter is shared across providers).
|
|
455
|
+
*/
|
|
456
|
+
const GROQ_PROVIDER_IDS = new Set(["groq"]);
|
|
443
457
|
// ─── Tool Compatibility Filter (ADR-005 Phase 3) ───────────────────────────
|
|
444
458
|
/**
|
|
445
459
|
* Check if a tool is compatible with a provider's capabilities.
|
|
@@ -461,8 +475,14 @@ export function isToolCompatibleWithProvider(toolName, providerCaps) {
|
|
|
461
475
|
/**
|
|
462
476
|
* Filter a list of tool names to only those compatible with a provider.
|
|
463
477
|
* Used by the routing pipeline to adjust tool sets when switching providers.
|
|
478
|
+
*
|
|
479
|
+
* @param toolNames - The full list of active tool names to filter.
|
|
480
|
+
* @param providerApi - The pi-ai API string (e.g. "openai-completions").
|
|
481
|
+
* @param provider - Optional provider ID (e.g. "groq"). Used to apply
|
|
482
|
+
* provider-specific limits that can't be expressed as API-level capabilities
|
|
483
|
+
* (e.g. Groq's 128-tool hard limit on the shared openai-completions adapter).
|
|
464
484
|
*/
|
|
465
|
-
export function filterToolsForProvider(toolNames, providerApi) {
|
|
485
|
+
export function filterToolsForProvider(toolNames, providerApi, provider) {
|
|
466
486
|
const providerCaps = getProviderCapabilities(providerApi);
|
|
467
487
|
// Provider doesn't support tool calling at all
|
|
468
488
|
if (!providerCaps.toolCalling) {
|
|
@@ -478,6 +498,14 @@ export function filterToolsForProvider(toolNames, providerApi) {
|
|
|
478
498
|
filtered.push(name);
|
|
479
499
|
}
|
|
480
500
|
}
|
|
501
|
+
// Groq enforces a hard limit of 128 tools per request (#4376).
|
|
502
|
+
// Trim the compatible list to GROQ_MAX_TOOLS and move the excess to filtered.
|
|
503
|
+
if (provider && GROQ_PROVIDER_IDS.has(provider) && compatible.length > GROQ_MAX_TOOLS) {
|
|
504
|
+
const trimmed = compatible.splice(GROQ_MAX_TOOLS);
|
|
505
|
+
filtered.push(...trimmed);
|
|
506
|
+
console.warn(`[gsd] Groq tool limit: ${compatible.length + trimmed.length} tools active but Groq allows at most ${GROQ_MAX_TOOLS}. ` +
|
|
507
|
+
`Trimming to the first ${GROQ_MAX_TOOLS} tools. Removed: ${trimmed.join(", ")}`);
|
|
508
|
+
}
|
|
481
509
|
return { compatible, filtered };
|
|
482
510
|
}
|
|
483
511
|
/**
|
|
@@ -486,8 +514,13 @@ export function filterToolsForProvider(toolNames, providerApi) {
|
|
|
486
514
|
*
|
|
487
515
|
* This is a hard filter only — it removes tools that would fail at the
|
|
488
516
|
* provider level. It does NOT remove tools based on soft heuristics.
|
|
517
|
+
*
|
|
518
|
+
* @param activeToolNames - The full list of currently active tool names.
|
|
519
|
+
* @param selectedModelApi - The pi-ai API string for the selected model.
|
|
520
|
+
* @param provider - Optional provider ID (e.g. "groq") for provider-specific
|
|
521
|
+
* limits beyond what the API-level capability profile expresses.
|
|
489
522
|
*/
|
|
490
|
-
export function adjustToolSet(activeToolNames, selectedModelApi) {
|
|
491
|
-
const { compatible, filtered } = filterToolsForProvider(activeToolNames, selectedModelApi);
|
|
523
|
+
export function adjustToolSet(activeToolNames, selectedModelApi, provider) {
|
|
524
|
+
const { compatible, filtered } = filterToolsForProvider(activeToolNames, selectedModelApi, provider);
|
|
492
525
|
return { toolNames: compatible, removedTools: filtered };
|
|
493
526
|
}
|
|
@@ -68,8 +68,13 @@ export function extractPackageReferences(description) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
// require('pkg') or import from 'pkg' in code blocks
|
|
72
|
-
|
|
71
|
+
// require('pkg') or `import ... from 'pkg'` in code blocks.
|
|
72
|
+
// The `from\s+['"]` branch MUST be preceded by an `import` keyword so that
|
|
73
|
+
// natural-language prose like `from "What's Next"` or `from 'master'` does
|
|
74
|
+
// not produce false package-existence failures. Requiring the leading import
|
|
75
|
+
// keyword anchors the match to JavaScript/TypeScript syntax.
|
|
76
|
+
// See: https://github.com/gsd-build/gsd-2/issues/4388
|
|
77
|
+
const importPattern = /(?:require\s*\(\s*['"]|import\b[\s\S]*?\bfrom\s+['"])([a-zA-Z0-9@/_-]+)['"\)]/g;
|
|
73
78
|
let importMatch;
|
|
74
79
|
while ((importMatch = importPattern.exec(description)) !== null) {
|
|
75
80
|
// Skip relative imports and node builtins
|
|
@@ -276,9 +281,23 @@ function extractPathFromAnnotation(raw) {
|
|
|
276
281
|
if (backtickMatch) {
|
|
277
282
|
return backtickMatch[2].trim();
|
|
278
283
|
}
|
|
284
|
+
// Strip leading/trailing double or single quotes wrapping the whole value.
|
|
285
|
+
// Plan documents sometimes emit `"src/foo.ts"` or `'src/bar.ts'` as input
|
|
286
|
+
// annotations. Stripping the wrapper allows the inner path to be checked
|
|
287
|
+
// correctly instead of producing a false-positive "file not found" error
|
|
288
|
+
// for a literal string with quote characters in it (#3747).
|
|
289
|
+
const quoteMatch = trimmed.match(/^(["'])([^"']+)\1$/);
|
|
290
|
+
if (quoteMatch) {
|
|
291
|
+
return quoteMatch[2].trim();
|
|
292
|
+
}
|
|
279
293
|
const annotatedMatch = trimmed.match(/^(.+?)\s+[—–-]\s+.+$/);
|
|
280
294
|
if (annotatedMatch) {
|
|
281
|
-
|
|
295
|
+
const prefix = annotatedMatch[1].trim();
|
|
296
|
+
const prefixBacktickMatch = prefix.match(/`([^`]+)`/);
|
|
297
|
+
if (prefixBacktickMatch && looksLikePathOrUrl(prefixBacktickMatch[1].trim())) {
|
|
298
|
+
return prefixBacktickMatch[1].trim();
|
|
299
|
+
}
|
|
300
|
+
return prefix.replace(/`/g, "").trim();
|
|
282
301
|
}
|
|
283
302
|
// Fallback: scan all backticked tokens and return the first one that looks
|
|
284
303
|
// like a path or URL. Handles prose-annotated bullets such as:
|
|
@@ -333,13 +352,19 @@ function containsGlobPattern(candidate) {
|
|
|
333
352
|
}
|
|
334
353
|
/**
|
|
335
354
|
* Build a set of files that will be created by tasks up to (but not including) taskIndex.
|
|
355
|
+
* Also includes outputs of completed tasks at any position — a completed task has already
|
|
356
|
+
* run and its outputs are available regardless of sequence position or disk state (#4071).
|
|
336
357
|
* All paths are normalized for consistent comparison.
|
|
337
358
|
*/
|
|
338
359
|
function getExpectedOutputsUpTo(tasks, taskIndex) {
|
|
339
360
|
const outputs = new Set();
|
|
340
|
-
for (let i = 0; i <
|
|
341
|
-
|
|
342
|
-
|
|
361
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
362
|
+
const task = tasks[i];
|
|
363
|
+
// Include prior tasks (i < taskIndex) OR completed tasks at any position
|
|
364
|
+
if (i < taskIndex || task.status === "completed") {
|
|
365
|
+
for (const file of task.expected_output) {
|
|
366
|
+
outputs.add(normalizeFilePath(file));
|
|
367
|
+
}
|
|
343
368
|
}
|
|
344
369
|
}
|
|
345
370
|
return outputs;
|
|
@@ -416,8 +441,14 @@ export function checkTaskOrdering(tasks, basePath) {
|
|
|
416
441
|
const task = tasks[i];
|
|
417
442
|
for (const file of task.expected_output) {
|
|
418
443
|
const normalizedFile = normalizeFilePath(file);
|
|
419
|
-
|
|
420
|
-
|
|
444
|
+
const existing = fileCreators.get(normalizedFile);
|
|
445
|
+
if (!existing || (!existing.completed && task.status === "completed")) {
|
|
446
|
+
fileCreators.set(normalizedFile, {
|
|
447
|
+
taskId: task.id,
|
|
448
|
+
index: i,
|
|
449
|
+
originalPath: file,
|
|
450
|
+
completed: task.status === "completed",
|
|
451
|
+
});
|
|
421
452
|
}
|
|
422
453
|
}
|
|
423
454
|
}
|
|
@@ -441,7 +472,11 @@ export function checkTaskOrdering(tasks, basePath) {
|
|
|
441
472
|
const creator = fileCreators.get(normalizedFile);
|
|
442
473
|
const absolutePath = resolve(basePath, normalizedFile);
|
|
443
474
|
const existsOnDisk = existsSync(absolutePath);
|
|
444
|
-
if
|
|
475
|
+
// Skip if the creating task has already completed — its output is available
|
|
476
|
+
// regardless of disk state (e.g. file was a temp artifact cleaned up after
|
|
477
|
+
// the task ran, or a replan introduced a new earlier-sequence task that
|
|
478
|
+
// reads this pre-execution output). (#4071)
|
|
479
|
+
if (creator && creator.index > i && !existsOnDisk && !creator.completed) {
|
|
445
480
|
// Task reads file that is created later — impossible ordering
|
|
446
481
|
results.push({
|
|
447
482
|
category: "file",
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* both the validation and runtime modules can import them without pulling
|
|
6
6
|
* in filesystem or loading logic.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Resolve whether context-mode features (gsd_exec sandbox + compaction
|
|
10
|
+
* snapshot) should be active. Default is ON: missing config or missing
|
|
11
|
+
* `enabled` is treated as true. Only `enabled: false` disables.
|
|
12
|
+
*/
|
|
13
|
+
export function isContextModeEnabled(prefs) {
|
|
14
|
+
return prefs?.context_mode?.enabled !== false;
|
|
15
|
+
}
|
|
8
16
|
/** Default preference values for each workflow mode. */
|
|
9
17
|
export const MODE_DEFAULTS = {
|
|
10
18
|
solo: {
|
|
@@ -87,6 +95,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set([
|
|
|
87
95
|
"flat_rate_providers",
|
|
88
96
|
"language",
|
|
89
97
|
"context_window_override",
|
|
98
|
+
"context_mode",
|
|
90
99
|
]);
|
|
91
100
|
/** Canonical list of all dispatch unit types. */
|
|
92
101
|
export const KNOWN_UNIT_TYPES = [
|
|
@@ -660,6 +660,55 @@ export function validatePreferences(preferences) {
|
|
|
660
660
|
errors.push("context_management must be an object");
|
|
661
661
|
}
|
|
662
662
|
}
|
|
663
|
+
// ─── Context Mode (gsd_exec sandbox) ────────────────────────────────────
|
|
664
|
+
if (preferences.context_mode !== undefined) {
|
|
665
|
+
if (typeof preferences.context_mode === "object" && preferences.context_mode !== null) {
|
|
666
|
+
const cmode = preferences.context_mode;
|
|
667
|
+
const validCmode = {};
|
|
668
|
+
if (cmode.enabled !== undefined) {
|
|
669
|
+
if (typeof cmode.enabled === "boolean")
|
|
670
|
+
validCmode.enabled = cmode.enabled;
|
|
671
|
+
else
|
|
672
|
+
errors.push("context_mode.enabled must be a boolean");
|
|
673
|
+
}
|
|
674
|
+
if (cmode.exec_timeout_ms !== undefined) {
|
|
675
|
+
const t = cmode.exec_timeout_ms;
|
|
676
|
+
if (typeof t === "number" && t >= 1000 && t <= 600_000)
|
|
677
|
+
validCmode.exec_timeout_ms = Math.floor(t);
|
|
678
|
+
else
|
|
679
|
+
errors.push("context_mode.exec_timeout_ms must be a number between 1000 and 600000");
|
|
680
|
+
}
|
|
681
|
+
if (cmode.exec_stdout_cap_bytes !== undefined) {
|
|
682
|
+
const b = cmode.exec_stdout_cap_bytes;
|
|
683
|
+
if (typeof b === "number" && b >= 4096 && b <= 16_777_216)
|
|
684
|
+
validCmode.exec_stdout_cap_bytes = Math.floor(b);
|
|
685
|
+
else
|
|
686
|
+
errors.push("context_mode.exec_stdout_cap_bytes must be a number between 4096 and 16777216");
|
|
687
|
+
}
|
|
688
|
+
if (cmode.exec_digest_chars !== undefined) {
|
|
689
|
+
const c = cmode.exec_digest_chars;
|
|
690
|
+
if (typeof c === "number" && c >= 0 && c <= 4000)
|
|
691
|
+
validCmode.exec_digest_chars = Math.floor(c);
|
|
692
|
+
else
|
|
693
|
+
errors.push("context_mode.exec_digest_chars must be a number between 0 and 4000");
|
|
694
|
+
}
|
|
695
|
+
if (cmode.exec_env_allowlist !== undefined) {
|
|
696
|
+
if (Array.isArray(cmode.exec_env_allowlist) &&
|
|
697
|
+
cmode.exec_env_allowlist.every((v) => typeof v === "string" && /^[A-Z_][A-Z0-9_]*$/i.test(v))) {
|
|
698
|
+
validCmode.exec_env_allowlist = cmode.exec_env_allowlist;
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
errors.push("context_mode.exec_env_allowlist must be an array of valid env var names");
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (Object.keys(validCmode).length > 0) {
|
|
705
|
+
validated.context_mode = validCmode;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
errors.push("context_mode must be an object");
|
|
710
|
+
}
|
|
711
|
+
}
|
|
663
712
|
// ─── Parallel Config ────────────────────────────────────────────────────
|
|
664
713
|
if (preferences.parallel && typeof preferences.parallel === "object") {
|
|
665
714
|
const p = preferences.parallel;
|
|
@@ -716,6 +765,40 @@ export function validatePreferences(preferences) {
|
|
|
716
765
|
validated.parallel = parallel;
|
|
717
766
|
}
|
|
718
767
|
}
|
|
768
|
+
// ─── Slice Parallel Config ───────────────────────────────────────────────
|
|
769
|
+
if (preferences.slice_parallel !== undefined) {
|
|
770
|
+
if (typeof preferences.slice_parallel === "object" && preferences.slice_parallel !== null) {
|
|
771
|
+
const sp = preferences.slice_parallel;
|
|
772
|
+
const validSp = {};
|
|
773
|
+
if (sp.enabled !== undefined) {
|
|
774
|
+
if (typeof sp.enabled === "boolean")
|
|
775
|
+
validSp.enabled = sp.enabled;
|
|
776
|
+
else
|
|
777
|
+
errors.push("slice_parallel.enabled must be a boolean");
|
|
778
|
+
}
|
|
779
|
+
if (sp.max_workers !== undefined) {
|
|
780
|
+
const maxWorkers = typeof sp.max_workers === "number" ? sp.max_workers : Number(sp.max_workers);
|
|
781
|
+
if (Number.isFinite(maxWorkers) && maxWorkers >= 1 && maxWorkers <= 8) {
|
|
782
|
+
validSp.max_workers = Math.floor(maxWorkers);
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
errors.push("slice_parallel.max_workers must be a number between 1 and 8");
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
const knownSliceParallelKeys = new Set(["enabled", "max_workers"]);
|
|
789
|
+
for (const key of Object.keys(sp)) {
|
|
790
|
+
if (!knownSliceParallelKeys.has(key)) {
|
|
791
|
+
warnings.push(`unknown slice_parallel key "${key}" — ignored`);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (Object.keys(validSp).length > 0) {
|
|
795
|
+
validated.slice_parallel = validSp;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
errors.push("slice_parallel must be an object");
|
|
800
|
+
}
|
|
801
|
+
}
|
|
719
802
|
// ─── Reactive Execution ─────────────────────────────────────────────────
|
|
720
803
|
if (preferences.reactive_execution !== undefined) {
|
|
721
804
|
if (typeof preferences.reactive_execution === "object" && preferences.reactive_execution !== null) {
|
|
@@ -26,12 +26,12 @@ export { resolveAllSkillReferences } from "./preferences-skills.js";
|
|
|
26
26
|
// These lived in preferences-skills.ts but imported loadEffectiveGSDPreferences
|
|
27
27
|
// back from this file, creating a circular dependency. Moved here since they
|
|
28
28
|
// are trivial wrappers over loadEffectiveGSDPreferences.
|
|
29
|
-
export function resolveSkillDiscoveryMode() {
|
|
30
|
-
const prefs = loadEffectiveGSDPreferences();
|
|
29
|
+
export function resolveSkillDiscoveryMode(basePath) {
|
|
30
|
+
const prefs = loadEffectiveGSDPreferences(basePath);
|
|
31
31
|
return prefs?.preferences.skill_discovery ?? "suggest";
|
|
32
32
|
}
|
|
33
|
-
export function resolveSkillStalenessDays() {
|
|
34
|
-
const prefs = loadEffectiveGSDPreferences();
|
|
33
|
+
export function resolveSkillStalenessDays(basePath) {
|
|
34
|
+
const prefs = loadEffectiveGSDPreferences(basePath);
|
|
35
35
|
return prefs?.preferences.skill_staleness_days ?? 60;
|
|
36
36
|
}
|
|
37
37
|
// ─── Re-exports: models ─────────────────────────────────────────────────────
|
|
@@ -46,16 +46,16 @@ function globalPreferencesPath() {
|
|
|
46
46
|
function legacyGlobalPreferencesPath() {
|
|
47
47
|
return join(homedir(), ".pi", "agent", "gsd-preferences.md");
|
|
48
48
|
}
|
|
49
|
-
function projectPreferencesPath() {
|
|
50
|
-
return join(gsdRoot(
|
|
49
|
+
function projectPreferencesPath(basePath = process.cwd()) {
|
|
50
|
+
return join(gsdRoot(basePath), "PREFERENCES.md");
|
|
51
51
|
}
|
|
52
52
|
// Legacy lowercase files can still exist in older projects. Keep them as a
|
|
53
53
|
// compatibility-only fallback, but route new reads/writes through PREFERENCES.md.
|
|
54
54
|
function legacyGlobalPreferencesPathLowercase() {
|
|
55
55
|
return join(gsdHome(), "preferences.md");
|
|
56
56
|
}
|
|
57
|
-
function legacyProjectPreferencesPathLowercase() {
|
|
58
|
-
return join(gsdRoot(
|
|
57
|
+
function legacyProjectPreferencesPathLowercase(basePath = process.cwd()) {
|
|
58
|
+
return join(gsdRoot(basePath), "preferences.md");
|
|
59
59
|
}
|
|
60
60
|
export function getGlobalGSDPreferencesPath() {
|
|
61
61
|
return globalPreferencesPath();
|
|
@@ -63,8 +63,8 @@ export function getGlobalGSDPreferencesPath() {
|
|
|
63
63
|
export function getLegacyGlobalGSDPreferencesPath() {
|
|
64
64
|
return legacyGlobalPreferencesPath();
|
|
65
65
|
}
|
|
66
|
-
export function getProjectGSDPreferencesPath() {
|
|
67
|
-
return projectPreferencesPath();
|
|
66
|
+
export function getProjectGSDPreferencesPath(basePath) {
|
|
67
|
+
return projectPreferencesPath(basePath);
|
|
68
68
|
}
|
|
69
69
|
// ─── Loading ────────────────────────────────────────────────────────────────
|
|
70
70
|
export function loadGlobalGSDPreferences() {
|
|
@@ -72,13 +72,13 @@ export function loadGlobalGSDPreferences() {
|
|
|
72
72
|
?? loadPreferencesFile(legacyGlobalPreferencesPathLowercase(), "global")
|
|
73
73
|
?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global");
|
|
74
74
|
}
|
|
75
|
-
export function loadProjectGSDPreferences() {
|
|
76
|
-
return loadPreferencesFile(projectPreferencesPath(), "project")
|
|
77
|
-
?? loadPreferencesFile(legacyProjectPreferencesPathLowercase(), "project");
|
|
75
|
+
export function loadProjectGSDPreferences(basePath) {
|
|
76
|
+
return loadPreferencesFile(projectPreferencesPath(basePath), "project")
|
|
77
|
+
?? loadPreferencesFile(legacyProjectPreferencesPathLowercase(basePath), "project");
|
|
78
78
|
}
|
|
79
|
-
export function loadEffectiveGSDPreferences() {
|
|
79
|
+
export function loadEffectiveGSDPreferences(basePath) {
|
|
80
80
|
const globalPreferences = loadGlobalGSDPreferences();
|
|
81
|
-
const projectPreferences = loadProjectGSDPreferences();
|
|
81
|
+
const projectPreferences = loadProjectGSDPreferences(basePath);
|
|
82
82
|
if (!globalPreferences && !projectPreferences)
|
|
83
83
|
return null;
|
|
84
84
|
let result;
|
|
@@ -489,8 +489,8 @@ export function resolvePreDispatchHooks() {
|
|
|
489
489
|
* Worktree isolation requires explicit opt-in because it depends on git
|
|
490
490
|
* branch infrastructure that must be set up before use.
|
|
491
491
|
*/
|
|
492
|
-
export function getIsolationMode() {
|
|
493
|
-
const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
492
|
+
export function getIsolationMode(basePath) {
|
|
493
|
+
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
|
|
494
494
|
if (prefs?.isolation === "worktree")
|
|
495
495
|
return "worktree";
|
|
496
496
|
if (prefs?.isolation === "branch")
|
|
@@ -22,6 +22,27 @@ import { join, dirname } from "node:path";
|
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
23
|
import { homedir } from "node:os";
|
|
24
24
|
import { logWarning } from "./workflow-logger.js";
|
|
25
|
+
function hasRequiredExtensionAssets(rootDir, exists = existsSync) {
|
|
26
|
+
return (exists(join(rootDir, "prompts")) &&
|
|
27
|
+
exists(join(rootDir, "templates", "task-summary.md")));
|
|
28
|
+
}
|
|
29
|
+
export function resolveExtensionDirFromCandidates(moduleDir, agentGsdDir, exists = existsSync) {
|
|
30
|
+
const moduleUsable = hasRequiredExtensionAssets(moduleDir, exists);
|
|
31
|
+
const agentUsable = hasRequiredExtensionAssets(agentGsdDir, exists);
|
|
32
|
+
// Prefer the user-local extension tree when both are valid. This avoids
|
|
33
|
+
// leaking npm/global-install paths into prompts on Windows.
|
|
34
|
+
if (agentUsable)
|
|
35
|
+
return agentGsdDir;
|
|
36
|
+
if (moduleUsable)
|
|
37
|
+
return moduleDir;
|
|
38
|
+
// Degraded fallback: if required template is missing in both locations,
|
|
39
|
+
// keep previous behavior and prefer whichever still has prompts/.
|
|
40
|
+
if (exists(join(moduleDir, "prompts")))
|
|
41
|
+
return moduleDir;
|
|
42
|
+
if (exists(join(agentGsdDir, "prompts")))
|
|
43
|
+
return agentGsdDir;
|
|
44
|
+
return moduleDir;
|
|
45
|
+
}
|
|
25
46
|
/**
|
|
26
47
|
* Resolve the GSD extension directory.
|
|
27
48
|
*
|
|
@@ -34,15 +55,9 @@ import { logWarning } from "./workflow-logger.js";
|
|
|
34
55
|
*/
|
|
35
56
|
function resolveExtensionDir() {
|
|
36
57
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
37
|
-
if (existsSync(join(moduleDir, "prompts")))
|
|
38
|
-
return moduleDir;
|
|
39
|
-
// Fallback: user-local agent directory
|
|
40
58
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
41
59
|
const agentGsdDir = join(gsdHome, "agent", "extensions", "gsd");
|
|
42
|
-
|
|
43
|
-
return agentGsdDir;
|
|
44
|
-
// Last resort: return the module dir (warmCache will silently handle the miss)
|
|
45
|
-
return moduleDir;
|
|
60
|
+
return resolveExtensionDirFromCandidates(moduleDir, agentGsdDir);
|
|
46
61
|
}
|
|
47
62
|
const __extensionDir = resolveExtensionDir();
|
|
48
63
|
const promptsDir = join(__extensionDir, "prompts");
|
|
@@ -162,6 +162,10 @@ Preserve the specification's exact terminology, emphasis, and specific framing.
|
|
|
162
162
|
6. For each architectural or pattern decision, call `gsd_decision_save` — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
|
|
163
163
|
7. {{commitInstruction}}
|
|
164
164
|
|
|
165
|
+
### Ready-phrase pre-condition (NON-BYPASSABLE)
|
|
166
|
+
|
|
167
|
+
Before emitting the ready phrase, verify in the CURRENT turn that you have written `.gsd/PROJECT.md`, `.gsd/REQUIREMENTS.md`, `{{contextPath}}`, and called `gsd_plan_milestone`. If any is missing, **STOP** — emit the missing tool calls in this same turn. The system rejects premature ready signals and retries are capped.
|
|
168
|
+
|
|
165
169
|
After writing the files, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
|
|
166
170
|
|
|
167
171
|
### Multi-Milestone
|
|
@@ -234,6 +238,10 @@ For single-milestone projects, do NOT write this file.
|
|
|
234
238
|
|
|
235
239
|
7. {{multiMilestoneCommitInstruction}}
|
|
236
240
|
|
|
241
|
+
### Ready-phrase pre-condition (NON-BYPASSABLE)
|
|
242
|
+
|
|
243
|
+
Before emitting the ready phrase, verify in the CURRENT turn that you have written `.gsd/PROJECT.md`, `.gsd/REQUIREMENTS.md`, the primary `CONTEXT.md`, called `gsd_plan_milestone` for the primary milestone, and written `.gsd/DISCUSSION-MANIFEST.json` with `gates_completed === total`. If any is missing, **STOP** — emit the missing tool calls in this same turn. The system rejects premature ready signals and retries are capped.
|
|
244
|
+
|
|
237
245
|
After writing the files, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
|
|
238
246
|
|
|
239
247
|
## Critical Rules
|
|
@@ -339,7 +339,20 @@ These sections are in addition to whatever other context the discussion surfaced
|
|
|
339
339
|
6. For each architectural or pattern decision made during discussion, call `gsd_decision_save` — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
|
|
340
340
|
7. {{commitInstruction}}
|
|
341
341
|
|
|
342
|
-
|
|
342
|
+
### Ready-phrase pre-condition (NON-BYPASSABLE)
|
|
343
|
+
|
|
344
|
+
Before emitting the ready phrase, verify in the CURRENT turn that you have:
|
|
345
|
+
|
|
346
|
+
- [ ] Written `.gsd/PROJECT.md` (step 2)
|
|
347
|
+
- [ ] Written `.gsd/REQUIREMENTS.md` (step 3)
|
|
348
|
+
- [ ] Written `{{contextPath}}` (step 4)
|
|
349
|
+
- [ ] Called `gsd_plan_milestone` (step 5)
|
|
350
|
+
|
|
351
|
+
If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missing tool calls in this same turn. The system detects missing artifacts and will reject premature ready signals — you will be asked again and retries are capped.
|
|
352
|
+
|
|
353
|
+
Do not announce the ready phrase as something you are "about to" do. Do not narrate "now writing the files" as a substitute for actually writing them. The ready phrase is a post-write signal, not an intent signal.
|
|
354
|
+
|
|
355
|
+
After completing steps 1–7 above, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
|
|
343
356
|
|
|
344
357
|
### Multi-Milestone
|
|
345
358
|
|
|
@@ -418,6 +431,20 @@ For single-milestone projects, do NOT write this file — it is only for multi-m
|
|
|
418
431
|
|
|
419
432
|
7. {{multiMilestoneCommitInstruction}}
|
|
420
433
|
|
|
421
|
-
|
|
434
|
+
### Ready-phrase pre-condition (NON-BYPASSABLE)
|
|
435
|
+
|
|
436
|
+
Before emitting the ready phrase, verify in the CURRENT turn that you have:
|
|
437
|
+
|
|
438
|
+
- [ ] Written `.gsd/PROJECT.md` (Phase 1)
|
|
439
|
+
- [ ] Written `.gsd/REQUIREMENTS.md` (Phase 1)
|
|
440
|
+
- [ ] Written primary-milestone `CONTEXT.md` (Phase 2)
|
|
441
|
+
- [ ] Called `gsd_plan_milestone` for the primary milestone (Phase 2)
|
|
442
|
+
- [ ] Written `.gsd/DISCUSSION-MANIFEST.json` with `gates_completed === total` (Phase 3)
|
|
443
|
+
|
|
444
|
+
If ANY box is unchecked, **STOP**. Do NOT emit the ready phrase. Emit the missing tool calls in this same turn. The system detects missing artifacts and will reject premature ready signals — you will be asked again and retries are capped.
|
|
445
|
+
|
|
446
|
+
Do not announce the ready phrase as something you are "about to" do. Do not narrate "now writing the files" as a substitute for actually writing them. The ready phrase is a post-write signal, not an intent signal.
|
|
447
|
+
|
|
448
|
+
After completing all phases above, say exactly: "Milestone M001 ready." — nothing else. Auto-mode will start automatically.
|
|
422
449
|
|
|
423
450
|
{{inlinedTemplates}}
|
|
@@ -15,8 +15,11 @@ Dispatch ALL slices simultaneously using the `subagent` tool in **parallel mode*
|
|
|
15
15
|
1. Call `subagent` with `tasks: [...]` containing one entry per slice below
|
|
16
16
|
2. Wait for ALL subagents to complete
|
|
17
17
|
3. Verify each slice's RESEARCH file was written (check the `.gsd/{{mid}}/` directory)
|
|
18
|
-
4. If
|
|
19
|
-
5.
|
|
18
|
+
4. If a subagent failed to write its RESEARCH file, retry it **once** individually
|
|
19
|
+
5. If it fails a second time, write a partial RESEARCH file for that slice with a `## BLOCKER` section explaining the failure — do NOT retry again
|
|
20
|
+
6. Report which slices completed research and which (if any) needed a blocker note
|
|
21
|
+
|
|
22
|
+
**Important**: Each failed slice gets exactly one retry. After that, write the blocker and move on. Never retry the same slice more than once.
|
|
20
23
|
|
|
21
24
|
## Subagent Prompts
|
|
22
25
|
|
|
@@ -3,9 +3,17 @@
|
|
|
3
3
|
* Tracks every bash command, file write, and file edit during a unit execution.
|
|
4
4
|
* Evidence is compared against LLM completion claims in evidence-cross-ref.ts.
|
|
5
5
|
*
|
|
6
|
+
* Evidence is persisted to .gsd/safety/evidence-<mid>-<sid>-<tid>.json so it
|
|
7
|
+
* survives session restarts (pause/resume, crash recovery). On unit start,
|
|
8
|
+
* call resetEvidence() then loadEvidenceFromDisk(). On every new tool call,
|
|
9
|
+
* saveEvidenceToDisk() is called automatically by recordToolCall/recordToolResult.
|
|
10
|
+
*
|
|
6
11
|
* Follows the same module-level Map pattern as auto-tool-tracking.ts.
|
|
7
12
|
* Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
8
13
|
*/
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, } from "node:fs";
|
|
15
|
+
import { join, dirname } from "node:path";
|
|
16
|
+
import { randomBytes } from "node:crypto";
|
|
9
17
|
// ─── Module State ───────────────────────────────────────────────────────────
|
|
10
18
|
let unitEvidence = [];
|
|
11
19
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
@@ -27,6 +35,94 @@ export function getFilePaths() {
|
|
|
27
35
|
.filter((e) => e.kind === "write" || e.kind === "edit")
|
|
28
36
|
.map(e => e.path);
|
|
29
37
|
}
|
|
38
|
+
// ─── Persistence (Bug #4385 — evidence must survive session restarts) ────────
|
|
39
|
+
/**
|
|
40
|
+
* Build the path for the evidence JSON file for a given unit.
|
|
41
|
+
* Lives under .gsd/safety/ which is gitignored and session-scoped.
|
|
42
|
+
*/
|
|
43
|
+
function evidencePath(basePath, milestoneId, sliceId, taskId) {
|
|
44
|
+
return join(basePath, ".gsd", "safety", `evidence-${milestoneId}-${sliceId}-${taskId}.json`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate that a parsed value is an array of EvidenceEntry objects.
|
|
48
|
+
* Rejects corrupt / schema-mismatch data rather than letting it poison state.
|
|
49
|
+
*/
|
|
50
|
+
function isEvidenceArray(data) {
|
|
51
|
+
if (!Array.isArray(data))
|
|
52
|
+
return false;
|
|
53
|
+
return data.every((e) => {
|
|
54
|
+
if (e === null || typeof e !== "object")
|
|
55
|
+
return false;
|
|
56
|
+
const rec = e;
|
|
57
|
+
if (typeof rec.toolCallId !== "string")
|
|
58
|
+
return false;
|
|
59
|
+
if (typeof rec.timestamp !== "number")
|
|
60
|
+
return false;
|
|
61
|
+
if (rec.kind === "bash") {
|
|
62
|
+
return (typeof rec.command === "string" &&
|
|
63
|
+
typeof rec.exitCode === "number" &&
|
|
64
|
+
typeof rec.outputSnippet === "string");
|
|
65
|
+
}
|
|
66
|
+
if (rec.kind === "write" || rec.kind === "edit") {
|
|
67
|
+
return typeof rec.path === "string";
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Persist the current in-memory evidence to disk so it survives a session
|
|
74
|
+
* restart. Called from saveEvidenceToDisk after recordToolCall/recordToolResult.
|
|
75
|
+
* Non-fatal — persistence failures must never break unit execution.
|
|
76
|
+
*/
|
|
77
|
+
export function saveEvidenceToDisk(basePath, milestoneId, sliceId, taskId) {
|
|
78
|
+
try {
|
|
79
|
+
const path = evidencePath(basePath, milestoneId, sliceId, taskId);
|
|
80
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
81
|
+
const tmp = `${path}.tmp.${randomBytes(4).toString("hex")}`;
|
|
82
|
+
writeFileSync(tmp, JSON.stringify(unitEvidence, null, 2) + "\n", "utf-8");
|
|
83
|
+
renameSync(tmp, path);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Non-fatal — don't let persistence failures break unit execution
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Load persisted evidence from disk into the in-memory array.
|
|
91
|
+
* Call after resetEvidence() on session resume to restore context for a
|
|
92
|
+
* partially-executed unit. If the file does not exist (fresh unit), this
|
|
93
|
+
* is a no-op — getEvidence() will return [] which is correct.
|
|
94
|
+
*/
|
|
95
|
+
export function loadEvidenceFromDisk(basePath, milestoneId, sliceId, taskId) {
|
|
96
|
+
try {
|
|
97
|
+
const path = evidencePath(basePath, milestoneId, sliceId, taskId);
|
|
98
|
+
if (!existsSync(path))
|
|
99
|
+
return;
|
|
100
|
+
const raw = readFileSync(path, "utf-8");
|
|
101
|
+
const parsed = JSON.parse(raw);
|
|
102
|
+
if (isEvidenceArray(parsed)) {
|
|
103
|
+
unitEvidence = parsed;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Non-fatal — corrupt / missing file is treated as empty evidence
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Delete the persisted evidence file for a unit after it has been fully
|
|
112
|
+
* processed. Prevents stale evidence from affecting future retries of
|
|
113
|
+
* the same unit ID.
|
|
114
|
+
*/
|
|
115
|
+
export function clearEvidenceFromDisk(basePath, milestoneId, sliceId, taskId) {
|
|
116
|
+
try {
|
|
117
|
+
const path = evidencePath(basePath, milestoneId, sliceId, taskId);
|
|
118
|
+
if (existsSync(path)) {
|
|
119
|
+
unlinkSync(path);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Non-fatal
|
|
124
|
+
}
|
|
125
|
+
}
|
|
30
126
|
// ─── Recording (called from register-hooks.ts) ─────────────────────────────
|
|
31
127
|
/**
|
|
32
128
|
* Record a tool call at dispatch time (before execution).
|
|
@@ -4,13 +4,18 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Uses tasks.expected_output (DB column, populated from per-task ## Expected Output)
|
|
6
6
|
* and tasks.files (from slice PLAN.md - Files: subline) as the expected set.
|
|
7
|
-
* Compares against git diff
|
|
7
|
+
* Compares against `git diff-tree --root --no-commit-id -r --name-only HEAD` after auto-commit.
|
|
8
|
+
* Using diff-tree --root handles initial commits, shallow clones, and merge commits correctly
|
|
9
|
+
* (Bug #4385 — git diff HEAD~1 failed on initial commits).
|
|
8
10
|
*
|
|
9
11
|
* Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
10
12
|
*/
|
|
13
|
+
import { createRequire } from "node:module";
|
|
11
14
|
import { execFileSync } from "node:child_process";
|
|
12
15
|
import { normalizePlannedFileReference } from "../files.js";
|
|
13
16
|
import { logWarning } from "../workflow-logger.js";
|
|
17
|
+
const _require = createRequire(import.meta.url);
|
|
18
|
+
const picomatch = _require("picomatch");
|
|
14
19
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
15
20
|
/**
|
|
16
21
|
* Validate file changes after auto-commit for an execute-task unit.
|
|
@@ -20,7 +25,7 @@ import { logWarning } from "../workflow-logger.js";
|
|
|
20
25
|
* @param expectedOutput - JSON array from tasks.expected_output DB column
|
|
21
26
|
* @param plannedFiles - JSON array from tasks.files DB column
|
|
22
27
|
*/
|
|
23
|
-
export function validateFileChanges(basePath, expectedOutput, plannedFiles) {
|
|
28
|
+
export function validateFileChanges(basePath, expectedOutput, plannedFiles, fileChangeAllowlist = []) {
|
|
24
29
|
const allExpected = new Set([...expectedOutput, ...plannedFiles]);
|
|
25
30
|
// If no expected files were planned, skip validation
|
|
26
31
|
if (allExpected.size === 0)
|
|
@@ -33,8 +38,11 @@ export function validateFileChanges(basePath, expectedOutput, plannedFiles) {
|
|
|
33
38
|
const projectFiles = actualFiles.filter(f => !f.startsWith(".gsd/") && !f.startsWith(".gsd\\"));
|
|
34
39
|
// Normalize expected paths (strip leading ./ or /)
|
|
35
40
|
const normalizedExpected = new Set([...allExpected].map((f) => normalizePlannedFileReference(f).replace(/^\.\//, "").replace(/^\//, "")));
|
|
36
|
-
//
|
|
37
|
-
const
|
|
41
|
+
// Build allowlist matchers once (dot: true so patterns like `**/.hidden` work).
|
|
42
|
+
const allowlistMatchers = fileChangeAllowlist.map(p => picomatch(p, { dot: true }));
|
|
43
|
+
const isAllowlisted = (f) => allowlistMatchers.some(m => m(f));
|
|
44
|
+
// Compute symmetric difference, excluding allowlisted files
|
|
45
|
+
const unexpectedFiles = projectFiles.filter(f => !normalizedExpected.has(f) && !isAllowlisted(f));
|
|
38
46
|
const missingFiles = [...normalizedExpected].filter(f => !projectFiles.includes(f));
|
|
39
47
|
const violations = [];
|
|
40
48
|
for (const f of unexpectedFiles) {
|
|
@@ -62,7 +70,7 @@ export function validateFileChanges(basePath, expectedOutput, plannedFiles) {
|
|
|
62
70
|
// ─── Internals ──────────────────────────────────────────────────────────────
|
|
63
71
|
function getChangedFilesFromLastCommit(basePath) {
|
|
64
72
|
try {
|
|
65
|
-
const result = execFileSync("git", ["diff", "--
|
|
73
|
+
const result = execFileSync("git", ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"], { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
66
74
|
return result ? result.split("\n").filter(Boolean) : [];
|
|
67
75
|
}
|
|
68
76
|
catch (e) {
|