gsd-pi 2.76.0 → 2.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -25
- package/dist/claude-cli-check.js +32 -3
- package/dist/mcp-server.d.ts +7 -0
- package/dist/mcp-server.js +35 -1
- package/dist/onboarding.js +45 -0
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +2 -8
- package/dist/resources/agents/researcher.md +1 -1
- package/dist/resources/extensions/claude-code-cli/readiness.js +31 -8
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
- package/dist/resources/extensions/gsd/auto/loop.js +9 -0
- package/dist/resources/extensions/gsd/auto/phases.js +104 -11
- package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
- package/dist/resources/extensions/gsd/auto/session.js +22 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +53 -16
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
- package/dist/resources/extensions/gsd/auto-start.js +58 -57
- package/dist/resources/extensions/gsd/auto-verification.js +33 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
- package/dist/resources/extensions/gsd/auto.js +70 -28
- package/dist/resources/extensions/gsd/blocked-models.js +68 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +93 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +12 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +84 -23
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +54 -89
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
- package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
- package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
- package/dist/resources/extensions/gsd/db-writer.js +88 -16
- package/dist/resources/extensions/gsd/doctor-git-checks.js +23 -29
- package/dist/resources/extensions/gsd/doctor-providers.js +51 -5
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +1 -0
- package/dist/resources/extensions/gsd/error-classifier.js +31 -3
- package/dist/resources/extensions/gsd/exec-history.js +120 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +168 -23
- package/dist/resources/extensions/gsd/guided-flow.js +190 -1
- package/dist/resources/extensions/gsd/health-widget.js +4 -1
- package/dist/resources/extensions/gsd/hook-emitter.js +108 -0
- package/dist/resources/extensions/gsd/init-wizard.js +15 -1
- package/dist/resources/extensions/gsd/key-manager.js +28 -0
- package/dist/resources/extensions/gsd/memory-backfill.js +126 -0
- package/dist/resources/extensions/gsd/memory-store.js +19 -0
- package/dist/resources/extensions/gsd/model-router.js +36 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
- package/dist/resources/extensions/gsd/preferences-types.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
- package/dist/resources/extensions/gsd/preferences.js +17 -17
- package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -0
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
- package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
- package/dist/resources/extensions/gsd/state.js +43 -4
- package/dist/resources/extensions/gsd/token-counter.js +22 -5
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +26 -1
- package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
- package/dist/resources/extensions/gsd/workflow-templates/spike.md +6 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
- package/dist/resources/extensions/search-the-web/command-search-provider.js +5 -4
- package/dist/resources/extensions/search-the-web/native-search.js +45 -13
- package/dist/resources/skills/api-design/SKILL.md +190 -0
- package/dist/resources/skills/create-mcp-server/SKILL.md +121 -0
- package/dist/resources/skills/decompose-into-slices/SKILL.md +139 -0
- package/dist/resources/skills/dependency-upgrade/SKILL.md +158 -0
- package/dist/resources/skills/design-an-interface/SKILL.md +102 -0
- package/dist/resources/skills/forensics/SKILL.md +153 -0
- package/dist/resources/skills/grill-me/SKILL.md +93 -0
- package/dist/resources/skills/handoff/SKILL.md +121 -0
- package/dist/resources/skills/observability/SKILL.md +174 -0
- package/dist/resources/skills/security-review/SKILL.md +181 -0
- package/dist/resources/skills/spike-wrap-up/SKILL.md +138 -0
- package/dist/resources/skills/tdd/SKILL.md +112 -0
- package/dist/resources/skills/verify-before-complete/SKILL.md +98 -0
- package/dist/resources/skills/write-docs/SKILL.md +82 -0
- package/dist/resources/skills/write-milestone-brief/SKILL.md +135 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/dist/welcome-screen.js +6 -1
- package/dist/wizard.js +2 -0
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
- package/packages/mcp-server/dist/remote-questions.js +732 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +7 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +70 -8
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts +14 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +49 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +163 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -3
- package/packages/mcp-server/src/mcp-server.test.ts +67 -0
- package/packages/mcp-server/src/remote-questions.test.ts +294 -0
- package/packages/mcp-server/src/remote-questions.ts +916 -0
- package/packages/mcp-server/src/server.ts +89 -14
- package/packages/mcp-server/src/session-manager.ts +43 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
- package/packages/mcp-server/src/workflow-tools.ts +215 -43
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +12 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +30 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +14 -0
- package/packages/pi-agent-core/src/types.ts +34 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/custom.d.ts +38 -0
- package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/custom.js +41 -0
- package/packages/pi-ai/dist/models/custom.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +13 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +13 -4
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +16 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +12 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +164 -14
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +15 -3
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +67 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +16 -3
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +67 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +289 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +1 -0
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/models/custom.ts +42 -0
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
- package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
- package/packages/pi-ai/src/providers/anthropic.ts +15 -4
- package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
- package/packages/pi-ai/src/providers/simple-options.ts +17 -1
- package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
- package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +200 -23
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +12 -2
- package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +84 -0
- package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +15 -5
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +84 -0
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +16 -5
- package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +363 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -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 +32 -2
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.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 +4 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +35 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +233 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +205 -2
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts +53 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.js +337 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +234 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js +40 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +55 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.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.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +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 +13 -1
- 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-abort-order.test.ts +3 -2
- package/packages/pi-coding-agent/src/core/agent-session.ts +38 -2
- package/packages/pi-coding-agent/src/core/extensions/index.ts +16 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +5 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +351 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +258 -0
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +269 -0
- package/packages/pi-coding-agent/src/core/hooks-runner.ts +460 -0
- package/packages/pi-coding-agent/src/core/index.ts +10 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
- package/packages/pi-coding-agent/src/core/model-registry-auth-header.test.ts +44 -0
- package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
- package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
- package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
- package/packages/pi-coding-agent/src/core/settings-manager.ts +57 -0
- package/packages/pi-coding-agent/src/index.ts +16 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/scripts/link-workspace-packages.cjs +1 -0
- package/src/resources/agents/researcher.md +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +32 -8
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
- package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
- package/src/resources/extensions/gsd/auto/loop.ts +9 -0
- package/src/resources/extensions/gsd/auto/phases.ts +131 -10
- package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
- package/src/resources/extensions/gsd/auto/session.ts +35 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +71 -15
- package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
- package/src/resources/extensions/gsd/auto-start.ts +60 -68
- package/src/resources/extensions/gsd/auto-verification.ts +33 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
- package/src/resources/extensions/gsd/auto.ts +73 -28
- package/src/resources/extensions/gsd/blocked-models.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +120 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +5 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +89 -26
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
- package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +55 -90
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
- package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
- package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
- package/src/resources/extensions/gsd/db-writer.ts +88 -17
- package/src/resources/extensions/gsd/doctor-git-checks.ts +23 -27
- package/src/resources/extensions/gsd/doctor-providers.ts +59 -6
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +2 -0
- package/src/resources/extensions/gsd/error-classifier.ts +36 -3
- package/src/resources/extensions/gsd/exec-history.ts +153 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- package/src/resources/extensions/gsd/gsd-db.ts +186 -23
- package/src/resources/extensions/gsd/guided-flow.ts +222 -1
- package/src/resources/extensions/gsd/health-widget.ts +3 -1
- package/src/resources/extensions/gsd/hook-emitter.ts +188 -0
- package/src/resources/extensions/gsd/init-wizard.ts +15 -1
- package/src/resources/extensions/gsd/journal.ts +2 -1
- package/src/resources/extensions/gsd/key-manager.ts +28 -0
- package/src/resources/extensions/gsd/memory-backfill.ts +140 -0
- package/src/resources/extensions/gsd/memory-store.ts +26 -0
- package/src/resources/extensions/gsd/model-router.ts +42 -1
- package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
- package/src/resources/extensions/gsd/preferences-types.ts +46 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
- package/src/resources/extensions/gsd/preferences.ts +17 -17
- package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -0
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
- package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
- package/src/resources/extensions/gsd/state.ts +45 -4
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +95 -1
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/blocked-models.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +68 -66
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +8 -4
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +148 -3
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +306 -1
- package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +40 -9
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-git-symlink-cwd.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/load-memory-block.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +103 -4
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
- package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
- package/src/resources/extensions/gsd/token-counter.ts +22 -5
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +31 -1
- package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +4 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/extensions/gsd/workflow-templates/spike.md +6 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
- package/src/resources/extensions/search-the-web/command-search-provider.ts +5 -4
- package/src/resources/extensions/search-the-web/native-search.ts +48 -12
- package/src/resources/skills/api-design/SKILL.md +190 -0
- package/src/resources/skills/create-mcp-server/SKILL.md +121 -0
- package/src/resources/skills/decompose-into-slices/SKILL.md +139 -0
- package/src/resources/skills/dependency-upgrade/SKILL.md +158 -0
- package/src/resources/skills/design-an-interface/SKILL.md +102 -0
- package/src/resources/skills/forensics/SKILL.md +153 -0
- package/src/resources/skills/grill-me/SKILL.md +93 -0
- package/src/resources/skills/handoff/SKILL.md +121 -0
- package/src/resources/skills/observability/SKILL.md +174 -0
- package/src/resources/skills/security-review/SKILL.md +181 -0
- package/src/resources/skills/spike-wrap-up/SKILL.md +138 -0
- package/src/resources/skills/tdd/SKILL.md +112 -0
- package/src/resources/skills/verify-before-complete/SKILL.md +98 -0
- package/src/resources/skills/write-docs/SKILL.md +82 -0
- package/src/resources/skills/write-milestone-brief/SKILL.md +135 -0
- /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD-2 / guided-flow — regression tests for #4573
|
|
3
|
+
*
|
|
4
|
+
* Covers two recovery paths:
|
|
5
|
+
* - maybeHandleReadyPhraseWithoutFiles: nudge when LLM emits
|
|
6
|
+
* "Milestone M001 ready." without writing CONTEXT.md / ROADMAP.md
|
|
7
|
+
* - maybeHandleEmptyIntentTurn: nudge when LLM narrates intent but
|
|
8
|
+
* emits no tool-use blocks
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, test, beforeEach } from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
setPendingAutoStart,
|
|
19
|
+
clearPendingAutoStart,
|
|
20
|
+
maybeHandleReadyPhraseWithoutFiles,
|
|
21
|
+
maybeHandleEmptyIntentTurn,
|
|
22
|
+
resetEmptyTurnCounter,
|
|
23
|
+
} from "../guided-flow.ts";
|
|
24
|
+
|
|
25
|
+
// ─── Test harness ──────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
interface MockCapture {
|
|
28
|
+
notifies: Array<{ msg: string; level: string }>;
|
|
29
|
+
messages: Array<{ payload: any; options: any }>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mkCapture(): MockCapture {
|
|
33
|
+
return { notifies: [], messages: [] };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mkCtx(cap: MockCapture): any {
|
|
37
|
+
return {
|
|
38
|
+
ui: {
|
|
39
|
+
notify: (msg: string, level: string) => {
|
|
40
|
+
cap.notifies.push({ msg, level });
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function mkPi(cap: MockCapture, opts: { sendThrows?: boolean } = {}): any {
|
|
47
|
+
return {
|
|
48
|
+
sendMessage: (payload: any, options: any) => {
|
|
49
|
+
if (opts.sendThrows) throw new Error("send failed");
|
|
50
|
+
cap.messages.push({ payload, options });
|
|
51
|
+
},
|
|
52
|
+
setActiveTools: () => undefined,
|
|
53
|
+
getActiveTools: () => [],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function mkBase(): string {
|
|
58
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-4573-"));
|
|
59
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
|
|
60
|
+
return base;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function assistantMsg(text: string, opts: { toolUse?: boolean } = {}): any {
|
|
64
|
+
const content: any[] = [];
|
|
65
|
+
if (text) content.push({ type: "text", text });
|
|
66
|
+
if (opts.toolUse) content.push({ type: "tool_use", name: "whatever", input: {} });
|
|
67
|
+
return { role: "assistant", content };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── ready-phrase recovery (Layer 2) ───────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
describe("#4573 maybeHandleReadyPhraseWithoutFiles", () => {
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
clearPendingAutoStart();
|
|
75
|
+
resetEmptyTurnCounter();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("no pending entry → no-op", () => {
|
|
79
|
+
const cap = mkCapture();
|
|
80
|
+
const event = { messages: [assistantMsg("Milestone M001 ready.")] };
|
|
81
|
+
const handled = maybeHandleReadyPhraseWithoutFiles(event);
|
|
82
|
+
assert.equal(handled, false);
|
|
83
|
+
assert.equal(cap.messages.length, 0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("pending entry, ready phrase, no files → notify + sendMessage", () => {
|
|
87
|
+
const base = mkBase();
|
|
88
|
+
try {
|
|
89
|
+
const cap = mkCapture();
|
|
90
|
+
setPendingAutoStart(base, {
|
|
91
|
+
basePath: base,
|
|
92
|
+
milestoneId: "M001",
|
|
93
|
+
ctx: mkCtx(cap),
|
|
94
|
+
pi: mkPi(cap),
|
|
95
|
+
});
|
|
96
|
+
const handled = maybeHandleReadyPhraseWithoutFiles({
|
|
97
|
+
messages: [assistantMsg("Milestone M001 ready.")],
|
|
98
|
+
});
|
|
99
|
+
assert.equal(handled, true);
|
|
100
|
+
assert.equal(cap.messages.length, 1);
|
|
101
|
+
assert.equal(cap.messages[0].payload.customType, "gsd-ready-no-files");
|
|
102
|
+
assert.equal(cap.messages[0].options.triggerTurn, true);
|
|
103
|
+
assert.ok(
|
|
104
|
+
cap.notifies.some((n) => /rejected/.test(n.msg)),
|
|
105
|
+
"user notified about rejection",
|
|
106
|
+
);
|
|
107
|
+
} finally {
|
|
108
|
+
clearPendingAutoStart();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("retry cap — after MAX_READY_REJECTS the nudge stops and entry clears", () => {
|
|
113
|
+
const base = mkBase();
|
|
114
|
+
try {
|
|
115
|
+
const cap = mkCapture();
|
|
116
|
+
setPendingAutoStart(base, {
|
|
117
|
+
basePath: base,
|
|
118
|
+
milestoneId: "M001",
|
|
119
|
+
ctx: mkCtx(cap),
|
|
120
|
+
pi: mkPi(cap),
|
|
121
|
+
});
|
|
122
|
+
const event = { messages: [assistantMsg("Milestone M001 ready.")] };
|
|
123
|
+
|
|
124
|
+
const first = maybeHandleReadyPhraseWithoutFiles(event);
|
|
125
|
+
const second = maybeHandleReadyPhraseWithoutFiles(event);
|
|
126
|
+
const third = maybeHandleReadyPhraseWithoutFiles(event); // > MAX
|
|
127
|
+
|
|
128
|
+
assert.equal(first, true);
|
|
129
|
+
assert.equal(second, true);
|
|
130
|
+
assert.equal(third, true); // still returns true (handled via give-up)
|
|
131
|
+
assert.equal(cap.messages.length, 2, "only 2 nudges sent (MAX_READY_REJECTS=2)");
|
|
132
|
+
assert.ok(
|
|
133
|
+
cap.notifies.some((n) => /Stopping auto-nudge/.test(n.msg)),
|
|
134
|
+
"gives up with error notify",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// After giving up, a fresh re-entry starts clean
|
|
138
|
+
const fourth = maybeHandleReadyPhraseWithoutFiles(event);
|
|
139
|
+
assert.equal(fourth, false, "pending entry was cleared — nothing to handle");
|
|
140
|
+
} finally {
|
|
141
|
+
clearPendingAutoStart();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("files present → no nudge (happy path already fired)", () => {
|
|
146
|
+
const base = mkBase();
|
|
147
|
+
try {
|
|
148
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"), "# ctx");
|
|
149
|
+
const cap = mkCapture();
|
|
150
|
+
setPendingAutoStart(base, {
|
|
151
|
+
basePath: base,
|
|
152
|
+
milestoneId: "M001",
|
|
153
|
+
ctx: mkCtx(cap),
|
|
154
|
+
pi: mkPi(cap),
|
|
155
|
+
});
|
|
156
|
+
const handled = maybeHandleReadyPhraseWithoutFiles({
|
|
157
|
+
messages: [assistantMsg("Milestone M001 ready.")],
|
|
158
|
+
});
|
|
159
|
+
assert.equal(handled, false);
|
|
160
|
+
assert.equal(cap.messages.length, 0);
|
|
161
|
+
} finally {
|
|
162
|
+
clearPendingAutoStart();
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("last message lacks ready phrase → no-op", () => {
|
|
167
|
+
const base = mkBase();
|
|
168
|
+
try {
|
|
169
|
+
const cap = mkCapture();
|
|
170
|
+
setPendingAutoStart(base, {
|
|
171
|
+
basePath: base,
|
|
172
|
+
milestoneId: "M001",
|
|
173
|
+
ctx: mkCtx(cap),
|
|
174
|
+
pi: mkPi(cap),
|
|
175
|
+
});
|
|
176
|
+
const handled = maybeHandleReadyPhraseWithoutFiles({
|
|
177
|
+
messages: [assistantMsg("Let me think about the slices first.")],
|
|
178
|
+
});
|
|
179
|
+
assert.equal(handled, false);
|
|
180
|
+
assert.equal(cap.messages.length, 0);
|
|
181
|
+
} finally {
|
|
182
|
+
clearPendingAutoStart();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("fresh entry after give-up resets counter", () => {
|
|
187
|
+
const base = mkBase();
|
|
188
|
+
try {
|
|
189
|
+
const cap = mkCapture();
|
|
190
|
+
// First cycle: exhaust cap
|
|
191
|
+
setPendingAutoStart(base, {
|
|
192
|
+
basePath: base,
|
|
193
|
+
milestoneId: "M001",
|
|
194
|
+
ctx: mkCtx(cap),
|
|
195
|
+
pi: mkPi(cap),
|
|
196
|
+
});
|
|
197
|
+
const event = { messages: [assistantMsg("Milestone M001 ready.")] };
|
|
198
|
+
maybeHandleReadyPhraseWithoutFiles(event);
|
|
199
|
+
maybeHandleReadyPhraseWithoutFiles(event);
|
|
200
|
+
maybeHandleReadyPhraseWithoutFiles(event); // clears entry
|
|
201
|
+
|
|
202
|
+
// New /gsd run — re-seeds entry; counter must be 0 again
|
|
203
|
+
cap.messages.length = 0;
|
|
204
|
+
setPendingAutoStart(base, {
|
|
205
|
+
basePath: base,
|
|
206
|
+
milestoneId: "M001",
|
|
207
|
+
ctx: mkCtx(cap),
|
|
208
|
+
pi: mkPi(cap),
|
|
209
|
+
});
|
|
210
|
+
const handled = maybeHandleReadyPhraseWithoutFiles(event);
|
|
211
|
+
assert.equal(handled, true);
|
|
212
|
+
assert.equal(cap.messages.length, 1, "fresh entry fires nudge again");
|
|
213
|
+
} finally {
|
|
214
|
+
clearPendingAutoStart();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// ─── empty-turn recovery (Layer 3) ────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
describe("#4573 maybeHandleEmptyIntentTurn", () => {
|
|
222
|
+
beforeEach(() => {
|
|
223
|
+
clearPendingAutoStart();
|
|
224
|
+
resetEmptyTurnCounter();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("no pending entry + isAuto false → no-op (interactive discuss is user-driven)", () => {
|
|
228
|
+
const event = { messages: [assistantMsg("I'll write the CONTEXT.md now.")] };
|
|
229
|
+
const handled = maybeHandleEmptyIntentTurn(event, false);
|
|
230
|
+
assert.equal(handled, false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("text-only turn WITHOUT commit phrase → not flagged (legitimate text)", () => {
|
|
234
|
+
const base = mkBase();
|
|
235
|
+
try {
|
|
236
|
+
const cap = mkCapture();
|
|
237
|
+
setPendingAutoStart(base, {
|
|
238
|
+
basePath: base,
|
|
239
|
+
milestoneId: "M001",
|
|
240
|
+
ctx: mkCtx(cap),
|
|
241
|
+
pi: mkPi(cap),
|
|
242
|
+
});
|
|
243
|
+
const handled = maybeHandleEmptyIntentTurn(
|
|
244
|
+
{ messages: [assistantMsg("Here is the roadmap preview — three slices.")] },
|
|
245
|
+
false,
|
|
246
|
+
);
|
|
247
|
+
assert.equal(handled, false);
|
|
248
|
+
assert.equal(cap.messages.length, 0);
|
|
249
|
+
} finally {
|
|
250
|
+
clearPendingAutoStart();
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("text-only turn ending in question → treated as user-handoff, not flagged", () => {
|
|
255
|
+
const base = mkBase();
|
|
256
|
+
try {
|
|
257
|
+
const cap = mkCapture();
|
|
258
|
+
setPendingAutoStart(base, {
|
|
259
|
+
basePath: base,
|
|
260
|
+
milestoneId: "M001",
|
|
261
|
+
ctx: mkCtx(cap),
|
|
262
|
+
pi: mkPi(cap),
|
|
263
|
+
});
|
|
264
|
+
const handled = maybeHandleEmptyIntentTurn(
|
|
265
|
+
{ messages: [assistantMsg("Ready to write, or want to adjust?")] },
|
|
266
|
+
false,
|
|
267
|
+
);
|
|
268
|
+
assert.equal(handled, false);
|
|
269
|
+
} finally {
|
|
270
|
+
clearPendingAutoStart();
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("commit-intent phrase WITHOUT tool call → nudge fires", () => {
|
|
275
|
+
const base = mkBase();
|
|
276
|
+
try {
|
|
277
|
+
const cap = mkCapture();
|
|
278
|
+
setPendingAutoStart(base, {
|
|
279
|
+
basePath: base,
|
|
280
|
+
milestoneId: "M001",
|
|
281
|
+
ctx: mkCtx(cap),
|
|
282
|
+
pi: mkPi(cap),
|
|
283
|
+
});
|
|
284
|
+
const handled = maybeHandleEmptyIntentTurn(
|
|
285
|
+
{ messages: [assistantMsg("I'll now write the CONTEXT.md file.")] },
|
|
286
|
+
false,
|
|
287
|
+
);
|
|
288
|
+
assert.equal(handled, true);
|
|
289
|
+
assert.equal(cap.messages.length, 1);
|
|
290
|
+
assert.equal(cap.messages[0].payload.customType, "gsd-empty-turn-recovery");
|
|
291
|
+
} finally {
|
|
292
|
+
clearPendingAutoStart();
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("commit-intent WITH tool-use block → not flagged", () => {
|
|
297
|
+
const base = mkBase();
|
|
298
|
+
try {
|
|
299
|
+
const cap = mkCapture();
|
|
300
|
+
setPendingAutoStart(base, {
|
|
301
|
+
basePath: base,
|
|
302
|
+
milestoneId: "M001",
|
|
303
|
+
ctx: mkCtx(cap),
|
|
304
|
+
pi: mkPi(cap),
|
|
305
|
+
});
|
|
306
|
+
const handled = maybeHandleEmptyIntentTurn(
|
|
307
|
+
{ messages: [assistantMsg("I'll write the file now.", { toolUse: true })] },
|
|
308
|
+
false,
|
|
309
|
+
);
|
|
310
|
+
assert.equal(handled, false);
|
|
311
|
+
assert.equal(cap.messages.length, 0);
|
|
312
|
+
} finally {
|
|
313
|
+
clearPendingAutoStart();
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("ready phrase is NOT treated as empty-turn (handled by other recovery path)", () => {
|
|
318
|
+
const base = mkBase();
|
|
319
|
+
try {
|
|
320
|
+
const cap = mkCapture();
|
|
321
|
+
setPendingAutoStart(base, {
|
|
322
|
+
basePath: base,
|
|
323
|
+
milestoneId: "M001",
|
|
324
|
+
ctx: mkCtx(cap),
|
|
325
|
+
pi: mkPi(cap),
|
|
326
|
+
});
|
|
327
|
+
const handled = maybeHandleEmptyIntentTurn(
|
|
328
|
+
{ messages: [assistantMsg("Milestone M001 ready.")] },
|
|
329
|
+
false,
|
|
330
|
+
);
|
|
331
|
+
assert.equal(handled, false);
|
|
332
|
+
} finally {
|
|
333
|
+
clearPendingAutoStart();
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("empty-turn retry cap — stops after MAX_EMPTY_TURN_RETRIES", () => {
|
|
338
|
+
const base = mkBase();
|
|
339
|
+
try {
|
|
340
|
+
const cap = mkCapture();
|
|
341
|
+
setPendingAutoStart(base, {
|
|
342
|
+
basePath: base,
|
|
343
|
+
milestoneId: "M001",
|
|
344
|
+
ctx: mkCtx(cap),
|
|
345
|
+
pi: mkPi(cap),
|
|
346
|
+
});
|
|
347
|
+
const event = { messages: [assistantMsg("I'll write the CONTEXT.md file.")] };
|
|
348
|
+
|
|
349
|
+
maybeHandleEmptyIntentTurn(event, false); // 1
|
|
350
|
+
maybeHandleEmptyIntentTurn(event, false); // 2
|
|
351
|
+
const third = maybeHandleEmptyIntentTurn(event, false); // > cap
|
|
352
|
+
|
|
353
|
+
assert.equal(cap.messages.length, 2, "only 2 nudges sent");
|
|
354
|
+
assert.equal(third, false, "after cap, no further injection");
|
|
355
|
+
assert.ok(
|
|
356
|
+
cap.notifies.some((n) => /Stopping auto-nudge/.test(n.msg)),
|
|
357
|
+
"user notified of give-up",
|
|
358
|
+
);
|
|
359
|
+
} finally {
|
|
360
|
+
clearPendingAutoStart();
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("resetEmptyTurnCounter clears state after a successful tool-use turn", () => {
|
|
365
|
+
const base = mkBase();
|
|
366
|
+
try {
|
|
367
|
+
const cap = mkCapture();
|
|
368
|
+
setPendingAutoStart(base, {
|
|
369
|
+
basePath: base,
|
|
370
|
+
milestoneId: "M001",
|
|
371
|
+
ctx: mkCtx(cap),
|
|
372
|
+
pi: mkPi(cap),
|
|
373
|
+
});
|
|
374
|
+
const event = { messages: [assistantMsg("I'll write the CONTEXT.md file.")] };
|
|
375
|
+
|
|
376
|
+
maybeHandleEmptyIntentTurn(event, false); // 1
|
|
377
|
+
maybeHandleEmptyIntentTurn(event, false); // 2 — at cap
|
|
378
|
+
resetEmptyTurnCounter(); // simulate a successful tool-use turn in between
|
|
379
|
+
|
|
380
|
+
cap.messages.length = 0;
|
|
381
|
+
const after = maybeHandleEmptyIntentTurn(event, false);
|
|
382
|
+
assert.equal(after, true, "counter reset — nudge fires again");
|
|
383
|
+
assert.equal(cap.messages.length, 1);
|
|
384
|
+
} finally {
|
|
385
|
+
clearPendingAutoStart();
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
});
|
|
@@ -45,9 +45,15 @@ describe('restore tools after discuss flow scoping (#3628)', () => {
|
|
|
45
45
|
})
|
|
46
46
|
|
|
47
47
|
it('savedTools is restored after sendMessage', () => {
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
// #4573: guided-flow.ts now contains multiple `triggerTurn: true` calls
|
|
49
|
+
// (ready-phrase and empty-turn recovery paths). The discuss-flow scoping
|
|
50
|
+
// sendMessage is the one that follows `savedTools = currentTools`, so
|
|
51
|
+
// anchor the search there rather than at the first `triggerTurn: true`.
|
|
52
|
+
const savedToolsAssign = src.indexOf('savedTools = currentTools')
|
|
53
|
+
assert.ok(savedToolsAssign !== -1, 'savedTools = currentTools must exist')
|
|
54
|
+
|
|
55
|
+
const sendMsg = src.indexOf('triggerTurn: true', savedToolsAssign)
|
|
56
|
+
assert.ok(sendMsg !== -1, 'discuss-flow sendMessage with triggerTurn must exist after savedTools capture')
|
|
51
57
|
|
|
52
58
|
// After sendMessage, savedTools should be restored via setActiveTools
|
|
53
59
|
const afterSend = src.slice(sendMsg, sendMsg + 500)
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for fix of #3723: auto-mode resume/crash-recovery dispatches
|
|
3
|
+
* from project root instead of milestone worktree.
|
|
4
|
+
*
|
|
5
|
+
* During resume, the paused-session metadata may record `worktreePath` that
|
|
6
|
+
* was active when the session paused. The resume path must use that path (or
|
|
7
|
+
* derive the worktree path via filesystem lookup) to set the dispatch context
|
|
8
|
+
* (`s.basePath`), rather than defaulting to the project root.
|
|
9
|
+
*
|
|
10
|
+
* The fix adds an early worktree-path resolution step in the paused-session
|
|
11
|
+
* resume block of auto.ts — immediately after `s.basePath = base` — so that
|
|
12
|
+
* the correct dispatch directory is used before the dispatch loop runs.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import test from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
import { readFileSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { randomUUID } from "node:crypto";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
import { dirname } from "node:path";
|
|
23
|
+
|
|
24
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const autoSrc = readFileSync(join(__dirname, "..", "auto.ts"), "utf-8");
|
|
26
|
+
|
|
27
|
+
// ── Source-structure tests ────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extract the paused-session resume block from auto.ts.
|
|
31
|
+
*
|
|
32
|
+
* The block we care about is the `if (s.paused) { ... }` section inside
|
|
33
|
+
* startAuto, which contains `s.basePath = base` (line ~1473) followed by the
|
|
34
|
+
* `enterMilestone` call.
|
|
35
|
+
*
|
|
36
|
+
* We find it by locating the `s.basePath = base` assignment that appears
|
|
37
|
+
* WITHIN the s.paused branch (there's only one: all other basePath assignments
|
|
38
|
+
* use originalBasePath or a different value). We extract from that assignment
|
|
39
|
+
* up to just before `enterMilestone(`.
|
|
40
|
+
*/
|
|
41
|
+
function getBasepathToEnterMilestoneSegment(): string {
|
|
42
|
+
// Find `s.basePath = base;` in the s.paused branch
|
|
43
|
+
// This assignment appears uniquely inside the resume block
|
|
44
|
+
const assignPattern = "s.basePath = base;";
|
|
45
|
+
const assignIdx = autoSrc.indexOf(assignPattern);
|
|
46
|
+
assert.ok(
|
|
47
|
+
assignIdx > -1,
|
|
48
|
+
`auto.ts must contain '${assignPattern}' in the resume block`,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Find the next enterMilestone call after this assignment
|
|
52
|
+
const enterMilestoneIdx = autoSrc.indexOf("enterMilestone(", assignIdx);
|
|
53
|
+
assert.ok(
|
|
54
|
+
enterMilestoneIdx > assignIdx,
|
|
55
|
+
"auto.ts must call enterMilestone after the s.basePath = base assignment",
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Return the code between the assignment and enterMilestone
|
|
59
|
+
return autoSrc.slice(assignIdx, enterMilestoneIdx);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
test("auto.ts resume block resolves paused-session worktreePath and applies it to s.basePath before entering worktree (fixes #3723)", () => {
|
|
63
|
+
// The segment between `s.basePath = base` and `enterMilestone(` must
|
|
64
|
+
// contain logic that resolves the paused-session worktreePath and applies
|
|
65
|
+
// it to s.basePath when the worktree exists on disk.
|
|
66
|
+
//
|
|
67
|
+
// The fix reads the worktree path from freshStartAssessment.pausedSession
|
|
68
|
+
// (since `meta` is out of scope at this point in startAuto) and assigns
|
|
69
|
+
// it to s.basePath, guarded by existsSync.
|
|
70
|
+
//
|
|
71
|
+
// Without this fix, the dispatch loop runs from `base` (project root)
|
|
72
|
+
// instead of the worktree, causing split-brain execution (#3723).
|
|
73
|
+
const segment = getBasepathToEnterMilestoneSegment();
|
|
74
|
+
|
|
75
|
+
// The fix uses freshStartAssessment.pausedSession?.worktreePath via a
|
|
76
|
+
// local variable (resumeWorktreePath) and assigns to s.basePath.
|
|
77
|
+
const hasWorktreePathResolution =
|
|
78
|
+
segment.includes("worktreePath") &&
|
|
79
|
+
segment.includes("existsSync") &&
|
|
80
|
+
segment.includes("s.basePath =");
|
|
81
|
+
|
|
82
|
+
assert.ok(
|
|
83
|
+
hasWorktreePathResolution,
|
|
84
|
+
"auto.ts must resolve the paused-session worktreePath, check existsSync, and assign " +
|
|
85
|
+
"s.basePath before enterMilestone — crash-recovery currently dispatches from project root " +
|
|
86
|
+
"instead of milestone worktree (issue #3723). The fix belongs between `s.basePath = base` " +
|
|
87
|
+
"and the enterMilestone call.",
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("auto.ts worktreePath assignment in resume block guards against non-existent path (fixes #3723)", () => {
|
|
92
|
+
// The assignment to s.basePath from the paused-session worktreePath must
|
|
93
|
+
// be guarded by existsSync to avoid setting an invalid basePath if the
|
|
94
|
+
// worktree was cleaned up between pause and resume.
|
|
95
|
+
const segment = getBasepathToEnterMilestoneSegment();
|
|
96
|
+
|
|
97
|
+
// Must have existsSync guard AND a worktreePath reference AND s.basePath assignment
|
|
98
|
+
const hasGuardedAssignment =
|
|
99
|
+
segment.includes("existsSync") &&
|
|
100
|
+
segment.includes("worktreePath") &&
|
|
101
|
+
segment.includes("s.basePath =");
|
|
102
|
+
|
|
103
|
+
assert.ok(
|
|
104
|
+
hasGuardedAssignment,
|
|
105
|
+
"auto.ts must guard the s.basePath = worktreePath assignment with existsSync (fixes #3723)",
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ── Functional tests ──────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
function makeTmpBase(): string {
|
|
112
|
+
const base = join(tmpdir(), `gsd-resume-wt-${randomUUID()}`);
|
|
113
|
+
mkdirSync(join(base, ".gsd", "runtime"), { recursive: true });
|
|
114
|
+
return base;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function cleanup(base: string): void {
|
|
118
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function writePausedSession(
|
|
122
|
+
base: string,
|
|
123
|
+
milestoneId: string,
|
|
124
|
+
worktreePath: string | null,
|
|
125
|
+
): void {
|
|
126
|
+
writeFileSync(
|
|
127
|
+
join(base, ".gsd", "runtime", "paused-session.json"),
|
|
128
|
+
JSON.stringify({
|
|
129
|
+
milestoneId,
|
|
130
|
+
originalBasePath: base,
|
|
131
|
+
stepMode: false,
|
|
132
|
+
worktreePath,
|
|
133
|
+
pausedAt: new Date().toISOString(),
|
|
134
|
+
}, null, 2),
|
|
135
|
+
"utf-8",
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function makeWorktreePath(base: string, milestoneId: string): string {
|
|
140
|
+
return join(base, ".gsd", "worktrees", milestoneId);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function setupWorktreeOnDisk(wt: string): void {
|
|
144
|
+
mkdirSync(wt, { recursive: true });
|
|
145
|
+
// Simulate a git worktree: .git file with gitdir pointer
|
|
146
|
+
writeFileSync(
|
|
147
|
+
join(wt, ".git"),
|
|
148
|
+
"gitdir: /project/.git/worktrees/M001-test\n",
|
|
149
|
+
"utf-8",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function writeRoadmap(base: string, milestoneId = "M001-test"): void {
|
|
154
|
+
const milestoneDir = join(base, ".gsd", "milestones", milestoneId);
|
|
155
|
+
mkdirSync(join(milestoneDir, "slices", "S01", "tasks"), { recursive: true });
|
|
156
|
+
writeFileSync(
|
|
157
|
+
join(milestoneDir, `${milestoneId}-ROADMAP.md`),
|
|
158
|
+
[
|
|
159
|
+
`# ${milestoneId}: Test Milestone`,
|
|
160
|
+
"",
|
|
161
|
+
"## Slices",
|
|
162
|
+
"",
|
|
163
|
+
"- [ ] **S01: Test slice** `risk:low`",
|
|
164
|
+
" After this: Demo",
|
|
165
|
+
].join("\n"),
|
|
166
|
+
"utf-8",
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
test("readPausedSessionMetadata round-trips worktreePath from paused-session.json", () => {
|
|
171
|
+
// Verify that the paused-session metadata correctly stores and reads back
|
|
172
|
+
// the worktreePath field — this is what the resume path in auto.ts uses
|
|
173
|
+
// to determine the dispatch basePath (#3723).
|
|
174
|
+
//
|
|
175
|
+
// Implemented inline to avoid slow import chain from interrupted-session.ts.
|
|
176
|
+
const base = makeTmpBase();
|
|
177
|
+
const wt = makeWorktreePath(base, "M001-test");
|
|
178
|
+
try {
|
|
179
|
+
setupWorktreeOnDisk(wt);
|
|
180
|
+
writePausedSession(base, "M001-test", wt);
|
|
181
|
+
|
|
182
|
+
// Simulate readPausedSessionMetadata without importing the full module
|
|
183
|
+
const pausedPath = join(base, ".gsd", "runtime", "paused-session.json");
|
|
184
|
+
const meta = JSON.parse(readFileSync(pausedPath, "utf-8"));
|
|
185
|
+
|
|
186
|
+
assert.ok(meta, "paused-session metadata must be readable");
|
|
187
|
+
assert.equal(meta.milestoneId, "M001-test");
|
|
188
|
+
assert.equal(meta.worktreePath, wt, "worktreePath must round-trip through paused-session.json");
|
|
189
|
+
} finally {
|
|
190
|
+
cleanup(base);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("auto.ts resume block uses worktreePath from freshStartAssessment.pausedSession as dispatch basePath when worktree exists (#3723)", () => {
|
|
195
|
+
// End-to-end structural verification: the auto.ts source must contain code
|
|
196
|
+
// that reads the worktreePath from freshStartAssessment.pausedSession AND
|
|
197
|
+
// applies it to s.basePath (guarded by existsSync) between the
|
|
198
|
+
// `s.basePath = base` assignment and `enterMilestone`.
|
|
199
|
+
//
|
|
200
|
+
// This is the core of the #3723 fix. Without this check, a session that
|
|
201
|
+
// paused while operating inside a worktree will resume dispatching from the
|
|
202
|
+
// project root, not the worktree — causing split-brain execution where some
|
|
203
|
+
// operations target the worktree and others target the project root.
|
|
204
|
+
const segment = getBasepathToEnterMilestoneSegment();
|
|
205
|
+
|
|
206
|
+
// Must reference freshStartAssessment.pausedSession?.worktreePath or
|
|
207
|
+
// equivalent local variable, AND contain an existsSync guard
|
|
208
|
+
const hasAssessmentWorktreePath =
|
|
209
|
+
segment.includes("freshStartAssessment.pausedSession") &&
|
|
210
|
+
segment.includes("worktreePath");
|
|
211
|
+
|
|
212
|
+
assert.ok(
|
|
213
|
+
hasAssessmentWorktreePath,
|
|
214
|
+
"auto.ts must read worktreePath from freshStartAssessment.pausedSession between " +
|
|
215
|
+
"s.basePath=base and enterMilestone",
|
|
216
|
+
);
|
|
217
|
+
assert.ok(
|
|
218
|
+
segment.includes("existsSync"),
|
|
219
|
+
"auto.ts must guard worktreePath usage with existsSync",
|
|
220
|
+
);
|
|
221
|
+
// The actual basePath re-assignment must be present in the segment
|
|
222
|
+
// (note: there are multiple `s.basePath =` assignments — we need one after
|
|
223
|
+
// the initial `s.basePath = base` assignment within this segment)
|
|
224
|
+
const worktreeAssignIdx = segment.lastIndexOf("s.basePath =");
|
|
225
|
+
const baseAssignIdx = segment.indexOf("s.basePath = base");
|
|
226
|
+
assert.ok(
|
|
227
|
+
worktreeAssignIdx > baseAssignIdx,
|
|
228
|
+
"auto.ts must assign s.basePath to the worktree path after `s.basePath = base` in the resume block",
|
|
229
|
+
);
|
|
230
|
+
});
|