gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.84a383f51
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 +7 -7
- package/dist/claude-cli-check.js +64 -37
- package/dist/cli-policy.d.ts +13 -0
- package/dist/cli-policy.js +17 -0
- package/dist/cli.js +95 -55
- package/dist/headless-query.d.ts +22 -0
- package/dist/headless-query.js +24 -4
- package/dist/headless.d.ts +10 -0
- package/dist/headless.js +16 -1
- package/dist/loader.js +7 -10
- package/dist/onboarding.d.ts +10 -0
- package/dist/onboarding.js +2 -2
- package/dist/provider-migrations.d.ts +2 -2
- package/dist/provider-migrations.js +5 -2
- package/dist/resource-loader.d.ts +5 -2
- package/dist/resource-loader.js +28 -5
- package/dist/resources/.managed-resources-content-hash +1 -0
- package/dist/resources/extensions/claude-code-cli/readiness.js +77 -45
- package/dist/resources/extensions/gsd/auto/loop.js +23 -0
- package/dist/resources/extensions/gsd/auto/phases.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -0
- package/dist/resources/extensions/gsd/auto.js +14 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +94 -31
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +34 -8
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +69 -5
- package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/forensics.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +12 -5
- package/dist/resources/extensions/gsd/gsd-db.js +11 -2
- package/dist/resources/extensions/gsd/guided-flow.js +23 -23
- package/dist/resources/extensions/gsd/memory-store.js +66 -31
- package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
- package/dist/resources/extensions/gsd/model-router.js +114 -9
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
- package/dist/resources/extensions/gsd/preferences-models.js +91 -15
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
- package/dist/resources/extensions/gsd/preferences.js +5 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
- package/dist/resources/extensions/gsd/state.js +42 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
- package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
- package/dist/resources/extensions/gsd/worktree-command.js +26 -46
- package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
- package/dist/resources/extensions/mcp-client/index.js +6 -3
- package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
- package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/rtk-shared.d.ts +3 -0
- package/dist/rtk-shared.js +17 -0
- package/dist/rtk.d.ts +2 -5
- package/dist/rtk.js +3 -20
- package/dist/runtime-checks.d.ts +27 -0
- package/dist/runtime-checks.js +38 -0
- 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 +12 -12
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
- 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_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 +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/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- 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 +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
- package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
- package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
- package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
- package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
- package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
- package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
- package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
- package/dist/web/standalone/package.json +2 -1
- package/dist/worktree-status-banner.d.ts +1 -0
- package/dist/worktree-status-banner.js +132 -0
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
- package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
- package/packages/mcp-server/dist/alias-telemetry.js +30 -0
- package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +74 -46
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
- package/packages/mcp-server/src/alias-telemetry.ts +30 -0
- package/packages/mcp-server/src/workflow-tools.test.ts +26 -0
- package/packages/mcp-server/src/workflow-tools.ts +93 -58
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +13 -0
- 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/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
- package/packages/pi-ai/src/types.ts +13 -0
- package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +4 -0
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
- package/packages/pi-coding-agent/src/core/messages.ts +4 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
- package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
- package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
- package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +78 -46
- package/src/resources/extensions/gsd/auto/loop.ts +24 -2
- package/src/resources/extensions/gsd/auto/phases.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +3 -1
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto/types.ts +1 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
- package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
- package/src/resources/extensions/gsd/auto-worktree.ts +38 -0
- package/src/resources/extensions/gsd/auto.ts +14 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +102 -31
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +39 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +39 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +75 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +22 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
- package/src/resources/extensions/gsd/doctor-types.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +13 -5
- package/src/resources/extensions/gsd/gsd-db.ts +12 -2
- package/src/resources/extensions/gsd/guided-flow.ts +25 -25
- package/src/resources/extensions/gsd/memory-store.ts +81 -28
- package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
- package/src/resources/extensions/gsd/model-router.ts +172 -9
- package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
- package/src/resources/extensions/gsd/preferences-models.ts +101 -15
- package/src/resources/extensions/gsd/preferences-types.ts +6 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
- package/src/resources/extensions/gsd/state.ts +42 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +178 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +9 -5
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
- package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
- package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
- package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
- package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
- package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
- package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
- package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
- package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
- package/src/resources/extensions/gsd/worktree-command.ts +31 -44
- package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
- package/src/resources/extensions/mcp-client/index.ts +6 -3
- package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
- package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
- package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
- package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
- package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
- package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
- package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
- package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
- package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
- /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_ssgManifest.js +0 -0
|
@@ -5,24 +5,16 @@ import { isToolCallEventType } from "@gsd/pi-coding-agent";
|
|
|
5
5
|
|
|
6
6
|
import type { GSDEcosystemBeforeAgentStartHandler } from "../ecosystem/gsd-extension-api.js";
|
|
7
7
|
import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
|
|
8
|
-
import { getEcosystemReadyPromise } from "../ecosystem/loader.js";
|
|
9
8
|
|
|
10
9
|
import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
|
|
10
|
+
import { clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
|
|
11
|
+
import { resolveManifest } from "../unit-context-manifest.js";
|
|
14
12
|
import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
|
|
15
|
-
import { cleanupQuickBranch } from "../quick.js";
|
|
16
|
-
import { getDiscussionMilestoneId } from "../guided-flow.js";
|
|
17
|
-
import { loadToolApiKeys } from "../commands-config.js";
|
|
18
13
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
19
|
-
import {
|
|
20
|
-
import { getAutoDashboardData, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto.js";
|
|
14
|
+
import { getAutoRuntimeSnapshot, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
|
|
21
15
|
|
|
22
|
-
import { isParallelActive, shutdownParallel } from "../parallel-orchestrator.js";
|
|
23
16
|
import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
24
17
|
import { saveActivityLog } from "../activity-log.js";
|
|
25
|
-
import { resetAskUserQuestionsCache } from "../../ask-user-questions.js";
|
|
26
18
|
import { recordToolCall as safetyRecordToolCall, recordToolResult as safetyRecordToolResult, saveEvidenceToDisk } from "../safety/evidence-collector.js";
|
|
27
19
|
import { parseUnitId } from "../unit-id.js";
|
|
28
20
|
import { classifyCommand } from "../safety/destructive-guard.js";
|
|
@@ -30,17 +22,45 @@ import { logWarning as safetyLogWarning } from "../workflow-logger.js";
|
|
|
30
22
|
import { installNotifyInterceptor } from "./notify-interceptor.js";
|
|
31
23
|
import { initNotificationStore } from "../notification-store.js";
|
|
32
24
|
import { initNotificationWidget } from "../notification-widget.js";
|
|
33
|
-
import { initHealthWidget } from "../health-widget.js";
|
|
34
25
|
|
|
35
26
|
// Skip the welcome screen on the very first session_start — cli.ts already
|
|
36
27
|
// printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
|
|
37
28
|
let isFirstSession = true;
|
|
38
29
|
|
|
30
|
+
async function deriveGsdState(basePath: string) {
|
|
31
|
+
const { deriveState } = await import("../state.js");
|
|
32
|
+
return deriveState(basePath);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function getDiscussionMilestoneIdFor(basePath: string): Promise<string | null> {
|
|
36
|
+
const { getDiscussionMilestoneId } = await import("../guided-flow.js");
|
|
37
|
+
return getDiscussionMilestoneId(basePath);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function loadToolApiKeysForSession(): Promise<void> {
|
|
41
|
+
const { loadToolApiKeys } = await import("../commands-config.js");
|
|
42
|
+
loadToolApiKeys();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function resetAskUserQuestionsTurnCache(): Promise<void> {
|
|
46
|
+
const { resetAskUserQuestionsCache } = await import("../../ask-user-questions.js");
|
|
47
|
+
resetAskUserQuestionsCache();
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
async function syncServiceTierStatus(ctx: ExtensionContext): Promise<void> {
|
|
40
51
|
const { getEffectiveServiceTier, formatServiceTierFooterStatus } = await import("../service-tier.js");
|
|
41
52
|
ctx.ui.setStatus("gsd-fast", formatServiceTierFooterStatus(getEffectiveServiceTier(), ctx.model?.id));
|
|
42
53
|
}
|
|
43
54
|
|
|
55
|
+
async function applyDisabledModelProviderPolicy(ctx: ExtensionContext): Promise<void> {
|
|
56
|
+
try {
|
|
57
|
+
const { resolveDisabledModelProvidersFromPreferences } = await import("../preferences.js");
|
|
58
|
+
ctx.modelRegistry.setDisabledModelProviders(resolveDisabledModelProvidersFromPreferences());
|
|
59
|
+
} catch {
|
|
60
|
+
// Non-fatal: keep default provider visibility if preferences cannot be loaded.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
export function registerHooks(
|
|
45
65
|
pi: ExtensionAPI,
|
|
46
66
|
ecosystemHandlers: GSDEcosystemBeforeAgentStartHandler[],
|
|
@@ -50,12 +70,14 @@ export function registerHooks(
|
|
|
50
70
|
installNotifyInterceptor(ctx);
|
|
51
71
|
initNotificationWidget(ctx);
|
|
52
72
|
if (!isAutoActive()) {
|
|
73
|
+
const { initHealthWidget } = await import("../health-widget.js");
|
|
53
74
|
initHealthWidget(ctx);
|
|
54
75
|
}
|
|
55
76
|
resetWriteGateState();
|
|
56
77
|
resetToolCallLoopGuard();
|
|
57
|
-
|
|
78
|
+
await resetAskUserQuestionsTurnCache();
|
|
58
79
|
await syncServiceTierStatus(ctx);
|
|
80
|
+
await applyDisabledModelProviderPolicy(ctx);
|
|
59
81
|
// Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
|
|
60
82
|
const { isInAutoWorktree } = await import("../auto-worktree.js");
|
|
61
83
|
if (!isInAutoWorktree(process.cwd())) {
|
|
@@ -91,7 +113,7 @@ export function registerHooks(
|
|
|
91
113
|
}
|
|
92
114
|
} catch { /* non-fatal */ }
|
|
93
115
|
}
|
|
94
|
-
|
|
116
|
+
await loadToolApiKeysForSession();
|
|
95
117
|
if (isAutoActive()) {
|
|
96
118
|
ctx.ui.setWidget("gsd-health", undefined);
|
|
97
119
|
}
|
|
@@ -102,9 +124,10 @@ export function registerHooks(
|
|
|
102
124
|
installNotifyInterceptor(ctx);
|
|
103
125
|
resetWriteGateState();
|
|
104
126
|
resetToolCallLoopGuard();
|
|
105
|
-
|
|
127
|
+
await resetAskUserQuestionsTurnCache();
|
|
106
128
|
clearDiscussionFlowState();
|
|
107
129
|
await syncServiceTierStatus(ctx);
|
|
130
|
+
await applyDisabledModelProviderPolicy(ctx);
|
|
108
131
|
// Skip MCP auto-prep when running inside an auto-worktree. The worktree
|
|
109
132
|
// already has .mcp.json from createAutoWorktree, and re-running the writer
|
|
110
133
|
// post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
|
|
@@ -114,23 +137,28 @@ export function registerHooks(
|
|
|
114
137
|
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
115
138
|
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
116
139
|
}
|
|
117
|
-
|
|
118
|
-
if (isAutoActive()) {
|
|
140
|
+
await loadToolApiKeysForSession();
|
|
141
|
+
if (!isAutoActive()) {
|
|
142
|
+
const { initHealthWidget } = await import("../health-widget.js");
|
|
143
|
+
initHealthWidget(ctx);
|
|
144
|
+
} else {
|
|
119
145
|
ctx.ui.setWidget("gsd-health", undefined);
|
|
120
146
|
}
|
|
121
147
|
});
|
|
122
148
|
|
|
123
149
|
pi.on("before_agent_start", async (event, ctx: ExtensionContext) => {
|
|
124
150
|
// Wait for ecosystem loader to finish (no-op after first turn).
|
|
151
|
+
const { getEcosystemReadyPromise } = await import("../ecosystem/loader.js");
|
|
125
152
|
await getEcosystemReadyPromise();
|
|
126
153
|
|
|
127
154
|
// GSD's own context injection (existing behavior — unchanged).
|
|
155
|
+
const { buildBeforeAgentStartResult } = await import("./system-context.js");
|
|
128
156
|
const gsdResult = await buildBeforeAgentStartResult(event, ctx);
|
|
129
157
|
|
|
130
158
|
// Refresh the snapshot used by ecosystem getPhase()/getActiveUnit().
|
|
131
159
|
// deriveState has its own ~100ms cache so this is cheap on repeat calls.
|
|
132
160
|
try {
|
|
133
|
-
const state = await
|
|
161
|
+
const state = await deriveGsdState(process.cwd());
|
|
134
162
|
updateSnapshot(state);
|
|
135
163
|
} catch {
|
|
136
164
|
updateSnapshot(null);
|
|
@@ -169,7 +197,8 @@ export function registerHooks(
|
|
|
169
197
|
|
|
170
198
|
pi.on("agent_end", async (event, ctx: ExtensionContext) => {
|
|
171
199
|
resetToolCallLoopGuard();
|
|
172
|
-
|
|
200
|
+
await resetAskUserQuestionsTurnCache();
|
|
201
|
+
const { handleAgentEnd } = await import("./agent-end-recovery.js");
|
|
173
202
|
await handleAgentEnd(pi, event, ctx);
|
|
174
203
|
});
|
|
175
204
|
|
|
@@ -178,6 +207,7 @@ export function registerHooks(
|
|
|
178
207
|
// quick-return state is pending, so this is safe to call on every turn.
|
|
179
208
|
pi.on("turn_end", async () => {
|
|
180
209
|
try {
|
|
210
|
+
const { cleanupQuickBranch } = await import("../quick.js");
|
|
181
211
|
cleanupQuickBranch();
|
|
182
212
|
} catch {
|
|
183
213
|
// Best-effort: don't break the turn lifecycle if cleanup fails.
|
|
@@ -194,8 +224,8 @@ export function registerHooks(
|
|
|
194
224
|
const basePath = process.cwd();
|
|
195
225
|
const { ensureDbOpen } = await import("./dynamic-tools.js");
|
|
196
226
|
await ensureDbOpen();
|
|
197
|
-
const state = await
|
|
198
|
-
if (!state.activeMilestone || !state.activeSlice
|
|
227
|
+
const state = await deriveGsdState(basePath);
|
|
228
|
+
if (!state.activeMilestone || !state.activeSlice) return;
|
|
199
229
|
// Write checkpoint for ALL phases, not just "executing" — discuss, research,
|
|
200
230
|
// and planning also carry in-memory state (user answers, gate verification)
|
|
201
231
|
// that would be lost on compaction (#4258).
|
|
@@ -210,21 +240,31 @@ export function registerHooks(
|
|
|
210
240
|
if (await loadFile(legacyContinue)) return;
|
|
211
241
|
|
|
212
242
|
const continuePath = join(sliceDir, `${state.activeSlice.id}-CONTINUE.md`);
|
|
243
|
+
const taskId = state.activeTask?.id ?? "none";
|
|
244
|
+
const taskTitle = state.activeTask?.title ?? "";
|
|
245
|
+
const phaseLabel = state.phase.replace(/-/g, " ");
|
|
246
|
+
|
|
213
247
|
await saveFile(continuePath, formatContinue({
|
|
214
248
|
frontmatter: {
|
|
215
249
|
milestone: state.activeMilestone.id,
|
|
216
250
|
slice: state.activeSlice.id,
|
|
217
|
-
task:
|
|
251
|
+
task: taskId,
|
|
218
252
|
step: 0,
|
|
219
253
|
totalSteps: 0,
|
|
220
254
|
status: "compacted" as const,
|
|
221
255
|
savedAt: new Date().toISOString(),
|
|
222
256
|
},
|
|
223
|
-
completedWork:
|
|
224
|
-
|
|
257
|
+
completedWork: state.activeTask
|
|
258
|
+
? `Task ${taskId} (${taskTitle}) was in progress when compaction occurred.`
|
|
259
|
+
: `Slice ${state.activeSlice.id} was in ${phaseLabel} phase when compaction occurred.`,
|
|
260
|
+
remainingWork: state.activeTask
|
|
261
|
+
? "Check the task plan for remaining steps."
|
|
262
|
+
: "Continue this slice from the latest planning/research/discussion artifacts.",
|
|
225
263
|
decisions: "Check task summary files for prior decisions.",
|
|
226
264
|
context: "Session was auto-compacted by Pi. Resume with /gsd.",
|
|
227
|
-
nextAction:
|
|
265
|
+
nextAction: state.activeTask
|
|
266
|
+
? `Resume task ${taskId}: ${taskTitle}.`
|
|
267
|
+
: `Resume ${phaseLabel} work for slice ${state.activeSlice.id}.`,
|
|
228
268
|
}));
|
|
229
269
|
});
|
|
230
270
|
|
|
@@ -246,7 +286,7 @@ export function registerHooks(
|
|
|
246
286
|
const basePath = process.cwd();
|
|
247
287
|
let activeContext: string | null = null;
|
|
248
288
|
try {
|
|
249
|
-
const state = await
|
|
289
|
+
const state = await deriveGsdState(basePath);
|
|
250
290
|
if (state.activeMilestone && state.activeSlice && state.activeTask) {
|
|
251
291
|
activeContext =
|
|
252
292
|
`Active: ${state.activeMilestone.id} / ${state.activeSlice.id} / ${state.activeTask.id}` +
|
|
@@ -265,6 +305,7 @@ export function registerHooks(
|
|
|
265
305
|
});
|
|
266
306
|
|
|
267
307
|
pi.on("session_shutdown", async (_event, ctx: ExtensionContext) => {
|
|
308
|
+
const { isParallelActive, shutdownParallel } = await import("../parallel-orchestrator.js");
|
|
268
309
|
if (isParallelActive()) {
|
|
269
310
|
try {
|
|
270
311
|
await shutdownParallel(process.cwd());
|
|
@@ -273,7 +314,7 @@ export function registerHooks(
|
|
|
273
314
|
}
|
|
274
315
|
}
|
|
275
316
|
if (!isAutoActive() && !isAutoPaused()) return;
|
|
276
|
-
const dash =
|
|
317
|
+
const dash = getAutoRuntimeSnapshot();
|
|
277
318
|
if (dash.currentUnit) {
|
|
278
319
|
saveActivityLog(ctx, dash.basePath, dash.currentUnit.type, dash.currentUnit.id);
|
|
279
320
|
}
|
|
@@ -302,7 +343,7 @@ export function registerHooks(
|
|
|
302
343
|
// If ask_user_questions was called with a gate ID but hasn't been confirmed,
|
|
303
344
|
// block all non-read-only tool calls to prevent the model from skipping gates.
|
|
304
345
|
if (getPendingGate()) {
|
|
305
|
-
const milestoneId =
|
|
346
|
+
const milestoneId = await getDiscussionMilestoneIdFor(discussionBasePath);
|
|
306
347
|
if (isToolCallEventType("bash", event)) {
|
|
307
348
|
const bashGuard = shouldBlockPendingGateBash(
|
|
308
349
|
event.input.command,
|
|
@@ -337,6 +378,36 @@ export function registerHooks(
|
|
|
337
378
|
if (queueGuard.block) return queueGuard;
|
|
338
379
|
}
|
|
339
380
|
|
|
381
|
+
// ── Planning-unit tools-policy enforcement (#4934): runtime half ─────
|
|
382
|
+
// The active auto-mode unit's manifest declares a ToolsPolicy. For
|
|
383
|
+
// planning/docs/read-only modes, deny writes outside .gsd/ (or the
|
|
384
|
+
// manifest's allowedPathGlobs), bash that isn't read-only, and
|
|
385
|
+
// subagent dispatch. Closes the b23 bug class where a discuss-milestone
|
|
386
|
+
// turn used the host Edit tool to modify user source files.
|
|
387
|
+
const dash = getAutoRuntimeSnapshot();
|
|
388
|
+
const activeUnitType = dash.currentUnit?.type;
|
|
389
|
+
if (activeUnitType) {
|
|
390
|
+
const manifest = resolveManifest(activeUnitType);
|
|
391
|
+
if (manifest) {
|
|
392
|
+
let planningInput = "";
|
|
393
|
+
if (isToolCallEventType("write", event)) {
|
|
394
|
+
planningInput = event.input.path;
|
|
395
|
+
} else if (isToolCallEventType("edit", event)) {
|
|
396
|
+
planningInput = event.input.path;
|
|
397
|
+
} else if (isToolCallEventType("bash", event)) {
|
|
398
|
+
planningInput = event.input.command;
|
|
399
|
+
}
|
|
400
|
+
const planningGuard = shouldBlockPlanningUnit(
|
|
401
|
+
event.toolName,
|
|
402
|
+
planningInput,
|
|
403
|
+
dash.basePath || discussionBasePath,
|
|
404
|
+
activeUnitType,
|
|
405
|
+
manifest.tools,
|
|
406
|
+
);
|
|
407
|
+
if (planningGuard.block) return planningGuard;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
340
411
|
// ── Single-writer engine: block direct writes to STATE.md ──────────
|
|
341
412
|
// Covers write, edit, and bash tools to prevent bypass vectors.
|
|
342
413
|
if (isToolCallEventType("write", event)) {
|
|
@@ -362,7 +433,7 @@ export function registerHooks(
|
|
|
362
433
|
const result = shouldBlockContextWrite(
|
|
363
434
|
event.toolName,
|
|
364
435
|
event.input.path,
|
|
365
|
-
|
|
436
|
+
await getDiscussionMilestoneIdFor(discussionBasePath),
|
|
366
437
|
isQueuePhaseActive(),
|
|
367
438
|
);
|
|
368
439
|
if (result.block) return result;
|
|
@@ -407,7 +478,7 @@ export function registerHooks(
|
|
|
407
478
|
recordToolInvocationError(event.toolName, errorText);
|
|
408
479
|
}
|
|
409
480
|
if (event.toolName !== "ask_user_questions") return;
|
|
410
|
-
const milestoneId =
|
|
481
|
+
const milestoneId = await getDiscussionMilestoneIdFor(process.cwd());
|
|
411
482
|
const queueActive = isQueuePhaseActive();
|
|
412
483
|
|
|
413
484
|
const details = event.details as any;
|
|
@@ -506,7 +577,7 @@ export function registerHooks(
|
|
|
506
577
|
safetyRecordToolResult(event.toolCallId, event.toolName, event.result, event.isError);
|
|
507
578
|
// Persist evidence to disk after each tool result so it survives a session
|
|
508
579
|
// restart mid-unit (Bug #4385 — non-persisted evidence false positives).
|
|
509
|
-
const dash =
|
|
580
|
+
const dash = getAutoRuntimeSnapshot();
|
|
510
581
|
if (dash.basePath && dash.currentUnit?.type === "execute-task") {
|
|
511
582
|
const { milestone: pMid, slice: pSid, task: pTid } = parseUnitId(dash.currentUnit.id);
|
|
512
583
|
if (pMid && pSid && pTid) {
|
|
@@ -4,13 +4,14 @@ import { join } from "node:path";
|
|
|
4
4
|
import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
5
5
|
import { Key } from "@gsd/pi-tui";
|
|
6
6
|
|
|
7
|
-
import { GSDDashboardOverlay } from "../dashboard-overlay.js";
|
|
8
|
-
import { GSDNotificationOverlay } from "../notification-overlay.js";
|
|
9
|
-
import { ParallelMonitorOverlay } from "../parallel-monitor-overlay.js";
|
|
10
7
|
import { GSD_SHORTCUTS } from "../shortcut-defs.js";
|
|
11
|
-
import { projectRoot } from "../commands/context.js";
|
|
12
8
|
import { shortcutDesc } from "../../shared/mod.js";
|
|
13
9
|
|
|
10
|
+
async function getProjectRoot(): Promise<string> {
|
|
11
|
+
const { projectRoot } = await import("../commands/context.js");
|
|
12
|
+
return projectRoot();
|
|
13
|
+
}
|
|
14
|
+
|
|
14
15
|
export function registerShortcuts(pi: ExtensionAPI): void {
|
|
15
16
|
const overlayOptions = {
|
|
16
17
|
width: "90%",
|
|
@@ -20,7 +21,10 @@ export function registerShortcuts(pi: ExtensionAPI): void {
|
|
|
20
21
|
} as const;
|
|
21
22
|
|
|
22
23
|
const openDashboardOverlay = async (ctx: ExtensionContext) => {
|
|
23
|
-
const basePath =
|
|
24
|
+
const [{ GSDDashboardOverlay }, basePath] = await Promise.all([
|
|
25
|
+
import("../dashboard-overlay.js"),
|
|
26
|
+
getProjectRoot(),
|
|
27
|
+
]);
|
|
24
28
|
if (!existsSync(join(basePath, ".gsd"))) {
|
|
25
29
|
ctx.ui.notify("No .gsd/ directory found. Run /gsd to start.", "info");
|
|
26
30
|
return;
|
|
@@ -35,6 +39,7 @@ export function registerShortcuts(pi: ExtensionAPI): void {
|
|
|
35
39
|
};
|
|
36
40
|
|
|
37
41
|
const openNotificationsOverlay = async (ctx: ExtensionContext) => {
|
|
42
|
+
const { GSDNotificationOverlay } = await import("../notification-overlay.js");
|
|
38
43
|
await ctx.ui.custom<boolean>(
|
|
39
44
|
(tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done(true)),
|
|
40
45
|
{
|
|
@@ -51,12 +56,13 @@ export function registerShortcuts(pi: ExtensionAPI): void {
|
|
|
51
56
|
};
|
|
52
57
|
|
|
53
58
|
const openParallelOverlay = async (ctx: ExtensionContext) => {
|
|
54
|
-
const basePath =
|
|
59
|
+
const basePath = await getProjectRoot();
|
|
55
60
|
const parallelDir = join(basePath, ".gsd", "parallel");
|
|
56
61
|
if (!existsSync(parallelDir)) {
|
|
57
62
|
ctx.ui.notify("No parallel workers found. Run /gsd parallel start first.", "info");
|
|
58
63
|
return;
|
|
59
64
|
}
|
|
65
|
+
const { ParallelMonitorOverlay } = await import("../parallel-monitor-overlay.js");
|
|
60
66
|
await ctx.ui.custom<boolean>(
|
|
61
67
|
(tui, theme, _kb, done) => new ParallelMonitorOverlay(tui, theme, () => done(true), basePath),
|
|
62
68
|
{
|
|
@@ -15,7 +15,7 @@ import { resolveGsdRootFile, resolveSliceFile, resolveSlicePath, resolveTaskFile
|
|
|
15
15
|
import { ensureCodebaseMapFresh, readCodebaseMap } from "../codebase-generator.js";
|
|
16
16
|
import { hasSkillSnapshot, detectNewSkills, formatSkillsXml } from "../skill-discovery.js";
|
|
17
17
|
import { getActiveAutoWorktreeContext } from "../auto-worktree.js";
|
|
18
|
-
import { getActiveWorktreeName, getWorktreeOriginalCwd } from "../worktree-
|
|
18
|
+
import { getActiveWorktreeName, getWorktreeOriginalCwd } from "../worktree-session-state.js";
|
|
19
19
|
import { deriveState } from "../state.js";
|
|
20
20
|
import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
|
|
21
21
|
import { toPosixPath } from "../../shared/mod.js";
|
|
@@ -207,7 +207,14 @@ export async function buildBeforeAgentStartResult(
|
|
|
207
207
|
? `\n\n## Subagent Model\n\nWhen spawning subagents via the \`subagent\` tool, always pass \`model: "${subagentModelConfig.primary}"\` in the tool call parameters. Never omit this — always specify it explicitly.`
|
|
208
208
|
: "";
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
// memoryBlock is FTS-queried against the user prompt and changes per call.
|
|
211
|
+
// Removing it from `fullSystem` keeps the system-prompt cache breakpoint
|
|
212
|
+
// stable across calls — the only scoped goal of this fix. The pi-ai
|
|
213
|
+
// Anthropic adapter additionally cache-marks the last user turn, so the
|
|
214
|
+
// memoryBlock injected via the context message may itself be cached up to
|
|
215
|
+
// that boundary; that's orthogonal and unchanged from prior behavior. The
|
|
216
|
+
// load-bearing win here is preserving the system+tools cache hit. (#5019)
|
|
217
|
+
const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${newSkillsBlock}${worktreeBlock}${subagentModelBlock}`;
|
|
211
218
|
|
|
212
219
|
stopContextTimer({
|
|
213
220
|
systemPromptSize: fullSystem.length,
|
|
@@ -216,12 +223,7 @@ export async function buildBeforeAgentStartResult(
|
|
|
216
223
|
hasNewSkills: newSkillsBlock.length > 0,
|
|
217
224
|
});
|
|
218
225
|
|
|
219
|
-
|
|
220
|
-
const contextMessage = injection
|
|
221
|
-
? { customType: "gsd-guided-context", content: injection, display: false as const }
|
|
222
|
-
: forensicsInjection
|
|
223
|
-
? { customType: "gsd-forensics", content: forensicsInjection, display: false as const }
|
|
224
|
-
: null;
|
|
226
|
+
const contextMessage = buildContextMessage({ memoryBlock, injection, forensicsInjection });
|
|
225
227
|
|
|
226
228
|
return {
|
|
227
229
|
systemPrompt: fullSystem,
|
|
@@ -229,6 +231,35 @@ export async function buildBeforeAgentStartResult(
|
|
|
229
231
|
};
|
|
230
232
|
}
|
|
231
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Route the per-call dynamic blocks (memory, guided-execute, forensics) into a
|
|
236
|
+
* single user-message context payload so they ride the volatile suffix instead
|
|
237
|
+
* of the cached system prefix. Priority when both memory and an injection are
|
|
238
|
+
* present: guided > forensics > memory-only. (#5019)
|
|
239
|
+
*
|
|
240
|
+
* Exported for direct unit testing — the surrounding bootstrap has too many
|
|
241
|
+
* filesystem and DB dependencies to exercise this routing logic in-place.
|
|
242
|
+
*/
|
|
243
|
+
export function buildContextMessage(opts: {
|
|
244
|
+
memoryBlock: string;
|
|
245
|
+
injection: string | null;
|
|
246
|
+
forensicsInjection: string | null;
|
|
247
|
+
}): { customType: string; content: string; display: false } | null {
|
|
248
|
+
const memoryContent = opts.memoryBlock.trim();
|
|
249
|
+
if (opts.injection) {
|
|
250
|
+
const content = memoryContent ? `${memoryContent}\n\n${opts.injection}` : opts.injection;
|
|
251
|
+
return { customType: "gsd-guided-context", content, display: false as const };
|
|
252
|
+
}
|
|
253
|
+
if (opts.forensicsInjection) {
|
|
254
|
+
const content = memoryContent ? `${memoryContent}\n\n${opts.forensicsInjection}` : opts.forensicsInjection;
|
|
255
|
+
return { customType: "gsd-forensics", content, display: false as const };
|
|
256
|
+
}
|
|
257
|
+
if (memoryContent) {
|
|
258
|
+
return { customType: "gsd-memory", content: memoryContent, display: false as const };
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
232
263
|
/**
|
|
233
264
|
* ADR-013 step 4 — auto-injection parity for the memories table.
|
|
234
265
|
*
|
|
@@ -3,15 +3,7 @@ import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { minimatch } from "minimatch";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* Declarative tools-policy for a unit. Inlined here because the worktree
|
|
8
|
-
* branch predates the full unit-context-manifest export (#4934).
|
|
9
|
-
*/
|
|
10
|
-
export type ToolsPolicy =
|
|
11
|
-
| { mode: "all" }
|
|
12
|
-
| { mode: "read-only" }
|
|
13
|
-
| { mode: "planning" }
|
|
14
|
-
| { mode: "docs"; allowedPathGlobs: readonly string[] };
|
|
6
|
+
import type { ToolsPolicy } from "../unit-context-manifest.js";
|
|
15
7
|
|
|
16
8
|
/**
|
|
17
9
|
* Regex matching milestone CONTEXT.md file names in both legacy M001
|
|
@@ -535,10 +527,33 @@ export function shouldBlockQueueExecutionInSnapshot(
|
|
|
535
527
|
}
|
|
536
528
|
|
|
537
529
|
// ─── Planning-unit tools-policy enforcement (#4934) ───────────────────────
|
|
530
|
+
//
|
|
531
|
+
// Runtime half of the declarative ToolsPolicy on UnitContextManifest. The
|
|
532
|
+
// manifest assigns each unit type a tools mode; this predicate is what
|
|
533
|
+
// actually rejects a tool call that violates it.
|
|
534
|
+
//
|
|
535
|
+
// Forensics: a discuss-milestone LLM turn used the host Edit tool to modify
|
|
536
|
+
// index.html in test app b23 (~/Github/test-apps/b23). With this predicate
|
|
537
|
+
// wired into the tool_call hook, the same call returns block=true with a
|
|
538
|
+
// HARD BLOCK reason that the model cannot rationalize past.
|
|
539
|
+
//
|
|
540
|
+
// Activation: the hook supplies the policy resolved from the active unit's
|
|
541
|
+
// manifest. When no unit is active (interactive sessions, unknown unit
|
|
542
|
+
// types), the hook passes null and this predicate is a no-op — falling
|
|
543
|
+
// through to the existing pendingGate / queue-execution / context-write
|
|
544
|
+
// guards.
|
|
538
545
|
|
|
539
546
|
const PLANNING_WRITE_TOOLS = new Set(["write", "edit", "multi_edit", "notebook_edit"]);
|
|
540
547
|
const PLANNING_SUBAGENT_TOOLS = new Set(["subagent", "task"]);
|
|
541
548
|
|
|
549
|
+
/**
|
|
550
|
+
* Read-only / planning-safe tools that any non-"all" mode allows. Mirrors
|
|
551
|
+
* QUEUE_SAFE_TOOLS / GATE_SAFE_TOOLS but is the inclusive default for
|
|
552
|
+
* planning units (which need their full discussion + research surface).
|
|
553
|
+
*
|
|
554
|
+
* gsd_* MCP tools are passed through unconditionally — they have their own
|
|
555
|
+
* domain validation (e.g. depth-verification gate, single-writer DB).
|
|
556
|
+
*/
|
|
542
557
|
const PLANNING_SAFE_TOOLS = new Set([
|
|
543
558
|
"read", "grep", "find", "ls", "glob",
|
|
544
559
|
"ask_user_questions",
|
|
@@ -555,6 +570,7 @@ function isPathUnderGsd(absPath: string, basePath: string): boolean {
|
|
|
555
570
|
function matchesAllowedGlob(absPath: string, basePath: string, globs: readonly string[]): boolean {
|
|
556
571
|
const rel = relative(basePath, absPath);
|
|
557
572
|
if (rel.startsWith("..") || isAbsolute(rel)) return false;
|
|
573
|
+
// Normalize Windows separators for minimatch.
|
|
558
574
|
const posix = rel.split(sep).join("/");
|
|
559
575
|
return globs.some(g => minimatch(posix, g, { dot: false, nocase: false }));
|
|
560
576
|
}
|
|
@@ -579,7 +595,12 @@ function blockReason(unitType: string, mode: string, what: string): string {
|
|
|
579
595
|
* - "docs" → like "planning" but also allows writes to paths
|
|
580
596
|
* matching `allowedPathGlobs` relative to basePath.
|
|
581
597
|
*
|
|
582
|
-
* `
|
|
598
|
+
* `pathOrCommand` is the file path for write/edit-shaped tools and the
|
|
599
|
+
* shell command for bash. Other tools ignore this argument.
|
|
600
|
+
*
|
|
601
|
+
* `policy` of null means "no manifest resolved" — pass-through. Callers
|
|
602
|
+
* that have no active unit (interactive sessions) pass null and this
|
|
603
|
+
* predicate is a no-op.
|
|
583
604
|
*/
|
|
584
605
|
export function shouldBlockPlanningUnit(
|
|
585
606
|
toolName: string,
|
|
@@ -593,16 +614,18 @@ export function shouldBlockPlanningUnit(
|
|
|
593
614
|
|
|
594
615
|
const tool = toolName;
|
|
595
616
|
|
|
617
|
+
// Read-only mode: only Read-class tools are permitted.
|
|
596
618
|
if (policy.mode === "read-only") {
|
|
597
619
|
if (PLANNING_SAFE_TOOLS.has(tool)) return { block: false };
|
|
598
620
|
if (tool.startsWith("gsd_")) return { block: false };
|
|
599
621
|
if (PLANNING_WRITE_TOOLS.has(tool) || tool === "bash" || PLANNING_SUBAGENT_TOOLS.has(tool)) {
|
|
600
622
|
return { block: true, reason: blockReason(unitType, policy.mode, `${tool} is not permitted (read-only)`) };
|
|
601
623
|
}
|
|
624
|
+
// Unknown tool in read-only mode — block by default.
|
|
602
625
|
return { block: true, reason: blockReason(unitType, policy.mode, `tool "${tool}" is not on the read-only allowlist`) };
|
|
603
626
|
}
|
|
604
627
|
|
|
605
|
-
// planning / docs modes
|
|
628
|
+
// planning / docs modes share the same surface for safe tools, bash, and subagent.
|
|
606
629
|
if (PLANNING_SAFE_TOOLS.has(tool)) return { block: false };
|
|
607
630
|
if (tool.startsWith("gsd_")) return { block: false };
|
|
608
631
|
|
|
@@ -628,8 +651,10 @@ export function shouldBlockPlanningUnit(
|
|
|
628
651
|
}
|
|
629
652
|
const absPath = isAbsolute(pathOrCommand) ? pathOrCommand : resolve(basePath, pathOrCommand);
|
|
630
653
|
|
|
654
|
+
// Always allow .gsd/ writes — that's where planning artifacts live.
|
|
631
655
|
if (isPathUnderGsd(absPath, basePath)) return { block: false };
|
|
632
656
|
|
|
657
|
+
// docs mode additionally allows the manifest's allowedPathGlobs.
|
|
633
658
|
if (policy.mode === "docs" && matchesAllowedGlob(absPath, basePath, policy.allowedPathGlobs)) {
|
|
634
659
|
return { block: false };
|
|
635
660
|
}
|
|
@@ -644,5 +669,8 @@ export function shouldBlockPlanningUnit(
|
|
|
644
669
|
};
|
|
645
670
|
}
|
|
646
671
|
|
|
672
|
+
// Unknown tool name — pass through. Other layers (queue, pending-gate,
|
|
673
|
+
// CONTEXT.md write) catch known mutating shapes; defaulting to allow here
|
|
674
|
+
// avoids breaking gsd_* MCP tools or future safe additions.
|
|
647
675
|
return { block: false };
|
|
648
676
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { loadRegistry } from "../workflow-templates.js";
|
|
6
|
-
import { resolveProjectRoot } from "../worktree.js";
|
|
7
6
|
|
|
8
7
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
9
8
|
|
|
@@ -15,7 +14,7 @@ export interface GsdCommandDefinition {
|
|
|
15
14
|
type CompletionMap = Record<string, readonly GsdCommandDefinition[]>;
|
|
16
15
|
|
|
17
16
|
export const GSD_COMMAND_DESCRIPTION =
|
|
18
|
-
"GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|model|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|debug|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|onboarding|inspect|extensions|update|fast|mcp|rethink|codebase|notifications|ship|do|session-report|backlog|pr-branch|add-tests|scan|language";
|
|
17
|
+
"GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|model|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|debug|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|onboarding|inspect|extensions|update|fast|mcp|rethink|workflow|codebase|notifications|ship|do|session-report|backlog|pr-branch|add-tests|scan|language";
|
|
19
18
|
|
|
20
19
|
export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
|
|
21
20
|
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
@@ -346,6 +345,77 @@ function getExtensionCompletions(prefix: string, action: string) {
|
|
|
346
345
|
}
|
|
347
346
|
}
|
|
348
347
|
|
|
348
|
+
function normalizePathForCompare(path: string): string {
|
|
349
|
+
return path.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function findWorktreeSegment(normalizedPath: string): { gsdIdx: number; afterWorktrees: number } | null {
|
|
353
|
+
const directMarker = "/.gsd/worktrees/";
|
|
354
|
+
const directIdx = normalizedPath.indexOf(directMarker);
|
|
355
|
+
if (directIdx !== -1) {
|
|
356
|
+
return { gsdIdx: directIdx, afterWorktrees: directIdx + directMarker.length };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const symlinkMatch = normalizedPath.match(/\/\.gsd\/projects\/[a-f0-9]+\/worktrees\//);
|
|
360
|
+
if (symlinkMatch?.index !== undefined) {
|
|
361
|
+
return { gsdIdx: symlinkMatch.index, afterWorktrees: symlinkMatch.index + symlinkMatch[0].length };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function resolveProjectRootFromGitFile(worktreePath: string): string | null {
|
|
368
|
+
try {
|
|
369
|
+
let dir = worktreePath;
|
|
370
|
+
for (let i = 0; i < 30; i++) {
|
|
371
|
+
const gitPath = join(dir, ".git");
|
|
372
|
+
if (existsSync(gitPath)) {
|
|
373
|
+
const content = readFileSync(gitPath, "utf8").trim();
|
|
374
|
+
if (content.startsWith("gitdir: ")) {
|
|
375
|
+
const gitDir = resolve(dir, content.slice(8));
|
|
376
|
+
const dotGitDir = resolve(gitDir, "..", "..");
|
|
377
|
+
if (dotGitDir.endsWith(".git") || dotGitDir.endsWith(".git/") || dotGitDir.endsWith(".git\\")) {
|
|
378
|
+
return resolve(dotGitDir, "..");
|
|
379
|
+
}
|
|
380
|
+
const commonDirPath = join(gitDir, "commondir");
|
|
381
|
+
if (existsSync(commonDirPath)) {
|
|
382
|
+
const commonDir = readFileSync(commonDirPath, "utf8").trim();
|
|
383
|
+
return resolve(resolve(gitDir, commonDir), "..");
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
const parent = resolve(dir, "..");
|
|
389
|
+
if (parent === dir) break;
|
|
390
|
+
dir = parent;
|
|
391
|
+
}
|
|
392
|
+
} catch {
|
|
393
|
+
// Completion must stay best-effort.
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function resolveProjectRootForCompletion(basePath: string): string {
|
|
399
|
+
if (process.env.GSD_PROJECT_ROOT) return process.env.GSD_PROJECT_ROOT;
|
|
400
|
+
|
|
401
|
+
const normalizedPath = normalizePathForCompare(basePath);
|
|
402
|
+
const segment = findWorktreeSegment(normalizedPath);
|
|
403
|
+
if (!segment) return basePath;
|
|
404
|
+
|
|
405
|
+
const separator = basePath.includes("\\") ? "\\" : "/";
|
|
406
|
+
const gsdMarker = `${separator}.gsd${separator}`;
|
|
407
|
+
const gsdIdx = basePath.indexOf(gsdMarker);
|
|
408
|
+
const candidate = gsdIdx !== -1 ? basePath.slice(0, gsdIdx) : basePath.slice(0, segment.gsdIdx);
|
|
409
|
+
|
|
410
|
+
const normalizedGsdHome = normalizePathForCompare(gsdHome);
|
|
411
|
+
const candidateGsdPath = normalizePathForCompare(join(candidate, ".gsd"));
|
|
412
|
+
if (candidateGsdPath === normalizedGsdHome || candidateGsdPath.startsWith(`${normalizedGsdHome}/`)) {
|
|
413
|
+
return resolveProjectRootFromGitFile(basePath) ?? basePath;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return candidate;
|
|
417
|
+
}
|
|
418
|
+
|
|
349
419
|
export function getGsdArgumentCompletions(prefix: string) {
|
|
350
420
|
const hasTrailingSpace = prefix.endsWith(" ");
|
|
351
421
|
const parts = prefix.trim().split(/\s+/);
|
|
@@ -406,7 +476,7 @@ export function getGsdArgumentCompletions(prefix: string) {
|
|
|
406
476
|
// Workflow definition-name completion for `workflow run <name>` and `workflow validate <name>`
|
|
407
477
|
if (command === "workflow" && (subcommand === "run" || subcommand === "validate") && parts.length <= 3) {
|
|
408
478
|
try {
|
|
409
|
-
const defsDir = join(
|
|
479
|
+
const defsDir = join(resolveProjectRootForCompletion(process.cwd()), ".gsd", "workflow-defs");
|
|
410
480
|
if (existsSync(defsDir)) {
|
|
411
481
|
return readdirSync(defsDir)
|
|
412
482
|
.filter((f) => f.endsWith(".yaml") && f.startsWith(third))
|
|
@@ -443,7 +513,7 @@ export function getGsdArgumentCompletions(prefix: string) {
|
|
|
443
513
|
} catch { /* ignore */ }
|
|
444
514
|
};
|
|
445
515
|
try {
|
|
446
|
-
const base =
|
|
516
|
+
const base = resolveProjectRootForCompletion(process.cwd());
|
|
447
517
|
scanDir(join(base, ".gsd", "workflows"), "project");
|
|
448
518
|
scanDir(join(base, ".gsd", "workflow-defs"), "project-legacy");
|
|
449
519
|
scanDir(join(gsdHome, "workflows"), "global");
|