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,140 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { loadSkills } from "@gsd/pi-coding-agent";
|
|
7
|
+
import { buildSkillActivationBlock } from "../auto-prompts.js";
|
|
8
|
+
import type { GSDPreferences } from "../preferences.js";
|
|
9
|
+
|
|
10
|
+
function makeTempBase(): string {
|
|
11
|
+
return mkdtempSync(join(tmpdir(), "gsd-skill-activation-"));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function cleanup(base: string): void {
|
|
15
|
+
rmSync(base, { recursive: true, force: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function writeSkill(base: string, name: string, description: string): void {
|
|
19
|
+
const dir = join(base, "skills", name);
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
writeFileSync(join(dir, "SKILL.md"), `---\nname: ${name}\ndescription: ${description}\n---\n\n# ${name}\n`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadOnlyTestSkills(base: string): void {
|
|
25
|
+
loadSkills({ cwd: base, includeDefaults: false, skillPaths: [join(base, "skills")] });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildBlock(
|
|
29
|
+
base: string,
|
|
30
|
+
params: Partial<Parameters<typeof buildSkillActivationBlock>[0]> = {},
|
|
31
|
+
preferences: GSDPreferences = {},
|
|
32
|
+
): string {
|
|
33
|
+
return buildSkillActivationBlock({
|
|
34
|
+
base,
|
|
35
|
+
milestoneId: "M001",
|
|
36
|
+
sliceId: "S01",
|
|
37
|
+
...params,
|
|
38
|
+
preferences,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test("buildSkillActivationBlock matches installed skills from task context", () => {
|
|
43
|
+
const base = makeTempBase();
|
|
44
|
+
try {
|
|
45
|
+
writeSkill(base, "react", "Use for React components, hooks, JSX, and frontend UI work.");
|
|
46
|
+
writeSkill(base, "swiftui", "Use for SwiftUI views, iOS layout, and Apple platform UI work.");
|
|
47
|
+
loadOnlyTestSkills(base);
|
|
48
|
+
|
|
49
|
+
const result = buildBlock(base, {
|
|
50
|
+
sliceTitle: "Build React dashboard",
|
|
51
|
+
taskId: "T01",
|
|
52
|
+
taskTitle: "Implement React settings panel",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
assert.match(result, /<skill_activation>/);
|
|
56
|
+
assert.match(result, /Call Skill\('react'\)/);
|
|
57
|
+
assert.doesNotMatch(result, /swiftui/);
|
|
58
|
+
} finally {
|
|
59
|
+
cleanup(base);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("buildSkillActivationBlock includes always_use_skills from preferences using exact Skill tool format", () => {
|
|
64
|
+
const base = makeTempBase();
|
|
65
|
+
try {
|
|
66
|
+
writeSkill(base, "swift-testing", "Use for Swift Testing assertions and verification patterns.");
|
|
67
|
+
loadOnlyTestSkills(base);
|
|
68
|
+
|
|
69
|
+
const result = buildBlock(base, { taskTitle: "Unrelated task title" }, {
|
|
70
|
+
always_use_skills: ["swift-testing"],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
assert.equal(result, "<skill_activation>Call Skill('swift-testing').</skill_activation>");
|
|
74
|
+
} finally {
|
|
75
|
+
cleanup(base);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("buildSkillActivationBlock includes skill_rules matches and task-plan skills_used", () => {
|
|
80
|
+
const base = makeTempBase();
|
|
81
|
+
try {
|
|
82
|
+
writeSkill(base, "prisma", "Use for Prisma schema, migrations, and ORM queries.");
|
|
83
|
+
writeSkill(base, "accessibility", "Use for accessibility, aria attributes, and keyboard support.");
|
|
84
|
+
loadOnlyTestSkills(base);
|
|
85
|
+
|
|
86
|
+
const taskPlan = [
|
|
87
|
+
"---",
|
|
88
|
+
"skills_used:",
|
|
89
|
+
" - accessibility",
|
|
90
|
+
"---",
|
|
91
|
+
"# T01: Example",
|
|
92
|
+
].join("\n");
|
|
93
|
+
|
|
94
|
+
const result = buildBlock(base, {
|
|
95
|
+
taskTitle: "Update prisma schema",
|
|
96
|
+
taskPlanContent: taskPlan,
|
|
97
|
+
}, {
|
|
98
|
+
skill_rules: [{ when: "prisma database schema", use: ["prisma"] }],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
assert.match(result, /Call Skill\('accessibility'\)/);
|
|
102
|
+
assert.match(result, /Call Skill\('prisma'\)/);
|
|
103
|
+
} finally {
|
|
104
|
+
cleanup(base);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("buildSkillActivationBlock honors avoid_skills", () => {
|
|
109
|
+
const base = makeTempBase();
|
|
110
|
+
try {
|
|
111
|
+
writeSkill(base, "react", "Use for React components and frontend UI work.");
|
|
112
|
+
loadOnlyTestSkills(base);
|
|
113
|
+
|
|
114
|
+
const result = buildBlock(base, {
|
|
115
|
+
taskTitle: "Implement React settings panel",
|
|
116
|
+
}, {
|
|
117
|
+
avoid_skills: ["react"],
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
assert.equal(result, "");
|
|
121
|
+
} finally {
|
|
122
|
+
cleanup(base);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("buildSkillActivationBlock falls back cleanly when nothing matches", () => {
|
|
127
|
+
const base = makeTempBase();
|
|
128
|
+
try {
|
|
129
|
+
writeSkill(base, "swiftui", "Use for SwiftUI apps.");
|
|
130
|
+
loadOnlyTestSkills(base);
|
|
131
|
+
|
|
132
|
+
const result = buildBlock(base, {
|
|
133
|
+
taskTitle: "Plain text docs task",
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
assert.equal(result, "");
|
|
137
|
+
} finally {
|
|
138
|
+
cleanup(base);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
@@ -422,25 +422,25 @@ assertTrue(
|
|
|
422
422
|
"overlay has 10 tab labels",
|
|
423
423
|
);
|
|
424
424
|
|
|
425
|
-
// Verify commands.ts integration
|
|
426
|
-
const
|
|
427
|
-
const
|
|
425
|
+
// Verify commands/handlers/core.ts integration
|
|
426
|
+
const coreHandlerPath = join(__dirname, "..", "commands", "handlers", "core.ts");
|
|
427
|
+
const coreHandlerSrc = readFileSync(coreHandlerPath, "utf-8");
|
|
428
428
|
|
|
429
|
-
console.log("\n=== commands.ts integration ===");
|
|
429
|
+
console.log("\n=== commands/handlers/core.ts integration ===");
|
|
430
430
|
|
|
431
431
|
assertTrue(
|
|
432
|
-
|
|
433
|
-
"
|
|
432
|
+
coreHandlerSrc.includes('"visualize"'),
|
|
433
|
+
"core.ts has visualize in subcommands array",
|
|
434
434
|
);
|
|
435
435
|
|
|
436
436
|
assertTrue(
|
|
437
|
-
|
|
438
|
-
"
|
|
437
|
+
coreHandlerSrc.includes("GSDVisualizerOverlay"),
|
|
438
|
+
"core.ts imports GSDVisualizerOverlay",
|
|
439
439
|
);
|
|
440
440
|
|
|
441
441
|
assertTrue(
|
|
442
|
-
|
|
443
|
-
"
|
|
442
|
+
coreHandlerSrc.includes("handleVisualize"),
|
|
443
|
+
"core.ts has handleVisualize handler",
|
|
444
444
|
);
|
|
445
445
|
|
|
446
446
|
report();
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getMainBranch,
|
|
12
12
|
getSliceBranchName,
|
|
13
13
|
parseSliceBranch,
|
|
14
|
+
resolveProjectRoot,
|
|
14
15
|
setActiveMilestoneId,
|
|
15
16
|
SLICE_BRANCH_RE,
|
|
16
17
|
} from "../worktree.ts";
|
|
@@ -165,6 +166,52 @@ async function main(): Promise<void> {
|
|
|
165
166
|
rmSync(repo, { recursive: true, force: true });
|
|
166
167
|
}
|
|
167
168
|
|
|
169
|
+
// ── detectWorktreeName: symlink-resolved paths ───────────────────────────
|
|
170
|
+
console.log("\n=== detectWorktreeName (symlink-resolved paths) ===");
|
|
171
|
+
assertEq(
|
|
172
|
+
detectWorktreeName("/Users/fran/.gsd/projects/89e1c9ad49bf/worktrees/M001"),
|
|
173
|
+
"M001",
|
|
174
|
+
"detects milestone in symlink-resolved path",
|
|
175
|
+
);
|
|
176
|
+
assertEq(
|
|
177
|
+
detectWorktreeName("/Users/fran/.gsd/projects/abc123/worktrees/M002/subdir"),
|
|
178
|
+
"M002",
|
|
179
|
+
"detects milestone with trailing subdir in symlink-resolved path",
|
|
180
|
+
);
|
|
181
|
+
assertEq(
|
|
182
|
+
detectWorktreeName("/Users/fran/.gsd/projects/abc123"),
|
|
183
|
+
null,
|
|
184
|
+
"returns null for project root without worktrees segment",
|
|
185
|
+
);
|
|
186
|
+
assertEq(
|
|
187
|
+
detectWorktreeName("/foo/.gsd/worktrees/M001"),
|
|
188
|
+
"M001",
|
|
189
|
+
"still detects direct layout path",
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// ── resolveProjectRoot: symlink-resolved paths ──────────────────────────
|
|
193
|
+
console.log("\n=== resolveProjectRoot (symlink-resolved paths) ===");
|
|
194
|
+
assertEq(
|
|
195
|
+
resolveProjectRoot("/Users/fran/.gsd/projects/89e1c9ad49bf/worktrees/M001"),
|
|
196
|
+
"/Users/fran",
|
|
197
|
+
"resolves to user home for symlink-resolved path",
|
|
198
|
+
);
|
|
199
|
+
assertEq(
|
|
200
|
+
resolveProjectRoot("/foo/.gsd/worktrees/M001"),
|
|
201
|
+
"/foo",
|
|
202
|
+
"still resolves direct layout path",
|
|
203
|
+
);
|
|
204
|
+
assertEq(
|
|
205
|
+
resolveProjectRoot("/some/repo"),
|
|
206
|
+
"/some/repo",
|
|
207
|
+
"returns unchanged for non-worktree path",
|
|
208
|
+
);
|
|
209
|
+
assertEq(
|
|
210
|
+
resolveProjectRoot("/data/.gsd/projects/deadbeef/worktrees/M003/nested"),
|
|
211
|
+
"/data",
|
|
212
|
+
"resolves correctly with nested subdirs after worktree name",
|
|
213
|
+
);
|
|
214
|
+
|
|
168
215
|
rmSync(base, { recursive: true, force: true });
|
|
169
216
|
report();
|
|
170
217
|
}
|
|
@@ -61,6 +61,16 @@ export interface TaskPlanEntry {
|
|
|
61
61
|
verify?: string; // e.g. "run tests" — extracted from "- Verify:" subline
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
export interface TaskPlanFrontmatter {
|
|
65
|
+
estimated_steps?: number; // optional scope estimate for plan quality validator
|
|
66
|
+
estimated_files?: number; // optional file-count estimate for scope warning heuristics
|
|
67
|
+
skills_used: string[]; // installed skill slugs/names to hand off to execute-task prompts
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface TaskPlanFile {
|
|
71
|
+
frontmatter: TaskPlanFrontmatter;
|
|
72
|
+
}
|
|
73
|
+
|
|
64
74
|
// ─── Verification Gate ─────────────────────────────────────────────────────
|
|
65
75
|
|
|
66
76
|
/** Result of a single verification command execution */
|
|
@@ -423,7 +433,6 @@ export interface Requirement {
|
|
|
423
433
|
|
|
424
434
|
// ─── Parallel Orchestration Types ────────────────────────────────────────
|
|
425
435
|
|
|
426
|
-
export type CompressionStrategy = "truncate" | "compress";
|
|
427
436
|
export type ContextSelectionMode = "full" | "smart";
|
|
428
437
|
|
|
429
438
|
export type MergeStrategy = "per-slice" | "per-milestone";
|
|
@@ -479,3 +488,11 @@ export interface ReactiveExecutionState {
|
|
|
479
488
|
};
|
|
480
489
|
updatedAt: string;
|
|
481
490
|
}
|
|
491
|
+
|
|
492
|
+
export interface BrowserFlowResult {
|
|
493
|
+
url: string;
|
|
494
|
+
passed: boolean;
|
|
495
|
+
checksTotal: number;
|
|
496
|
+
checksPassed: number;
|
|
497
|
+
duration: number;
|
|
498
|
+
}
|
|
@@ -37,6 +37,21 @@ export interface AuditWarningJSON {
|
|
|
37
37
|
fixAvailable: boolean;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
export interface BrowserEvidenceCheckJSON {
|
|
41
|
+
description: string;
|
|
42
|
+
passed: boolean;
|
|
43
|
+
actual?: string;
|
|
44
|
+
evidence?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface BrowserEvidenceJSON {
|
|
49
|
+
url: string;
|
|
50
|
+
passed: boolean;
|
|
51
|
+
checks: BrowserEvidenceCheckJSON[];
|
|
52
|
+
duration: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
export interface EvidenceJSON {
|
|
41
56
|
schemaVersion: 1;
|
|
42
57
|
taskId: string;
|
|
@@ -49,6 +64,7 @@ export interface EvidenceJSON {
|
|
|
49
64
|
maxRetries?: number;
|
|
50
65
|
runtimeErrors?: RuntimeErrorJSON[];
|
|
51
66
|
auditWarnings?: AuditWarningJSON[];
|
|
67
|
+
browser?: BrowserEvidenceJSON;
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// Data loader for workflow visualizer overlay — aggregates state + metrics.
|
|
2
2
|
|
|
3
3
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
4
5
|
import { deriveState } from './state.js';
|
|
5
6
|
import { parseRoadmap, parsePlan, parseSummary, loadFile } from './files.js';
|
|
6
|
-
import { findMilestoneIds } from './
|
|
7
|
-
import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile } from './paths.js';
|
|
7
|
+
import { findMilestoneIds } from './milestone-ids.js';
|
|
8
|
+
import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile, gsdRoot } from './paths.js';
|
|
8
9
|
import {
|
|
9
10
|
getLedger,
|
|
10
11
|
getProjectTotals,
|
|
@@ -21,6 +22,8 @@ import { loadEffectiveGSDPreferences } from './preferences.js';
|
|
|
21
22
|
import { runProviderChecks, type ProviderCheckResult } from './doctor-providers.js';
|
|
22
23
|
import { generateSkillHealthReport } from './skill-health.js';
|
|
23
24
|
import { runEnvironmentChecks, type EnvironmentCheckResult } from './doctor-environment.js';
|
|
25
|
+
import { computeProgressScore } from './progress-score.js';
|
|
26
|
+
import { getHealthHistory } from './doctor-proactive.js';
|
|
24
27
|
|
|
25
28
|
import type { Phase } from './types.js';
|
|
26
29
|
import type { CaptureEntry } from './captures.js';
|
|
@@ -161,6 +164,27 @@ export interface SkillSummaryInfo {
|
|
|
161
164
|
topIssue: string | null;
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
/** A single doctor history entry for visualizer display. */
|
|
168
|
+
export interface VisualizerDoctorEntry {
|
|
169
|
+
ts: string;
|
|
170
|
+
ok: boolean;
|
|
171
|
+
errors: number;
|
|
172
|
+
warnings: number;
|
|
173
|
+
fixes: number;
|
|
174
|
+
codes: string[];
|
|
175
|
+
issues?: Array<{ severity: string; code: string; message: string; unitId: string }>;
|
|
176
|
+
fixDescriptions?: string[];
|
|
177
|
+
scope?: string;
|
|
178
|
+
summary?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Current progress score snapshot for health display. */
|
|
182
|
+
export interface VisualizerProgressScore {
|
|
183
|
+
level: "green" | "yellow" | "red";
|
|
184
|
+
summary: string;
|
|
185
|
+
signals: Array<{ kind: "positive" | "negative" | "neutral"; label: string }>;
|
|
186
|
+
}
|
|
187
|
+
|
|
164
188
|
export interface HealthInfo {
|
|
165
189
|
budgetCeiling: number | undefined;
|
|
166
190
|
tokenProfile: string;
|
|
@@ -174,6 +198,10 @@ export interface HealthInfo {
|
|
|
174
198
|
providers: ProviderStatusSummary[];
|
|
175
199
|
skillSummary: SkillSummaryInfo;
|
|
176
200
|
environmentIssues: import("./doctor-environment.js").EnvironmentCheckResult[];
|
|
201
|
+
/** Persisted doctor run history (most recent first, up to 20 entries). */
|
|
202
|
+
doctorHistory?: VisualizerDoctorEntry[];
|
|
203
|
+
/** Current in-memory progress score (null if auto-mode not active). */
|
|
204
|
+
progressScore?: VisualizerProgressScore | null;
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
export interface VisualizerData {
|
|
@@ -608,6 +636,26 @@ function loadHealth(units: UnitMetrics[], totals: ProjectTotals | null, basePath
|
|
|
608
636
|
environmentIssues = runEnvironmentChecks(basePath).filter(r => r.status !== "ok");
|
|
609
637
|
} catch { /* non-fatal */ }
|
|
610
638
|
|
|
639
|
+
// Doctor run history — persisted across sessions (sync read to keep loadHealth sync)
|
|
640
|
+
let doctorHistory: VisualizerDoctorEntry[] = [];
|
|
641
|
+
try {
|
|
642
|
+
const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
|
|
643
|
+
if (existsSync(historyPath)) {
|
|
644
|
+
const lines = readFileSync(historyPath, "utf-8").split("\n").filter(l => l.trim());
|
|
645
|
+
doctorHistory = lines.slice(-20).reverse().map(l => JSON.parse(l) as VisualizerDoctorEntry);
|
|
646
|
+
}
|
|
647
|
+
} catch { /* non-fatal */ }
|
|
648
|
+
|
|
649
|
+
// Current progress score — only meaningful when auto-mode has health data
|
|
650
|
+
let progressScore: VisualizerProgressScore | null = null;
|
|
651
|
+
try {
|
|
652
|
+
const history = getHealthHistory();
|
|
653
|
+
if (history.length > 0) {
|
|
654
|
+
const score = computeProgressScore();
|
|
655
|
+
progressScore = { level: score.level, summary: score.summary, signals: score.signals };
|
|
656
|
+
}
|
|
657
|
+
} catch { /* non-fatal */ }
|
|
658
|
+
|
|
611
659
|
return {
|
|
612
660
|
budgetCeiling,
|
|
613
661
|
tokenProfile,
|
|
@@ -621,6 +669,8 @@ function loadHealth(units: UnitMetrics[], totals: ProjectTotals | null, basePath
|
|
|
621
669
|
providers,
|
|
622
670
|
skillSummary,
|
|
623
671
|
environmentIssues,
|
|
672
|
+
doctorHistory,
|
|
673
|
+
progressScore,
|
|
624
674
|
};
|
|
625
675
|
}
|
|
626
676
|
|
|
@@ -1150,6 +1150,64 @@ export function renderHealthView(
|
|
|
1150
1150
|
}
|
|
1151
1151
|
}
|
|
1152
1152
|
|
|
1153
|
+
// Progress score section — current traffic light status
|
|
1154
|
+
if (health.progressScore) {
|
|
1155
|
+
lines.push("");
|
|
1156
|
+
lines.push(th.fg("accent", th.bold("Progress Score")));
|
|
1157
|
+
lines.push("");
|
|
1158
|
+
const ps = health.progressScore;
|
|
1159
|
+
const scoreColor = ps.level === "green" ? "success" : ps.level === "yellow" ? "warning" : "error";
|
|
1160
|
+
const scoreIcon = ps.level === "green" ? "●" : ps.level === "yellow" ? "◐" : "○";
|
|
1161
|
+
lines.push(` ${th.fg(scoreColor, scoreIcon)} ${th.fg(scoreColor, ps.summary)}`);
|
|
1162
|
+
for (const signal of ps.signals) {
|
|
1163
|
+
const prefix = signal.kind === "positive" ? th.fg("success", " ✓")
|
|
1164
|
+
: signal.kind === "negative" ? th.fg("error", " ✗")
|
|
1165
|
+
: th.fg("dim", " ·");
|
|
1166
|
+
lines.push(` ${prefix} ${th.fg("dim", signal.label)}`);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Doctor history section — persisted across sessions
|
|
1171
|
+
const doctorHistory = health.doctorHistory ?? [];
|
|
1172
|
+
if (doctorHistory.length > 0) {
|
|
1173
|
+
lines.push("");
|
|
1174
|
+
lines.push(th.fg("accent", th.bold("Doctor History")));
|
|
1175
|
+
lines.push("");
|
|
1176
|
+
|
|
1177
|
+
for (const entry of doctorHistory.slice(0, 10)) {
|
|
1178
|
+
const icon = entry.ok ? th.fg("success", "✓") : th.fg("error", "✗");
|
|
1179
|
+
const ts = entry.ts.replace("T", " ").slice(0, 19);
|
|
1180
|
+
const scopeTag = entry.scope ? th.fg("accent", ` [${entry.scope}]`) : "";
|
|
1181
|
+
// Prefer human-readable summary, fall back to counts
|
|
1182
|
+
const detail = entry.summary
|
|
1183
|
+
? th.fg("text", entry.summary)
|
|
1184
|
+
: th.fg("text", `${entry.errors} errors, ${entry.warnings} warnings, ${entry.fixes} fixes`);
|
|
1185
|
+
lines.push(` ${icon} ${th.fg("dim", ts)}${scopeTag} ${detail}`);
|
|
1186
|
+
|
|
1187
|
+
// Show issue details if available
|
|
1188
|
+
if (entry.issues && entry.issues.length > 0) {
|
|
1189
|
+
for (const issue of entry.issues.slice(0, 3)) {
|
|
1190
|
+
const issuePfx = issue.severity === "error" ? th.fg("error", " ✗") : th.fg("warning", " ⚠");
|
|
1191
|
+
lines.push(` ${issuePfx} ${th.fg("dim", truncateToWidth(issue.message, width - 12))}`);
|
|
1192
|
+
}
|
|
1193
|
+
if (entry.issues.length > 3) {
|
|
1194
|
+
lines.push(` ${th.fg("dim", `+${entry.issues.length - 3} more`)}`);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// Show fixes if available
|
|
1199
|
+
if (entry.fixDescriptions && entry.fixDescriptions.length > 0) {
|
|
1200
|
+
for (const fix of entry.fixDescriptions.slice(0, 2)) {
|
|
1201
|
+
lines.push(` ${th.fg("success", "↳")} ${th.fg("dim", truncateToWidth(fix, width - 12))}`);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (doctorHistory.length > 10) {
|
|
1207
|
+
lines.push(` ${th.fg("dim", `...${doctorHistory.length - 10} older entries`)}`);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1153
1211
|
// Skills section
|
|
1154
1212
|
if (health.skillSummary?.total > 0) {
|
|
1155
1213
|
lines.push("");
|
|
@@ -67,40 +67,60 @@ export function captureIntegrationBranch(basePath: string, milestoneId: string,
|
|
|
67
67
|
|
|
68
68
|
// ─── Pure Utility Functions (unchanged) ────────────────────────────────────
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Find the worktrees segment in a path, supporting both direct
|
|
72
|
+
* (`/.gsd/worktrees/`) and symlink-resolved (`/.gsd/projects/<hash>/worktrees/`)
|
|
73
|
+
* layouts. When `.gsd` is a symlink to `~/.gsd/projects/<hash>`, resolved
|
|
74
|
+
* paths contain the intermediate `projects/<hash>/` segment that the old
|
|
75
|
+
* single-marker check missed.
|
|
76
|
+
*/
|
|
77
|
+
function findWorktreeSegment(normalizedPath: string): { gsdIdx: number; afterWorktrees: number } | null {
|
|
78
|
+
// Direct layout: /.gsd/worktrees/<name>
|
|
79
|
+
const directMarker = "/.gsd/worktrees/";
|
|
80
|
+
const idx = normalizedPath.indexOf(directMarker);
|
|
81
|
+
if (idx !== -1) {
|
|
82
|
+
return { gsdIdx: idx, afterWorktrees: idx + directMarker.length };
|
|
83
|
+
}
|
|
84
|
+
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/<name>
|
|
85
|
+
const symlinkRe = /\/\.gsd\/projects\/[a-f0-9]+\/worktrees\//;
|
|
86
|
+
const match = normalizedPath.match(symlinkRe);
|
|
87
|
+
if (match && match.index !== undefined) {
|
|
88
|
+
return { gsdIdx: match.index, afterWorktrees: match.index + match[0].length };
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
70
93
|
/**
|
|
71
94
|
* Detect the active worktree name from the current working directory.
|
|
72
95
|
* Returns null if not inside a GSD worktree (.gsd/worktrees/<name>/).
|
|
73
96
|
*/
|
|
74
97
|
export function detectWorktreeName(basePath: string): string | null {
|
|
75
98
|
const normalizedPath = basePath.replaceAll("\\", "/");
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const afterMarker = normalizedPath.slice(idx + marker.length);
|
|
99
|
+
const seg = findWorktreeSegment(normalizedPath);
|
|
100
|
+
if (!seg) return null;
|
|
101
|
+
const afterMarker = normalizedPath.slice(seg.afterWorktrees);
|
|
80
102
|
const name = afterMarker.split("/")[0];
|
|
81
103
|
return name || null;
|
|
82
104
|
}
|
|
83
105
|
|
|
84
106
|
/**
|
|
85
107
|
* Resolve the project root from a path that may be inside a worktree.
|
|
86
|
-
* If the path contains
|
|
87
|
-
*
|
|
108
|
+
* If the path contains a worktrees segment, returns the portion before
|
|
109
|
+
* `/.gsd/`. Otherwise returns the input unchanged.
|
|
88
110
|
*
|
|
89
111
|
* Use this in commands that call `process.cwd()` to ensure they always
|
|
90
112
|
* operate against the real project root, not a worktree subdirectory.
|
|
91
113
|
*/
|
|
92
114
|
export function resolveProjectRoot(basePath: string): string {
|
|
93
115
|
const normalizedPath = basePath.replaceAll("\\", "/");
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// Return the original path up to the .gsd/ marker (un-normalized)
|
|
98
|
-
// Account for potential OS-specific separators
|
|
116
|
+
const seg = findWorktreeSegment(normalizedPath);
|
|
117
|
+
if (!seg) return basePath;
|
|
118
|
+
// Return the original path up to the /.gsd/ boundary
|
|
99
119
|
const sep = basePath.includes("\\") ? "\\" : "/";
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
return basePath.slice(0,
|
|
120
|
+
const gsdMarker = `${sep}.gsd${sep}`;
|
|
121
|
+
const gsdIdx = basePath.indexOf(gsdMarker);
|
|
122
|
+
if (gsdIdx !== -1) return basePath.slice(0, gsdIdx);
|
|
123
|
+
return basePath.slice(0, seg.gsdIdx);
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
/**
|
|
@@ -114,6 +114,22 @@ function getServerConfig(name: string): McpServerConfig | undefined {
|
|
|
114
114
|
return readConfigs().find((s) => s.name === name);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/** Resolve ${VAR} references in env values against process.env. */
|
|
118
|
+
function resolveEnv(env: Record<string, string>): Record<string, string> {
|
|
119
|
+
const resolved: Record<string, string> = {};
|
|
120
|
+
for (const [key, value] of Object.entries(env)) {
|
|
121
|
+
if (typeof value === "string") {
|
|
122
|
+
resolved[key] = value.replace(
|
|
123
|
+
/\$\{([^}]+)\}/g,
|
|
124
|
+
(_match, varName) => process.env[varName] ?? "",
|
|
125
|
+
);
|
|
126
|
+
} else {
|
|
127
|
+
resolved[key] = value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return resolved;
|
|
131
|
+
}
|
|
132
|
+
|
|
117
133
|
async function getOrConnect(name: string, signal?: AbortSignal): Promise<Client> {
|
|
118
134
|
const existing = connections.get(name);
|
|
119
135
|
if (existing) return existing.client;
|
|
@@ -128,7 +144,7 @@ async function getOrConnect(name: string, signal?: AbortSignal): Promise<Client>
|
|
|
128
144
|
transport = new StdioClientTransport({
|
|
129
145
|
command: config.command,
|
|
130
146
|
args: config.args,
|
|
131
|
-
env: config.env ? { ...process.env, ...config.env } as Record<string, string> : undefined,
|
|
147
|
+
env: config.env ? { ...process.env, ...resolveEnv(config.env) } as Record<string, string> : undefined,
|
|
132
148
|
cwd: config.cwd,
|
|
133
149
|
stderr: "pipe",
|
|
134
150
|
});
|
|
@@ -7,6 +7,10 @@ import { join } from "node:path";
|
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { readPromptRecord } from "./store.js";
|
|
9
9
|
|
|
10
|
+
function getGsdHome(): string {
|
|
11
|
+
return process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
export interface LatestPromptSummary {
|
|
11
15
|
id: string;
|
|
12
16
|
status: string;
|
|
@@ -14,7 +18,7 @@ export interface LatestPromptSummary {
|
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export function getLatestPromptSummary(): LatestPromptSummary | null {
|
|
17
|
-
const runtimeDir = join(
|
|
21
|
+
const runtimeDir = join(getGsdHome(), "runtime", "remote-questions");
|
|
18
22
|
if (!existsSync(runtimeDir)) return null;
|
|
19
23
|
const files = readdirSync(runtimeDir).filter((f) => f.endsWith(".json"));
|
|
20
24
|
if (files.length === 0) return null;
|
|
@@ -7,8 +7,12 @@ import { join } from "node:path";
|
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import type { RemotePrompt, RemotePromptRecord, RemotePromptRef, RemoteAnswer, RemotePromptStatus } from "./types.js";
|
|
9
9
|
|
|
10
|
+
function getGsdHome(): string {
|
|
11
|
+
return process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
function runtimeDir(): string {
|
|
11
|
-
return join(
|
|
15
|
+
return join(getGsdHome(), "runtime", "remote-questions");
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
function recordPath(id: string): string {
|
|
@@ -17,7 +17,8 @@ import { resolveSearchProviderFromPreferences } from '../gsd/preferences.js'
|
|
|
17
17
|
// Compute authFilePath locally instead of importing from app-paths.ts,
|
|
18
18
|
// because extensions are copied to ~/.gsd/agent/extensions/ at runtime
|
|
19
19
|
// where the relative import '../../../app-paths.ts' doesn't resolve.
|
|
20
|
-
const
|
|
20
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), '.gsd')
|
|
21
|
+
const authFilePath = join(gsdHome, 'agent', 'auth.json')
|
|
21
22
|
|
|
22
23
|
export type SearchProvider = 'tavily' | 'brave' | 'ollama'
|
|
23
24
|
export type SearchProviderPreference = SearchProvider | 'auto'
|
|
@@ -50,7 +50,7 @@ export function parseFrontmatterMap(lines: string[]): Record<string, unknown> {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Array item (2-space indent)
|
|
53
|
-
const arrayMatch = line.match(/^ - (.*)$/);
|
|
53
|
+
const arrayMatch = line.match(/^ - ?(.*)$/);
|
|
54
54
|
if (arrayMatch && currentKey) {
|
|
55
55
|
// If there's a pending nested object, push it
|
|
56
56
|
if (currentObj && Object.keys(currentObj).length > 0) {
|
|
@@ -452,7 +452,7 @@ async function runSingleAgent(
|
|
|
452
452
|
|
|
453
453
|
async function runSingleAgentInCmuxSplit(
|
|
454
454
|
cmuxClient: CmuxClient,
|
|
455
|
-
|
|
455
|
+
directionOrSurfaceId: "right" | "down" | string,
|
|
456
456
|
defaultCwd: string,
|
|
457
457
|
agents: AgentConfig[],
|
|
458
458
|
agentName: string,
|
|
@@ -503,7 +503,12 @@ async function runSingleAgentInCmuxSplit(
|
|
|
503
503
|
const stdoutPath = path.join(tmpOutputDir, "stdout.jsonl");
|
|
504
504
|
const stderrPath = path.join(tmpOutputDir, "stderr.log");
|
|
505
505
|
const exitPath = path.join(tmpOutputDir, "exit.code");
|
|
506
|
-
|
|
506
|
+
// Accept either a pre-created surface ID or a direction to create a new split
|
|
507
|
+
const isDirection = directionOrSurfaceId === "right" || directionOrSurfaceId === "down"
|
|
508
|
+
|| directionOrSurfaceId === "left" || directionOrSurfaceId === "up";
|
|
509
|
+
const cmuxSurfaceId = isDirection
|
|
510
|
+
? await cmuxClient.createSplit(directionOrSurfaceId as "right" | "down" | "left" | "up")
|
|
511
|
+
: directionOrSurfaceId;
|
|
507
512
|
if (!cmuxSurfaceId) {
|
|
508
513
|
return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
|
|
509
514
|
}
|
|
@@ -806,12 +811,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
806
811
|
const MAX_RETRIES = 1; // Retry failed tasks once
|
|
807
812
|
const batchId = crypto.randomUUID();
|
|
808
813
|
const batchSize = params.tasks.length;
|
|
814
|
+
// Pre-create a grid layout for cmux splits so agents get a clean tiled arrangement
|
|
815
|
+
const gridSurfaces = cmuxSplitsEnabled
|
|
816
|
+
? await cmuxClient.createGridLayout(Math.min(batchSize, MAX_CONCURRENCY))
|
|
817
|
+
: [];
|
|
809
818
|
const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
|
|
810
819
|
const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
|
|
811
820
|
const runTask = () => cmuxSplitsEnabled
|
|
812
821
|
? runSingleAgentInCmuxSplit(
|
|
813
822
|
cmuxClient,
|
|
814
|
-
index % 2 === 0 ? "right" : "down",
|
|
823
|
+
gridSurfaces[index] ?? (index % 2 === 0 ? "right" : "down"),
|
|
815
824
|
ctx.cwd,
|
|
816
825
|
agents,
|
|
817
826
|
t.agent,
|