gsd-pi 2.29.0 → 2.30.0-dev.7e1bbce
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 +24 -17
- package/dist/cli.js +51 -0
- package/dist/extension-registry.d.ts +63 -0
- package/dist/extension-registry.js +166 -0
- package/dist/headless.js +4 -0
- package/dist/help-text.js +35 -0
- package/dist/loader.js +10 -1
- package/dist/resource-loader.js +11 -1
- package/dist/resources/extensions/async-jobs/extension-manifest.json +13 -0
- package/dist/resources/extensions/bg-shell/extension-manifest.json +14 -0
- package/dist/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/dist/resources/extensions/browser-tools/extension-manifest.json +37 -0
- package/dist/resources/extensions/context7/extension-manifest.json +12 -0
- package/dist/resources/extensions/google-search/extension-manifest.json +12 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +31 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/dist/resources/extensions/gsd/auto-post-unit.ts +45 -13
- package/dist/resources/extensions/gsd/auto-prompts.ts +40 -17
- package/dist/resources/extensions/gsd/auto-recovery.ts +18 -23
- package/dist/resources/extensions/gsd/auto-start.ts +18 -32
- package/dist/resources/extensions/gsd/auto-worktree.ts +21 -182
- package/dist/resources/extensions/gsd/auto.ts +2 -24
- package/dist/resources/extensions/gsd/captures.ts +4 -10
- package/dist/resources/extensions/gsd/commands-extensions.ts +328 -0
- package/dist/resources/extensions/gsd/commands-handlers.ts +22 -2
- package/dist/resources/extensions/gsd/commands-logs.ts +13 -14
- package/dist/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/dist/resources/extensions/gsd/commands.ts +108 -24
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +2 -1
- package/dist/resources/extensions/gsd/detection.ts +2 -1
- package/dist/resources/extensions/gsd/doctor-checks.ts +49 -1
- package/dist/resources/extensions/gsd/doctor-types.ts +3 -1
- package/dist/resources/extensions/gsd/extension-manifest.json +18 -0
- package/dist/resources/extensions/gsd/forensics.ts +2 -2
- package/dist/resources/extensions/gsd/git-service.ts +3 -2
- package/dist/resources/extensions/gsd/gitignore.ts +9 -63
- package/dist/resources/extensions/gsd/gsd-db.ts +1 -165
- package/dist/resources/extensions/gsd/guided-flow.ts +8 -5
- package/dist/resources/extensions/gsd/index.ts +16 -3
- package/dist/resources/extensions/gsd/json-persistence.ts +16 -1
- package/dist/resources/extensions/gsd/md-importer.ts +3 -2
- package/dist/resources/extensions/gsd/mechanical-completion.ts +430 -0
- package/dist/resources/extensions/gsd/migrate/command.ts +3 -2
- package/dist/resources/extensions/gsd/migrate/writer.ts +2 -1
- package/dist/resources/extensions/gsd/migrate-external.ts +123 -0
- package/dist/resources/extensions/gsd/paths.ts +24 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +6 -5
- package/dist/resources/extensions/gsd/preferences-models.ts +7 -1
- package/dist/resources/extensions/gsd/preferences-validation.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +10 -5
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -1
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/dist/resources/extensions/gsd/queue-order.ts +10 -11
- package/dist/resources/extensions/gsd/repo-identity.ts +148 -0
- package/dist/resources/extensions/gsd/resource-version.ts +99 -0
- package/dist/resources/extensions/gsd/roadmap-slices.ts +22 -7
- package/dist/resources/extensions/gsd/session-forensics.ts +4 -3
- package/dist/resources/extensions/gsd/session-lock.ts +53 -4
- package/dist/resources/extensions/gsd/session-status-io.ts +23 -41
- package/dist/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
- package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
- package/dist/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
- package/dist/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +10 -37
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
- package/dist/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
- package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
- package/dist/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +2 -1
- package/dist/resources/extensions/gsd/types.ts +2 -0
- package/dist/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/dist/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/dist/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/dist/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/dist/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/dist/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/dist/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/dist/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/dist/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/dist/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +1 -11
- package/dist/resources/extensions/gsd/worktree-manager.ts +3 -2
- package/dist/resources/extensions/gsd/worktree.ts +42 -5
- package/dist/resources/extensions/mac-tools/extension-manifest.json +16 -0
- package/dist/resources/extensions/mcp-client/index.ts +459 -0
- package/dist/resources/extensions/mcporter/extension-manifest.json +12 -0
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +8 -19
- package/dist/resources/extensions/remote-questions/extension-manifest.json +11 -0
- package/dist/resources/extensions/remote-questions/http-client.ts +76 -0
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +11 -17
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +8 -19
- package/dist/resources/extensions/search-the-web/extension-manifest.json +13 -0
- package/dist/resources/extensions/slash-commands/extension-manifest.json +11 -0
- package/dist/resources/extensions/subagent/extension-manifest.json +13 -0
- package/dist/resources/extensions/ttsr/extension-manifest.json +11 -0
- package/dist/resources/extensions/universal-config/extension-manifest.json +13 -0
- package/dist/resources/extensions/voice/extension-manifest.json +12 -0
- package/dist/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/dist/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/dist/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/dist/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/dist/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/dist/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/dist/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/dist/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/dist/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/dist/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/dist/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/dist/resources/skills/create-skill/SKILL.md +184 -0
- package/dist/resources/skills/create-skill/references/api-security.md +226 -0
- package/dist/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/dist/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/dist/resources/skills/create-skill/references/core-principles.md +437 -0
- package/dist/resources/skills/create-skill/references/executable-code.md +175 -0
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/dist/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/dist/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/dist/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/dist/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/dist/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/dist/resources/skills/create-skill/references/using-templates.md +112 -0
- package/dist/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/dist/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/dist/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/dist/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/dist/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/dist/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/dist/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/dist/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/dist/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/dist/resources/skills/react-best-practices/SKILL.md +1 -1
- package/dist/worktree-cli.d.ts +34 -0
- package/dist/worktree-cli.js +294 -0
- package/dist/worktree-name-gen.d.ts +7 -0
- package/dist/worktree-name-gen.js +44 -0
- package/package.json +1 -1
- package/packages/native/dist/native.d.ts +2 -0
- package/packages/native/dist/native.js +19 -5
- package/packages/native/src/native.ts +23 -9
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +13 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +13 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/extension-manifest.json +13 -0
- package/src/resources/extensions/bg-shell/extension-manifest.json +14 -0
- package/src/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/src/resources/extensions/browser-tools/extension-manifest.json +37 -0
- package/src/resources/extensions/context7/extension-manifest.json +12 -0
- package/src/resources/extensions/google-search/extension-manifest.json +12 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +31 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/src/resources/extensions/gsd/auto-post-unit.ts +45 -13
- package/src/resources/extensions/gsd/auto-prompts.ts +40 -17
- package/src/resources/extensions/gsd/auto-recovery.ts +18 -23
- package/src/resources/extensions/gsd/auto-start.ts +18 -32
- package/src/resources/extensions/gsd/auto-worktree.ts +21 -182
- package/src/resources/extensions/gsd/auto.ts +2 -24
- package/src/resources/extensions/gsd/captures.ts +4 -10
- package/src/resources/extensions/gsd/commands-extensions.ts +328 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +22 -2
- package/src/resources/extensions/gsd/commands-logs.ts +13 -14
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/src/resources/extensions/gsd/commands.ts +108 -24
- package/src/resources/extensions/gsd/dashboard-overlay.ts +2 -1
- package/src/resources/extensions/gsd/detection.ts +2 -1
- package/src/resources/extensions/gsd/doctor-checks.ts +49 -1
- package/src/resources/extensions/gsd/doctor-types.ts +3 -1
- package/src/resources/extensions/gsd/extension-manifest.json +18 -0
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +3 -2
- package/src/resources/extensions/gsd/gitignore.ts +9 -63
- package/src/resources/extensions/gsd/gsd-db.ts +1 -165
- package/src/resources/extensions/gsd/guided-flow.ts +8 -5
- package/src/resources/extensions/gsd/index.ts +16 -3
- package/src/resources/extensions/gsd/json-persistence.ts +16 -1
- package/src/resources/extensions/gsd/md-importer.ts +3 -2
- package/src/resources/extensions/gsd/mechanical-completion.ts +430 -0
- package/src/resources/extensions/gsd/migrate/command.ts +3 -2
- package/src/resources/extensions/gsd/migrate/writer.ts +2 -1
- package/src/resources/extensions/gsd/migrate-external.ts +123 -0
- package/src/resources/extensions/gsd/paths.ts +24 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +6 -5
- package/src/resources/extensions/gsd/preferences-models.ts +7 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +10 -5
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -1
- package/src/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/src/resources/extensions/gsd/queue-order.ts +10 -11
- package/src/resources/extensions/gsd/repo-identity.ts +148 -0
- package/src/resources/extensions/gsd/resource-version.ts +99 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +22 -7
- package/src/resources/extensions/gsd/session-forensics.ts +4 -3
- package/src/resources/extensions/gsd/session-lock.ts +53 -4
- package/src/resources/extensions/gsd/session-status-io.ts +23 -41
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
- package/src/resources/extensions/gsd/tests/git-service.test.ts +10 -37
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -1
- package/src/resources/extensions/gsd/types.ts +2 -0
- package/src/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/src/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/src/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/src/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/src/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/src/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/src/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/src/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/src/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/src/resources/extensions/gsd/worktree-command.ts +1 -11
- package/src/resources/extensions/gsd/worktree-manager.ts +3 -2
- package/src/resources/extensions/gsd/worktree.ts +42 -5
- package/src/resources/extensions/mac-tools/extension-manifest.json +16 -0
- package/src/resources/extensions/mcp-client/index.ts +459 -0
- package/src/resources/extensions/mcporter/extension-manifest.json +12 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +8 -19
- package/src/resources/extensions/remote-questions/extension-manifest.json +11 -0
- package/src/resources/extensions/remote-questions/http-client.ts +76 -0
- package/src/resources/extensions/remote-questions/slack-adapter.ts +11 -17
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +8 -19
- package/src/resources/extensions/search-the-web/extension-manifest.json +13 -0
- package/src/resources/extensions/slash-commands/extension-manifest.json +11 -0
- package/src/resources/extensions/subagent/extension-manifest.json +13 -0
- package/src/resources/extensions/ttsr/extension-manifest.json +11 -0
- package/src/resources/extensions/universal-config/extension-manifest.json +13 -0
- package/src/resources/extensions/voice/extension-manifest.json +12 -0
- package/src/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/src/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/src/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/src/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/src/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/src/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/src/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/src/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/src/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/src/resources/skills/create-skill/SKILL.md +184 -0
- package/src/resources/skills/create-skill/references/api-security.md +226 -0
- package/src/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/src/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/src/resources/skills/create-skill/references/core-principles.md +437 -0
- package/src/resources/skills/create-skill/references/executable-code.md +175 -0
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/src/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/src/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/src/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/src/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/src/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/src/resources/skills/create-skill/references/using-templates.md +112 -0
- package/src/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/src/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/src/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/src/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/src/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/src/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/src/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/src/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/src/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/src/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/src/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/src/resources/skills/react-best-practices/SKILL.md +1 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.ts +0 -198
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
- package/dist/resources/extensions/mcporter/index.ts +0 -525
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -198
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
- package/src/resources/extensions/mcporter/index.ts +0 -525
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { createRequire } from "node:module";
|
|
20
|
-
import { existsSync, readFileSync, mkdirSync, unlinkSync } from "node:fs";
|
|
20
|
+
import { existsSync, readFileSync, mkdirSync, unlinkSync, rmSync, statSync } from "node:fs";
|
|
21
21
|
import { join, dirname } from "node:path";
|
|
22
22
|
import { gsdRoot } from "./paths.js";
|
|
23
23
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
@@ -92,11 +92,12 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
92
92
|
return acquireFallbackLock(basePath, lp, lockData);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
const gsdDir = gsdRoot(basePath);
|
|
96
|
+
|
|
95
97
|
try {
|
|
96
98
|
// Try to acquire an exclusive OS-level lock on the lock file.
|
|
97
99
|
// We lock the directory (gsdRoot) since proper-lockfile works best
|
|
98
100
|
// on directories, and the lock file itself may not exist yet.
|
|
99
|
-
const gsdDir = gsdRoot(basePath);
|
|
100
101
|
mkdirSync(gsdDir, { recursive: true });
|
|
101
102
|
|
|
102
103
|
const release = lockfile.lockSync(gsdDir, {
|
|
@@ -109,16 +110,53 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
109
110
|
_lockedPath = basePath;
|
|
110
111
|
_lockPid = process.pid;
|
|
111
112
|
|
|
113
|
+
// Safety net: clean up lock dir on process exit if _releaseFunction
|
|
114
|
+
// wasn't called (e.g., normal exit after clean completion) (#1245).
|
|
115
|
+
const lockDirForCleanup = join(gsdDir + ".lock");
|
|
116
|
+
process.once("exit", () => {
|
|
117
|
+
try {
|
|
118
|
+
if (_releaseFunction) { _releaseFunction(); _releaseFunction = null; }
|
|
119
|
+
} catch { /* best-effort */ }
|
|
120
|
+
try {
|
|
121
|
+
if (existsSync(lockDirForCleanup)) rmSync(lockDirForCleanup, { recursive: true, force: true });
|
|
122
|
+
} catch { /* best-effort */ }
|
|
123
|
+
});
|
|
124
|
+
|
|
112
125
|
// Write the informational lock data
|
|
113
126
|
atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
|
|
114
127
|
|
|
115
128
|
return { acquired: true };
|
|
116
129
|
} catch (err) {
|
|
117
|
-
// Lock is held by another process
|
|
130
|
+
// Lock is held by another process — or the .gsd.lock/ directory is stranded.
|
|
131
|
+
// Check: if auto.lock is gone and no process is alive, the lock dir is stale.
|
|
118
132
|
const existingData = readExistingLockData(lp);
|
|
119
133
|
const existingPid = existingData?.pid;
|
|
134
|
+
|
|
135
|
+
// If no lock file or no alive process, try to clean up and re-acquire (#1245)
|
|
136
|
+
if (!existingData || (existingPid && !isPidAlive(existingPid))) {
|
|
137
|
+
try {
|
|
138
|
+
const lockDir = join(gsdDir + ".lock");
|
|
139
|
+
if (existsSync(lockDir)) rmSync(lockDir, { recursive: true, force: true });
|
|
140
|
+
if (existsSync(lp)) unlinkSync(lp);
|
|
141
|
+
|
|
142
|
+
// Retry acquisition after cleanup
|
|
143
|
+
const release = lockfile.lockSync(gsdDir, {
|
|
144
|
+
realpath: false,
|
|
145
|
+
stale: 300_000,
|
|
146
|
+
update: 10_000,
|
|
147
|
+
});
|
|
148
|
+
_releaseFunction = release;
|
|
149
|
+
_lockedPath = basePath;
|
|
150
|
+
_lockPid = process.pid;
|
|
151
|
+
atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
|
|
152
|
+
return { acquired: true };
|
|
153
|
+
} catch {
|
|
154
|
+
// Retry also failed — fall through to the error path
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
120
158
|
const reason = existingPid
|
|
121
|
-
? `Another auto-mode session (PID ${existingPid})
|
|
159
|
+
? `Another auto-mode session (PID ${existingPid}) appears to be running.\nStop it with \`kill ${existingPid}\` before starting a new session.`
|
|
122
160
|
: `Another auto-mode session is already running on this project.`;
|
|
123
161
|
|
|
124
162
|
return { acquired: false, reason, existingPid };
|
|
@@ -233,6 +271,17 @@ export function releaseSessionLock(basePath: string): void {
|
|
|
233
271
|
// Non-fatal
|
|
234
272
|
}
|
|
235
273
|
|
|
274
|
+
// Remove the proper-lockfile directory (.gsd.lock/) if it exists.
|
|
275
|
+
// proper-lockfile creates this directory as the OS-level lock mechanism.
|
|
276
|
+
// If the process exits without calling _releaseFunction (SIGKILL, crash),
|
|
277
|
+
// this directory is stranded and blocks the next session (#1245).
|
|
278
|
+
try {
|
|
279
|
+
const lockDir = join(gsdRoot(basePath) + ".lock");
|
|
280
|
+
if (existsSync(lockDir)) rmSync(lockDir, { recursive: true, force: true });
|
|
281
|
+
} catch {
|
|
282
|
+
// Non-fatal
|
|
283
|
+
}
|
|
284
|
+
|
|
236
285
|
_lockedPath = null;
|
|
237
286
|
_lockPid = 0;
|
|
238
287
|
}
|
|
@@ -11,9 +11,6 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
-
writeFileSync,
|
|
15
|
-
readFileSync,
|
|
16
|
-
renameSync,
|
|
17
14
|
unlinkSync,
|
|
18
15
|
readdirSync,
|
|
19
16
|
mkdirSync,
|
|
@@ -21,6 +18,7 @@ import {
|
|
|
21
18
|
} from "node:fs";
|
|
22
19
|
import { join } from "node:path";
|
|
23
20
|
import { gsdRoot } from "./paths.js";
|
|
21
|
+
import { loadJsonFileOrNull, writeJsonFileAtomic } from "./json-persistence.js";
|
|
24
22
|
|
|
25
23
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
26
24
|
|
|
@@ -49,9 +47,16 @@ export interface SignalMessage {
|
|
|
49
47
|
const PARALLEL_DIR = "parallel";
|
|
50
48
|
const STATUS_SUFFIX = ".status.json";
|
|
51
49
|
const SIGNAL_SUFFIX = ".signal.json";
|
|
52
|
-
const TMP_SUFFIX = ".tmp";
|
|
53
50
|
const DEFAULT_STALE_TIMEOUT_MS = 30_000;
|
|
54
51
|
|
|
52
|
+
function isSessionStatus(data: unknown): data is SessionStatus {
|
|
53
|
+
return data !== null && typeof data === "object" && "milestoneId" in data && "pid" in data;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isSignalMessage(data: unknown): data is SignalMessage {
|
|
57
|
+
return data !== null && typeof data === "object" && "signal" in data && "sentAt" in data;
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
56
61
|
|
|
57
62
|
function parallelDir(basePath: string): string {
|
|
@@ -86,25 +91,13 @@ function isPidAlive(pid: number): boolean {
|
|
|
86
91
|
|
|
87
92
|
/** Write session status atomically (write to .tmp, then rename). */
|
|
88
93
|
export function writeSessionStatus(basePath: string, status: SessionStatus): void {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const dest = statusPath(basePath, status.milestoneId);
|
|
92
|
-
const tmp = dest + TMP_SUFFIX;
|
|
93
|
-
writeFileSync(tmp, JSON.stringify(status, null, 2), "utf-8");
|
|
94
|
-
renameSync(tmp, dest);
|
|
95
|
-
} catch { /* non-fatal */ }
|
|
94
|
+
ensureParallelDir(basePath);
|
|
95
|
+
writeJsonFileAtomic(statusPath(basePath, status.milestoneId), status);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/** Read a specific milestone's session status. */
|
|
99
99
|
export function readSessionStatus(basePath: string, milestoneId: string): SessionStatus | null {
|
|
100
|
-
|
|
101
|
-
const p = statusPath(basePath, milestoneId);
|
|
102
|
-
if (!existsSync(p)) return null;
|
|
103
|
-
const raw = readFileSync(p, "utf-8");
|
|
104
|
-
return JSON.parse(raw) as SessionStatus;
|
|
105
|
-
} catch {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
100
|
+
return loadJsonFileOrNull(statusPath(basePath, milestoneId), isSessionStatus);
|
|
108
101
|
}
|
|
109
102
|
|
|
110
103
|
/** Read all session status files from .gsd/parallel/. */
|
|
@@ -114,13 +107,10 @@ export function readAllSessionStatuses(basePath: string): SessionStatus[] {
|
|
|
114
107
|
|
|
115
108
|
const results: SessionStatus[] = [];
|
|
116
109
|
try {
|
|
117
|
-
const
|
|
118
|
-
for (const entry of entries) {
|
|
110
|
+
for (const entry of readdirSync(dir)) {
|
|
119
111
|
if (!entry.endsWith(STATUS_SUFFIX)) continue;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
results.push(JSON.parse(raw) as SessionStatus);
|
|
123
|
-
} catch { /* skip corrupt files */ }
|
|
112
|
+
const status = loadJsonFileOrNull(join(dir, entry), isSessionStatus);
|
|
113
|
+
if (status) results.push(status);
|
|
124
114
|
}
|
|
125
115
|
} catch { /* non-fatal */ }
|
|
126
116
|
return results;
|
|
@@ -138,27 +128,19 @@ export function removeSessionStatus(basePath: string, milestoneId: string): void
|
|
|
138
128
|
|
|
139
129
|
/** Write a signal file for a worker to consume. */
|
|
140
130
|
export function sendSignal(basePath: string, milestoneId: string, signal: SessionSignal): void {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const tmp = dest + TMP_SUFFIX;
|
|
145
|
-
const msg: SignalMessage = { signal, sentAt: Date.now(), from: "coordinator" };
|
|
146
|
-
writeFileSync(tmp, JSON.stringify(msg, null, 2), "utf-8");
|
|
147
|
-
renameSync(tmp, dest);
|
|
148
|
-
} catch { /* non-fatal */ }
|
|
131
|
+
ensureParallelDir(basePath);
|
|
132
|
+
const msg: SignalMessage = { signal, sentAt: Date.now(), from: "coordinator" };
|
|
133
|
+
writeJsonFileAtomic(signalPath(basePath, milestoneId), msg);
|
|
149
134
|
}
|
|
150
135
|
|
|
151
136
|
/** Read and delete a signal file (atomic consume). Returns null if no signal pending. */
|
|
152
137
|
export function consumeSignal(basePath: string, milestoneId: string): SignalMessage | null {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
unlinkSync(p);
|
|
158
|
-
return JSON.parse(raw) as SignalMessage;
|
|
159
|
-
} catch {
|
|
160
|
-
return null;
|
|
138
|
+
const p = signalPath(basePath, milestoneId);
|
|
139
|
+
const msg = loadJsonFileOrNull(p, isSignalMessage);
|
|
140
|
+
if (msg) {
|
|
141
|
+
try { unlinkSync(p); } catch { /* non-fatal */ }
|
|
161
142
|
}
|
|
143
|
+
return msg;
|
|
162
144
|
}
|
|
163
145
|
|
|
164
146
|
// ─── Stale Detection ───────────────────────────────────────────────────────
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import test from "node:test";
|
|
8
8
|
import assert from "node:assert/strict";
|
|
9
|
-
import { existsSync, mkdtempSync, mkdirSync, readdirSync, rmSync, utimesSync, writeFileSync, readFileSync } from "node:fs";
|
|
9
|
+
import { existsSync, mkdtempSync, mkdirSync, readdirSync, realpathSync, rmSync, utimesSync, writeFileSync, readFileSync } from "node:fs";
|
|
10
10
|
import { join, dirname } from "node:path";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
@@ -18,7 +18,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
18
18
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
19
19
|
|
|
20
20
|
function createTmpDir(): string {
|
|
21
|
-
return mkdtempSync(join(tmpdir(), "gsd-activity-test-"));
|
|
21
|
+
return realpathSync(mkdtempSync(join(tmpdir(), "gsd-activity-test-")));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function writeActivityFile(dir: string, seq: string, name: string): string {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
getBudgetAlertLevel,
|
|
6
6
|
getBudgetEnforcementAction,
|
|
7
7
|
getNewBudgetAlertLevel,
|
|
8
|
-
} from "../auto.js";
|
|
8
|
+
} from "../auto-budget.js";
|
|
9
9
|
|
|
10
10
|
test("getBudgetAlertLevel returns the expected threshold bucket", () => {
|
|
11
11
|
assert.equal(getBudgetAlertLevel(0.10), 0);
|
|
@@ -308,8 +308,8 @@ test("loadPersistedKeys unions keys from project root and worktree", () => {
|
|
|
308
308
|
});
|
|
309
309
|
|
|
310
310
|
test("completed-units.json set-union merge produces correct result", () => {
|
|
311
|
-
// Verify that a manual set-union merge
|
|
312
|
-
//
|
|
311
|
+
// Verify that a manual set-union merge correctly merges two JSON arrays
|
|
312
|
+
// of completed-unit keys.
|
|
313
313
|
const projectRoot = makeTmpBase();
|
|
314
314
|
const worktree = makeTmpBase();
|
|
315
315
|
try {
|
|
@@ -320,7 +320,7 @@ test("completed-units.json set-union merge produces correct result", () => {
|
|
|
320
320
|
writeFileSync(prKeysFile, JSON.stringify(["a", "b"]));
|
|
321
321
|
writeFileSync(wtKeysFile, JSON.stringify(["b", "c", "d"]));
|
|
322
322
|
|
|
323
|
-
// Perform
|
|
323
|
+
// Perform a set-union merge of two JSON key arrays
|
|
324
324
|
const srcKeys: string[] = JSON.parse(readFileSync(wtKeysFile, "utf8"));
|
|
325
325
|
let dstKeys: string[] = [];
|
|
326
326
|
if (existsSync(prKeysFile)) {
|
|
@@ -17,8 +17,8 @@ import { tmpdir } from "node:os";
|
|
|
17
17
|
import {
|
|
18
18
|
_getUnitConsecutiveSkips,
|
|
19
19
|
_resetUnitConsecutiveSkips,
|
|
20
|
-
MAX_CONSECUTIVE_SKIPS,
|
|
21
20
|
} from "../auto.ts";
|
|
21
|
+
import { MAX_CONSECUTIVE_SKIPS } from "../auto/session.ts";
|
|
22
22
|
import { persistCompletedKey, removePersistedKey, loadPersistedKeys } from "../auto-recovery.ts";
|
|
23
23
|
import { createTestContext } from "./test-helpers.ts";
|
|
24
24
|
|
|
@@ -153,64 +153,6 @@ async function main(): Promise<void> {
|
|
|
153
153
|
// After teardown, originalBase should be null
|
|
154
154
|
assertEq(getAutoWorktreeOriginalBase(), null, "no split-brain: originalBase cleared");
|
|
155
155
|
|
|
156
|
-
// ─── #778: reconcile plan checkboxes on re-attach ─────────────────
|
|
157
|
-
console.log("\n=== #778: reconcile plan checkboxes on re-attach ===");
|
|
158
|
-
{
|
|
159
|
-
// Simulate: T01 [x] was committed to milestone branch, T02 [x] was
|
|
160
|
-
// written to project root by syncStateToProjectRoot() but the
|
|
161
|
-
// auto-commit crashed before it fired. On restart the worktree is
|
|
162
|
-
// re-created from the milestone branch HEAD (T02 still [ ]).
|
|
163
|
-
// reconcilePlanCheckboxes should forward-apply T02 [x] from the root.
|
|
164
|
-
|
|
165
|
-
const planRelPath = join(".gsd", "milestones", "M004", "slices", "S01", "S01-PLAN.md");
|
|
166
|
-
const planDir = join(tempDir, ".gsd", "milestones", "M004", "slices", "S01");
|
|
167
|
-
const { mkdirSync: mkdir, writeFileSync: write, readFileSync: read } = await import("node:fs");
|
|
168
|
-
|
|
169
|
-
// Plan on integration branch (project root): T01 [x], T02 [x]
|
|
170
|
-
mkdir(planDir, { recursive: true });
|
|
171
|
-
write(
|
|
172
|
-
join(tempDir, planRelPath),
|
|
173
|
-
"# S01 Plan\n- [x] **T01:** task one\n- [x] **T02:** task two\n- [ ] **T03:** task three\n",
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
// Write integration-branch plan to git so milestone branch starts from it
|
|
177
|
-
run(`git add .`, tempDir);
|
|
178
|
-
run(`git commit -m "add plan with T01 and T02 checked" --allow-empty`, tempDir);
|
|
179
|
-
|
|
180
|
-
// Create milestone branch with only T01 [x] (simulating crash before T02 commit)
|
|
181
|
-
const milestoneBranch = "milestone/M004";
|
|
182
|
-
run(`git checkout -b ${milestoneBranch}`, tempDir);
|
|
183
|
-
mkdir(planDir, { recursive: true });
|
|
184
|
-
write(
|
|
185
|
-
join(tempDir, planRelPath),
|
|
186
|
-
"# S01 Plan\n- [x] **T01:** task one\n- [ ] **T02:** task two\n- [ ] **T03:** task three\n",
|
|
187
|
-
);
|
|
188
|
-
run(`git add .`, tempDir);
|
|
189
|
-
run(`git commit -m "milestone: only T01 checked"`, tempDir);
|
|
190
|
-
run(`git checkout main`, tempDir);
|
|
191
|
-
|
|
192
|
-
// Restore project root plan (T01+T02 [x]) — simulates syncStateToProjectRoot
|
|
193
|
-
write(
|
|
194
|
-
join(tempDir, planRelPath),
|
|
195
|
-
"# S01 Plan\n- [x] **T01:** task one\n- [x] **T02:** task two\n- [ ] **T03:** task three\n",
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
// Create worktree re-attached to existing milestone branch (T02 still [ ] in branch)
|
|
199
|
-
const wtPath = createAutoWorktree(tempDir, "M004");
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
const wtPlanPath = join(wtPath, planRelPath);
|
|
203
|
-
assertTrue(existsSync(wtPlanPath), "plan file exists in worktree after re-attach");
|
|
204
|
-
|
|
205
|
-
const wtPlan = read(wtPlanPath, "utf-8");
|
|
206
|
-
assertTrue(wtPlan.includes("- [x] **T02:"), "T02 should be [x] after reconciliation (was [ ] on branch)");
|
|
207
|
-
assertTrue(wtPlan.includes("- [x] **T01:"), "T01 stays [x]");
|
|
208
|
-
assertTrue(wtPlan.includes("- [ ] **T03:"), "T03 stays [ ] (not in root either)");
|
|
209
|
-
} finally {
|
|
210
|
-
teardownAutoWorktree(tempDir, "M004");
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
156
|
} finally {
|
|
215
157
|
// Always restore cwd and clean up
|
|
216
158
|
process.chdir(savedCwd);
|
|
@@ -231,15 +231,14 @@ None
|
|
|
231
231
|
const detect = await runGSDDoctor(dir);
|
|
232
232
|
const gitignoreIssues = detect.issues.filter(i => i.code === "gitignore_missing_patterns");
|
|
233
233
|
assertTrue(gitignoreIssues.length > 0, "detects missing gitignore patterns");
|
|
234
|
-
assertTrue(gitignoreIssues[0]?.message.includes(".gsd
|
|
234
|
+
assertTrue(gitignoreIssues[0]?.message.includes(".gsd"), "message lists missing .gsd pattern");
|
|
235
235
|
|
|
236
236
|
const fixed = await runGSDDoctor(dir, { fix: true });
|
|
237
237
|
assertTrue(fixed.fixesApplied.some(f => f.includes("added missing GSD runtime patterns")), "fix adds patterns");
|
|
238
238
|
|
|
239
|
-
// Verify
|
|
239
|
+
// Verify .gsd entry was added (external state symlink)
|
|
240
240
|
const content = readFileSync(join(dir, ".gitignore"), "utf-8");
|
|
241
|
-
assertTrue(content.includes(".gsd
|
|
242
|
-
assertTrue(content.includes(".gsd/auto.lock"), "gitignore now has auto.lock pattern");
|
|
241
|
+
assertTrue(content.includes(".gsd"), "gitignore now has .gsd entry");
|
|
243
242
|
}
|
|
244
243
|
} else {
|
|
245
244
|
console.log("\n=== gitignore_missing_patterns (skipped on Windows) ===");
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// Tests for the SEPARATOR_PREFIX convention used by ExtensionSelectorComponent
|
|
1
|
+
// Tests for the SEPARATOR_PREFIX convention used by ExtensionSelectorComponent
|
|
2
|
+
// and the two-step provider→model picker in configureModels.
|
|
3
|
+
//
|
|
2
4
|
// We cannot import the component directly in node:test because its transitive
|
|
3
5
|
// dependency (countdown-timer.ts) uses TypeScript parameter properties which
|
|
4
6
|
// are unsupported under --experimental-strip-types. Instead we duplicate the
|
|
@@ -69,16 +71,17 @@ describe("separator detection", () => {
|
|
|
69
71
|
});
|
|
70
72
|
});
|
|
71
73
|
|
|
72
|
-
describe("model
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
describe("two-step provider→model picker", () => {
|
|
75
|
+
// Simulate the grouping logic from configureModels
|
|
76
|
+
const availableModels = [
|
|
77
|
+
{ id: "claude-opus-4-6", provider: "anthropic" },
|
|
78
|
+
{ id: "gpt-4o", provider: "openai" },
|
|
79
|
+
{ id: "claude-sonnet-4-5", provider: "anthropic" },
|
|
80
|
+
{ id: "o3-mini", provider: "openai" },
|
|
81
|
+
{ id: "claude-haiku-4-5", provider: "anthropic" },
|
|
82
|
+
];
|
|
81
83
|
|
|
84
|
+
function buildProviderGroups() {
|
|
82
85
|
const byProvider = new Map<string, typeof availableModels>();
|
|
83
86
|
for (const m of availableModels) {
|
|
84
87
|
let group = byProvider.get(m.provider);
|
|
@@ -89,34 +92,53 @@ describe("model grouping", () => {
|
|
|
89
92
|
group.push(m);
|
|
90
93
|
}
|
|
91
94
|
const providers = Array.from(byProvider.keys()).sort((a, b) => a.localeCompare(b));
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
for (const provider of providers) {
|
|
95
|
-
const group = byProvider.get(provider)!;
|
|
96
|
-
modelOptions.push(`${SEPARATOR_PREFIX} ${provider} (${group.length}) ${SEPARATOR_PREFIX}`);
|
|
97
|
-
for (const m of group) {
|
|
98
|
-
modelOptions.push(`${m.id} · ${m.provider}`);
|
|
99
|
-
}
|
|
95
|
+
for (const group of byProvider.values()) {
|
|
96
|
+
group.sort((a, b) => a.id.localeCompare(b.id));
|
|
100
97
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
assert.
|
|
115
|
-
assert.
|
|
116
|
-
assert.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
98
|
+
return { byProvider, providers };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
test("provider menu lists providers with model counts", () => {
|
|
102
|
+
const { providers, byProvider } = buildProviderGroups();
|
|
103
|
+
const providerOptions = providers.map(p => {
|
|
104
|
+
const count = byProvider.get(p)!.length;
|
|
105
|
+
return `${p} (${count} models)`;
|
|
106
|
+
});
|
|
107
|
+
providerOptions.push("(keep current)", "(clear)", "(type manually)");
|
|
108
|
+
|
|
109
|
+
assert.strictEqual(providerOptions[0], "anthropic (3 models)");
|
|
110
|
+
assert.strictEqual(providerOptions[1], "openai (2 models)");
|
|
111
|
+
assert.strictEqual(providerOptions[2], "(keep current)");
|
|
112
|
+
assert.strictEqual(providerOptions[3], "(clear)");
|
|
113
|
+
assert.strictEqual(providerOptions[4], "(type manually)");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("model menu for a provider is sorted alphabetically", () => {
|
|
117
|
+
const { byProvider } = buildProviderGroups();
|
|
118
|
+
const anthropicModels = byProvider.get("anthropic")!;
|
|
119
|
+
const modelOptions = anthropicModels.map(m => m.id);
|
|
120
|
+
|
|
121
|
+
assert.strictEqual(modelOptions[0], "claude-haiku-4-5");
|
|
122
|
+
assert.strictEqual(modelOptions[1], "claude-opus-4-6");
|
|
123
|
+
assert.strictEqual(modelOptions[2], "claude-sonnet-4-5");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("provider name is extracted correctly from choice string", () => {
|
|
127
|
+
const choice = "anthropic (3 models)";
|
|
128
|
+
const providerName = choice.replace(/ \(\d+ models?\)$/, "");
|
|
129
|
+
assert.strictEqual(providerName, "anthropic");
|
|
130
|
+
|
|
131
|
+
const singleChoice = "ollama (1 model)";
|
|
132
|
+
const singleProvider = singleChoice.replace(/ \(\d+ models?\)$/, "");
|
|
133
|
+
assert.strictEqual(singleProvider, "ollama");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("openai models are sorted within their group", () => {
|
|
137
|
+
const { byProvider } = buildProviderGroups();
|
|
138
|
+
const openaiModels = byProvider.get("openai")!;
|
|
139
|
+
const modelOptions = openaiModels.map(m => m.id);
|
|
140
|
+
|
|
141
|
+
assert.strictEqual(modelOptions[0], "gpt-4o");
|
|
142
|
+
assert.strictEqual(modelOptions[1], "o3-mini");
|
|
121
143
|
});
|
|
122
144
|
});
|
|
@@ -311,7 +311,7 @@ async function main(): Promise<void> {
|
|
|
311
311
|
// Test 2: Uncommitted .gsd/ planning files are available in worktree
|
|
312
312
|
//
|
|
313
313
|
// When auto-mode starts, .gsd/ files may be untracked/uncommitted.
|
|
314
|
-
//
|
|
314
|
+
// Planning artifacts should be carried into the worktree even if
|
|
315
315
|
// they weren't committed on the feature branch.
|
|
316
316
|
// ================================================================
|
|
317
317
|
console.log("\n=== Untracked planning files copied to worktree ===");
|
|
@@ -341,23 +341,10 @@ async function main(): Promise<void> {
|
|
|
341
341
|
const wtPath = createAutoWorktree(repo, milestoneId);
|
|
342
342
|
tempDirs.push(wtPath);
|
|
343
343
|
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
);
|
|
349
|
-
assertTrue(
|
|
350
|
-
existsSync(join(wtPath, ".gsd", "milestones", milestoneId, "slices", "S01", "S01-PLAN.md")),
|
|
351
|
-
"S01-PLAN.md copied to worktree",
|
|
352
|
-
);
|
|
353
|
-
assertTrue(
|
|
354
|
-
existsSync(join(wtPath, ".gsd", "PROJECT.md")),
|
|
355
|
-
"PROJECT.md copied to worktree",
|
|
356
|
-
);
|
|
357
|
-
assertTrue(
|
|
358
|
-
existsSync(join(wtPath, ".gsd", "DECISIONS.md")),
|
|
359
|
-
"DECISIONS.md copied to worktree",
|
|
360
|
-
);
|
|
344
|
+
// With external state, worktree .gsd is a symlink to shared state.
|
|
345
|
+
// Verify symlink was created (planning files are shared, not copied).
|
|
346
|
+
const wtGsd = join(wtPath, ".gsd");
|
|
347
|
+
assertTrue(existsSync(wtGsd), "worktree .gsd exists (symlink or dir)");
|
|
361
348
|
|
|
362
349
|
// Clean up: chdir back before teardown
|
|
363
350
|
process.chdir(savedCwd);
|
|
@@ -1167,53 +1167,26 @@ async function main(): Promise<void> {
|
|
|
1167
1167
|
rmSync(repo, { recursive: true, force: true });
|
|
1168
1168
|
}
|
|
1169
1169
|
|
|
1170
|
-
// ─── ensureGitignore:
|
|
1170
|
+
// ─── ensureGitignore: always adds .gsd to gitignore ──────────────────
|
|
1171
1171
|
|
|
1172
|
-
console.log("\n=== ensureGitignore:
|
|
1172
|
+
console.log("\n=== ensureGitignore: adds .gsd entry ===");
|
|
1173
1173
|
|
|
1174
1174
|
{
|
|
1175
1175
|
const { ensureGitignore } = await import("../gitignore.ts");
|
|
1176
|
-
const repo = mkdtempSync(join(tmpdir(), "gsd-gitignore-
|
|
1176
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-gitignore-external-state-"));
|
|
1177
1177
|
|
|
1178
|
-
//
|
|
1179
|
-
const modified = ensureGitignore(repo
|
|
1180
|
-
assertTrue(modified, "
|
|
1178
|
+
// Should add .gsd to gitignore (external state dir is a symlink)
|
|
1179
|
+
const modified = ensureGitignore(repo);
|
|
1180
|
+
assertTrue(modified, "ensureGitignore: gitignore was modified");
|
|
1181
1181
|
|
|
1182
1182
|
const { readFileSync } = await import("node:fs");
|
|
1183
1183
|
const content = readFileSync(join(repo, ".gitignore"), "utf-8");
|
|
1184
|
-
|
|
1185
|
-
assertTrue(
|
|
1186
|
-
|
|
1187
|
-
// Should NOT contain individual runtime patterns (those are subsumed by blanket .gsd/)
|
|
1188
|
-
// But it's OK if it does — the blanket .gsd/ covers everything
|
|
1184
|
+
const lines = content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#"));
|
|
1185
|
+
assertTrue(lines.includes(".gsd"), "ensureGitignore: .gitignore contains .gsd");
|
|
1189
1186
|
|
|
1190
1187
|
// Idempotent — calling again doesn't add duplicates
|
|
1191
|
-
const modified2 = ensureGitignore(repo
|
|
1192
|
-
assertTrue(!modified2, "
|
|
1193
|
-
|
|
1194
|
-
rmSync(repo, { recursive: true, force: true });
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
// ─── ensureGitignore: commit_docs true removes blanket .gsd/ ────────
|
|
1198
|
-
|
|
1199
|
-
console.log("\n=== ensureGitignore: commit_docs true self-heals ===");
|
|
1200
|
-
|
|
1201
|
-
{
|
|
1202
|
-
const { ensureGitignore } = await import("../gitignore.ts");
|
|
1203
|
-
const repo = mkdtempSync(join(tmpdir(), "gsd-gitignore-selfheal-"));
|
|
1204
|
-
|
|
1205
|
-
// Start with a gitignore that has a blanket .gsd/ (e.g., user switched setting)
|
|
1206
|
-
writeFileSync(join(repo, ".gitignore"), ".gsd/\n");
|
|
1207
|
-
|
|
1208
|
-
const modified = ensureGitignore(repo, { commitDocs: true });
|
|
1209
|
-
assertTrue(modified, "commit_docs=true: gitignore was modified");
|
|
1210
|
-
|
|
1211
|
-
const { readFileSync } = await import("node:fs");
|
|
1212
|
-
const content = readFileSync(join(repo, ".gitignore"), "utf-8");
|
|
1213
|
-
// Blanket .gsd/ should be removed
|
|
1214
|
-
const lines = content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#"));
|
|
1215
|
-
assertTrue(!lines.includes(".gsd/"), "commit_docs=true: blanket .gsd/ was removed");
|
|
1216
|
-
assertTrue(!lines.includes(".gsd"), "commit_docs=true: blanket .gsd was removed");
|
|
1188
|
+
const modified2 = ensureGitignore(repo);
|
|
1189
|
+
assertTrue(!modified2, "ensureGitignore: second call is idempotent");
|
|
1217
1190
|
|
|
1218
1191
|
rmSync(repo, { recursive: true, force: true });
|
|
1219
1192
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import test from 'node:test';
|
|
12
12
|
import assert from 'node:assert/strict';
|
|
13
|
-
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
|
|
13
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, realpathSync } from 'node:fs';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
import { tmpdir } from 'node:os';
|
|
16
16
|
import { GSD_ROOT_FILES, resolveGsdRootFile } from '../paths.ts';
|
|
@@ -27,7 +27,7 @@ test('knowledge: KNOWLEDGE key exists in GSD_ROOT_FILES', () => {
|
|
|
27
27
|
// ─── resolveGsdRootFile resolves KNOWLEDGE.md ───────────────────────────────
|
|
28
28
|
|
|
29
29
|
test('knowledge: resolveGsdRootFile returns canonical path when KNOWLEDGE.md exists', () => {
|
|
30
|
-
const tmp = mkdtempSync(join(tmpdir(), 'gsd-knowledge-'));
|
|
30
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
|
|
31
31
|
const gsdDir = join(tmp, '.gsd');
|
|
32
32
|
mkdirSync(gsdDir, { recursive: true });
|
|
33
33
|
writeFileSync(join(gsdDir, 'KNOWLEDGE.md'), '# Project Knowledge\n');
|
|
@@ -39,7 +39,7 @@ test('knowledge: resolveGsdRootFile returns canonical path when KNOWLEDGE.md exi
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
test('knowledge: resolveGsdRootFile resolves when legacy knowledge.md exists', () => {
|
|
42
|
-
const tmp = mkdtempSync(join(tmpdir(), 'gsd-knowledge-'));
|
|
42
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
|
|
43
43
|
const gsdDir = join(tmp, '.gsd');
|
|
44
44
|
mkdirSync(gsdDir, { recursive: true });
|
|
45
45
|
writeFileSync(join(gsdDir, 'knowledge.md'), '# Project Knowledge\n');
|
|
@@ -58,7 +58,7 @@ test('knowledge: resolveGsdRootFile resolves when legacy knowledge.md exists', (
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
test('knowledge: resolveGsdRootFile returns canonical path when file does not exist', () => {
|
|
61
|
-
const tmp = mkdtempSync(join(tmpdir(), 'gsd-knowledge-'));
|
|
61
|
+
const tmp = realpathSync(mkdtempSync(join(tmpdir(), 'gsd-knowledge-')));
|
|
62
62
|
const gsdDir = join(tmp, '.gsd');
|
|
63
63
|
mkdirSync(gsdDir, { recursive: true });
|
|
64
64
|
|