gsd-pi 2.76.0-dev.4100bd590 → 2.76.0-dev.479ad0e78
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/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/extensions/claude-code-cli/readiness.js +4 -3
- 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 +58 -5
- 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 +14 -3
- 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-worktree.js +51 -53
- package/dist/resources/extensions/gsd/auto.js +70 -28
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -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/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
- 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-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/error-classifier.js +10 -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 +149 -31
- 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/init-wizard.js +15 -1
- package/dist/resources/extensions/gsd/key-manager.js +28 -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/discuss-headless.md +8 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- 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/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/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/worktree-resolver.js +50 -10
- package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/dist/resources/skills/write-docs/SKILL.md +2 -1
- 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 +17 -17
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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 +17 -17
- 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 +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/dist/welcome-screen.js +6 -1
- package/dist/wizard.js +2 -0
- package/package.json +1 -1
- 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.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +64 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -1
- 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 +84 -43
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/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-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 +8 -3
- 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/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-shared.ts +26 -5
- package/packages/pi-ai/src/providers/anthropic.ts +9 -3
- 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/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 +7 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
- 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/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-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/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/src/core/agent-session-abort-order.test.ts +3 -2
- package/packages/pi-coding-agent/src/core/agent-session.ts +11 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -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-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/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/scripts/link-workspace-packages.cjs +1 -0
- package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
- 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 +82 -4
- 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 +17 -2
- 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-worktree.ts +62 -63
- package/src/resources/extensions/gsd/auto.ts +73 -28
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -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/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
- 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-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/error-classifier.ts +10 -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 +157 -33
- 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/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/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/discuss-headless.md +8 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- 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/tests/auto-loop.test.ts +188 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
- 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/clean-root-preflight.test.ts +186 -0
- 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/doctor-providers.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -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/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/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/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 +2 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -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 +48 -0
- 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/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/resume-tool.ts +40 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
- package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/src/resources/skills/write-docs/SKILL.md +2 -1
- /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_ssgManifest.js +0 -0
|
@@ -180,14 +180,6 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
-
function isProjectGsdSymlink(basePath) {
|
|
184
|
-
try {
|
|
185
|
-
return lstatSyncFn(join(basePath, ".gsd")).isSymbolicLink();
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
183
|
// ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
|
|
192
184
|
/** Patterns for machine-generated build artifacts that can be safely
|
|
193
185
|
* auto-resolved by accepting --theirs during merge. These files are
|
|
@@ -1429,50 +1421,15 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1429
1421
|
});
|
|
1430
1422
|
}
|
|
1431
1423
|
}
|
|
1432
|
-
// 7.
|
|
1433
|
-
// blocked by unrelated local changes (#2151). clearProjectRootStateFiles
|
|
1434
|
-
// only removes untracked .gsd/ files; tracked dirty files elsewhere (e.g.
|
|
1435
|
-
// .planning/work-state.json with stash conflict markers) are invisible to
|
|
1436
|
-
// that cleanup but will cause `git merge --squash` to reject.
|
|
1437
|
-
let stashed = false;
|
|
1438
|
-
try {
|
|
1439
|
-
const status = execFileSync("git", ["status", "--porcelain"], {
|
|
1440
|
-
cwd: originalBasePath_,
|
|
1441
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1442
|
-
encoding: "utf-8",
|
|
1443
|
-
}).trim();
|
|
1444
|
-
if (status) {
|
|
1445
|
-
// Use --include-untracked to stash untracked files that would block
|
|
1446
|
-
// the squash merge, but EXCLUDE .gsd/milestones/ (#2505).
|
|
1447
|
-
// --include-untracked without exclusion sweeps queued milestone
|
|
1448
|
-
// CONTEXT files into the stash. If stash pop later fails, those files
|
|
1449
|
-
// are permanently trapped in the stash entry and lost on the next
|
|
1450
|
-
// stash push or drop.
|
|
1451
|
-
//
|
|
1452
|
-
// When `.gsd` itself is a symlink, Git rejects pathspecs below it
|
|
1453
|
-
// ("beyond a symbolic link"). In that layout, exclude the whole symlink
|
|
1454
|
-
// and keep stashing real project files that could block the merge.
|
|
1455
|
-
const stashPathspecs = isProjectGsdSymlink(originalBasePath_)
|
|
1456
|
-
? [".", ":(exclude).gsd"]
|
|
1457
|
-
: [":(exclude).gsd/milestones"];
|
|
1458
|
-
execFileSync("git", [
|
|
1459
|
-
"stash", "push", "--include-untracked",
|
|
1460
|
-
"-m", `gsd: pre-merge stash for ${milestoneId}`,
|
|
1461
|
-
"--", ...stashPathspecs,
|
|
1462
|
-
], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
|
|
1463
|
-
stashed = true;
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
catch (err) {
|
|
1467
|
-
// Stash failure is non-fatal — proceed without stash and let the merge
|
|
1468
|
-
// report the dirty tree if it fails.
|
|
1469
|
-
logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1470
|
-
}
|
|
1471
|
-
// 7a. Shelter queued milestone directories before the squash merge (#2505).
|
|
1424
|
+
// 7. Shelter queued milestone directories before the squash merge (#2505).
|
|
1472
1425
|
// The milestone branch may contain copies of queued milestone dirs (via
|
|
1473
1426
|
// copyPlanningArtifacts), so `git merge --squash` rejects when those same
|
|
1474
1427
|
// files exist as untracked in the working tree. Temporarily move them to
|
|
1475
1428
|
// a backup location, then restore after the merge+commit.
|
|
1429
|
+
//
|
|
1430
|
+
// MUST run BEFORE the pre-merge stash (step 7a) so `--include-untracked`
|
|
1431
|
+
// does not sweep queued CONTEXT files into the stash. If stash pop later
|
|
1432
|
+
// fails, files trapped inside the stash are permanently lost (#2505).
|
|
1476
1433
|
const milestonesDir = join(gsdRoot(originalBasePath_), "milestones");
|
|
1477
1434
|
const shelterDir = join(gsdRoot(originalBasePath_), ".milestone-shelter");
|
|
1478
1435
|
const shelteredDirs = [];
|
|
@@ -1526,6 +1483,31 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1526
1483
|
// Non-fatal — proceed with merge; untracked files may block it
|
|
1527
1484
|
logWarning("worktree", `milestone shelter operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1528
1485
|
}
|
|
1486
|
+
// 7a. Stash pre-existing dirty files so the squash merge is not blocked by
|
|
1487
|
+
// unrelated local changes (#2151). Includes untracked files to handle
|
|
1488
|
+
// locally-added files that conflict with tracked files on the milestone
|
|
1489
|
+
// branch. Passing NO pathspec lets git skip gitignored paths silently;
|
|
1490
|
+
// adding an explicit pathspec trips a `git add`-style fatal on ignored
|
|
1491
|
+
// entries (e.g. a gitignored `.gsd` symlink under ADR-002) (#4573).
|
|
1492
|
+
// Queued CONTEXT files under `.gsd/milestones/*` are already sheltered
|
|
1493
|
+
// in step 7 above, so they won't be swept into the stash.
|
|
1494
|
+
let stashed = false;
|
|
1495
|
+
try {
|
|
1496
|
+
const status = execFileSync("git", ["status", "--porcelain"], {
|
|
1497
|
+
cwd: originalBasePath_,
|
|
1498
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1499
|
+
encoding: "utf-8",
|
|
1500
|
+
}).trim();
|
|
1501
|
+
if (status) {
|
|
1502
|
+
execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd: pre-merge stash for ${milestoneId}`], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
|
|
1503
|
+
stashed = true;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
catch (err) {
|
|
1507
|
+
// Stash failure is non-fatal — proceed without stash and let the merge
|
|
1508
|
+
// report the dirty tree if it fails.
|
|
1509
|
+
logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1510
|
+
}
|
|
1529
1511
|
// 7b. Clean up stale merge state before attempting squash merge (#2912).
|
|
1530
1512
|
// A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
|
|
1531
1513
|
// or interrupted operation) causes `git merge --squash` to refuse with
|
|
@@ -1761,16 +1743,32 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1761
1743
|
// When a milestone only produced .gsd/ metadata (summaries, roadmaps) but no
|
|
1762
1744
|
// real code, the user sees "milestone complete" but nothing changed in their
|
|
1763
1745
|
// codebase. Surface this so the caller can warn the user.
|
|
1746
|
+
//
|
|
1747
|
+
// Bug #4385 fix: use `git diff-tree --root` instead of `git diff HEAD~1 HEAD`.
|
|
1748
|
+
// `HEAD~1` does not exist on initial commits and is unreliable on shallow clones
|
|
1749
|
+
// and merge commits. `diff-tree --root` handles all three cases correctly.
|
|
1750
|
+
// The empty-tree hash (4b825dc…) is the universal fallback for refs that don't exist.
|
|
1751
|
+
const GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
1764
1752
|
let codeFilesChanged = false;
|
|
1765
1753
|
if (!nothingToCommit) {
|
|
1766
1754
|
try {
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1755
|
+
const diffTreeOutput = execFileSync("git", ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
1756
|
+
const mergedFiles = diffTreeOutput ? diffTreeOutput.split("\n").filter(Boolean) : [];
|
|
1757
|
+
codeFilesChanged = mergedFiles.some((f) => !f.startsWith(".gsd/"));
|
|
1769
1758
|
}
|
|
1770
1759
|
catch (e) {
|
|
1771
|
-
//
|
|
1772
|
-
|
|
1773
|
-
|
|
1760
|
+
// diff-tree failed (e.g. unborn HEAD in a brand-new repo) — fall back to
|
|
1761
|
+
// comparing against the empty tree so initial-commit repos still report changes.
|
|
1762
|
+
try {
|
|
1763
|
+
const fallbackOutput = execFileSync("git", ["diff", "--name-only", GIT_EMPTY_TREE, "HEAD"], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
1764
|
+
const fallbackFiles = fallbackOutput ? fallbackOutput.split("\n").filter(Boolean) : [];
|
|
1765
|
+
codeFilesChanged = fallbackFiles.some((f) => !f.startsWith(".gsd/"));
|
|
1766
|
+
}
|
|
1767
|
+
catch {
|
|
1768
|
+
// Truly unable to determine — assume code was changed to avoid silent data loss
|
|
1769
|
+
logWarning("worktree", `diff-tree and empty-tree fallback both failed (assuming code changed): ${e.message}`);
|
|
1770
|
+
codeFilesChanged = true;
|
|
1771
|
+
}
|
|
1774
1772
|
}
|
|
1775
1773
|
}
|
|
1776
1774
|
// 10. Auto-push if enabled
|
|
@@ -37,8 +37,9 @@ import { getRtkSessionSavings } from "../shared/rtk-session-stats.js";
|
|
|
37
37
|
import { deactivateGSD } from "../shared/gsd-phase-state.js";
|
|
38
38
|
import { initMetrics, resetMetrics, getLedger, getProjectTotals, formatCost, formatTokenCount, } from "./metrics.js";
|
|
39
39
|
import { setLogBasePath, logWarning } from "./workflow-logger.js";
|
|
40
|
+
import { preflightCleanRoot, postflightPopStash } from "./clean-root-preflight.js";
|
|
40
41
|
import { homedir } from "node:os";
|
|
41
|
-
import { join } from "node:path";
|
|
42
|
+
import { isAbsolute, join } from "node:path";
|
|
42
43
|
import { pathToFileURL } from "node:url";
|
|
43
44
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
44
45
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
@@ -55,7 +56,7 @@ import { getErrorMessage } from "./error-utils.js";
|
|
|
55
56
|
import { recoverFailedMigration } from "./migrate-external.js";
|
|
56
57
|
import { initRegistry, convertDispatchRules } from "./rule-registry.js";
|
|
57
58
|
import { emitJournalEvent as _emitJournalEvent } from "./journal.js";
|
|
58
|
-
import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache,
|
|
59
|
+
import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, } from "./auto-dashboard.js";
|
|
59
60
|
import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
|
|
60
61
|
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
61
62
|
import { countPendingCaptures } from "./captures.js";
|
|
@@ -136,6 +137,24 @@ function restoreMilestoneLockEnv() {
|
|
|
136
137
|
s.hadMilestoneLockEnv = false;
|
|
137
138
|
s.milestoneLockEnvCaptured = false;
|
|
138
139
|
}
|
|
140
|
+
function normalizeSessionFilePath(raw) {
|
|
141
|
+
if (typeof raw !== "string")
|
|
142
|
+
return null;
|
|
143
|
+
const trimmed = raw.trim();
|
|
144
|
+
if (!trimmed)
|
|
145
|
+
return null;
|
|
146
|
+
const firstLine = trimmed.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
147
|
+
if (!firstLine)
|
|
148
|
+
return null;
|
|
149
|
+
// Guard against accidental message concatenation by trimming to .jsonl.
|
|
150
|
+
const jsonlIndex = firstLine.toLowerCase().indexOf(".jsonl");
|
|
151
|
+
const candidate = jsonlIndex >= 0 ? firstLine.slice(0, jsonlIndex + ".jsonl".length) : firstLine;
|
|
152
|
+
if (!isAbsolute(candidate))
|
|
153
|
+
return null;
|
|
154
|
+
if (!candidate.toLowerCase().endsWith(".jsonl"))
|
|
155
|
+
return null;
|
|
156
|
+
return candidate;
|
|
157
|
+
}
|
|
139
158
|
export function startAutoDetached(ctx, pi, base, verboseMode, options) {
|
|
140
159
|
void startAuto(ctx, pi, base, verboseMode, options).catch((err) => {
|
|
141
160
|
const message = getErrorMessage(err);
|
|
@@ -145,8 +164,8 @@ export function startAutoDetached(ctx, pi, base, verboseMode, options) {
|
|
|
145
164
|
});
|
|
146
165
|
}
|
|
147
166
|
/** Returns true if the project is configured for `isolation:worktree` mode. */
|
|
148
|
-
export function shouldUseWorktreeIsolation() {
|
|
149
|
-
const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
167
|
+
export function shouldUseWorktreeIsolation(basePath) {
|
|
168
|
+
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
|
|
150
169
|
if (prefs?.isolation === "worktree")
|
|
151
170
|
return true;
|
|
152
171
|
// Default is false — worktree isolation requires explicit opt-in
|
|
@@ -215,7 +234,7 @@ export function getAutoDashboardData() {
|
|
|
215
234
|
const rtkSavings = sessionId && s.basePath
|
|
216
235
|
? getRtkSessionSavings(s.basePath, sessionId)
|
|
217
236
|
: null;
|
|
218
|
-
const rtkEnabled = loadEffectiveGSDPreferences()?.preferences.experimental?.rtk === true;
|
|
237
|
+
const rtkEnabled = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences.experimental?.rtk === true;
|
|
219
238
|
// Pending capture count — lazy check, non-fatal
|
|
220
239
|
let pendingCaptureCount = 0;
|
|
221
240
|
try {
|
|
@@ -393,7 +412,7 @@ function clearUnitTimeout() {
|
|
|
393
412
|
}
|
|
394
413
|
/** Build snapshot metric opts. */
|
|
395
414
|
function buildSnapshotOpts(_unitType, _unitId) {
|
|
396
|
-
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
415
|
+
const prefs = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
|
|
397
416
|
const uokFlags = resolveUokFlags(prefs);
|
|
398
417
|
return {
|
|
399
418
|
...(s.autoStartTime > 0 ? { autoSessionKey: String(s.autoStartTime) } : {}),
|
|
@@ -427,7 +446,7 @@ function handleLostSessionLock(ctx, lockStatus) {
|
|
|
427
446
|
restoreProjectRootEnv();
|
|
428
447
|
restoreMilestoneLockEnv();
|
|
429
448
|
deregisterSigtermHandler();
|
|
430
|
-
clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
|
|
449
|
+
clearCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences);
|
|
431
450
|
const base = lockBase();
|
|
432
451
|
const lockFilePath = base ? join(gsdRoot(base), "auto.lock") : "unknown";
|
|
433
452
|
const recoverySuggestion = "\nTo recover, run: gsd doctor --fix";
|
|
@@ -443,7 +462,6 @@ function handleLostSessionLock(ctx, lockStatus) {
|
|
|
443
462
|
ctx?.ui.notify(message, "error");
|
|
444
463
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
445
464
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
446
|
-
ctx?.ui.setFooter(undefined);
|
|
447
465
|
if (ctx)
|
|
448
466
|
initHealthWidget(ctx);
|
|
449
467
|
}
|
|
@@ -480,7 +498,6 @@ function cleanupAfterLoopExit(ctx) {
|
|
|
480
498
|
if (!s.paused) {
|
|
481
499
|
ctx.ui.setStatus("gsd-auto", undefined);
|
|
482
500
|
ctx.ui.setWidget("gsd-progress", undefined);
|
|
483
|
-
ctx.ui.setFooter(undefined);
|
|
484
501
|
initHealthWidget(ctx);
|
|
485
502
|
}
|
|
486
503
|
// Restore CWD out of worktree back to original project root
|
|
@@ -498,7 +515,7 @@ function cleanupAfterLoopExit(ctx) {
|
|
|
498
515
|
export async function stopAuto(ctx, pi, reason) {
|
|
499
516
|
if (!s.active && !s.paused)
|
|
500
517
|
return;
|
|
501
|
-
const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
|
|
518
|
+
const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
|
|
502
519
|
const reasonSuffix = reason ? ` — ${reason}` : "";
|
|
503
520
|
try {
|
|
504
521
|
// ── Step 1: Timers and locks ──
|
|
@@ -695,13 +712,16 @@ export async function stopAuto(ctx, pi, reason) {
|
|
|
695
712
|
catch (err) { /* non-fatal */
|
|
696
713
|
logWarning("engine", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
697
714
|
}
|
|
698
|
-
// ── Step 13: Restore original model (before reset clears IDs) ──
|
|
715
|
+
// ── Step 13: Restore original model + thinking (before reset clears IDs) ──
|
|
699
716
|
try {
|
|
700
717
|
if (pi && ctx && s.originalModelId && s.originalModelProvider) {
|
|
701
718
|
const original = ctx.modelRegistry.find(s.originalModelProvider, s.originalModelId);
|
|
702
719
|
if (original)
|
|
703
720
|
await pi.setModel(original);
|
|
704
721
|
}
|
|
722
|
+
if (pi && s.originalThinkingLevel) {
|
|
723
|
+
pi.setThinkingLevel(s.originalThinkingLevel);
|
|
724
|
+
}
|
|
705
725
|
}
|
|
706
726
|
catch (e) {
|
|
707
727
|
debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
|
|
@@ -740,7 +760,6 @@ export async function stopAuto(ctx, pi, reason) {
|
|
|
740
760
|
// UI cleanup
|
|
741
761
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
742
762
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
743
|
-
ctx?.ui.setFooter(undefined);
|
|
744
763
|
if (ctx)
|
|
745
764
|
initHealthWidget(ctx);
|
|
746
765
|
restoreProjectRootEnv();
|
|
@@ -775,7 +794,7 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
|
|
|
775
794
|
// Pass errorContext so runUnitPhase can distinguish user-initiated pause
|
|
776
795
|
// from provider-error pause and avoid hard-stopping (#2762).
|
|
777
796
|
resolveAgentEndCancelled(_errorContext);
|
|
778
|
-
s.pausedSessionFile = ctx?.sessionManager?.getSessionFile() ?? null;
|
|
797
|
+
s.pausedSessionFile = normalizeSessionFilePath(ctx?.sessionManager?.getSessionFile() ?? null);
|
|
779
798
|
// Persist paused-session metadata so resume survives /exit (#1383).
|
|
780
799
|
// The fresh-start bootstrap checks for this file and restores worktree context.
|
|
781
800
|
try {
|
|
@@ -829,7 +848,6 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
|
|
|
829
848
|
s.verificationRetryCount.clear();
|
|
830
849
|
ctx?.ui.setStatus("gsd-auto", "paused");
|
|
831
850
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
832
|
-
ctx?.ui.setFooter(undefined);
|
|
833
851
|
if (ctx)
|
|
834
852
|
initHealthWidget(ctx);
|
|
835
853
|
const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
|
|
@@ -975,6 +993,9 @@ function buildLoopDeps() {
|
|
|
975
993
|
},
|
|
976
994
|
// Journal
|
|
977
995
|
emitJournalEvent: (entry) => _emitJournalEvent(s.basePath, entry),
|
|
996
|
+
// Clean-root preflight gate (#2909)
|
|
997
|
+
preflightCleanRoot,
|
|
998
|
+
postflightPopStash,
|
|
978
999
|
};
|
|
979
1000
|
}
|
|
980
1001
|
/**
|
|
@@ -1031,7 +1052,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1031
1052
|
unlinkSync(pausedPath);
|
|
1032
1053
|
}
|
|
1033
1054
|
catch (e) {
|
|
1034
|
-
|
|
1055
|
+
if (e.code !== "ENOENT") {
|
|
1056
|
+
logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
|
|
1057
|
+
}
|
|
1035
1058
|
}
|
|
1036
1059
|
ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
|
|
1037
1060
|
}
|
|
@@ -1049,7 +1072,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1049
1072
|
unlinkSync(pausedPath);
|
|
1050
1073
|
}
|
|
1051
1074
|
catch (err) {
|
|
1052
|
-
|
|
1075
|
+
if (err.code !== "ENOENT") {
|
|
1076
|
+
logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1077
|
+
}
|
|
1053
1078
|
}
|
|
1054
1079
|
ctx.ui.notify(`Paused milestone ${meta.milestoneId} is ${!mDir ? "missing" : "already complete"}. Starting fresh.`, "info");
|
|
1055
1080
|
}
|
|
@@ -1057,7 +1082,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1057
1082
|
s.currentMilestoneId = meta.milestoneId;
|
|
1058
1083
|
s.originalBasePath = meta.originalBasePath || base;
|
|
1059
1084
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
1060
|
-
s.pausedSessionFile = meta.sessionFile ?? null;
|
|
1085
|
+
s.pausedSessionFile = normalizeSessionFilePath(meta.sessionFile ?? null);
|
|
1061
1086
|
s.pausedUnitType = meta.unitType ?? null;
|
|
1062
1087
|
s.pausedUnitId = meta.unitId ?? null;
|
|
1063
1088
|
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
@@ -1067,7 +1092,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1067
1092
|
unlinkSync(pausedPath);
|
|
1068
1093
|
}
|
|
1069
1094
|
catch (e) {
|
|
1070
|
-
|
|
1095
|
+
if (e.code !== "ENOENT") {
|
|
1096
|
+
logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
|
|
1097
|
+
}
|
|
1071
1098
|
}
|
|
1072
1099
|
ctx.ui.notify(`Resuming paused session for ${meta.milestoneId}${meta.worktreePath && existsSync(meta.worktreePath) ? ` (worktree)` : ""}.`, "info");
|
|
1073
1100
|
}
|
|
@@ -1077,7 +1104,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1077
1104
|
unlinkSync(pausedPath);
|
|
1078
1105
|
}
|
|
1079
1106
|
catch (e) {
|
|
1080
|
-
|
|
1107
|
+
if (e.code !== "ENOENT") {
|
|
1108
|
+
logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
|
|
1109
|
+
}
|
|
1081
1110
|
}
|
|
1082
1111
|
}
|
|
1083
1112
|
}
|
|
@@ -1133,7 +1162,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1133
1162
|
unlinkSync(s.pausedSessionFile);
|
|
1134
1163
|
}
|
|
1135
1164
|
catch (err) {
|
|
1136
|
-
|
|
1165
|
+
if (err.code !== "ENOENT") {
|
|
1166
|
+
logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1167
|
+
}
|
|
1137
1168
|
}
|
|
1138
1169
|
s.pausedSessionFile = null;
|
|
1139
1170
|
}
|
|
@@ -1143,6 +1174,17 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1143
1174
|
s.stepMode = requestedStepMode;
|
|
1144
1175
|
s.cmdCtx = ctx;
|
|
1145
1176
|
s.basePath = base;
|
|
1177
|
+
// ── Resume worktree: if the paused session was inside a milestone worktree,
|
|
1178
|
+
// apply that path as the dispatch basePath immediately (#3723).
|
|
1179
|
+
// This ensures the dispatch loop runs from the worktree directory even when
|
|
1180
|
+
// enterMilestone guard conditions differ between the original and resumed
|
|
1181
|
+
// session (e.g. isolation mode changed, detectWorktreeName differs across
|
|
1182
|
+
// process restarts). We guard with existsSync so a stale or deleted
|
|
1183
|
+
// worktree directory safely falls back to the project root.
|
|
1184
|
+
const resumeWorktreePath = freshStartAssessment.pausedSession?.worktreePath;
|
|
1185
|
+
if (resumeWorktreePath && existsSync(resumeWorktreePath)) {
|
|
1186
|
+
s.basePath = resumeWorktreePath;
|
|
1187
|
+
}
|
|
1146
1188
|
// Ensure the workflow-logger audit log is pinned to the project root
|
|
1147
1189
|
// even when auto-mode is entered via a path that bypasses the
|
|
1148
1190
|
// bootstrap/dynamic-tools ensureDbOpen() → setLogBasePath() chain
|
|
@@ -1161,7 +1203,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1161
1203
|
});
|
|
1162
1204
|
// ── Auto-worktree / branch-mode: re-enter on resume ──
|
|
1163
1205
|
if (s.currentMilestoneId &&
|
|
1164
|
-
getIsolationMode() !== "none" &&
|
|
1206
|
+
getIsolationMode(s.originalBasePath || s.basePath) !== "none" &&
|
|
1165
1207
|
s.originalBasePath &&
|
|
1166
1208
|
!isInAutoWorktree(s.basePath) &&
|
|
1167
1209
|
!detectWorktreeName(s.basePath) &&
|
|
@@ -1172,7 +1214,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1172
1214
|
}
|
|
1173
1215
|
registerSigtermHandler(lockBase());
|
|
1174
1216
|
ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
|
|
1175
|
-
ctx.ui.
|
|
1217
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
1176
1218
|
ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
|
|
1177
1219
|
restoreHookState(s.basePath);
|
|
1178
1220
|
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
|
@@ -1194,7 +1236,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1194
1236
|
await openProjectDbIfPresent(s.basePath);
|
|
1195
1237
|
try {
|
|
1196
1238
|
await rebuildState(s.basePath);
|
|
1197
|
-
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1239
|
+
syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
|
|
1198
1240
|
}
|
|
1199
1241
|
catch (e) {
|
|
1200
1242
|
debugLog("resume-rebuild-state-failed", {
|
|
@@ -1224,7 +1266,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1224
1266
|
}
|
|
1225
1267
|
updateSessionLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
|
|
1226
1268
|
writeLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
|
|
1227
|
-
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1269
|
+
logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1228
1270
|
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1229
1271
|
startAutoCommandPolling(s.basePath);
|
|
1230
1272
|
await runAutoLoopWithUok({
|
|
@@ -1250,13 +1292,13 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1250
1292
|
return;
|
|
1251
1293
|
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1252
1294
|
try {
|
|
1253
|
-
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1295
|
+
syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
|
|
1254
1296
|
}
|
|
1255
1297
|
catch (err) {
|
|
1256
1298
|
// Best-effort only — sidebar sync must never block auto-mode startup
|
|
1257
1299
|
logWarning("engine", `cmux sync failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1258
1300
|
}
|
|
1259
|
-
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
|
|
1301
|
+
logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
|
|
1260
1302
|
startAutoCommandPolling(s.basePath);
|
|
1261
1303
|
// Dispatch the first unit
|
|
1262
1304
|
await runAutoLoopWithUok({
|
|
@@ -1361,8 +1403,8 @@ export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, trigg
|
|
|
1361
1403
|
`Ensure the model is defined in models.json and has auth configured.`, "warning");
|
|
1362
1404
|
}
|
|
1363
1405
|
}
|
|
1364
|
-
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
1365
|
-
writeLock(lockBase(), hookUnitType, triggerUnitId, sessionFile);
|
|
1406
|
+
const sessionFile = normalizeSessionFilePath(ctx.sessionManager.getSessionFile());
|
|
1407
|
+
writeLock(lockBase(), hookUnitType, triggerUnitId, sessionFile ?? undefined);
|
|
1366
1408
|
clearUnitTimeout();
|
|
1367
1409
|
const supervisor = resolveAutoSupervisorConfig();
|
|
1368
1410
|
const hookHardTimeoutMs = (supervisor.hard_timeout_minutes ?? 30) * 60 * 1000;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logWarning } from "../workflow-logger.js";
|
|
2
|
-
import { checkAutoStartAfterDiscuss } from "../guided-flow.js";
|
|
2
|
+
import { checkAutoStartAfterDiscuss, maybeHandleReadyPhraseWithoutFiles, maybeHandleEmptyIntentTurn, resetEmptyTurnCounter, } from "../guided-flow.js";
|
|
3
3
|
import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
|
|
4
4
|
import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
|
|
5
5
|
import { pauseAutoForProviderError } from "../provider-error-pause.js";
|
|
@@ -53,6 +53,19 @@ export async function handleAgentEnd(pi, event, ctx) {
|
|
|
53
53
|
clearDiscussionFlowState();
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
+
// #4573 — When the LLM emits "Milestone X ready." but the required files
|
|
57
|
+
// are missing, `checkAutoStartAfterDiscuss` returns false silently. Surface
|
|
58
|
+
// that and nudge the LLM to complete the writes before the user hits the
|
|
59
|
+
// downstream "All milestones complete" warning loop.
|
|
60
|
+
if (maybeHandleReadyPhraseWithoutFiles(event))
|
|
61
|
+
return;
|
|
62
|
+
// #4573 — Empty-turn recovery: if the LLM announced intent in prose but
|
|
63
|
+
// emitted no tool calls, nudge it to execute. Fires only when auto-mode is
|
|
64
|
+
// active or a discussion autostart is pending (non-auto interactive discuss
|
|
65
|
+
// is user-driven). Runs before `isAutoActive` early return so pending
|
|
66
|
+
// discussions (where isAutoActive may be false) still get recovered.
|
|
67
|
+
if (maybeHandleEmptyIntentTurn(event, isAutoActive()))
|
|
68
|
+
return;
|
|
56
69
|
if (!isAutoActive())
|
|
57
70
|
return;
|
|
58
71
|
if (isSessionSwitchInFlight())
|
|
@@ -286,6 +299,9 @@ export async function handleAgentEnd(pi, event, ctx) {
|
|
|
286
299
|
// ── Success path ─────────────────────────────────────────────────────────
|
|
287
300
|
try {
|
|
288
301
|
resetRetryState(retryState);
|
|
302
|
+
// #4573 — Reset the empty-turn counter on any successful agent turn so
|
|
303
|
+
// transient stalls don't accumulate across independent units.
|
|
304
|
+
resetEmptyTurnCounter();
|
|
289
305
|
resolveAgentEnd(event);
|
|
290
306
|
}
|
|
291
307
|
catch (err) {
|
|
@@ -19,6 +19,18 @@ function registerAlias(pi, toolDef, aliasName, canonicalName) {
|
|
|
19
19
|
promptGuidelines: [`Alias for ${canonicalName} — prefer the canonical name.`],
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Read a tool result's structured payload, accommodating MCP's `details` →
|
|
24
|
+
* `structuredContent` rename (#4472, #4477). In-process executions still
|
|
25
|
+
* deliver the payload on `result.details`; MCP-routed executions deliver it
|
|
26
|
+
* on `result.structuredContent` (post `adaptExecutorResult` transform). All
|
|
27
|
+
* `renderResult` callbacks in this file route through this helper so a future
|
|
28
|
+
* field rename only needs to be applied in one place.
|
|
29
|
+
*/
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- result shape varies by tool
|
|
31
|
+
function readDetails(result) {
|
|
32
|
+
return result?.details ?? result?.structuredContent;
|
|
33
|
+
}
|
|
22
34
|
export function registerDbTools(pi) {
|
|
23
35
|
// ─── gsd_decision_save (formerly gsd_save_decision) ─────────────────────
|
|
24
36
|
const decisionSaveExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
@@ -92,7 +104,7 @@ export function registerDbTools(pi) {
|
|
|
92
104
|
return new Text(text, 0, 0);
|
|
93
105
|
},
|
|
94
106
|
renderResult(result, _options, theme) {
|
|
95
|
-
const d = result
|
|
107
|
+
const d = readDetails(result);
|
|
96
108
|
if (result.isError || d?.error) {
|
|
97
109
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
98
110
|
}
|
|
@@ -175,7 +187,7 @@ export function registerDbTools(pi) {
|
|
|
175
187
|
return new Text(text, 0, 0);
|
|
176
188
|
},
|
|
177
189
|
renderResult(result, _options, theme) {
|
|
178
|
-
const d = result
|
|
190
|
+
const d = readDetails(result);
|
|
179
191
|
if (result.isError || d?.error) {
|
|
180
192
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
181
193
|
}
|
|
@@ -255,7 +267,7 @@ export function registerDbTools(pi) {
|
|
|
255
267
|
return new Text(text, 0, 0);
|
|
256
268
|
},
|
|
257
269
|
renderResult(result, _options, theme) {
|
|
258
|
-
const d = result
|
|
270
|
+
const d = readDetails(result);
|
|
259
271
|
if (result.isError || d?.error) {
|
|
260
272
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
261
273
|
}
|
|
@@ -301,7 +313,7 @@ export function registerDbTools(pi) {
|
|
|
301
313
|
return new Text(text, 0, 0);
|
|
302
314
|
},
|
|
303
315
|
renderResult(result, _options, theme) {
|
|
304
|
-
const d = result
|
|
316
|
+
const d = readDetails(result);
|
|
305
317
|
if (result.isError || d?.error) {
|
|
306
318
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
307
319
|
}
|
|
@@ -382,7 +394,7 @@ export function registerDbTools(pi) {
|
|
|
382
394
|
return new Text(theme.fg("toolTitle", theme.bold("milestone_generate_id")), 0, 0);
|
|
383
395
|
},
|
|
384
396
|
renderResult(result, _options, theme) {
|
|
385
|
-
const d = result
|
|
397
|
+
const d = readDetails(result);
|
|
386
398
|
if (result.isError || d?.error) {
|
|
387
399
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
388
400
|
}
|
|
@@ -967,13 +979,31 @@ export function registerDbTools(pi) {
|
|
|
967
979
|
text += theme.fg("dim", ` → ${args.verdict ?? ""}`);
|
|
968
980
|
return new Text(text, 0, 0);
|
|
969
981
|
},
|
|
982
|
+
/**
|
|
983
|
+
* Render the save_gate_result tool output for the TUI.
|
|
984
|
+
*
|
|
985
|
+
* Prefers structured fields, but falls back to `content[0].text` when the
|
|
986
|
+
* structured payload is empty. Defensive: the structural fix on this
|
|
987
|
+
* branch plumbs `details` through MCP via `structuredContent`, but older
|
|
988
|
+
* hosts, a future handler that forgets `structuredContent`, or any drop
|
|
989
|
+
* of non-standard return fields would otherwise render as
|
|
990
|
+
* "undefined: undefined". Same fallback applies to error rendering, and
|
|
991
|
+
* we strip a leading `Error:` from the fallback text to avoid producing
|
|
992
|
+
* `Error: Error: ...`.
|
|
993
|
+
*/
|
|
970
994
|
renderResult(result, _options, theme) {
|
|
971
|
-
const d = result
|
|
995
|
+
const d = readDetails(result);
|
|
972
996
|
if (result.isError || d?.error) {
|
|
973
|
-
|
|
997
|
+
const rawMsg = d?.error ?? result.content?.[0]?.text ?? "unknown";
|
|
998
|
+
const msg = rawMsg.replace(/^\s*Error:\s*/i, "");
|
|
999
|
+
return new Text(theme.fg("error", `Error: ${msg}`), 0, 0);
|
|
1000
|
+
}
|
|
1001
|
+
if (!d?.gateId || !d?.verdict) {
|
|
1002
|
+
const text = result.content?.[0]?.text ?? "Gate result saved";
|
|
1003
|
+
return new Text(theme.fg("success", text), 0, 0);
|
|
974
1004
|
}
|
|
975
|
-
const color = d
|
|
976
|
-
return new Text(theme.fg(color, `${d
|
|
1005
|
+
const color = d.verdict === "flag" ? "warning" : "success";
|
|
1006
|
+
return new Text(theme.fg(color, `${d.gateId}: ${d.verdict}`), 0, 0);
|
|
977
1007
|
},
|
|
978
1008
|
};
|
|
979
1009
|
pi.registerTool(saveGateResultTool);
|