@opengsd/gsd-pi 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/onboarding.js +11 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +20 -25
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +118 -53
- package/dist/resources/extensions/cursor-cli/index.js +16 -0
- package/dist/resources/extensions/cursor-cli/models.js +63 -0
- package/dist/resources/extensions/cursor-cli/package.json +11 -0
- package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
- package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
- package/dist/resources/extensions/gsd/activity-log.js +4 -1
- package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
- package/dist/resources/extensions/gsd/auto/closeout.js +45 -42
- package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +1 -1
- package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
- package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +15 -5
- package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
- package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
- package/dist/resources/extensions/gsd/auto-start.js +4 -3
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +38 -11
- package/dist/resources/extensions/gsd/auto.js +23 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +114 -5
- package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +10 -2
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
- package/dist/resources/extensions/gsd/codebase-generator.js +157 -19
- package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
- package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
- package/dist/resources/extensions/gsd/db/queries.js +39 -0
- package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +206 -14
- package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
- package/dist/resources/extensions/gsd/doctor.js +93 -3
- package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
- package/dist/resources/extensions/gsd/flat-phase-migration.js +74 -23
- package/dist/resources/extensions/gsd/git-service.js +14 -18
- package/dist/resources/extensions/gsd/gsd-db.js +52 -0
- package/dist/resources/extensions/gsd/guidance.js +1 -0
- package/dist/resources/extensions/gsd/guided-flow.js +40 -2
- package/dist/resources/extensions/gsd/key-manager.js +1 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +25 -8
- package/dist/resources/extensions/gsd/migrate-external.js +24 -7
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +3 -3
- package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
- package/dist/resources/extensions/gsd/preferences-models.js +6 -4
- package/dist/resources/extensions/gsd/preferences-validation.js +25 -2
- package/dist/resources/extensions/gsd/preferences.js +29 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
- package/dist/resources/extensions/gsd/repo-identity.js +16 -0
- package/dist/resources/extensions/gsd/repository-registry.js +6 -1
- package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
- package/dist/resources/extensions/gsd/safe-fs.js +23 -2
- package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
- package/dist/resources/extensions/gsd/unit-registry.js +27 -2
- package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
- package/dist/resources/extensions/gsd/workspace.js +26 -8
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +67 -5
- package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
- 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 +7 -7
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- 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/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +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/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/wizard.d.ts +8 -0
- package/dist/wizard.js +8 -1
- package/integrations/hermes/CONTEXT.md +47 -0
- package/integrations/hermes/README.md +21 -0
- package/integrations/hermes/__init__.py +31 -0
- package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
- package/integrations/hermes/docs/setup.md +4 -0
- package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
- package/integrations/hermes/open_gsd_hermes/config.py +2 -0
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +594 -31
- package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
- package/integrations/hermes/plugin.yaml +4 -0
- package/integrations/hermes/tests/test_config.py +6 -0
- package/integrations/hermes/tests/test_directory_loader.py +37 -0
- package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
- package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
- package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
- package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
- package/integrations/hermes/tests/test_milestone_client.py +447 -0
- package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
- package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
- package/package.json +8 -8
- package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
- package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
- package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
- package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-runtime.js +25 -1
- package/packages/daemon/dist/cloud-runtime.js.map +1 -1
- package/packages/daemon/dist/cloud-runtime.test.js +51 -0
- package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
- package/packages/daemon/dist/message-batcher.d.ts +7 -0
- package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
- package/packages/daemon/dist/message-batcher.js +13 -0
- package/packages/daemon/dist/message-batcher.js.map +1 -1
- package/packages/daemon/dist/message-batcher.test.js +48 -0
- package/packages/daemon/dist/message-batcher.test.js.map +1 -1
- package/packages/daemon/dist/session-manager.js +1 -1
- package/packages/daemon/dist/session-manager.js.map +1 -1
- package/packages/daemon/package.json +5 -5
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/server.d.ts +18 -5
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +46 -5
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +1 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/packages/pi-ai/dist/env-api-keys.js +3 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -2
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/dist/rpc-client.js +1 -1
- package/packages/rpc-client/dist/rpc-client.js.map +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/scripts/link-workspace-packages.cjs +14 -4
- package/src/resources/GSD-WORKFLOW.md +20 -25
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
- package/src/resources/extensions/cursor-cli/index.ts +18 -0
- package/src/resources/extensions/cursor-cli/models.ts +65 -0
- package/src/resources/extensions/cursor-cli/package.json +11 -0
- package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
- package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
- package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
- package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
- package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
- package/src/resources/extensions/gsd/activity-log.ts +4 -1
- package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
- package/src/resources/extensions/gsd/auto/closeout.ts +54 -56
- package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +1 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
- package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -5
- package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
- package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
- package/src/resources/extensions/gsd/auto-start.ts +4 -3
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +50 -11
- package/src/resources/extensions/gsd/auto.ts +22 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +128 -5
- package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +12 -2
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
- package/src/resources/extensions/gsd/codebase-generator.ts +179 -18
- package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
- package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
- package/src/resources/extensions/gsd/db/queries.ts +40 -0
- package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +269 -18
- package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
- package/src/resources/extensions/gsd/doctor-types.ts +5 -1
- package/src/resources/extensions/gsd/doctor.ts +95 -3
- package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
- package/src/resources/extensions/gsd/flat-phase-migration.ts +98 -22
- package/src/resources/extensions/gsd/git-service.ts +14 -17
- package/src/resources/extensions/gsd/gsd-db.ts +72 -0
- package/src/resources/extensions/gsd/guidance.ts +2 -0
- package/src/resources/extensions/gsd/guided-flow.ts +45 -2
- package/src/resources/extensions/gsd/key-manager.ts +1 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +25 -7
- package/src/resources/extensions/gsd/migrate-external.ts +23 -6
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +4 -4
- package/src/resources/extensions/gsd/model-router.ts +4 -5
- package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
- package/src/resources/extensions/gsd/preferences-models.ts +6 -3
- package/src/resources/extensions/gsd/preferences-validation.ts +28 -2
- package/src/resources/extensions/gsd/preferences.ts +38 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
- package/src/resources/extensions/gsd/repo-identity.ts +17 -0
- package/src/resources/extensions/gsd/repository-registry.ts +7 -1
- package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
- package/src/resources/extensions/gsd/safe-fs.ts +22 -2
- package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
- package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +365 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +397 -5
- package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +152 -22
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
- package/src/resources/extensions/gsd/tests/preferences.test.ts +150 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
- package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
- package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
- package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
- package/src/resources/extensions/gsd/unit-registry.ts +34 -2
- package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
- package/src/resources/extensions/gsd/workspace.ts +42 -8
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +135 -11
- package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
- /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_ssgManifest.js +0 -0
|
@@ -82,7 +82,13 @@ function ensureMilestoneRowForAcceptedHandoff(
|
|
|
82
82
|
entry: PendingAutoStartEntry,
|
|
83
83
|
contextFile: string | null,
|
|
84
84
|
): boolean {
|
|
85
|
-
if (!isDbAvailable())
|
|
85
|
+
if (!isDbAvailable()) {
|
|
86
|
+
logWarning(
|
|
87
|
+
"guided",
|
|
88
|
+
`R3b: milestone ${entry.milestoneId} DB-row recovery skipped because DB is unavailable`,
|
|
89
|
+
);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
86
92
|
|
|
87
93
|
const { basePath, milestoneId } = entry;
|
|
88
94
|
const milestoneRow = getMilestone(milestoneId);
|
|
@@ -122,12 +128,14 @@ function ensureMilestoneRowForAcceptedHandoff(
|
|
|
122
128
|
`(attempt ${entry.r3bRecoveryCount + 1}/${MAX_DB_ROW_RECOVERIES})`,
|
|
123
129
|
);
|
|
124
130
|
|
|
131
|
+
let inserted = false;
|
|
125
132
|
try {
|
|
126
|
-
insertMilestone({ id: milestoneId, title: milestoneId, status: "queued" });
|
|
133
|
+
inserted = insertMilestone({ id: milestoneId, title: milestoneId, status: "queued" });
|
|
127
134
|
} catch (e) {
|
|
128
135
|
logWarning("guided", `R3b: insertMilestone failed: ${(e as Error).message}`);
|
|
129
136
|
}
|
|
130
137
|
|
|
138
|
+
if (inserted) return true;
|
|
131
139
|
if (getMilestone(milestoneId)) return true;
|
|
132
140
|
|
|
133
141
|
noteDbRowRecoveryMiss(entry);
|
|
@@ -163,7 +163,7 @@ Diagnostics record the file path, scope (global/project), severity (error/warnin
|
|
|
163
163
|
- **Deprecated:** `merge_to_main` — no longer valid; milestone-level merge is always used. Remove this setting.
|
|
164
164
|
|
|
165
165
|
- `workspace`: configures multi-repository parent workspaces. Keys:
|
|
166
|
-
- `mode`: `"project"` (default single-repo behavior) or `"parent"` (one `.gsd` controlling child repos).
|
|
166
|
+
- `mode`: `"project"` (default single-repo behavior) or `"parent"` (one `.gsd` controlling child repos). In `"parent"` mode at least one child repository must be declared under `repositories`, otherwise the setting is rejected at validation time.
|
|
167
167
|
- `repositories`: object map of repository IDs to config.
|
|
168
168
|
- Repository ID format: `^[A-Za-z0-9][A-Za-z0-9._-]*$`.
|
|
169
169
|
- Reserved ID: `project` is implicit and always maps to the project root; user-defined `workspace.repositories.project` is rejected.
|
|
@@ -171,6 +171,8 @@ Diagnostics record the file path, scope (global/project), severity (error/warnin
|
|
|
171
171
|
- `role`: optional short description of what the repo owns.
|
|
172
172
|
- `verification`: optional array of verification commands for that repo.
|
|
173
173
|
- `commit_policy`: optional `"auto"` or `"skip"` to include or skip commit actions per repo.
|
|
174
|
+
- Planning tools consume repository IDs through `targetRepositories`: `gsd_plan_slice.targetRepositories` sets a slice-wide default, and `gsd_plan_task.targetRepositories` records the repositories an individual task touches. Values must be declared IDs or the implicit `project`; omit these fields in single-repo projects.
|
|
175
|
+
- In parent mode with declared child repositories, `/gsd codebase generate` and automatic `.gsd/CODEBASE.md` refreshes enumerate `project` plus each child repository, render repo-labeled sections, and store repository IDs in map metadata.
|
|
174
176
|
|
|
175
177
|
## Workspace Example
|
|
176
178
|
|
|
@@ -198,6 +200,8 @@ workspace:
|
|
|
198
200
|
|
|
199
201
|
This config sets a parent workspace with two child repositories. The implicit `project` repository is always created for the project root and cannot be user-defined.
|
|
200
202
|
|
|
203
|
+
In `"parent"` mode, slice/task `targetRepositories` default to the declared child repositories (here `frontend` and `backend`) rather than the root `project` repo. In `"project"` mode (the default), `targetRepositories` defaults to `["project"]`.
|
|
204
|
+
|
|
201
205
|
- `unique_milestone_ids`: boolean — when `true`, generates milestone IDs in `M{seq}-{rand6}` format (e.g. `M001-eh88as`) instead of plain sequential `M001`. Prevents ID collisions in team workflows where multiple contributors create milestones concurrently. Both formats coexist — existing `M001`-style milestones remain valid. Default: `false`.
|
|
202
206
|
|
|
203
207
|
- `budget_ceiling`: number — maximum dollar amount to spend on auto-mode. When reached, behavior is controlled by `budget_enforcement`. Default: no limit.
|
|
@@ -303,7 +307,9 @@ This config sets a parent workspace with two child repositories. The implicit `p
|
|
|
303
307
|
- `verification`: string[] (optional) — repository-specific verification commands used when global `verification_commands` is not set.
|
|
304
308
|
- `commit_policy`: `"auto"` | `"skip"` (optional) — per-repository closeout commit behavior.
|
|
305
309
|
- `project` is always an implicit repository target mapped to the project root for backward compatibility.
|
|
306
|
-
-
|
|
310
|
+
- `targetRepositories` on `gsd_plan_slice` sets the slice-wide default. `targetRepositories` on `gsd_plan_task` records or overrides the repository IDs for an individual task.
|
|
311
|
+
- Values must match declared repository IDs or the implicit `project`; omitted plan/task `targetRepositories` defaults to `["project"]`.
|
|
312
|
+
- `/gsd codebase` is workspace-aware in parent mode with declared child repositories: generated `.gsd/CODEBASE.md` uses workspace-relative paths, groups files under `## [repo-id]` headings, and refreshes when repository registry metadata changes.
|
|
307
313
|
|
|
308
314
|
- `verification_commands`: string[] — shell commands to run as verification after task execution (e.g., `["npm test", "npm run lint"]`). Commands run in order; if any fails, the task is marked as needing fixes.
|
|
309
315
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { isAbsolute, join, relative } from "node:path";
|
|
2
|
+
import { isAbsolute, join, relative, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import type { DoctorIssue } from "./doctor-types.js";
|
|
5
5
|
import {
|
|
6
|
+
deleteArtifactByPath,
|
|
6
7
|
getAllMilestones,
|
|
7
8
|
getMilestoneSlices,
|
|
8
9
|
getSliceTasks,
|
|
9
10
|
isDbAvailable,
|
|
10
11
|
isMemoriesFtsAvailable,
|
|
12
|
+
repairTaskCompletionFromSummary,
|
|
11
13
|
_getAdapter,
|
|
12
14
|
} from "./gsd-db.js";
|
|
13
15
|
import { MEMORIES_FTS_REBUILT_KEY } from "./db-memory-fts-schema.js";
|
|
@@ -20,11 +22,47 @@ import { readEvents } from "./workflow-events.js";
|
|
|
20
22
|
import { flushWorkflowProjections } from "./projection-flush.js";
|
|
21
23
|
import { parseRoadmapSlices } from "./roadmap-slices.js";
|
|
22
24
|
import { parsePlan } from "./parsers-legacy.js";
|
|
25
|
+
import { parseSummary } from "./files.js";
|
|
26
|
+
|
|
27
|
+
const USER_AUTHORED_ARTIFACT_TYPES = new Set(["CONTEXT", "RESEARCH"]);
|
|
28
|
+
|
|
29
|
+
function escapeRegExp(value: string): string {
|
|
30
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
31
|
+
}
|
|
23
32
|
|
|
24
33
|
function relativeFile(basePath: string, filePath: string): string {
|
|
25
34
|
return relative(basePath, filePath).split("\\").join("/");
|
|
26
35
|
}
|
|
27
36
|
|
|
37
|
+
function normalizedArtifactType(artifactType: string): string {
|
|
38
|
+
return artifactType.trim().toUpperCase();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isUserAuthoredArtifactType(artifactType: string): boolean {
|
|
42
|
+
return USER_AUTHORED_ARTIFACT_TYPES.has(normalizedArtifactType(artifactType));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function userContentRecoveryCommand(artifactType: string): string {
|
|
46
|
+
return normalizedArtifactType(artifactType) === "CONTEXT" ? "/gsd discuss" : "/gsd auto";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function userContentMissingMessage(path: string, artifactType: string): string {
|
|
50
|
+
const type = normalizedArtifactType(artifactType) || "UNKNOWN";
|
|
51
|
+
return `Artifact \`${path}\` is a user-authored ${type} file recorded in the database but missing from disk. Re-run \`${userContentRecoveryCommand(type)}\` in this milestone to regenerate it.`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function artifactPathRelativeToGsd(artifactPath: string): string {
|
|
55
|
+
const parts = artifactPath.split(/[\\/]+/);
|
|
56
|
+
const gsdIndex = parts.lastIndexOf(".gsd");
|
|
57
|
+
if (gsdIndex < 0 || gsdIndex === parts.length - 1) return artifactPath;
|
|
58
|
+
return parts.slice(gsdIndex + 1).join("/");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isPathInside(basePath: string, candidatePath: string): boolean {
|
|
62
|
+
const rel = relative(basePath, candidatePath);
|
|
63
|
+
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
64
|
+
}
|
|
65
|
+
|
|
28
66
|
function reportCheckboxDbStatusDivergence(
|
|
29
67
|
issues: DoctorIssue[],
|
|
30
68
|
basePath: string,
|
|
@@ -123,11 +161,137 @@ function isClearedByMilestoneShellProjectionFlush(
|
|
|
123
161
|
}
|
|
124
162
|
|
|
125
163
|
function artifactExistsOnDisk(basePath: string, artifactPath: string): boolean {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
164
|
+
return resolveArtifactDiskPath(basePath, artifactPath) !== null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function resolveArtifactDiskPath(basePath: string, artifactPath: string): string | null {
|
|
168
|
+
const relativeArtifactPath = artifactPathRelativeToGsd(artifactPath);
|
|
169
|
+
if (isAbsolute(relativeArtifactPath)) {
|
|
170
|
+
return existsSync(relativeArtifactPath) ? relativeArtifactPath : null;
|
|
171
|
+
}
|
|
172
|
+
for (const root of [gsdProjectionRoot(basePath), gsdRoot(basePath)]) {
|
|
173
|
+
const candidate = join(root, relativeArtifactPath);
|
|
174
|
+
if (isPathInside(root, candidate) && existsSync(candidate)) return candidate;
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function taskExistsInPlan(basePath: string, milestoneId: string, sliceId: string, taskId: string): boolean {
|
|
180
|
+
const planPath = resolveSliceFile(basePath, milestoneId, sliceId, "PLAN");
|
|
181
|
+
if (!planPath || !existsSync(planPath)) return false;
|
|
182
|
+
try {
|
|
183
|
+
return parsePlan(readFileSync(planPath, "utf-8")).tasks.some((task) => task.id === taskId);
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isPassingVerificationResult(value: string): boolean {
|
|
190
|
+
const normalized = value.trim().toLowerCase();
|
|
191
|
+
if (!normalized) return false;
|
|
192
|
+
if (/\b(fail(?:ed|ing)?|error|blocked|mixed|untested)\b/.test(normalized)) return false;
|
|
193
|
+
if (
|
|
194
|
+
/\b(?:not|never|no|didn't|did\s+not|cannot|can't|won't|couldn't)\s+(?:\w+\s+){0,3}?(?:pass(?:ed|ing)?|success(?:ful)?|succeeded)\b/.test(
|
|
195
|
+
normalized,
|
|
196
|
+
)
|
|
197
|
+
) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return /\b(pass(?:ed|ing)?|success(?:ful)?|succeeded)\b/.test(normalized) || normalized === "all-pass";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function readTaskCompletionEvidenceFromSummary(
|
|
204
|
+
basePath: string,
|
|
205
|
+
row: {
|
|
206
|
+
path: string;
|
|
207
|
+
milestone_id: string;
|
|
208
|
+
slice_id: string | null;
|
|
209
|
+
task_id: string | null;
|
|
210
|
+
task_status: string | null;
|
|
211
|
+
task_count: number;
|
|
212
|
+
},
|
|
213
|
+
): {
|
|
214
|
+
completedAt: string;
|
|
215
|
+
verificationResult: string;
|
|
216
|
+
title: string;
|
|
217
|
+
oneLiner: string;
|
|
218
|
+
narrative: string;
|
|
219
|
+
duration: string;
|
|
220
|
+
blockerDiscovered: boolean;
|
|
221
|
+
deviations: string;
|
|
222
|
+
knownIssues: string;
|
|
223
|
+
keyFiles: string[];
|
|
224
|
+
keyDecisions: string[];
|
|
225
|
+
fullSummaryMd: string;
|
|
226
|
+
} | null {
|
|
227
|
+
if (!row.slice_id || !row.task_id) return null;
|
|
228
|
+
if (!row.task_status && Number(row.task_count) > 0 && !taskExistsInPlan(basePath, row.milestone_id, row.slice_id, row.task_id)) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const diskPath = resolveArtifactDiskPath(basePath, row.path);
|
|
232
|
+
if (!diskPath) return null;
|
|
233
|
+
try {
|
|
234
|
+
const fullSummaryMd = readFileSync(diskPath, "utf-8");
|
|
235
|
+
const summary = parseSummary(fullSummaryMd);
|
|
236
|
+
const fm = summary.frontmatter;
|
|
237
|
+
if (fm.id !== row.task_id || fm.parent !== row.slice_id || fm.milestone !== row.milestone_id) return null;
|
|
238
|
+
if (fm.blocker_discovered) return null;
|
|
239
|
+
if (!isPassingVerificationResult(fm.verification_result)) return null;
|
|
240
|
+
const completedAt = fm.completed_at.trim();
|
|
241
|
+
if (!completedAt || !Number.isFinite(Date.parse(completedAt))) return null;
|
|
242
|
+
return {
|
|
243
|
+
completedAt,
|
|
244
|
+
verificationResult: fm.verification_result.trim(),
|
|
245
|
+
title: summary.title.replace(new RegExp(`^${escapeRegExp(row.task_id)}:\\s*`), "").trim(),
|
|
246
|
+
oneLiner: summary.oneLiner,
|
|
247
|
+
narrative: summary.whatHappened,
|
|
248
|
+
duration: fm.duration,
|
|
249
|
+
blockerDiscovered: fm.blocker_discovered,
|
|
250
|
+
deviations: summary.deviations,
|
|
251
|
+
knownIssues: summary.knownLimitations,
|
|
252
|
+
keyFiles: fm.key_files.filter((file) => file !== "(none)"),
|
|
253
|
+
keyDecisions: fm.key_decisions.filter((decision) => decision !== "(none)"),
|
|
254
|
+
fullSummaryMd,
|
|
255
|
+
};
|
|
256
|
+
} catch {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function repairTaskArtifactDbStatusDivergence(
|
|
262
|
+
basePath: string,
|
|
263
|
+
row: {
|
|
264
|
+
path: string;
|
|
265
|
+
milestone_id: string;
|
|
266
|
+
slice_id: string | null;
|
|
267
|
+
task_id: string | null;
|
|
268
|
+
task_status: string | null;
|
|
269
|
+
task_count: number;
|
|
270
|
+
},
|
|
271
|
+
): boolean {
|
|
272
|
+
const evidence = readTaskCompletionEvidenceFromSummary(basePath, row);
|
|
273
|
+
if (!evidence || !row.slice_id || !row.task_id) return false;
|
|
274
|
+
repairTaskCompletionFromSummary({
|
|
275
|
+
milestoneId: row.milestone_id,
|
|
276
|
+
sliceId: row.slice_id,
|
|
277
|
+
taskId: row.task_id,
|
|
278
|
+
...evidence,
|
|
279
|
+
});
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function artifactDbStatusDivergenceFixable(
|
|
284
|
+
basePath: string,
|
|
285
|
+
row: {
|
|
286
|
+
path: string;
|
|
287
|
+
milestone_id: string;
|
|
288
|
+
slice_id: string | null;
|
|
289
|
+
task_id: string | null;
|
|
290
|
+
task_status: string | null;
|
|
291
|
+
task_count: number;
|
|
292
|
+
},
|
|
293
|
+
): boolean {
|
|
294
|
+
return readTaskCompletionEvidenceFromSummary(basePath, row) !== null;
|
|
131
295
|
}
|
|
132
296
|
|
|
133
297
|
function artifactUnitId(row: { milestone_id: string | null; slice_id: string | null; task_id: string | null }): string {
|
|
@@ -144,10 +308,56 @@ function artifactScope(row: { milestone_id: string | null; slice_id: string | nu
|
|
|
144
308
|
return "project";
|
|
145
309
|
}
|
|
146
310
|
|
|
311
|
+
type ArtifactRow = {
|
|
312
|
+
path: string;
|
|
313
|
+
artifact_type: string;
|
|
314
|
+
milestone_id: string | null;
|
|
315
|
+
slice_id: string | null;
|
|
316
|
+
task_id: string | null;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
function sameArtifactIdentity(left: ArtifactRow, right: ArtifactRow): boolean {
|
|
320
|
+
return left.artifact_type === right.artifact_type &&
|
|
321
|
+
left.milestone_id === right.milestone_id &&
|
|
322
|
+
left.slice_id === right.slice_id &&
|
|
323
|
+
left.task_id === right.task_id;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function isMilestonesArtifactPath(artifactPath: string): boolean {
|
|
327
|
+
return artifactPathRelativeToGsd(artifactPath).startsWith("milestones/");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function expectedMilestonesArtifactPath(row: ArtifactRow): string | null {
|
|
331
|
+
if (!row.milestone_id) return null;
|
|
332
|
+
const artifactType = normalizedArtifactType(row.artifact_type);
|
|
333
|
+
if (!artifactType) return null;
|
|
334
|
+
if (row.slice_id && row.task_id) {
|
|
335
|
+
return `milestones/${row.milestone_id}/slices/${row.slice_id}/tasks/${row.task_id}-${artifactType}.md`;
|
|
336
|
+
}
|
|
337
|
+
if (row.slice_id) {
|
|
338
|
+
return `milestones/${row.milestone_id}/slices/${row.slice_id}/${row.slice_id}-${artifactType}.md`;
|
|
339
|
+
}
|
|
340
|
+
return `milestones/${row.milestone_id}/${row.milestone_id}-${artifactType}.md`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function hasPresentMilestonesReplacement(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
|
|
344
|
+
const expectedPath = expectedMilestonesArtifactPath(row);
|
|
345
|
+
if (expectedPath && artifactExistsOnDisk(basePath, expectedPath)) return true;
|
|
346
|
+
|
|
347
|
+
return artifactRows.some(
|
|
348
|
+
(other) =>
|
|
349
|
+
other.path !== row.path &&
|
|
350
|
+
isMilestonesArtifactPath(other.path) &&
|
|
351
|
+
sameArtifactIdentity(row, other) &&
|
|
352
|
+
artifactExistsOnDisk(basePath, other.path),
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
147
356
|
export async function checkEngineHealth(
|
|
148
357
|
basePath: string,
|
|
149
358
|
issues: DoctorIssue[],
|
|
150
359
|
fixesApplied: string[],
|
|
360
|
+
options?: { repair?: boolean },
|
|
151
361
|
): Promise<void> {
|
|
152
362
|
const dbPath = resolveGsdPathContract(basePath).projectDb;
|
|
153
363
|
|
|
@@ -357,6 +567,7 @@ export async function checkEngineHealth(
|
|
|
357
567
|
}
|
|
358
568
|
|
|
359
569
|
// f. Artifact rows reference files that no longer exist on disk.
|
|
570
|
+
const missingUserContentArtifacts: Array<{ path: string; artifactType: string }> = [];
|
|
360
571
|
try {
|
|
361
572
|
const artifactRows = adapter
|
|
362
573
|
.prepare(
|
|
@@ -365,30 +576,54 @@ export async function checkEngineHealth(
|
|
|
365
576
|
WHERE path != ''
|
|
366
577
|
ORDER BY path`,
|
|
367
578
|
)
|
|
368
|
-
.all() as
|
|
369
|
-
path: string;
|
|
370
|
-
artifact_type: string;
|
|
371
|
-
milestone_id: string | null;
|
|
372
|
-
slice_id: string | null;
|
|
373
|
-
task_id: string | null;
|
|
374
|
-
}>;
|
|
579
|
+
.all() as ArtifactRow[];
|
|
375
580
|
|
|
376
581
|
for (const row of artifactRows) {
|
|
377
582
|
if (artifactExistsOnDisk(basePath, row.path)) continue;
|
|
378
583
|
const unitId = artifactUnitId(row);
|
|
584
|
+
const issuePath = artifactPathRelativeToGsd(row.path);
|
|
585
|
+
if (options?.repair && issuePath.startsWith("phases/") && hasPresentMilestonesReplacement(basePath, row, artifactRows)) {
|
|
586
|
+
// Route the write through the Single Writer owner (gsd-db.ts) instead
|
|
587
|
+
// of issuing raw DELETE SQL here — doctor is a read-only consumer and
|
|
588
|
+
// the single-writer invariant forbids write SQL outside the allowlist.
|
|
589
|
+
deleteArtifactByPath(row.path);
|
|
590
|
+
fixesApplied.push(`pruned stale flat-phase artifact row ${row.path}`);
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
if (isUserAuthoredArtifactType(row.artifact_type)) {
|
|
594
|
+
const artifactType = normalizedArtifactType(row.artifact_type);
|
|
595
|
+
missingUserContentArtifacts.push({ path: issuePath, artifactType });
|
|
596
|
+
issues.push({
|
|
597
|
+
severity: "warning",
|
|
598
|
+
code: "artifact_user_content_missing",
|
|
599
|
+
scope: artifactScope(row),
|
|
600
|
+
unitId,
|
|
601
|
+
message: userContentMissingMessage(issuePath, artifactType),
|
|
602
|
+
file: issuePath,
|
|
603
|
+
fixable: false,
|
|
604
|
+
});
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
379
607
|
issues.push({
|
|
380
608
|
severity: "error",
|
|
381
609
|
code: "artifact_file_missing",
|
|
382
610
|
scope: artifactScope(row),
|
|
383
611
|
unitId,
|
|
384
|
-
message: `Artifact ${
|
|
385
|
-
file:
|
|
386
|
-
fixable:
|
|
612
|
+
message: `Artifact ${issuePath} is recorded in the database as ${row.artifact_type || "UNKNOWN"} but no matching file exists on disk`,
|
|
613
|
+
file: issuePath,
|
|
614
|
+
fixable: issuePath.startsWith("phases/") && hasPresentMilestonesReplacement(basePath, row, artifactRows),
|
|
387
615
|
});
|
|
388
616
|
}
|
|
389
617
|
} catch {
|
|
390
618
|
// Non-fatal — artifact file existence check failed
|
|
391
619
|
}
|
|
620
|
+
if (options?.repair) {
|
|
621
|
+
for (const artifact of missingUserContentArtifacts) {
|
|
622
|
+
fixesApplied.push(
|
|
623
|
+
`skipped user-authored ${artifact.artifactType} artifact ${artifact.path} (content cannot be regenerated from the database)`,
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
392
627
|
|
|
393
628
|
// g. Completion artifacts disagree with open DB hierarchy rows.
|
|
394
629
|
try {
|
|
@@ -434,13 +669,29 @@ export async function checkEngineHealth(
|
|
|
434
669
|
: row.milestone_id;
|
|
435
670
|
if (seen.has(unitId)) continue;
|
|
436
671
|
seen.add(unitId);
|
|
672
|
+
const fixable = artifactDbStatusDivergenceFixable(basePath, row);
|
|
673
|
+
let repairFailed = false;
|
|
674
|
+
if (options?.repair && fixable) {
|
|
675
|
+
try {
|
|
676
|
+
if (repairTaskArtifactDbStatusDivergence(basePath, row)) {
|
|
677
|
+
fixesApplied.push(`repaired task completion from SUMMARY artifact for ${unitId}`);
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
} catch {
|
|
681
|
+
repairFailed = true;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
437
684
|
issues.push({
|
|
438
685
|
severity: "error",
|
|
439
686
|
code: "artifact_db_status_divergence",
|
|
440
687
|
scope: row.task_id ? "task" : row.slice_id ? "slice" : "milestone",
|
|
441
688
|
unitId,
|
|
442
|
-
message:
|
|
443
|
-
|
|
689
|
+
message: repairFailed
|
|
690
|
+
? `Completion artifact ${row.path} exists while DB state for ${unitId} is still open or missing. Doctor found valid SUMMARY completion evidence but could not repair the database state.`
|
|
691
|
+
: fixable
|
|
692
|
+
? `Completion artifact ${row.path} exists while DB state for ${unitId} is still open or missing. Doctor can repair this from the SUMMARY completion evidence.`
|
|
693
|
+
: `Completion artifact ${row.path} exists while DB state for ${unitId} is still open or missing. Runtime will not import it silently; run explicit recovery/repair after review.`,
|
|
694
|
+
fixable,
|
|
444
695
|
});
|
|
445
696
|
}
|
|
446
697
|
} catch {
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
import { existsSync, readFileSync } from "node:fs";
|
|
15
15
|
import { access, readFile } from "node:fs/promises";
|
|
16
|
+
import { homedir } from "node:os";
|
|
16
17
|
import { delimiter, join } from "node:path";
|
|
17
18
|
import { AuthStorage } from "@gsd/pi-coding-agent";
|
|
18
19
|
import { getEnvApiKey } from "@gsd/pi-ai";
|
|
20
|
+
import { isCursorAgentReadyUncached } from "../cursor-cli/readiness.js";
|
|
19
21
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
20
22
|
import { getAuthPath, PROVIDER_REGISTRY, supportsBrowserOAuth, type ProviderCategory } from "./key-manager.js";
|
|
21
|
-
import { homedir } from "node:os";
|
|
22
23
|
|
|
23
24
|
// ── Types ──────────────────────────────────────────────────────────────────────
|
|
24
25
|
|
|
@@ -49,6 +50,7 @@ const CLI_AUTH_PROVIDERS = new Set([
|
|
|
49
50
|
"claude-code",
|
|
50
51
|
"google-gemini-cli",
|
|
51
52
|
"google-antigravity",
|
|
53
|
+
"cursor-agent",
|
|
52
54
|
]);
|
|
53
55
|
|
|
54
56
|
/**
|
|
@@ -57,9 +59,9 @@ const CLI_AUTH_PROVIDERS = new Set([
|
|
|
57
59
|
* e.g. GitHub Copilot subscriptions can access Claude and GPT models.
|
|
58
60
|
*/
|
|
59
61
|
const PROVIDER_ROUTES: Record<string, string[]> = {
|
|
60
|
-
anthropic: ["github-copilot", "claude-code"],
|
|
61
|
-
openai: ["github-copilot", "openai-codex"],
|
|
62
|
-
google: ["google-antigravity", "google-gemini-cli"],
|
|
62
|
+
anthropic: ["github-copilot", "claude-code", "cursor-agent"],
|
|
63
|
+
openai: ["github-copilot", "openai-codex", "cursor-agent"],
|
|
64
|
+
google: ["google-antigravity", "google-gemini-cli", "cursor-agent"],
|
|
63
65
|
};
|
|
64
66
|
|
|
65
67
|
// ── Model → Provider ID mapping ───────────────────────────────────────────────
|
|
@@ -86,6 +88,7 @@ function modelToProviderId(model: string): string | null {
|
|
|
86
88
|
anthropic: "anthropic",
|
|
87
89
|
openai: "openai",
|
|
88
90
|
"github-copilot": "github-copilot",
|
|
91
|
+
"cursor-agent": "cursor-agent",
|
|
89
92
|
};
|
|
90
93
|
if (prefixMap[prefix]) return prefixMap[prefix];
|
|
91
94
|
return rawPrefix;
|
|
@@ -95,6 +98,7 @@ function modelToProviderId(model: string): string | null {
|
|
|
95
98
|
if (lower.startsWith("claude")) return "anthropic";
|
|
96
99
|
if (lower.startsWith("gpt-") || lower.startsWith("o1") || lower.startsWith("o3")) return "openai";
|
|
97
100
|
if (lower.startsWith("gemini")) return "google";
|
|
101
|
+
if (lower.startsWith("composer")) return "cursor-agent";
|
|
98
102
|
if (lower.startsWith("llama") || lower.startsWith("mixtral")) return "groq";
|
|
99
103
|
if (lower.startsWith("grok")) return "xai";
|
|
100
104
|
if (lower.startsWith("mistral") || lower.startsWith("codestral")) return "mistral";
|
|
@@ -160,11 +164,13 @@ const CLI_BINARY_MAP: Record<string, string[]> = {
|
|
|
160
164
|
"claude-code": ["claude", "claude-code"],
|
|
161
165
|
"google-gemini-cli": ["gemini"],
|
|
162
166
|
"google-antigravity": ["agy"],
|
|
167
|
+
"cursor-agent": ["cursor-agent"],
|
|
163
168
|
};
|
|
164
169
|
|
|
165
170
|
const CLI_AUTH_PATH_CHECK_PROVIDERS = new Set([
|
|
166
171
|
"google-gemini-cli",
|
|
167
172
|
"google-antigravity",
|
|
173
|
+
"cursor-agent",
|
|
168
174
|
]);
|
|
169
175
|
|
|
170
176
|
let asyncCliBinaryPathCache: Map<string, boolean> | null = null;
|
|
@@ -212,6 +218,18 @@ function isCliBinaryInPath(providerId: string): boolean {
|
|
|
212
218
|
return pathDirs.some(dir => executableNames.some(name => existsSync(join(dir, name))));
|
|
213
219
|
}
|
|
214
220
|
|
|
221
|
+
function isExternalCliProviderReady(providerId: string): boolean {
|
|
222
|
+
if (providerId === "cursor-agent") return isCursorAgentReadyUncached();
|
|
223
|
+
return isCliBinaryInPath(providerId);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function cursorAgentFailureDetail(): string {
|
|
227
|
+
if (!isCliBinaryInPath("cursor-agent")) {
|
|
228
|
+
return "Install Cursor Agent and ensure `cursor-agent` is on PATH";
|
|
229
|
+
}
|
|
230
|
+
return "Run `cursor-agent login` or set CURSOR_API_KEY";
|
|
231
|
+
}
|
|
232
|
+
|
|
215
233
|
async function isCliBinaryInPathAsync(providerId: string): Promise<boolean> {
|
|
216
234
|
const executableNames = cliExecutableNames(providerId);
|
|
217
235
|
if (executableNames.length === 0) return false;
|
|
@@ -345,6 +363,12 @@ function resolveKeyFromAuthOrEnv(providerId: string): KeyLookup | null {
|
|
|
345
363
|
}
|
|
346
364
|
|
|
347
365
|
function resolveKey(providerId: string): KeyLookup {
|
|
366
|
+
if (providerId === "cursor-agent") {
|
|
367
|
+
return isExternalCliProviderReady(providerId)
|
|
368
|
+
? { found: true, source: "env", backedOff: false }
|
|
369
|
+
: { found: false, source: "none", backedOff: false };
|
|
370
|
+
}
|
|
371
|
+
|
|
348
372
|
const direct = resolveKeyFromAuthOrEnv(providerId);
|
|
349
373
|
if (direct) return direct;
|
|
350
374
|
|
|
@@ -354,7 +378,7 @@ function resolveKey(providerId: string): KeyLookup {
|
|
|
354
378
|
|
|
355
379
|
// Cross-provider routes can use a local CLI when it is installed. Explicit
|
|
356
380
|
// external CLI provider selections are handled in checkLlmProviders() below.
|
|
357
|
-
if (CLI_AUTH_PROVIDERS.has(providerId) &&
|
|
381
|
+
if (CLI_AUTH_PROVIDERS.has(providerId) && isExternalCliProviderReady(providerId)) {
|
|
358
382
|
return { found: true, source: "env", backedOff: false };
|
|
359
383
|
}
|
|
360
384
|
|
|
@@ -373,17 +397,22 @@ function checkLlmProviders(): ProviderCheckResult[] {
|
|
|
373
397
|
if (CLI_AUTH_PROVIDERS.has(providerId)) {
|
|
374
398
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
375
399
|
const label = info?.label ?? providerId;
|
|
376
|
-
if (CLI_AUTH_PATH_CHECK_PROVIDERS.has(providerId) && !
|
|
400
|
+
if (CLI_AUTH_PATH_CHECK_PROVIDERS.has(providerId) && !isExternalCliProviderReady(providerId)) {
|
|
377
401
|
const binaries = CLI_BINARY_MAP[providerId]?.map(binary => `\`${binary}\``).join(" or ");
|
|
402
|
+
const detail = providerId === "cursor-agent"
|
|
403
|
+
? cursorAgentFailureDetail()
|
|
404
|
+
: binaries
|
|
405
|
+
? `Install ${label} and ensure ${binaries} is on PATH`
|
|
406
|
+
: `Install ${label} and ensure its CLI is on PATH`;
|
|
378
407
|
results.push({
|
|
379
408
|
name: providerId,
|
|
380
409
|
label,
|
|
381
410
|
category: "llm",
|
|
382
411
|
status: "error",
|
|
383
|
-
message:
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
412
|
+
message: providerId === "cursor-agent" && isCliBinaryInPath(providerId)
|
|
413
|
+
? `${label} — CLI not authenticated`
|
|
414
|
+
: `${label} — CLI not found`,
|
|
415
|
+
detail,
|
|
387
416
|
required: true,
|
|
388
417
|
});
|
|
389
418
|
continue;
|
|
@@ -85,6 +85,7 @@ export type DoctorIssueCode =
|
|
|
85
85
|
| "db_orphaned_slice"
|
|
86
86
|
| "db_done_task_no_summary"
|
|
87
87
|
| "artifact_file_missing"
|
|
88
|
+
| "artifact_user_content_missing"
|
|
88
89
|
| "artifact_db_status_divergence"
|
|
89
90
|
| "checkbox_db_status_divergence"
|
|
90
91
|
| "completed_milestone_reopened"
|
|
@@ -94,7 +95,10 @@ export type DoctorIssueCode =
|
|
|
94
95
|
| "projection_drift"
|
|
95
96
|
// Milestone filesystem/DB drift (#4996)
|
|
96
97
|
| "orphan_milestone_dir"
|
|
97
|
-
| "orphan_milestone_db"
|
|
98
|
+
| "orphan_milestone_db"
|
|
99
|
+
// Parent-workspace declared repository checks (#818)
|
|
100
|
+
| "workspace_repo_path_missing"
|
|
101
|
+
| "workspace_repo_not_a_repo";
|
|
98
102
|
|
|
99
103
|
/**
|
|
100
104
|
* Issue codes that represent global or completion-critical state.
|