gsd-pi 2.38.0-dev.eeb3520 → 2.39.0-dev.20aba06
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 +15 -11
- package/dist/app-paths.js +1 -1
- package/dist/cli.js +9 -0
- package/dist/extension-discovery.d.ts +5 -3
- package/dist/extension-discovery.js +14 -9
- package/dist/extension-registry.js +2 -2
- package/dist/remote-questions-config.js +2 -2
- package/dist/resource-loader.js +100 -3
- package/dist/resources/extensions/async-jobs/index.js +10 -0
- package/dist/resources/extensions/browser-tools/index.js +3 -1
- package/dist/resources/extensions/browser-tools/package.json +3 -1
- package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
- package/dist/resources/extensions/cmux/index.js +55 -1
- package/dist/resources/extensions/context7/package.json +1 -1
- package/dist/resources/extensions/get-secrets-from-user.js +5 -24
- package/dist/resources/extensions/github-sync/cli.js +284 -0
- package/dist/resources/extensions/github-sync/index.js +73 -0
- package/dist/resources/extensions/github-sync/mapping.js +67 -0
- package/dist/resources/extensions/github-sync/sync.js +424 -0
- package/dist/resources/extensions/github-sync/templates.js +118 -0
- package/dist/resources/extensions/github-sync/types.js +7 -0
- package/dist/resources/extensions/google-search/package.json +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +6 -23
- package/dist/resources/extensions/gsd/auto-dashboard.js +7 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
- package/dist/resources/extensions/gsd/auto-loop.js +923 -787
- package/dist/resources/extensions/gsd/auto-post-unit.js +107 -70
- package/dist/resources/extensions/gsd/auto-prompts.js +205 -51
- package/dist/resources/extensions/gsd/auto-start.js +19 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
- package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
- package/dist/resources/extensions/gsd/auto.js +149 -100
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +126 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +233 -0
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +59 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +38 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +156 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +46 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +300 -0
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -0
- package/dist/resources/extensions/gsd/captures.js +9 -1
- package/dist/resources/extensions/gsd/commands/catalog.js +278 -0
- package/dist/resources/extensions/gsd/commands/context.js +84 -0
- package/dist/resources/extensions/gsd/commands/dispatcher.js +21 -0
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +72 -0
- package/dist/resources/extensions/gsd/commands/handlers/core.js +246 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +166 -0
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +94 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +102 -0
- package/dist/resources/extensions/gsd/commands/index.js +11 -0
- package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +17 -4
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +8 -1169
- package/dist/resources/extensions/gsd/context-budget.js +2 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +9 -0
- package/dist/resources/extensions/gsd/detection.js +1 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
- package/dist/resources/extensions/gsd/doctor-format.js +15 -0
- package/dist/resources/extensions/gsd/doctor-proactive.js +80 -10
- package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
- package/dist/resources/extensions/gsd/doctor.js +234 -12
- package/dist/resources/extensions/gsd/env-utils.js +29 -0
- package/dist/resources/extensions/gsd/exit-command.js +2 -1
- package/dist/resources/extensions/gsd/export-html.js +46 -0
- package/dist/resources/extensions/gsd/export.js +1 -1
- package/dist/resources/extensions/gsd/files.js +48 -9
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-service.js +30 -12
- package/dist/resources/extensions/gsd/gitignore.js +16 -3
- package/dist/resources/extensions/gsd/guided-flow.js +149 -38
- package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
- package/dist/resources/extensions/gsd/health-widget.js +4 -87
- package/dist/resources/extensions/gsd/index.js +4 -1111
- package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
- package/dist/resources/extensions/gsd/migrate-external.js +18 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
- package/dist/resources/extensions/gsd/package.json +1 -1
- package/dist/resources/extensions/gsd/paths.js +3 -0
- package/dist/resources/extensions/gsd/preferences-models.js +0 -12
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
- package/dist/resources/extensions/gsd/preferences.js +22 -11
- package/dist/resources/extensions/gsd/progress-score.js +20 -1
- package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
- package/dist/resources/extensions/gsd/prompts/forensics.md +121 -46
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/dist/resources/extensions/gsd/repo-identity.js +21 -4
- package/dist/resources/extensions/gsd/resource-version.js +2 -1
- package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
- package/dist/resources/extensions/gsd/state.js +42 -23
- package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/dist/resources/extensions/gsd/visualizer-data.js +27 -2
- package/dist/resources/extensions/gsd/visualizer-views.js +52 -0
- package/dist/resources/extensions/gsd/worktree.js +35 -16
- package/dist/resources/extensions/mcp-client/index.js +14 -1
- package/dist/resources/extensions/remote-questions/status.js +4 -1
- package/dist/resources/extensions/remote-questions/store.js +4 -1
- package/dist/resources/extensions/search-the-web/provider.js +2 -1
- package/dist/resources/extensions/shared/frontmatter.js +1 -1
- package/dist/resources/extensions/subagent/index.js +12 -3
- package/dist/resources/extensions/subagent/isolation.js +2 -1
- package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
- package/dist/resources/extensions/universal-config/package.json +1 -1
- package/dist/welcome-screen.d.ts +13 -0
- package/dist/welcome-screen.js +97 -0
- package/package.json +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +107 -24
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +70 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +8 -2
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +244 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +58 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +54 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +63 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +38 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -457
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +122 -23
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
- package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/skills.ts +11 -2
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +302 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +59 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +68 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +71 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +37 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +18 -510
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/index.ts +11 -0
- package/src/resources/extensions/browser-tools/index.ts +3 -0
- package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
- package/src/resources/extensions/cmux/index.ts +57 -1
- package/src/resources/extensions/get-secrets-from-user.ts +5 -24
- package/src/resources/extensions/github-sync/cli.ts +364 -0
- package/src/resources/extensions/github-sync/index.ts +93 -0
- package/src/resources/extensions/github-sync/mapping.ts +81 -0
- package/src/resources/extensions/github-sync/sync.ts +556 -0
- package/src/resources/extensions/github-sync/templates.ts +183 -0
- package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
- package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
- package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
- package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
- package/src/resources/extensions/github-sync/types.ts +47 -0
- package/src/resources/extensions/gsd/auto/session.ts +7 -25
- package/src/resources/extensions/gsd/auto-dashboard.ts +10 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
- package/src/resources/extensions/gsd/auto-loop.ts +1285 -1138
- package/src/resources/extensions/gsd/auto-post-unit.ts +90 -46
- package/src/resources/extensions/gsd/auto-prompts.ts +250 -53
- package/src/resources/extensions/gsd/auto-start.ts +24 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
- package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
- package/src/resources/extensions/gsd/auto.ts +152 -111
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +142 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +238 -0
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +90 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +46 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +167 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +55 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +340 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +51 -0
- package/src/resources/extensions/gsd/captures.ts +10 -1
- package/src/resources/extensions/gsd/commands/catalog.ts +301 -0
- package/src/resources/extensions/gsd/commands/context.ts +101 -0
- package/src/resources/extensions/gsd/commands/dispatcher.ts +32 -0
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +74 -0
- package/src/resources/extensions/gsd/commands/handlers/core.ts +274 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +169 -0
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +118 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +109 -0
- package/src/resources/extensions/gsd/commands/index.ts +14 -0
- package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +18 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +10 -1307
- package/src/resources/extensions/gsd/context-budget.ts +2 -12
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/src/resources/extensions/gsd/detection.ts +2 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
- package/src/resources/extensions/gsd/doctor-format.ts +20 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +106 -10
- package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
- package/src/resources/extensions/gsd/doctor-types.ts +16 -1
- package/src/resources/extensions/gsd/doctor.ts +243 -14
- package/src/resources/extensions/gsd/env-utils.ts +31 -0
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/export-html.ts +51 -0
- package/src/resources/extensions/gsd/export.ts +1 -1
- package/src/resources/extensions/gsd/files.ts +51 -11
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-service.ts +44 -10
- package/src/resources/extensions/gsd/gitignore.ts +17 -3
- package/src/resources/extensions/gsd/guided-flow.ts +177 -44
- package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
- package/src/resources/extensions/gsd/health-widget.ts +4 -89
- package/src/resources/extensions/gsd/index.ts +12 -1307
- package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
- package/src/resources/extensions/gsd/migrate-external.ts +18 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
- package/src/resources/extensions/gsd/paths.ts +4 -0
- package/src/resources/extensions/gsd/preferences-models.ts +0 -12
- package/src/resources/extensions/gsd/preferences-types.ts +4 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
- package/src/resources/extensions/gsd/preferences.ts +25 -11
- package/src/resources/extensions/gsd/progress-score.ts +23 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
- package/src/resources/extensions/gsd/prompts/forensics.md +121 -46
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -8
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/src/resources/extensions/gsd/repo-identity.ts +23 -4
- package/src/resources/extensions/gsd/resource-version.ts +3 -1
- package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
- package/src/resources/extensions/gsd/state.ts +39 -21
- package/src/resources/extensions/gsd/templates/runtime.md +21 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +135 -77
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
- package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -16
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +10 -10
- package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
- package/src/resources/extensions/gsd/types.ts +18 -1
- package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +52 -2
- package/src/resources/extensions/gsd/visualizer-views.ts +58 -0
- package/src/resources/extensions/gsd/worktree.ts +35 -15
- package/src/resources/extensions/mcp-client/index.ts +17 -1
- package/src/resources/extensions/remote-questions/status.ts +5 -1
- package/src/resources/extensions/remote-questions/store.ts +5 -1
- package/src/resources/extensions/search-the-web/provider.ts +2 -1
- package/src/resources/extensions/shared/frontmatter.ts +1 -1
- package/src/resources/extensions/subagent/index.ts +12 -3
- package/src/resources/extensions/subagent/isolation.ts +3 -1
- package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
- package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
- package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
- package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
- package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
- package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
- package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
- package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
- package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
- package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
- package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
- package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
3
|
+
|
|
4
|
+
import { findMilestoneIds, nextMilestoneId } from "../guided-flow.js";
|
|
5
|
+
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
6
|
+
import { ensureDbOpen } from "./dynamic-tools.js";
|
|
7
|
+
|
|
8
|
+
export function registerDbTools(pi: ExtensionAPI): void {
|
|
9
|
+
pi.registerTool({
|
|
10
|
+
name: "gsd_save_decision",
|
|
11
|
+
label: "Save Decision",
|
|
12
|
+
description:
|
|
13
|
+
"Record a project decision to the GSD database and regenerate DECISIONS.md. " +
|
|
14
|
+
"Decision IDs are auto-assigned — never provide an ID manually.",
|
|
15
|
+
promptSnippet: "Record a project decision to the GSD database (auto-assigns ID, regenerates DECISIONS.md)",
|
|
16
|
+
promptGuidelines: [
|
|
17
|
+
"Use gsd_save_decision when recording an architectural, pattern, library, or observability decision.",
|
|
18
|
+
"Decision IDs are auto-assigned (D001, D002, ...) — never guess or provide an ID.",
|
|
19
|
+
"All fields except revisable and when_context are required.",
|
|
20
|
+
"The tool writes to the DB and regenerates .gsd/DECISIONS.md automatically.",
|
|
21
|
+
],
|
|
22
|
+
parameters: Type.Object({
|
|
23
|
+
scope: Type.String({ description: "Scope of the decision (e.g. 'architecture', 'library', 'observability')" }),
|
|
24
|
+
decision: Type.String({ description: "What is being decided" }),
|
|
25
|
+
choice: Type.String({ description: "The choice made" }),
|
|
26
|
+
rationale: Type.String({ description: "Why this choice was made" }),
|
|
27
|
+
revisable: Type.Optional(Type.String({ description: "Whether this can be revisited (default: 'Yes')" })),
|
|
28
|
+
when_context: Type.Optional(Type.String({ description: "When/context for the decision (e.g. milestone ID)" })),
|
|
29
|
+
}),
|
|
30
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
31
|
+
const dbAvailable = await ensureDbOpen();
|
|
32
|
+
if (!dbAvailable) {
|
|
33
|
+
return {
|
|
34
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot save decision." }],
|
|
35
|
+
details: { operation: "save_decision", error: "db_unavailable" } as any,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const { saveDecisionToDb } = await import("../db-writer.js");
|
|
40
|
+
const { id } = await saveDecisionToDb(
|
|
41
|
+
{
|
|
42
|
+
scope: params.scope,
|
|
43
|
+
decision: params.decision,
|
|
44
|
+
choice: params.choice,
|
|
45
|
+
rationale: params.rationale,
|
|
46
|
+
revisable: params.revisable,
|
|
47
|
+
when_context: params.when_context,
|
|
48
|
+
},
|
|
49
|
+
process.cwd(),
|
|
50
|
+
);
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text" as const, text: `Saved decision ${id}` }],
|
|
53
|
+
details: { operation: "save_decision", id } as any,
|
|
54
|
+
};
|
|
55
|
+
} catch (err) {
|
|
56
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
57
|
+
process.stderr.write(`gsd-db: gsd_save_decision tool failed: ${msg}\n`);
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text" as const, text: `Error saving decision: ${msg}` }],
|
|
60
|
+
details: { operation: "save_decision", error: msg } as any,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
pi.registerTool({
|
|
67
|
+
name: "gsd_update_requirement",
|
|
68
|
+
label: "Update Requirement",
|
|
69
|
+
description:
|
|
70
|
+
"Update an existing requirement in the GSD database and regenerate REQUIREMENTS.md. " +
|
|
71
|
+
"Provide the requirement ID (e.g. R001) and any fields to update.",
|
|
72
|
+
promptSnippet: "Update an existing GSD requirement by ID (regenerates REQUIREMENTS.md)",
|
|
73
|
+
promptGuidelines: [
|
|
74
|
+
"Use gsd_update_requirement to change status, validation, notes, or other fields on an existing requirement.",
|
|
75
|
+
"The id parameter is required — it must be an existing RXXX identifier.",
|
|
76
|
+
"All other fields are optional — only provided fields are updated.",
|
|
77
|
+
"The tool verifies the requirement exists before updating.",
|
|
78
|
+
],
|
|
79
|
+
parameters: Type.Object({
|
|
80
|
+
id: Type.String({ description: "The requirement ID (e.g. R001, R014)" }),
|
|
81
|
+
status: Type.Optional(Type.String({ description: "New status (e.g. 'active', 'validated', 'deferred')" })),
|
|
82
|
+
validation: Type.Optional(Type.String({ description: "Validation criteria or proof" })),
|
|
83
|
+
notes: Type.Optional(Type.String({ description: "Additional notes" })),
|
|
84
|
+
description: Type.Optional(Type.String({ description: "Updated description" })),
|
|
85
|
+
primary_owner: Type.Optional(Type.String({ description: "Primary owning slice" })),
|
|
86
|
+
supporting_slices: Type.Optional(Type.String({ description: "Supporting slices" })),
|
|
87
|
+
}),
|
|
88
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
89
|
+
const dbAvailable = await ensureDbOpen();
|
|
90
|
+
if (!dbAvailable) {
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot update requirement." }],
|
|
93
|
+
details: { operation: "update_requirement", id: params.id, error: "db_unavailable" } as any,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const db = await import("../gsd-db.js");
|
|
98
|
+
const existing = db.getRequirementById(params.id);
|
|
99
|
+
if (!existing) {
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: "text" as const, text: `Error: Requirement ${params.id} not found.` }],
|
|
102
|
+
details: { operation: "update_requirement", id: params.id, error: "not_found" } as any,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const { updateRequirementInDb } = await import("../db-writer.js");
|
|
106
|
+
const updates: Record<string, string | undefined> = {};
|
|
107
|
+
if (params.status !== undefined) updates.status = params.status;
|
|
108
|
+
if (params.validation !== undefined) updates.validation = params.validation;
|
|
109
|
+
if (params.notes !== undefined) updates.notes = params.notes;
|
|
110
|
+
if (params.description !== undefined) updates.description = params.description;
|
|
111
|
+
if (params.primary_owner !== undefined) updates.primary_owner = params.primary_owner;
|
|
112
|
+
if (params.supporting_slices !== undefined) updates.supporting_slices = params.supporting_slices;
|
|
113
|
+
await updateRequirementInDb(params.id, updates, process.cwd());
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text" as const, text: `Updated requirement ${params.id}` }],
|
|
116
|
+
details: { operation: "update_requirement", id: params.id } as any,
|
|
117
|
+
};
|
|
118
|
+
} catch (err) {
|
|
119
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
120
|
+
process.stderr.write(`gsd-db: gsd_update_requirement tool failed: ${msg}\n`);
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text" as const, text: `Error updating requirement: ${msg}` }],
|
|
123
|
+
details: { operation: "update_requirement", id: params.id, error: msg } as any,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
pi.registerTool({
|
|
130
|
+
name: "gsd_save_summary",
|
|
131
|
+
label: "Save Summary",
|
|
132
|
+
description:
|
|
133
|
+
"Save a summary, research, context, or assessment artifact to the GSD database and write it to disk. " +
|
|
134
|
+
"Computes the file path from milestone/slice/task IDs automatically.",
|
|
135
|
+
promptSnippet: "Save a GSD artifact (summary/research/context/assessment) to DB and disk",
|
|
136
|
+
promptGuidelines: [
|
|
137
|
+
"Use gsd_save_summary to persist structured artifacts (SUMMARY, RESEARCH, CONTEXT, ASSESSMENT).",
|
|
138
|
+
"milestone_id is required. slice_id and task_id are optional — they determine the file path.",
|
|
139
|
+
"The tool computes the relative path automatically: milestones/M001/M001-SUMMARY.md, milestones/M001/slices/S01/S01-SUMMARY.md, etc.",
|
|
140
|
+
"artifact_type must be one of: SUMMARY, RESEARCH, CONTEXT, ASSESSMENT.",
|
|
141
|
+
],
|
|
142
|
+
parameters: Type.Object({
|
|
143
|
+
milestone_id: Type.String({ description: "Milestone ID (e.g. M001)" }),
|
|
144
|
+
slice_id: Type.Optional(Type.String({ description: "Slice ID (e.g. S01)" })),
|
|
145
|
+
task_id: Type.Optional(Type.String({ description: "Task ID (e.g. T01)" })),
|
|
146
|
+
artifact_type: Type.String({ description: "One of: SUMMARY, RESEARCH, CONTEXT, ASSESSMENT" }),
|
|
147
|
+
content: Type.String({ description: "The full markdown content of the artifact" }),
|
|
148
|
+
}),
|
|
149
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
150
|
+
const dbAvailable = await ensureDbOpen();
|
|
151
|
+
if (!dbAvailable) {
|
|
152
|
+
return {
|
|
153
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot save artifact." }],
|
|
154
|
+
details: { operation: "save_summary", error: "db_unavailable" } as any,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const validTypes = ["SUMMARY", "RESEARCH", "CONTEXT", "ASSESSMENT"];
|
|
158
|
+
if (!validTypes.includes(params.artifact_type)) {
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: "text" as const, text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${validTypes.join(", ")}` }],
|
|
161
|
+
details: { operation: "save_summary", error: "invalid_artifact_type" } as any,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
let relativePath: string;
|
|
166
|
+
if (params.task_id && params.slice_id) {
|
|
167
|
+
relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/tasks/${params.task_id}-${params.artifact_type}.md`;
|
|
168
|
+
} else if (params.slice_id) {
|
|
169
|
+
relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/${params.slice_id}-${params.artifact_type}.md`;
|
|
170
|
+
} else {
|
|
171
|
+
relativePath = `milestones/${params.milestone_id}/${params.milestone_id}-${params.artifact_type}.md`;
|
|
172
|
+
}
|
|
173
|
+
const { saveArtifactToDb } = await import("../db-writer.js");
|
|
174
|
+
await saveArtifactToDb(
|
|
175
|
+
{
|
|
176
|
+
path: relativePath,
|
|
177
|
+
artifact_type: params.artifact_type,
|
|
178
|
+
content: params.content,
|
|
179
|
+
milestone_id: params.milestone_id,
|
|
180
|
+
slice_id: params.slice_id,
|
|
181
|
+
task_id: params.task_id,
|
|
182
|
+
},
|
|
183
|
+
process.cwd(),
|
|
184
|
+
);
|
|
185
|
+
return {
|
|
186
|
+
content: [{ type: "text" as const, text: `Saved ${params.artifact_type} artifact to ${relativePath}` }],
|
|
187
|
+
details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type } as any,
|
|
188
|
+
};
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
191
|
+
process.stderr.write(`gsd-db: gsd_save_summary tool failed: ${msg}\n`);
|
|
192
|
+
return {
|
|
193
|
+
content: [{ type: "text" as const, text: `Error saving artifact: ${msg}` }],
|
|
194
|
+
details: { operation: "save_summary", error: msg } as any,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const reservedMilestoneIds = new Set<string>();
|
|
201
|
+
pi.registerTool({
|
|
202
|
+
name: "gsd_generate_milestone_id",
|
|
203
|
+
label: "Generate Milestone ID",
|
|
204
|
+
description:
|
|
205
|
+
"Generate the next milestone ID for a new GSD milestone. " +
|
|
206
|
+
"Scans existing milestones on disk and respects the unique_milestone_ids preference. " +
|
|
207
|
+
"Always use this tool when creating a new milestone — never invent milestone IDs manually.",
|
|
208
|
+
promptSnippet: "Generate a valid milestone ID (respects unique_milestone_ids preference)",
|
|
209
|
+
promptGuidelines: [
|
|
210
|
+
"ALWAYS call gsd_generate_milestone_id before creating a new milestone directory or writing milestone files.",
|
|
211
|
+
"Never invent or hardcode milestone IDs like M001, M002 — always use this tool.",
|
|
212
|
+
"Call it once per milestone you need to create. For multi-milestone projects, call it once for each milestone in sequence.",
|
|
213
|
+
"The tool returns the correct format based on project preferences (e.g. M001 or M001-r5jzab).",
|
|
214
|
+
],
|
|
215
|
+
parameters: Type.Object({}),
|
|
216
|
+
async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
|
|
217
|
+
try {
|
|
218
|
+
const basePath = process.cwd();
|
|
219
|
+
const existingIds = findMilestoneIds(basePath);
|
|
220
|
+
const uniqueEnabled = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
221
|
+
const allIds = [...new Set([...existingIds, ...reservedMilestoneIds])];
|
|
222
|
+
const newId = nextMilestoneId(allIds, uniqueEnabled);
|
|
223
|
+
reservedMilestoneIds.add(newId);
|
|
224
|
+
return {
|
|
225
|
+
content: [{ type: "text" as const, text: newId }],
|
|
226
|
+
details: { operation: "generate_milestone_id", id: newId, existingCount: existingIds.length, reservedCount: reservedMilestoneIds.size, uniqueEnabled } as any,
|
|
227
|
+
};
|
|
228
|
+
} catch (err) {
|
|
229
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
230
|
+
return {
|
|
231
|
+
content: [{ type: "text" as const, text: `Error generating milestone ID: ${msg}` }],
|
|
232
|
+
details: { operation: "generate_milestone_id", error: msg } as any,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
5
|
+
import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
|
|
6
|
+
|
|
7
|
+
import { DEFAULT_BASH_TIMEOUT_SECS } from "../constants.js";
|
|
8
|
+
|
|
9
|
+
export async function ensureDbOpen(): Promise<boolean> {
|
|
10
|
+
try {
|
|
11
|
+
const db = await import("../gsd-db.js");
|
|
12
|
+
if (db.isDbAvailable()) return true;
|
|
13
|
+
const dbPath = join(process.cwd(), ".gsd", "gsd.db");
|
|
14
|
+
if (existsSync(dbPath)) {
|
|
15
|
+
return db.openDatabase(dbPath);
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function registerDynamicTools(pi: ExtensionAPI): void {
|
|
24
|
+
const baseBash = createBashTool(process.cwd(), {
|
|
25
|
+
spawnHook: (ctx) => ({ ...ctx, cwd: process.cwd() }),
|
|
26
|
+
});
|
|
27
|
+
const dynamicBash = {
|
|
28
|
+
...baseBash,
|
|
29
|
+
execute: async (
|
|
30
|
+
toolCallId: string,
|
|
31
|
+
params: { command: string; timeout?: number },
|
|
32
|
+
signal?: AbortSignal,
|
|
33
|
+
onUpdate?: unknown,
|
|
34
|
+
ctx?: unknown,
|
|
35
|
+
) => {
|
|
36
|
+
const paramsWithTimeout = {
|
|
37
|
+
...params,
|
|
38
|
+
timeout: params.timeout ?? DEFAULT_BASH_TIMEOUT_SECS,
|
|
39
|
+
};
|
|
40
|
+
return (baseBash as any).execute(toolCallId, paramsWithTimeout, signal, onUpdate, ctx);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
pi.registerTool(dynamicBash as any);
|
|
44
|
+
|
|
45
|
+
const baseWrite = createWriteTool(process.cwd());
|
|
46
|
+
pi.registerTool({
|
|
47
|
+
...baseWrite,
|
|
48
|
+
execute: async (
|
|
49
|
+
toolCallId: string,
|
|
50
|
+
params: { path: string; content: string },
|
|
51
|
+
signal?: AbortSignal,
|
|
52
|
+
onUpdate?: unknown,
|
|
53
|
+
ctx?: unknown,
|
|
54
|
+
) => {
|
|
55
|
+
const fresh = createWriteTool(process.cwd());
|
|
56
|
+
return (fresh as any).execute(toolCallId, params, signal, onUpdate, ctx);
|
|
57
|
+
},
|
|
58
|
+
} as any);
|
|
59
|
+
|
|
60
|
+
const baseRead = createReadTool(process.cwd());
|
|
61
|
+
pi.registerTool({
|
|
62
|
+
...baseRead,
|
|
63
|
+
execute: async (
|
|
64
|
+
toolCallId: string,
|
|
65
|
+
params: { path: string; offset?: number; limit?: number },
|
|
66
|
+
signal?: AbortSignal,
|
|
67
|
+
onUpdate?: unknown,
|
|
68
|
+
ctx?: unknown,
|
|
69
|
+
) => {
|
|
70
|
+
const fresh = createReadTool(process.cwd());
|
|
71
|
+
return (fresh as any).execute(toolCallId, params, signal, onUpdate, ctx);
|
|
72
|
+
},
|
|
73
|
+
} as any);
|
|
74
|
+
|
|
75
|
+
const baseEdit = createEditTool(process.cwd());
|
|
76
|
+
pi.registerTool({
|
|
77
|
+
...baseEdit,
|
|
78
|
+
execute: async (
|
|
79
|
+
toolCallId: string,
|
|
80
|
+
params: { path: string; oldText: string; newText: string },
|
|
81
|
+
signal?: AbortSignal,
|
|
82
|
+
onUpdate?: unknown,
|
|
83
|
+
ctx?: unknown,
|
|
84
|
+
) => {
|
|
85
|
+
const fresh = createEditTool(process.cwd());
|
|
86
|
+
return (fresh as any).execute(toolCallId, params, signal, onUpdate, ctx);
|
|
87
|
+
},
|
|
88
|
+
} as any);
|
|
89
|
+
}
|
|
90
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
import { registerGSDCommand } from "../commands.js";
|
|
4
|
+
import { registerExitCommand } from "../exit-command.js";
|
|
5
|
+
import { registerWorktreeCommand } from "../worktree-command.js";
|
|
6
|
+
import { registerDbTools } from "./db-tools.js";
|
|
7
|
+
import { registerDynamicTools } from "./dynamic-tools.js";
|
|
8
|
+
import { registerHooks } from "./register-hooks.js";
|
|
9
|
+
import { registerShortcuts } from "./register-shortcuts.js";
|
|
10
|
+
|
|
11
|
+
function installEpipeGuard(): void {
|
|
12
|
+
if (!process.listeners("uncaughtException").some((listener) => listener.name === "_gsdEpipeGuard")) {
|
|
13
|
+
const _gsdEpipeGuard = (err: Error): void => {
|
|
14
|
+
if ((err as NodeJS.ErrnoException).code === "EPIPE") {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT" && (err as any).syscall?.startsWith("spawn")) {
|
|
18
|
+
process.stderr.write(`[gsd] spawn ENOENT: ${(err as any).path ?? "unknown"} — command not found\n`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
throw err;
|
|
22
|
+
};
|
|
23
|
+
process.on("uncaughtException", _gsdEpipeGuard);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function registerGsdExtension(pi: ExtensionAPI): void {
|
|
28
|
+
registerGSDCommand(pi);
|
|
29
|
+
registerWorktreeCommand(pi);
|
|
30
|
+
registerExitCommand(pi);
|
|
31
|
+
|
|
32
|
+
installEpipeGuard();
|
|
33
|
+
|
|
34
|
+
pi.registerCommand("kill", {
|
|
35
|
+
description: "Exit GSD immediately (no cleanup)",
|
|
36
|
+
handler: async (_args: string, _ctx: ExtensionCommandContext) => {
|
|
37
|
+
process.exit(0);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
registerDynamicTools(pi);
|
|
42
|
+
registerDbTools(pi);
|
|
43
|
+
registerShortcuts(pi);
|
|
44
|
+
registerHooks(pi);
|
|
45
|
+
}
|
|
46
|
+
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
4
|
+
import { isToolCallEventType } from "@gsd/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
|
|
7
|
+
import { buildBeforeAgentStartResult } from "./system-context.js";
|
|
8
|
+
import { handleAgentEnd } from "./agent-end-recovery.js";
|
|
9
|
+
import { clearDiscussionFlowState, isDepthVerified, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite } from "./write-gate.js";
|
|
10
|
+
import { getDiscussionMilestoneId } from "../guided-flow.js";
|
|
11
|
+
import { loadToolApiKeys } from "../commands-config.js";
|
|
12
|
+
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
13
|
+
import { deriveState } from "../state.js";
|
|
14
|
+
import { getAutoDashboardData, isAutoActive, isAutoPaused, markToolEnd, markToolStart } from "../auto.js";
|
|
15
|
+
import { isParallelActive, shutdownParallel } from "../parallel-orchestrator.js";
|
|
16
|
+
import { saveActivityLog } from "../activity-log.js";
|
|
17
|
+
import { maybeRenderGsdHeader } from "./register-shortcuts.js";
|
|
18
|
+
|
|
19
|
+
export function registerHooks(pi: ExtensionAPI): void {
|
|
20
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
21
|
+
resetWriteGateState();
|
|
22
|
+
maybeRenderGsdHeader(ctx);
|
|
23
|
+
loadToolApiKeys();
|
|
24
|
+
try {
|
|
25
|
+
const [{ getRemoteConfigStatus }, { getLatestPromptSummary }] = await Promise.all([
|
|
26
|
+
import("../../remote-questions/config.js"),
|
|
27
|
+
import("../../remote-questions/status.js"),
|
|
28
|
+
]);
|
|
29
|
+
const status = getRemoteConfigStatus();
|
|
30
|
+
const latest = getLatestPromptSummary();
|
|
31
|
+
if (!status.includes("not configured")) {
|
|
32
|
+
const suffix = latest ? `\nLast remote prompt: ${latest.id} (${latest.status})` : "";
|
|
33
|
+
ctx.ui.notify(`${status}${suffix}`, status.includes("disabled") ? "warning" : "info");
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// ignore
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
pi.on("before_agent_start", async (event, ctx: ExtensionContext) => {
|
|
41
|
+
return buildBeforeAgentStartResult(event, ctx);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
pi.on("agent_end", async (event, ctx: ExtensionContext) => {
|
|
45
|
+
await handleAgentEnd(pi, event, ctx);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
pi.on("session_before_compact", async () => {
|
|
49
|
+
if (isAutoActive() || isAutoPaused()) {
|
|
50
|
+
return { cancel: true };
|
|
51
|
+
}
|
|
52
|
+
const basePath = process.cwd();
|
|
53
|
+
const state = await deriveState(basePath);
|
|
54
|
+
if (!state.activeMilestone || !state.activeSlice || !state.activeTask) return;
|
|
55
|
+
if (state.phase !== "executing") return;
|
|
56
|
+
|
|
57
|
+
const sliceDir = resolveSlicePath(basePath, state.activeMilestone.id, state.activeSlice.id);
|
|
58
|
+
if (!sliceDir) return;
|
|
59
|
+
|
|
60
|
+
const existingFile = resolveSliceFile(basePath, state.activeMilestone.id, state.activeSlice.id, "CONTINUE");
|
|
61
|
+
if (existingFile && await loadFile(existingFile)) return;
|
|
62
|
+
const legacyContinue = join(sliceDir, "continue.md");
|
|
63
|
+
if (await loadFile(legacyContinue)) return;
|
|
64
|
+
|
|
65
|
+
const continuePath = join(sliceDir, `${state.activeSlice.id}-CONTINUE.md`);
|
|
66
|
+
await saveFile(continuePath, formatContinue({
|
|
67
|
+
frontmatter: {
|
|
68
|
+
milestone: state.activeMilestone.id,
|
|
69
|
+
slice: state.activeSlice.id,
|
|
70
|
+
task: state.activeTask.id,
|
|
71
|
+
step: 0,
|
|
72
|
+
totalSteps: 0,
|
|
73
|
+
status: "compacted" as const,
|
|
74
|
+
savedAt: new Date().toISOString(),
|
|
75
|
+
},
|
|
76
|
+
completedWork: `Task ${state.activeTask.id} (${state.activeTask.title}) was in progress when compaction occurred.`,
|
|
77
|
+
remainingWork: "Check the task plan for remaining steps.",
|
|
78
|
+
decisions: "Check task summary files for prior decisions.",
|
|
79
|
+
context: "Session was auto-compacted by Pi. Resume with /gsd.",
|
|
80
|
+
nextAction: `Resume task ${state.activeTask.id}: ${state.activeTask.title}.`,
|
|
81
|
+
}));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
pi.on("session_shutdown", async (_event, ctx: ExtensionContext) => {
|
|
85
|
+
if (isParallelActive()) {
|
|
86
|
+
try {
|
|
87
|
+
await shutdownParallel(process.cwd());
|
|
88
|
+
} catch {
|
|
89
|
+
// best-effort
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!isAutoActive() && !isAutoPaused()) return;
|
|
93
|
+
const dash = getAutoDashboardData();
|
|
94
|
+
if (dash.currentUnit) {
|
|
95
|
+
saveActivityLog(ctx, dash.basePath, dash.currentUnit.type, dash.currentUnit.id);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
pi.on("tool_call", async (event) => {
|
|
100
|
+
if (!isToolCallEventType("write", event)) return;
|
|
101
|
+
const result = shouldBlockContextWrite(
|
|
102
|
+
event.toolName,
|
|
103
|
+
event.input.path,
|
|
104
|
+
getDiscussionMilestoneId(),
|
|
105
|
+
isDepthVerified(),
|
|
106
|
+
isQueuePhaseActive(),
|
|
107
|
+
);
|
|
108
|
+
if (result.block) return result;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
pi.on("tool_result", async (event) => {
|
|
112
|
+
if (event.toolName !== "ask_user_questions") return;
|
|
113
|
+
const milestoneId = getDiscussionMilestoneId();
|
|
114
|
+
if (!milestoneId) return;
|
|
115
|
+
|
|
116
|
+
const details = event.details as any;
|
|
117
|
+
if (details?.cancelled || !details?.response) return;
|
|
118
|
+
|
|
119
|
+
const questions: any[] = (event.input as any)?.questions ?? [];
|
|
120
|
+
for (const question of questions) {
|
|
121
|
+
if (typeof question.id === "string" && question.id.includes("depth_verification")) {
|
|
122
|
+
markDepthVerified();
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const basePath = process.cwd();
|
|
128
|
+
const milestoneDir = resolveMilestonePath(basePath, milestoneId);
|
|
129
|
+
if (!milestoneDir) return;
|
|
130
|
+
|
|
131
|
+
const discussionPath = join(milestoneDir, buildMilestoneFileName(milestoneId, "DISCUSSION"));
|
|
132
|
+
const timestamp = new Date().toISOString();
|
|
133
|
+
const lines: string[] = [`## Exchange — ${timestamp}`, ""];
|
|
134
|
+
for (const question of questions) {
|
|
135
|
+
lines.push(`### ${question.header ?? "Question"}`, "", question.question ?? "");
|
|
136
|
+
if (Array.isArray(question.options)) {
|
|
137
|
+
lines.push("");
|
|
138
|
+
for (const opt of question.options) {
|
|
139
|
+
lines.push(`- **${opt.label}** — ${opt.description ?? ""}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const answer = details.response?.answers?.[question.id];
|
|
143
|
+
if (answer) {
|
|
144
|
+
lines.push("");
|
|
145
|
+
const selected = Array.isArray(answer.selected) ? answer.selected.join(", ") : answer.selected;
|
|
146
|
+
lines.push(`**Selected:** ${selected}`);
|
|
147
|
+
if (answer.notes) {
|
|
148
|
+
lines.push(`**Notes:** ${answer.notes}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
lines.push("");
|
|
152
|
+
}
|
|
153
|
+
lines.push("---", "");
|
|
154
|
+
const existing = await loadFile(discussionPath) ?? `# ${milestoneId} Discussion Log\n\n`;
|
|
155
|
+
await saveFile(discussionPath, existing + lines.join("\n"));
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
pi.on("tool_execution_start", async (event) => {
|
|
159
|
+
if (!isAutoActive()) return;
|
|
160
|
+
markToolStart(event.toolCallId);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
pi.on("tool_execution_end", async (event) => {
|
|
164
|
+
markToolEnd(event.toolCallId);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
5
|
+
import { Key, Text } from "@gsd/pi-tui";
|
|
6
|
+
|
|
7
|
+
import { GSDDashboardOverlay } from "../dashboard-overlay.js";
|
|
8
|
+
import { shortcutDesc } from "../../shared/mod.js";
|
|
9
|
+
|
|
10
|
+
export const GSD_LOGO_LINES = [
|
|
11
|
+
" ██████╗ ███████╗██████╗ ",
|
|
12
|
+
" ██╔════╝ ██╔════╝██╔══██╗",
|
|
13
|
+
" ██║ ███╗███████╗██║ ██║",
|
|
14
|
+
" ██║ ██║╚════██║██║ ██║",
|
|
15
|
+
" ╚██████╔╝███████║██████╔╝",
|
|
16
|
+
" ╚═════╝ ╚══════╝╚═════╝ ",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export function registerShortcuts(pi: ExtensionAPI): void {
|
|
20
|
+
pi.registerShortcut(Key.ctrlAlt("g"), {
|
|
21
|
+
description: shortcutDesc("Open GSD dashboard", "/gsd status"),
|
|
22
|
+
handler: async (ctx) => {
|
|
23
|
+
if (!existsSync(join(process.cwd(), ".gsd"))) {
|
|
24
|
+
ctx.ui.notify("No .gsd/ directory found. Run /gsd to start.", "info");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await ctx.ui.custom<void>(
|
|
28
|
+
(tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done()),
|
|
29
|
+
{
|
|
30
|
+
overlay: true,
|
|
31
|
+
overlayOptions: {
|
|
32
|
+
width: "90%",
|
|
33
|
+
minWidth: 80,
|
|
34
|
+
maxHeight: "92%",
|
|
35
|
+
anchor: "center",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function maybeRenderGsdHeader(ctx: { ui: any }): void {
|
|
44
|
+
try {
|
|
45
|
+
const theme = ctx.ui.theme;
|
|
46
|
+
const version = process.env.GSD_VERSION || "0.0.0";
|
|
47
|
+
const logoText = GSD_LOGO_LINES.map((line) => theme.fg("accent", line)).join("\n");
|
|
48
|
+
const titleLine = ` ${theme.bold("Get Shit Done")} ${theme.fg("dim", `v${version}`)}`;
|
|
49
|
+
const headerContent = `${logoText}\n${titleLine}`;
|
|
50
|
+
ctx.ui.setHeader((_ui: unknown, _theme: unknown) => new Text(headerContent, 1, 0));
|
|
51
|
+
} catch {
|
|
52
|
+
// no TUI
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|