gsd-pi 2.70.1 → 2.71.0-dev.4c35d99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -17
- package/dist/cli.js +29 -3
- package/dist/headless-events.d.ts +2 -0
- package/dist/headless-events.js +7 -0
- package/dist/headless.js +16 -3
- package/dist/mcp-server.js +40 -17
- package/dist/provider-migrations.d.ts +10 -0
- package/dist/provider-migrations.js +12 -0
- package/dist/resource-loader.js +139 -13
- package/dist/resources/GSD-WORKFLOW.md +1 -1
- package/dist/resources/agents/debugger.md +58 -0
- package/dist/resources/agents/doc-writer.md +43 -0
- package/dist/resources/agents/git-ops.md +56 -0
- package/dist/resources/agents/javascript-pro.md +46 -271
- package/dist/resources/agents/planner.md +55 -0
- package/dist/resources/agents/refactorer.md +47 -0
- package/dist/resources/agents/reviewer.md +48 -0
- package/dist/resources/agents/security.md +59 -0
- package/dist/resources/agents/tester.md +50 -0
- package/dist/resources/agents/typescript-pro.md +41 -235
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +242 -40
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
- package/dist/resources/extensions/gsd/auto/loop.js +32 -1
- package/dist/resources/extensions/gsd/auto/phases.js +5 -1
- package/dist/resources/extensions/gsd/auto/session.js +11 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
- package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
- package/dist/resources/extensions/gsd/auto-start.js +37 -18
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +56 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +6 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
- package/dist/resources/extensions/gsd/commands/context.js +15 -6
- package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
- package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
- package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/file-lock.js +60 -0
- package/dist/resources/extensions/gsd/forensics.js +19 -6
- package/dist/resources/extensions/gsd/gate-registry.js +208 -0
- package/dist/resources/extensions/gsd/gsd-db.js +41 -0
- package/dist/resources/extensions/gsd/guided-flow.js +17 -20
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/metrics.js +1 -0
- package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
- package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
- package/dist/resources/extensions/gsd/notification-store.js +56 -5
- package/dist/resources/extensions/gsd/notification-widget.js +5 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
- package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +5 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/dist/resources/extensions/gsd/prompts/execute-task.md +22 -19
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
- package/dist/resources/extensions/gsd/session-model-override.js +25 -0
- package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
- package/dist/resources/extensions/gsd/state.js +241 -332
- package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +38 -1
- package/dist/resources/extensions/gsd/workflow-events.js +25 -13
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +1 -1
- package/dist/resources/extensions/ollama/index.js +13 -5
- package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
- package/dist/resources/extensions/subagent/agents.js +8 -0
- package/dist/resources/extensions/subagent/index.js +17 -0
- package/dist/resources/skills/create-skill/SKILL.md +2 -0
- package/dist/startup-model-validation.d.ts +0 -1
- package/dist/startup-model-validation.js +6 -2
- 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 +4 -4
- 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/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- 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/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- 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/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/env-writer.d.ts +39 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
- package/packages/mcp-server/dist/env-writer.js +158 -0
- package/packages/mcp-server/dist/env-writer.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +23 -3
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +192 -44
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +22 -12
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +280 -0
- package/packages/mcp-server/src/env-writer.ts +183 -0
- package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +247 -41
- package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
- package/packages/mcp-server/src/workflow-tools.ts +32 -12
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +7 -4
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
- package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
- package/packages/pi-ai/src/providers/anthropic.ts +8 -4
- package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -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 +2 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +388 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -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-resolver-initial-model-auth.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- 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/tool-execution.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +43 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +175 -25
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- 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 +62 -5
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +468 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
- package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
- package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +205 -31
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +70 -5
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
- package/packages/pi-tui/dist/components/input.d.ts +2 -0
- package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/input.js +7 -4
- package/packages/pi-tui/dist/components/input.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +17 -1
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
- package/packages/pi-tui/src/components/input.ts +7 -4
- package/packages/pi-tui/src/components/markdown.ts +22 -1
- package/pkg/package.json +1 -1
- package/src/resources/GSD-WORKFLOW.md +1 -1
- package/src/resources/agents/debugger.md +58 -0
- package/src/resources/agents/doc-writer.md +43 -0
- package/src/resources/agents/git-ops.md +56 -0
- package/src/resources/agents/javascript-pro.md +46 -271
- package/src/resources/agents/planner.md +55 -0
- package/src/resources/agents/refactorer.md +47 -0
- package/src/resources/agents/reviewer.md +48 -0
- package/src/resources/agents/security.md +59 -0
- package/src/resources/agents/tester.md +50 -0
- package/src/resources/agents/typescript-pro.md +41 -235
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +288 -39
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +330 -2
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop.ts +45 -1
- package/src/resources/extensions/gsd/auto/phases.ts +6 -0
- package/src/resources/extensions/gsd/auto/session.ts +11 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
- package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
- package/src/resources/extensions/gsd/auto-start.ts +44 -20
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +72 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
- package/src/resources/extensions/gsd/commands/context.ts +16 -5
- package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
- package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
- package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
- package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/file-lock.ts +59 -0
- package/src/resources/extensions/gsd/forensics.ts +23 -7
- package/src/resources/extensions/gsd/gate-registry.ts +251 -0
- package/src/resources/extensions/gsd/gsd-db.ts +51 -0
- package/src/resources/extensions/gsd/guided-flow.ts +17 -19
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
- package/src/resources/extensions/gsd/metrics.ts +12 -1
- package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
- package/src/resources/extensions/gsd/notification-store.ts +54 -5
- package/src/resources/extensions/gsd/notification-widget.ts +5 -14
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
- package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
- package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +5 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +33 -13
- package/src/resources/extensions/gsd/prompts/execute-task.md +22 -19
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +3 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
- package/src/resources/extensions/gsd/session-model-override.ts +36 -0
- package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
- package/src/resources/extensions/gsd/state.ts +285 -344
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
- package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +64 -26
- package/src/resources/extensions/gsd/types.ts +26 -0
- package/src/resources/extensions/gsd/workflow-events.ts +34 -25
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +1 -1
- package/src/resources/extensions/ollama/index.ts +13 -3
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
- package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
- package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
- package/src/resources/extensions/subagent/agents.ts +10 -0
- package/src/resources/extensions/subagent/index.ts +18 -0
- package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
- package/src/resources/skills/create-skill/SKILL.md +2 -0
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → OI4n_CKC-lM8IQbvGJ_tK}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → OI4n_CKC-lM8IQbvGJ_tK}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// GSD Extension — Tests for extracted deriveStateFromDb helper functions
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
//
|
|
4
|
+
// Tests the composable helpers extracted from deriveStateFromDb:
|
|
5
|
+
// reconcileDiskToDb, buildCompletenessSet, buildRegistryAndFindActive,
|
|
6
|
+
// handleNoActiveMilestone, resolveSliceDependencies, reconcileSliceTasks,
|
|
7
|
+
// detectBlockers, checkReplanTrigger, checkInterruptedWork
|
|
8
|
+
//
|
|
9
|
+
// Helpers are private — exercised through deriveStateFromDb integration.
|
|
10
|
+
|
|
11
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { tmpdir } from 'node:os';
|
|
16
|
+
|
|
17
|
+
import { invalidateStateCache, deriveStateFromDb } from '../state.ts';
|
|
18
|
+
import {
|
|
19
|
+
openDatabase,
|
|
20
|
+
closeDatabase,
|
|
21
|
+
insertMilestone,
|
|
22
|
+
insertSlice,
|
|
23
|
+
insertTask,
|
|
24
|
+
updateTaskStatus,
|
|
25
|
+
} from '../gsd-db.ts';
|
|
26
|
+
|
|
27
|
+
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
function createFixtureBase(): string {
|
|
30
|
+
const base = mkdtempSync(join(tmpdir(), 'gsd-helpers-'));
|
|
31
|
+
mkdirSync(join(base, '.gsd', 'milestones'), { recursive: true });
|
|
32
|
+
return base;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function writeFile(base: string, relativePath: string, content: string): void {
|
|
36
|
+
const full = join(base, '.gsd', relativePath);
|
|
37
|
+
mkdirSync(join(full, '..'), { recursive: true });
|
|
38
|
+
writeFileSync(full, content);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function cleanup(base: string): void {
|
|
42
|
+
rmSync(base, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ROADMAP_CONTENT = `# M001: Test Milestone
|
|
46
|
+
|
|
47
|
+
**Vision:** Test helpers.
|
|
48
|
+
|
|
49
|
+
## Slices
|
|
50
|
+
|
|
51
|
+
- [ ] **S01: First Slice** \`risk:low\` \`depends:[]\`
|
|
52
|
+
> After this: Slice done.
|
|
53
|
+
|
|
54
|
+
- [ ] **S02: Second Slice** \`risk:low\` \`depends:[S01]\`
|
|
55
|
+
> After this: All done.
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const PLAN_CONTENT = `# S01: First Slice
|
|
59
|
+
|
|
60
|
+
**Goal:** Test executing.
|
|
61
|
+
**Demo:** Tests pass.
|
|
62
|
+
|
|
63
|
+
## Tasks
|
|
64
|
+
|
|
65
|
+
- [ ] **T01: First Task** \`est:10m\`
|
|
66
|
+
First task description.
|
|
67
|
+
|
|
68
|
+
- [x] **T02: Done Task** \`est:10m\`
|
|
69
|
+
Already done.
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
73
|
+
// Tests
|
|
74
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
75
|
+
|
|
76
|
+
describe('derive-state-helpers', () => {
|
|
77
|
+
|
|
78
|
+
// ─── handleNoActiveMilestone: all parked ─────────────────────────────
|
|
79
|
+
test('handleNoActiveMilestone: all milestones parked returns pre-planning with unpark hint', async () => {
|
|
80
|
+
const base = createFixtureBase();
|
|
81
|
+
try {
|
|
82
|
+
writeFile(base, 'milestones/M001/M001-CONTEXT.md', '# M001\n\nContext.');
|
|
83
|
+
writeFile(base, 'milestones/M001/M001-PARKED.md', 'Parked.');
|
|
84
|
+
writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002\n\nContext.');
|
|
85
|
+
writeFile(base, 'milestones/M002/M002-PARKED.md', 'Also parked.');
|
|
86
|
+
|
|
87
|
+
openDatabase(':memory:');
|
|
88
|
+
insertMilestone({ id: 'M001', title: 'First', status: 'parked' });
|
|
89
|
+
insertMilestone({ id: 'M002', title: 'Second', status: 'parked' });
|
|
90
|
+
|
|
91
|
+
invalidateStateCache();
|
|
92
|
+
const state = await deriveStateFromDb(base);
|
|
93
|
+
|
|
94
|
+
assert.equal(state.phase, 'pre-planning', 'all-parked: phase is pre-planning');
|
|
95
|
+
assert.equal(state.activeMilestone, null, 'all-parked: no active milestone');
|
|
96
|
+
assert.ok(state.nextAction.includes('parked'), 'all-parked: nextAction mentions parked');
|
|
97
|
+
assert.ok(state.nextAction.includes('unpark'), 'all-parked: nextAction hints unpark');
|
|
98
|
+
assert.equal(state.registry.length, 2, 'all-parked: both in registry');
|
|
99
|
+
assert.ok(state.registry.every(e => e.status === 'parked'), 'all-parked: all registry entries parked');
|
|
100
|
+
} finally {
|
|
101
|
+
closeDatabase();
|
|
102
|
+
cleanup(base);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ─── handleNoActiveMilestone: all complete with active requirements ──
|
|
107
|
+
test('handleNoActiveMilestone: all complete with unmapped requirements', async () => {
|
|
108
|
+
const base = createFixtureBase();
|
|
109
|
+
try {
|
|
110
|
+
writeFile(base, 'milestones/M001/M001-SUMMARY.md', '# M001 Summary\n\nDone.');
|
|
111
|
+
writeFile(base, 'REQUIREMENTS.md', `# Requirements\n\n## Active\n\n### R001 — Unmapped\n- Status: active\n- Description: Not mapped.\n`);
|
|
112
|
+
|
|
113
|
+
openDatabase(':memory:');
|
|
114
|
+
insertMilestone({ id: 'M001', title: 'First', status: 'complete' });
|
|
115
|
+
|
|
116
|
+
invalidateStateCache();
|
|
117
|
+
const state = await deriveStateFromDb(base);
|
|
118
|
+
|
|
119
|
+
assert.equal(state.phase, 'complete', 'complete-reqs: phase is complete');
|
|
120
|
+
assert.ok(state.nextAction.includes('1 active requirement'), 'complete-reqs: nextAction notes unmapped reqs');
|
|
121
|
+
assert.equal(state.requirements?.active, 1, 'complete-reqs: requirements.active = 1');
|
|
122
|
+
} finally {
|
|
123
|
+
closeDatabase();
|
|
124
|
+
cleanup(base);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// ─── resolveSliceDependencies: GSD_SLICE_LOCK with missing slice ────
|
|
129
|
+
test('resolveSliceDependencies: GSD_SLICE_LOCK pointing to non-existent slice returns blocked', async () => {
|
|
130
|
+
const base = createFixtureBase();
|
|
131
|
+
const origLock = process.env.GSD_SLICE_LOCK;
|
|
132
|
+
try {
|
|
133
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
134
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
135
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
136
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
137
|
+
|
|
138
|
+
openDatabase(':memory:');
|
|
139
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
140
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
141
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
142
|
+
|
|
143
|
+
process.env.GSD_SLICE_LOCK = 'S99';
|
|
144
|
+
|
|
145
|
+
invalidateStateCache();
|
|
146
|
+
const state = await deriveStateFromDb(base);
|
|
147
|
+
|
|
148
|
+
assert.equal(state.phase, 'blocked', 'slice-lock-miss: phase is blocked');
|
|
149
|
+
assert.ok(state.blockers.some(b => b.includes('GSD_SLICE_LOCK=S99')), 'slice-lock-miss: blocker mentions lock');
|
|
150
|
+
} finally {
|
|
151
|
+
if (origLock !== undefined) process.env.GSD_SLICE_LOCK = origLock;
|
|
152
|
+
else delete process.env.GSD_SLICE_LOCK;
|
|
153
|
+
closeDatabase();
|
|
154
|
+
cleanup(base);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// ─── resolveSliceDependencies: GSD_SLICE_LOCK with valid slice ──────
|
|
159
|
+
test('resolveSliceDependencies: GSD_SLICE_LOCK targeting valid slice bypasses deps', async () => {
|
|
160
|
+
const base = createFixtureBase();
|
|
161
|
+
const origLock = process.env.GSD_SLICE_LOCK;
|
|
162
|
+
try {
|
|
163
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
164
|
+
// S02 depends on S01 but we lock to S02 directly
|
|
165
|
+
writeFile(base, 'milestones/M001/slices/S02/S02-PLAN.md', `# S02\n\n**Goal:** Test.\n**Demo:** Pass.\n\n## Tasks\n\n- [ ] **T01: Task** \`est:5m\`\n Do thing.\n`);
|
|
166
|
+
writeFile(base, 'milestones/M001/slices/S02/tasks/.gitkeep', '');
|
|
167
|
+
writeFile(base, 'milestones/M001/slices/S02/tasks/T01-PLAN.md', '# T01 Plan');
|
|
168
|
+
|
|
169
|
+
openDatabase(':memory:');
|
|
170
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
171
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'pending', risk: 'low', depends: [] });
|
|
172
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
173
|
+
insertTask({ id: 'T01', sliceId: 'S02', milestoneId: 'M001', title: 'Task', status: 'pending' });
|
|
174
|
+
|
|
175
|
+
process.env.GSD_SLICE_LOCK = 'S02';
|
|
176
|
+
|
|
177
|
+
invalidateStateCache();
|
|
178
|
+
const state = await deriveStateFromDb(base);
|
|
179
|
+
|
|
180
|
+
assert.equal(state.activeSlice?.id, 'S02', 'slice-lock-valid: activeSlice is S02 (locked)');
|
|
181
|
+
assert.equal(state.phase, 'executing', 'slice-lock-valid: phase is executing');
|
|
182
|
+
} finally {
|
|
183
|
+
if (origLock !== undefined) process.env.GSD_SLICE_LOCK = origLock;
|
|
184
|
+
else delete process.env.GSD_SLICE_LOCK;
|
|
185
|
+
closeDatabase();
|
|
186
|
+
cleanup(base);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ─── reconcileSliceTasks: plan file imports tasks when DB empty ──────
|
|
191
|
+
test('reconcileSliceTasks: imports tasks from plan file when DB has zero tasks (#3600)', async () => {
|
|
192
|
+
const base = createFixtureBase();
|
|
193
|
+
try {
|
|
194
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
195
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
196
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
197
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
198
|
+
|
|
199
|
+
openDatabase(':memory:');
|
|
200
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
201
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
202
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
203
|
+
// No tasks inserted — reconcileSliceTasks should import from plan file
|
|
204
|
+
|
|
205
|
+
invalidateStateCache();
|
|
206
|
+
const state = await deriveStateFromDb(base);
|
|
207
|
+
|
|
208
|
+
// Plan has T01 (pending) and T02 (done) — reconciliation imports both
|
|
209
|
+
assert.equal(state.phase, 'executing', 'task-reconcile: phase is executing (tasks imported)');
|
|
210
|
+
assert.equal(state.activeTask?.id, 'T01', 'task-reconcile: activeTask is T01');
|
|
211
|
+
assert.equal(state.progress?.tasks?.total, 2, 'task-reconcile: total tasks = 2');
|
|
212
|
+
assert.equal(state.progress?.tasks?.done, 1, 'task-reconcile: done tasks = 1 (T02 was [x])');
|
|
213
|
+
} finally {
|
|
214
|
+
closeDatabase();
|
|
215
|
+
cleanup(base);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// ─── reconcileSliceTasks: stale task reconciled from disk summary ────
|
|
220
|
+
test('reconcileSliceTasks: stale pending task reconciled to complete when disk SUMMARY exists (#2514)', async () => {
|
|
221
|
+
const base = createFixtureBase();
|
|
222
|
+
try {
|
|
223
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
224
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
225
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
226
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
227
|
+
// T01 has a summary on disk but DB still says pending
|
|
228
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-SUMMARY.md', '# T01 Summary\n\nDone on disk.');
|
|
229
|
+
|
|
230
|
+
openDatabase(':memory:');
|
|
231
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
232
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
233
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
234
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
235
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Done Task', status: 'complete' });
|
|
236
|
+
|
|
237
|
+
invalidateStateCache();
|
|
238
|
+
const state = await deriveStateFromDb(base);
|
|
239
|
+
|
|
240
|
+
// T01 should have been reconciled to complete (SUMMARY exists on disk)
|
|
241
|
+
// Both tasks complete → phase should be summarizing
|
|
242
|
+
assert.equal(state.phase, 'summarizing', 'stale-task: phase is summarizing (T01 reconciled)');
|
|
243
|
+
assert.equal(state.activeTask, null, 'stale-task: no active task (all done)');
|
|
244
|
+
assert.equal(state.progress?.tasks?.done, 2, 'stale-task: tasks.done = 2');
|
|
245
|
+
} finally {
|
|
246
|
+
closeDatabase();
|
|
247
|
+
cleanup(base);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// ─── detectBlockers: blocker_discovered triggers replanning ──────────
|
|
252
|
+
test('detectBlockers: task with blocker_discovered triggers replanning-slice', async () => {
|
|
253
|
+
const base = createFixtureBase();
|
|
254
|
+
try {
|
|
255
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
256
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
257
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
258
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
259
|
+
// T02 completed with blocker discovered — written in summary frontmatter
|
|
260
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T02-SUMMARY.md',
|
|
261
|
+
'---\nblocker_discovered: true\n---\n\n# T02 Summary\n\nFound a blocker.');
|
|
262
|
+
|
|
263
|
+
openDatabase(':memory:');
|
|
264
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
265
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
266
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
267
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
268
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Done Task', status: 'complete' });
|
|
269
|
+
|
|
270
|
+
invalidateStateCache();
|
|
271
|
+
const state = await deriveStateFromDb(base);
|
|
272
|
+
|
|
273
|
+
assert.equal(state.phase, 'replanning-slice', 'blocker: phase is replanning-slice');
|
|
274
|
+
assert.ok(state.blockers.some(b => b.includes('T02')), 'blocker: blockers mention T02');
|
|
275
|
+
} finally {
|
|
276
|
+
closeDatabase();
|
|
277
|
+
cleanup(base);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// ─── checkInterruptedWork: continue.md triggers resume hint ─────────
|
|
282
|
+
test('checkInterruptedWork: continue.md present triggers resume nextAction', async () => {
|
|
283
|
+
const base = createFixtureBase();
|
|
284
|
+
try {
|
|
285
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
286
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-PLAN.md', PLAN_CONTENT);
|
|
287
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/.gitkeep', '');
|
|
288
|
+
writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
|
|
289
|
+
writeFile(base, 'milestones/M001/slices/S01/S01-CONTINUE.md', 'Resume from here.');
|
|
290
|
+
|
|
291
|
+
openDatabase(':memory:');
|
|
292
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
293
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'First', status: 'active', risk: 'low', depends: [] });
|
|
294
|
+
insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second', status: 'pending', risk: 'low', depends: ['S01'] });
|
|
295
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'First Task', status: 'pending' });
|
|
296
|
+
insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', title: 'Done Task', status: 'complete' });
|
|
297
|
+
|
|
298
|
+
invalidateStateCache();
|
|
299
|
+
const state = await deriveStateFromDb(base);
|
|
300
|
+
|
|
301
|
+
assert.equal(state.phase, 'executing', 'continue: phase is still executing');
|
|
302
|
+
assert.ok(state.nextAction.includes('Resume interrupted work'), 'continue: nextAction mentions resume');
|
|
303
|
+
assert.ok(state.nextAction.includes('continue.md'), 'continue: nextAction mentions continue.md');
|
|
304
|
+
} finally {
|
|
305
|
+
closeDatabase();
|
|
306
|
+
cleanup(base);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// ─── buildCompletenessSet: SUMMARY-on-disk marks complete ───────────
|
|
311
|
+
test('buildCompletenessSet: milestone with SUMMARY on disk treated as complete', async () => {
|
|
312
|
+
const base = createFixtureBase();
|
|
313
|
+
try {
|
|
314
|
+
// M001 has summary on disk but DB status is still 'active'
|
|
315
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
316
|
+
writeFile(base, 'milestones/M001/M001-SUMMARY.md', '# M001 Summary\n\nDone.');
|
|
317
|
+
// M002 is the real active milestone
|
|
318
|
+
writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002\n\nActive.');
|
|
319
|
+
|
|
320
|
+
openDatabase(':memory:');
|
|
321
|
+
insertMilestone({ id: 'M001', title: 'First', status: 'active' });
|
|
322
|
+
insertMilestone({ id: 'M002', title: 'Second', status: 'active' });
|
|
323
|
+
|
|
324
|
+
invalidateStateCache();
|
|
325
|
+
const state = await deriveStateFromDb(base);
|
|
326
|
+
|
|
327
|
+
// M001 should be complete (summary on disk), M002 should be active
|
|
328
|
+
const m1 = state.registry.find(e => e.id === 'M001');
|
|
329
|
+
assert.equal(m1?.status, 'complete', 'summary-disk: M001 marked complete via disk SUMMARY');
|
|
330
|
+
assert.equal(state.activeMilestone?.id, 'M002', 'summary-disk: M002 is active');
|
|
331
|
+
} finally {
|
|
332
|
+
closeDatabase();
|
|
333
|
+
cleanup(base);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// ─── reconcileDiskToDb: disk slices synced into DB (#2533) ──────────
|
|
338
|
+
test('reconcileDiskToDb: slices in ROADMAP.md but missing from DB are auto-inserted (#2533)', async () => {
|
|
339
|
+
const base = createFixtureBase();
|
|
340
|
+
try {
|
|
341
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
|
|
342
|
+
|
|
343
|
+
openDatabase(':memory:');
|
|
344
|
+
insertMilestone({ id: 'M001', title: 'Test', status: 'active' });
|
|
345
|
+
// No slices inserted — reconcileDiskToDb should insert from roadmap
|
|
346
|
+
|
|
347
|
+
invalidateStateCache();
|
|
348
|
+
const state = await deriveStateFromDb(base);
|
|
349
|
+
|
|
350
|
+
// Slices should have been reconciled from roadmap, S01 should be the active slice
|
|
351
|
+
assert.equal(state.activeMilestone?.id, 'M001', 'slice-reconcile: M001 is active');
|
|
352
|
+
assert.equal(state.activeSlice?.id, 'S01', 'slice-reconcile: S01 reconciled and active');
|
|
353
|
+
assert.ok((state.progress?.slices?.total ?? 0) >= 2, 'slice-reconcile: at least 2 slices reconciled');
|
|
354
|
+
} finally {
|
|
355
|
+
closeDatabase();
|
|
356
|
+
cleanup(base);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// ─── Queue order: milestones sorted by custom queue order ───────────
|
|
361
|
+
test('deriveStateFromDb respects custom queue order from QUEUE-ORDER.json', async () => {
|
|
362
|
+
const base = createFixtureBase();
|
|
363
|
+
try {
|
|
364
|
+
// M003 should come first per queue order, M001 second
|
|
365
|
+
const queueOrder = JSON.stringify({ order: ['M003', 'M001', 'M002'], updatedAt: new Date().toISOString() });
|
|
366
|
+
writeFileSync(join(base, '.gsd', 'QUEUE-ORDER.json'), queueOrder);
|
|
367
|
+
writeFile(base, 'milestones/M001/M001-CONTEXT.md', '# M001\n\nContext.');
|
|
368
|
+
writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002\n\nContext.');
|
|
369
|
+
writeFile(base, 'milestones/M003/M003-CONTEXT.md', '# M003\n\nContext.');
|
|
370
|
+
|
|
371
|
+
openDatabase(':memory:');
|
|
372
|
+
// Insert in natural order — queue ordering should override
|
|
373
|
+
insertMilestone({ id: 'M001', title: 'First', status: 'active' });
|
|
374
|
+
insertMilestone({ id: 'M002', title: 'Second', status: 'active' });
|
|
375
|
+
insertMilestone({ id: 'M003', title: 'Third', status: 'active' });
|
|
376
|
+
|
|
377
|
+
invalidateStateCache();
|
|
378
|
+
const state = await deriveStateFromDb(base);
|
|
379
|
+
|
|
380
|
+
// M003 should be the active milestone (first in queue)
|
|
381
|
+
assert.equal(state.activeMilestone?.id, 'M003', 'queue-order: M003 is active (first in queue)');
|
|
382
|
+
assert.equal(state.registry[0]?.id, 'M003', 'queue-order: registry[0] is M003');
|
|
383
|
+
} finally {
|
|
384
|
+
closeDatabase();
|
|
385
|
+
cleanup(base);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// ─── handleAllSlicesDone: needs-remediation re-triggers validation ──
|
|
390
|
+
test('handleAllSlicesDone: needs-remediation verdict triggers validating-milestone', async () => {
|
|
391
|
+
const base = createFixtureBase();
|
|
392
|
+
try {
|
|
393
|
+
const doneRoadmap = `# M001: Remediation Test\n\n**Vision:** Test.\n\n## Slices\n\n- [x] **S01: Done** \`risk:low\` \`depends:[]\`\n > Done.\n`;
|
|
394
|
+
writeFile(base, 'milestones/M001/M001-ROADMAP.md', doneRoadmap);
|
|
395
|
+
writeFile(base, 'milestones/M001/M001-VALIDATION.md',
|
|
396
|
+
'---\nverdict: needs-remediation\nremediation_round: 1\n---\n\n# Validation\nNeeds remediation.');
|
|
397
|
+
|
|
398
|
+
openDatabase(':memory:');
|
|
399
|
+
insertMilestone({ id: 'M001', title: 'Remediation Test', status: 'active' });
|
|
400
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Done', status: 'complete', risk: 'low', depends: [] });
|
|
401
|
+
|
|
402
|
+
invalidateStateCache();
|
|
403
|
+
const state = await deriveStateFromDb(base);
|
|
404
|
+
|
|
405
|
+
assert.equal(state.phase, 'validating-milestone', 'remediation: phase is validating-milestone');
|
|
406
|
+
assert.equal(state.activeMilestone?.id, 'M001', 'remediation: activeMilestone is M001');
|
|
407
|
+
} finally {
|
|
408
|
+
closeDatabase();
|
|
409
|
+
cleanup(base);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// ─── Deferred queued shell: shell milestone deferred, real one promoted ──
|
|
414
|
+
test('buildRegistryAndFindActive: queued shell deferred, later real milestone becomes active (#3470)', async () => {
|
|
415
|
+
const base = createFixtureBase();
|
|
416
|
+
try {
|
|
417
|
+
// M001: queued shell — no content, no slices
|
|
418
|
+
mkdirSync(join(base, '.gsd', 'milestones', 'M001'), { recursive: true });
|
|
419
|
+
// M002: real milestone with context
|
|
420
|
+
writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002: Real\n\nActive milestone.');
|
|
421
|
+
|
|
422
|
+
openDatabase(':memory:');
|
|
423
|
+
insertMilestone({ id: 'M001', title: 'Shell', status: 'queued' });
|
|
424
|
+
insertMilestone({ id: 'M002', title: 'Real', status: 'active' });
|
|
425
|
+
|
|
426
|
+
invalidateStateCache();
|
|
427
|
+
const state = await deriveStateFromDb(base);
|
|
428
|
+
|
|
429
|
+
// M002 should be active (M001 queued shell deferred)
|
|
430
|
+
assert.equal(state.activeMilestone?.id, 'M002', 'deferred-shell: M002 is active (shell deferred)');
|
|
431
|
+
} finally {
|
|
432
|
+
closeDatabase();
|
|
433
|
+
cleanup(base);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
});
|
|
@@ -27,10 +27,19 @@ describe("discuss incremental persistence (#2152)", () => {
|
|
|
27
27
|
assert.match(content, /Incremental persistence/, "should have incremental persistence section");
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
test("new-project discuss prompt includes CONTEXT-DRAFT save instruction", () => {
|
|
31
|
+
const content = readFileSync(join(promptsDir, "discuss.md"), "utf-8");
|
|
32
|
+
assert.match(content, /CONTEXT-DRAFT/, "should mention CONTEXT-DRAFT");
|
|
33
|
+
assert.match(content, /Incremental persistence/, "should have incremental persistence section");
|
|
34
|
+
assert.match(content, /gsd_summary_save/, "should use gsd_summary_save tool");
|
|
35
|
+
});
|
|
36
|
+
|
|
30
37
|
test("drafts are saved silently without user notification", () => {
|
|
31
38
|
const milestone = readFileSync(join(promptsDir, "guided-discuss-milestone.md"), "utf-8");
|
|
32
39
|
const slice = readFileSync(join(promptsDir, "guided-discuss-slice.md"), "utf-8");
|
|
40
|
+
const discuss = readFileSync(join(promptsDir, "discuss.md"), "utf-8");
|
|
33
41
|
assert.match(milestone, /Do NOT mention this save to the user/);
|
|
34
42
|
assert.match(slice, /Do NOT mention this to the user/);
|
|
43
|
+
assert.match(discuss, /Do NOT mention this save to the user/);
|
|
35
44
|
});
|
|
36
45
|
});
|
|
@@ -145,6 +145,33 @@ test("dispatch guard falls back to positional ordering when no dependencies decl
|
|
|
145
145
|
);
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
+
test("dispatch guard ignores positionally-earlier reverse dependents for zero-dependency slices (#3720)", (t) => {
|
|
149
|
+
const repo = setupRepo();
|
|
150
|
+
t.after(() => teardownRepo(repo));
|
|
151
|
+
|
|
152
|
+
mkdirSync(join(repo, ".gsd", "milestones", "M015"), { recursive: true });
|
|
153
|
+
|
|
154
|
+
insertMilestone({ id: "M015", title: "Reverse dependency fallback" });
|
|
155
|
+
insertSlice({ id: "S03", milestoneId: "M015", title: "Complete prerequisite", status: "complete", depends: [], sequence: 0 });
|
|
156
|
+
insertSlice({ id: "S04", milestoneId: "M015", title: "Depends on S04A", status: "pending", depends: ["S03", "S04A"], sequence: 0 });
|
|
157
|
+
insertSlice({ id: "S04A", milestoneId: "M015", title: "No explicit deps", status: "pending", depends: [], sequence: 0 });
|
|
158
|
+
|
|
159
|
+
writeFileSync(join(repo, ".gsd", "milestones", "M015", "M015-ROADMAP.md"), "# M015\n");
|
|
160
|
+
|
|
161
|
+
// S04A has no declared dependencies and should not be blocked by S04, because
|
|
162
|
+
// S04 itself depends on S04A. With sequence=0, DB ordering falls back to id.
|
|
163
|
+
assert.equal(
|
|
164
|
+
getPriorSliceCompletionBlocker(repo, "main", "execute-task", "M015/S04A/T02"),
|
|
165
|
+
null,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// The reverse direction is still blocked normally.
|
|
169
|
+
assert.equal(
|
|
170
|
+
getPriorSliceCompletionBlocker(repo, "main", "execute-task", "M015/S04/T01"),
|
|
171
|
+
"Cannot dispatch execute-task M015/S04/T01: dependency slice M015/S04A is not complete.",
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
148
175
|
test("dispatch guard allows slice with all declared dependencies complete", (t) => {
|
|
149
176
|
const repo = setupRepo();
|
|
150
177
|
t.after(() => teardownRepo(repo));
|
|
@@ -574,6 +574,42 @@ test("runProviderChecks reports ok for OpenAI via openai-codex auth.json (#2922)
|
|
|
574
574
|
rmSync(tmpHome, { recursive: true, force: true });
|
|
575
575
|
});
|
|
576
576
|
|
|
577
|
+
test("runProviderChecks reports ok for claude-code without any API key", () => {
|
|
578
|
+
const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-cc-repo-")));
|
|
579
|
+
mkdirSync(join(repo, ".gsd"), { recursive: true });
|
|
580
|
+
writeFileSync(
|
|
581
|
+
join(repo, ".gsd", "PREFERENCES.md"),
|
|
582
|
+
[
|
|
583
|
+
"---",
|
|
584
|
+
"models:",
|
|
585
|
+
" execution:",
|
|
586
|
+
" model: claude-sonnet-4-6",
|
|
587
|
+
" provider: claude-code",
|
|
588
|
+
"---",
|
|
589
|
+
"",
|
|
590
|
+
].join("\n"),
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
const tmpHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-cc-home-")));
|
|
594
|
+
|
|
595
|
+
withEnv({
|
|
596
|
+
HOME: tmpHome,
|
|
597
|
+
ANTHROPIC_API_KEY: undefined,
|
|
598
|
+
ANTHROPIC_OAUTH_TOKEN: undefined,
|
|
599
|
+
}, () => {
|
|
600
|
+
withCwd(repo, () => {
|
|
601
|
+
const results = runProviderChecks();
|
|
602
|
+
const cc = results.find(r => r.name === "claude-code");
|
|
603
|
+
assert.ok(cc, "claude-code result should exist");
|
|
604
|
+
assert.equal(cc!.status, "ok", "claude-code uses CLI auth — must be ok without API keys");
|
|
605
|
+
assert.ok(cc!.message.includes("CLI auth"), "should indicate CLI auth");
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
rmSync(repo, { recursive: true, force: true });
|
|
610
|
+
rmSync(tmpHome, { recursive: true, force: true });
|
|
611
|
+
});
|
|
612
|
+
|
|
577
613
|
test("PROVIDER_ROUTES includes google-gemini-cli as route for google (#2922)", async () => {
|
|
578
614
|
const { readFileSync: readFS } = await import("node:fs");
|
|
579
615
|
const { dirname: dirn, join: joinPath } = await import("node:path");
|
package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const promptsDir = join(__dirname, "..", "prompts");
|
|
9
|
+
|
|
10
|
+
test("execute-task prompt requires reading existing artifacts before write", () => {
|
|
11
|
+
const prompt = readFileSync(join(promptsDir, "execute-task.md"), "utf-8");
|
|
12
|
+
|
|
13
|
+
assert.match(
|
|
14
|
+
prompt,
|
|
15
|
+
/Before any `Write` that creates an artifact or output file, check whether that path already exists\./,
|
|
16
|
+
"execute-task prompt should require an existence check before creating artifacts",
|
|
17
|
+
);
|
|
18
|
+
assert.match(
|
|
19
|
+
prompt,
|
|
20
|
+
/If it does, read it first and decide whether the work is already done, should be extended, or truly needs replacement\./,
|
|
21
|
+
"execute-task prompt should require reading existing artifacts before replacement",
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("guided resume prompt checks for pre-existing artifacts", () => {
|
|
26
|
+
const prompt = readFileSync(join(promptsDir, "guided-resume-task.md"), "utf-8");
|
|
27
|
+
|
|
28
|
+
assert.match(
|
|
29
|
+
prompt,
|
|
30
|
+
/Before you create any expected artifact or output file, check whether it already exists and read it first/i,
|
|
31
|
+
"guided resume prompt should guard pre-existing artifacts",
|
|
32
|
+
);
|
|
33
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
|
|
8
|
+
import { withFileLock, withFileLockSync } from "../file-lock.ts";
|
|
9
|
+
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
|
|
12
|
+
function hasProperLockfile(): boolean {
|
|
13
|
+
try {
|
|
14
|
+
require("proper-lockfile");
|
|
15
|
+
return true;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("withFileLockSync: executes callback when file does not exist", () => {
|
|
22
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-file-lock-test-"));
|
|
23
|
+
try {
|
|
24
|
+
const missingPath = join(dir, "missing.txt");
|
|
25
|
+
let called = 0;
|
|
26
|
+
const result = withFileLockSync(missingPath, () => {
|
|
27
|
+
called++;
|
|
28
|
+
return "ok";
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
assert.equal(result, "ok");
|
|
32
|
+
assert.equal(called, 1, "callback should execute exactly once");
|
|
33
|
+
} finally {
|
|
34
|
+
rmSync(dir, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("withFileLock: executes callback when file does not exist", async () => {
|
|
39
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-file-lock-test-"));
|
|
40
|
+
try {
|
|
41
|
+
const missingPath = join(dir, "missing.txt");
|
|
42
|
+
let called = 0;
|
|
43
|
+
const result = await withFileLock(missingPath, async () => {
|
|
44
|
+
called++;
|
|
45
|
+
return "ok";
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
assert.equal(result, "ok");
|
|
49
|
+
assert.equal(called, 1, "callback should execute exactly once");
|
|
50
|
+
} finally {
|
|
51
|
+
rmSync(dir, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("withFileLockSync: falls back to unlocked callback on ELOCKED", () => {
|
|
56
|
+
if (!hasProperLockfile() || process.platform === "win32") {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const lockfile = require("proper-lockfile");
|
|
61
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-file-lock-test-"));
|
|
62
|
+
const filePath = join(dir, "locked.jsonl");
|
|
63
|
+
writeFileSync(filePath, "{}\n", "utf-8");
|
|
64
|
+
|
|
65
|
+
const release = lockfile.lockSync(filePath, { retries: 0, stale: 10000 });
|
|
66
|
+
try {
|
|
67
|
+
let called = 0;
|
|
68
|
+
const result = withFileLockSync(filePath, () => {
|
|
69
|
+
called++;
|
|
70
|
+
return "fallback-ok";
|
|
71
|
+
});
|
|
72
|
+
assert.equal(result, "fallback-ok");
|
|
73
|
+
assert.equal(called, 1, "callback should run even when lock acquisition fails");
|
|
74
|
+
} finally {
|
|
75
|
+
release();
|
|
76
|
+
rmSync(dir, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("withFileLock: falls back to unlocked callback on ELOCKED", async () => {
|
|
81
|
+
if (!hasProperLockfile() || process.platform === "win32") {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const lockfile = require("proper-lockfile");
|
|
86
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-file-lock-test-"));
|
|
87
|
+
const filePath = join(dir, "locked.jsonl");
|
|
88
|
+
writeFileSync(filePath, "{}\n", "utf-8");
|
|
89
|
+
|
|
90
|
+
const release = await lockfile.lock(filePath, { retries: 0, stale: 10000 });
|
|
91
|
+
try {
|
|
92
|
+
let called = 0;
|
|
93
|
+
const result = await withFileLock(filePath, async () => {
|
|
94
|
+
called++;
|
|
95
|
+
return "fallback-ok";
|
|
96
|
+
});
|
|
97
|
+
assert.equal(result, "fallback-ok");
|
|
98
|
+
assert.equal(called, 1, "callback should run even when lock acquisition fails");
|
|
99
|
+
} finally {
|
|
100
|
+
await release();
|
|
101
|
+
rmSync(dir, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
});
|