@opengsd/gsd-pi 1.1.1-dev.616a1a1 → 1.1.1-dev.74e8dd1
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/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +167 -16
- package/dist/resources/extensions/gsd/auto/phases.js +4 -3
- package/dist/resources/extensions/gsd/auto-dashboard.js +15 -4
- package/dist/resources/extensions/gsd/auto-dispatch.js +39 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +113 -7
- package/dist/resources/extensions/gsd/auto-prompts.js +9 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -4
- package/dist/resources/extensions/gsd/auto-start.js +94 -15
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
- package/dist/resources/extensions/gsd/auto.js +22 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +79 -0
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +30 -9
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +6 -2
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +7 -3
- package/dist/resources/extensions/gsd/commands-maintenance.js +172 -2
- package/dist/resources/extensions/gsd/commands-mcp-status.js +107 -59
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
- package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.js +2 -1
- package/dist/resources/extensions/gsd/error-classifier.js +2 -1
- package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
- package/dist/resources/extensions/gsd/gsd-db.js +37 -4
- package/dist/resources/extensions/gsd/guided-flow.js +1 -1
- package/dist/resources/extensions/gsd/mcp-filter.js +3 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +67 -8
- package/dist/resources/extensions/gsd/migration-auto-check.js +2 -2
- package/dist/resources/extensions/gsd/prompts/run-uat.md +10 -4
- package/dist/resources/extensions/gsd/prompts/system.md +3 -1
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
- package/dist/resources/extensions/gsd/skill-activation.js +20 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +4 -2
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +1 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
- package/dist/resources/extensions/gsd/state.js +15 -12
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +120 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +109 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -9
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +366 -3
- package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -1
- package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -0
- package/dist/resources/extensions/mcp-client/manager.js +31 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
- 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/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 +4 -4
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +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/.next/server/server-reference-manifest.json +1 -1
- package/package.json +2 -2
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +14 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +72 -31
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +82 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/image-models.generated.d.ts +15 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +15 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +338 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +412 -112
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -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 +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/terminal.d.ts +1 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +8 -4
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +196 -16
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +239 -63
- package/src/resources/extensions/gsd/auto/phases.ts +5 -3
- package/src/resources/extensions/gsd/auto-dashboard.ts +16 -4
- package/src/resources/extensions/gsd/auto-dispatch.ts +48 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +138 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +9 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +4 -4
- package/src/resources/extensions/gsd/auto-start.ts +112 -17
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
- package/src/resources/extensions/gsd/auto.ts +35 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +86 -0
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +51 -14
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -2
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +7 -3
- package/src/resources/extensions/gsd/commands-maintenance.ts +197 -2
- package/src/resources/extensions/gsd/commands-mcp-status.ts +134 -57
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
- package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +2 -1
- package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
- package/src/resources/extensions/gsd/gsd-db.ts +41 -6
- package/src/resources/extensions/gsd/guided-flow.ts +1 -1
- package/src/resources/extensions/gsd/mcp-filter.ts +3 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +92 -10
- package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +10 -4
- package/src/resources/extensions/gsd/prompts/system.md +3 -1
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +3 -0
- package/src/resources/extensions/gsd/skill-activation.ts +20 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +4 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +1 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
- package/src/resources/extensions/gsd/state.ts +16 -12
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +143 -2
- package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +24 -2
- package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +50 -13
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/gsd-rebuild.test.ts +199 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +13 -6
- package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +39 -1
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +167 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +130 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +14 -9
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +440 -2
- package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +26 -0
- package/src/resources/extensions/mcp-client/manager.ts +33 -1
- package/src/resources/extensions/mcp-client/tests/manager.test.ts +35 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5f6f7839b5420a75
|
|
@@ -15,10 +15,11 @@ import { homedir } from "node:os";
|
|
|
15
15
|
import { createRequire } from "node:module";
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
|
|
18
|
-
import { buildWorkflowMcpServers, resolveWorkflowMcpProjectRoot } from "../gsd/workflow-mcp.js";
|
|
19
|
-
import { buildProjectGsdMcpServers } from "../gsd/mcp-project-config.js";
|
|
18
|
+
import { buildWorkflowMcpServers, getRequiredWorkflowToolsForAutoUnit, resolveWorkflowMcpProjectRoot, } from "../gsd/workflow-mcp.js";
|
|
19
|
+
import { buildProjectGsdMcpServers, ensureProjectWorkflowMcpConfig } from "../gsd/mcp-project-config.js";
|
|
20
20
|
import { loadProjectGSDPreferences } from "../gsd/preferences.js";
|
|
21
|
-
import { discoverBrowserMcpServerName, discoverMcpServerNames, discoverWorkflowMcpServerName, computeMcpDisallowedTools } from "../gsd/mcp-filter.js";
|
|
21
|
+
import { discoverBrowserMcpServerName, discoverMcpServers, discoverMcpServerNames, discoverWorkflowMcpServerName, computeMcpDisallowedTools, } from "../gsd/mcp-filter.js";
|
|
22
|
+
import { RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES, RUN_UAT_FORBIDDEN_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES, resolveToolPresentationPlan } from "../gsd/tool-presentation-plan.js";
|
|
22
23
|
import { showInterviewRound } from "../shared/tui.js";
|
|
23
24
|
export function serverToolUseToToolCallLike(block) {
|
|
24
25
|
const argumentsValue = block.input && typeof block.input === "object" && !Array.isArray(block.input)
|
|
@@ -164,6 +165,32 @@ function extractMessageText(msg) {
|
|
|
164
165
|
}
|
|
165
166
|
return "";
|
|
166
167
|
}
|
|
168
|
+
const GSD_PHASE_PATTERNS = [
|
|
169
|
+
["run-uat", /\b(?:UNIT:\s*Run UAT|run-uat)\b/i],
|
|
170
|
+
["complete-milestone", /\b(?:UNIT:\s*Complete Milestone|complete-milestone)\b/i],
|
|
171
|
+
["validate-milestone", /\b(?:UNIT:\s*Validate Milestone|validate-milestone)\b/i],
|
|
172
|
+
["reassess-roadmap", /\b(?:UNIT:\s*Reassess Roadmap|reassess-roadmap)\b/i],
|
|
173
|
+
["complete-slice", /\b(?:UNIT:\s*Complete Slice|complete-slice)\b/i],
|
|
174
|
+
["replan-slice", /\b(?:UNIT:\s*Replan Slice|replan-slice)\b/i],
|
|
175
|
+
["plan-slice", /\b(?:UNIT:\s*Plan Slice|plan-slice|gsd_plan_slice)\b/i],
|
|
176
|
+
["plan-milestone", /\b(?:UNIT:\s*Plan Milestone|plan-milestone|gsd_plan_milestone)\b/i],
|
|
177
|
+
["execute-task", /\b(?:UNIT:\s*Execute Task|execute-task|execute-task-simple|reactive-execute)\b/i],
|
|
178
|
+
["gate-evaluate", /\b(?:UNIT:\s*Gate Evaluate|gate-evaluate|gsd_save_gate_result)\b/i],
|
|
179
|
+
["research-milestone", /\b(?:UNIT:\s*Research Milestone|research-milestone)\b/i],
|
|
180
|
+
["research-slice", /\b(?:UNIT:\s*Research Slice|research-slice)\b/i],
|
|
181
|
+
["discuss-milestone", /\b(?:Discuss milestone|discuss-milestone)\b/i],
|
|
182
|
+
];
|
|
183
|
+
export function inferGsdPhaseFromContext(context) {
|
|
184
|
+
const text = [
|
|
185
|
+
context.systemPrompt ?? "",
|
|
186
|
+
...context.messages.map((message) => extractMessageText(message)),
|
|
187
|
+
].join("\n");
|
|
188
|
+
for (const [phase, pattern] of GSD_PHASE_PATTERNS) {
|
|
189
|
+
if (pattern.test(text))
|
|
190
|
+
return phase;
|
|
191
|
+
}
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
167
194
|
/**
|
|
168
195
|
* Build a full conversational prompt from GSD's context messages.
|
|
169
196
|
*
|
|
@@ -1175,22 +1202,106 @@ function mapThinkingLevelToAnthropicEffort(level, modelId) {
|
|
|
1175
1202
|
return "high";
|
|
1176
1203
|
}
|
|
1177
1204
|
}
|
|
1178
|
-
function
|
|
1205
|
+
function parseAllowedMcpToolName(toolName) {
|
|
1206
|
+
const match = /^mcp__(.+)__(\*|[^*]+)$/.exec(toolName);
|
|
1207
|
+
return match?.[1] && match[2] ? { server: match[1], tool: match[2] } : undefined;
|
|
1208
|
+
}
|
|
1209
|
+
function browserMcpServerNameFromAllowedTools(allowedTools) {
|
|
1179
1210
|
if (!Array.isArray(allowedTools))
|
|
1180
1211
|
return undefined;
|
|
1181
1212
|
for (const toolName of allowedTools) {
|
|
1182
1213
|
if (typeof toolName !== "string")
|
|
1183
1214
|
continue;
|
|
1184
|
-
const
|
|
1185
|
-
if (
|
|
1186
|
-
|
|
1215
|
+
const parsed = parseAllowedMcpToolName(toolName);
|
|
1216
|
+
if (!parsed)
|
|
1217
|
+
continue;
|
|
1218
|
+
if (parsed.server === "gsd-browser" || parsed.tool.startsWith("browser_")) {
|
|
1219
|
+
return parsed.server;
|
|
1220
|
+
}
|
|
1187
1221
|
}
|
|
1188
1222
|
return undefined;
|
|
1189
1223
|
}
|
|
1190
|
-
function
|
|
1224
|
+
function workflowMcpServerNameFromAllowedTools(allowedTools) {
|
|
1191
1225
|
if (!Array.isArray(allowedTools))
|
|
1192
1226
|
return undefined;
|
|
1193
|
-
|
|
1227
|
+
const browserServerName = browserMcpServerNameFromAllowedTools(allowedTools);
|
|
1228
|
+
for (const toolName of allowedTools) {
|
|
1229
|
+
if (typeof toolName !== "string")
|
|
1230
|
+
continue;
|
|
1231
|
+
const parsed = parseAllowedMcpToolName(toolName);
|
|
1232
|
+
if (!parsed || parsed.server === browserServerName || parsed.tool.startsWith("browser_"))
|
|
1233
|
+
continue;
|
|
1234
|
+
return parsed.server;
|
|
1235
|
+
}
|
|
1236
|
+
return undefined;
|
|
1237
|
+
}
|
|
1238
|
+
function isRecord(value) {
|
|
1239
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1240
|
+
}
|
|
1241
|
+
function isStringRecord(value) {
|
|
1242
|
+
return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
1243
|
+
}
|
|
1244
|
+
function cloneSdkMcpServerConfig(config) {
|
|
1245
|
+
if (!isRecord(config))
|
|
1246
|
+
return undefined;
|
|
1247
|
+
const cloned = {};
|
|
1248
|
+
for (const key of ["type", "command", "cwd", "url"]) {
|
|
1249
|
+
if (typeof config[key] === "string")
|
|
1250
|
+
cloned[key] = config[key];
|
|
1251
|
+
}
|
|
1252
|
+
if (Array.isArray(config.args)) {
|
|
1253
|
+
cloned.args = config.args.filter((arg) => typeof arg === "string");
|
|
1254
|
+
}
|
|
1255
|
+
if (isStringRecord(config.env))
|
|
1256
|
+
cloned.env = { ...config.env };
|
|
1257
|
+
if (isStringRecord(config.headers))
|
|
1258
|
+
cloned.headers = { ...config.headers };
|
|
1259
|
+
if (isRecord(config.oauth))
|
|
1260
|
+
cloned.oauth = { ...config.oauth };
|
|
1261
|
+
return Object.keys(cloned).length > 0 ? cloned : undefined;
|
|
1262
|
+
}
|
|
1263
|
+
function resolveProjectMcpServerConfig(projectRoot, serverName, fallbackServers) {
|
|
1264
|
+
if (!serverName)
|
|
1265
|
+
return undefined;
|
|
1266
|
+
const projectServer = discoverMcpServers(projectRoot).find((server) => server.name === serverName);
|
|
1267
|
+
return cloneSdkMcpServerConfig(projectServer?.config) ?? cloneSdkMcpServerConfig(fallbackServers?.[serverName]);
|
|
1268
|
+
}
|
|
1269
|
+
function resolveProjectMcpServerConfigs(projectRoot, serverNames, fallbackServers) {
|
|
1270
|
+
const resolved = {};
|
|
1271
|
+
for (const serverName of serverNames) {
|
|
1272
|
+
const serverConfig = resolveProjectMcpServerConfig(projectRoot, serverName, fallbackServers);
|
|
1273
|
+
if (serverName && serverConfig)
|
|
1274
|
+
resolved[serverName] = serverConfig;
|
|
1275
|
+
}
|
|
1276
|
+
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
|
1277
|
+
}
|
|
1278
|
+
function resolveExactWorkflowMcpToolsForPhase(gsdPhase, workflowServerName, workflowExplicitlyBlocked) {
|
|
1279
|
+
if (!gsdPhase || !workflowServerName || workflowExplicitlyBlocked)
|
|
1280
|
+
return [];
|
|
1281
|
+
const requiredTools = gsdPhase === "run-uat"
|
|
1282
|
+
? [...RUN_UAT_WORKFLOW_TOOL_NAMES]
|
|
1283
|
+
: getRequiredWorkflowToolsForAutoUnit(gsdPhase);
|
|
1284
|
+
const supportTools = gsdPhase === "run-uat" ? [] : ["gsd_milestone_status"];
|
|
1285
|
+
const requestedToolNames = [...new Set([...requiredTools, ...supportTools])];
|
|
1286
|
+
if (requestedToolNames.length === 0)
|
|
1287
|
+
return [];
|
|
1288
|
+
return resolveToolPresentationPlan({
|
|
1289
|
+
phase: gsdPhase,
|
|
1290
|
+
surface: "claude-code-sdk",
|
|
1291
|
+
workflowMcpServerName: workflowServerName,
|
|
1292
|
+
requestedToolNames,
|
|
1293
|
+
}).presentedToolNames;
|
|
1294
|
+
}
|
|
1295
|
+
export function autoInitClaudeCodeWorkflowMcp(cwd) {
|
|
1296
|
+
const projectRoot = resolveWorkflowMcpProjectRoot(cwd);
|
|
1297
|
+
try {
|
|
1298
|
+
ensureProjectWorkflowMcpConfig(projectRoot);
|
|
1299
|
+
}
|
|
1300
|
+
catch (err) {
|
|
1301
|
+
if (process.env.GSD_DEBUG === "1") {
|
|
1302
|
+
console.warn(`[claude-code] workflow MCP auto-init failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1194
1305
|
}
|
|
1195
1306
|
/**
|
|
1196
1307
|
* Build the options object passed to the Claude Agent SDK's `query()` call.
|
|
@@ -1204,7 +1315,7 @@ function browserMcpServerNameFromAllowedTools(allowedTools) {
|
|
|
1204
1315
|
* behaviour pass `permissionMode: "bypassPermissions"` explicitly.
|
|
1205
1316
|
*/
|
|
1206
1317
|
export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
1207
|
-
const { reasoning, cwd, ...sdkExtraOptions } = extraOptions;
|
|
1318
|
+
const { reasoning, cwd, gsdPhase, ...sdkExtraOptions } = extraOptions;
|
|
1208
1319
|
const sdkCwd = typeof cwd === "string" && cwd.trim().length > 0 ? cwd : process.cwd();
|
|
1209
1320
|
// Claude Code runs in the milestone worktree for file/shell work, but workflow MCP
|
|
1210
1321
|
// config (.mcp.json) and server discovery live at the project root.
|
|
@@ -1283,13 +1394,38 @@ export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
|
1283
1394
|
const browserMcpTools = !browserExplicitlyBlocked && browserServerName
|
|
1284
1395
|
? [`mcp__${browserServerName}__*`]
|
|
1285
1396
|
: [];
|
|
1286
|
-
const
|
|
1397
|
+
const phaseUsesBrowserMcp = !gsdPhase || gsdPhase === "run-uat";
|
|
1398
|
+
const allowedBrowserMcpTools = phaseUsesBrowserMcp ? browserMcpTools : [];
|
|
1399
|
+
const inlinePhaseMcpServers = gsdPhase
|
|
1400
|
+
? resolveProjectMcpServerConfigs(projectRoot, [
|
|
1401
|
+
workflowExplicitlyBlocked ? undefined : workflowServerName,
|
|
1402
|
+
phaseUsesBrowserMcp && !browserExplicitlyBlocked ? browserServerName : undefined,
|
|
1403
|
+
], defaultMcpServers.servers)
|
|
1404
|
+
: undefined;
|
|
1405
|
+
const sdkMcpServers = inlinePhaseMcpServers ?? filteredMcpServers;
|
|
1406
|
+
const strictMcpConfig = !!inlinePhaseMcpServers;
|
|
1407
|
+
// Strict phase configs inline the exact MCP servers GSD needs. Loading
|
|
1408
|
+
// project/local settings at the same time can duplicate those servers and
|
|
1409
|
+
// leave allowed mcp__... tools with no registered backing tool.
|
|
1410
|
+
const settingSources = strictMcpConfig ? [] : ["project", "local"];
|
|
1411
|
+
const exactWorkflowMcpTools = resolveExactWorkflowMcpToolsForPhase(gsdPhase, workflowServerName, workflowExplicitlyBlocked);
|
|
1412
|
+
const runUatDisallowedTools = gsdPhase === "run-uat" && workflowServerName
|
|
1413
|
+
? [
|
|
1414
|
+
...RUN_UAT_FORBIDDEN_TOOL_NAMES.filter((toolName) => !toolName.startsWith("mcp__")),
|
|
1415
|
+
"WebFetch",
|
|
1416
|
+
"Agent",
|
|
1417
|
+
`mcp__${workflowServerName}__gsd_exec`,
|
|
1418
|
+
`mcp__${workflowServerName}__gsd_summary_save`,
|
|
1419
|
+
`mcp__${workflowServerName}__gsd_save_gate_result`,
|
|
1420
|
+
]
|
|
1421
|
+
: [];
|
|
1287
1422
|
const disallowedTools = [...new Set([
|
|
1288
1423
|
"ToolSearch",
|
|
1289
|
-
...(workflowMcpTools.length > 0 ? ["AskUserQuestion"] : []),
|
|
1424
|
+
...(workflowMcpTools.length > 0 || exactWorkflowMcpTools.length > 0 ? ["AskUserQuestion"] : []),
|
|
1425
|
+
...runUatDisallowedTools,
|
|
1290
1426
|
...extraDisallowedTools,
|
|
1291
1427
|
])];
|
|
1292
|
-
const
|
|
1428
|
+
const standardClaudeTools = [
|
|
1293
1429
|
"Read",
|
|
1294
1430
|
"Write",
|
|
1295
1431
|
"Edit",
|
|
@@ -1299,8 +1435,19 @@ export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
|
1299
1435
|
"Agent",
|
|
1300
1436
|
"WebFetch",
|
|
1301
1437
|
"WebSearch",
|
|
1302
|
-
...(workflowMcpTools.length > 0 ? gsdMcpTools : ["AskUserQuestion", ...browserMcpTools]),
|
|
1303
1438
|
];
|
|
1439
|
+
const allowedTools = gsdPhase === "run-uat"
|
|
1440
|
+
? [
|
|
1441
|
+
...RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES,
|
|
1442
|
+
...(exactWorkflowMcpTools.length > 0 ? exactWorkflowMcpTools : []),
|
|
1443
|
+
...allowedBrowserMcpTools,
|
|
1444
|
+
]
|
|
1445
|
+
: [
|
|
1446
|
+
...standardClaudeTools,
|
|
1447
|
+
...exactWorkflowMcpTools,
|
|
1448
|
+
...(workflowMcpTools.length > 0 ? workflowMcpTools : ["AskUserQuestion"]),
|
|
1449
|
+
...allowedBrowserMcpTools,
|
|
1450
|
+
];
|
|
1304
1451
|
const supportsAdaptive = modelSupportsAdaptiveThinking(modelId);
|
|
1305
1452
|
const effort = reasoning && supportsAdaptive
|
|
1306
1453
|
? mapThinkingLevelToAnthropicEffort(reasoning, modelId)
|
|
@@ -1321,11 +1468,12 @@ export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
|
1321
1468
|
cwd: sdkCwd,
|
|
1322
1469
|
permissionMode,
|
|
1323
1470
|
allowDangerouslySkipPermissions: permissionMode === "bypassPermissions",
|
|
1324
|
-
settingSources
|
|
1471
|
+
settingSources,
|
|
1325
1472
|
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
1326
1473
|
disallowedTools,
|
|
1327
1474
|
...(allowedTools.length > 0 ? { allowedTools } : {}),
|
|
1328
|
-
...(
|
|
1475
|
+
...(sdkMcpServers ? { mcpServers: sdkMcpServers } : {}),
|
|
1476
|
+
...(strictMcpConfig ? { strictMcpConfig: true } : {}),
|
|
1329
1477
|
betas: (modelId.includes("sonnet")
|
|
1330
1478
|
|| modelId.includes("opus-4-7")
|
|
1331
1479
|
|| modelId.includes("opus-4.7")
|
|
@@ -1599,6 +1747,8 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
1599
1747
|
const onExternalToolCall = options?.onExternalToolCall;
|
|
1600
1748
|
const onExternalToolResult = options?.onExternalToolResult;
|
|
1601
1749
|
const cwd = resolveClaudeCodeCwd(options);
|
|
1750
|
+
autoInitClaudeCodeWorkflowMcp(cwd);
|
|
1751
|
+
const gsdPhase = inferGsdPhaseFromContext(context);
|
|
1602
1752
|
const canUseToolHandler = createClaudeCodeCanUseToolHandler(uiContext);
|
|
1603
1753
|
// When no UI is available (headless / auto-mode), auto-approve all
|
|
1604
1754
|
// tool requests. This replaces the old bypassPermissions workaround.
|
|
@@ -1606,6 +1756,7 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
1606
1756
|
?? (async (_toolName, _input, opts) => ({ behavior: "allow", toolUseID: opts.toolUseID }));
|
|
1607
1757
|
const sdkOpts = buildSdkOptions(modelId, "", { permissionMode }, {
|
|
1608
1758
|
cwd,
|
|
1759
|
+
gsdPhase,
|
|
1609
1760
|
reasoning: options?.reasoning,
|
|
1610
1761
|
canUseTool: canUseToolFallback,
|
|
1611
1762
|
...(uiContext
|
|
@@ -51,13 +51,13 @@ import { resolveManifest } from "../unit-context-manifest.js";
|
|
|
51
51
|
import { createWorktreeSafetyModule } from "../worktree-safety.js";
|
|
52
52
|
import { isSuspiciousGhostCompletion } from "../auto-unit-closeout.js";
|
|
53
53
|
import { decideVerificationRetry, verificationRetryKey } from "./verification-retry-policy.js";
|
|
54
|
-
import { buildPhaseHandoffOutcome, setAutoOutcomeWidget } from "../auto-dashboard.js";
|
|
54
|
+
import { buildPhaseHandoffOutcome, setAutoActiveStatus, setAutoOutcomeWidget } from "../auto-dashboard.js";
|
|
55
55
|
import { getConsecutiveDispatchBlocker } from "../dispatch-guard.js";
|
|
56
56
|
import { captureRootDirtySnapshot, detectRootWriteLeak, formatRootWriteLeakMessage, } from "../root-write-leak-guard.js";
|
|
57
57
|
import { classifyError, isTransient } from "../error-classifier.js";
|
|
58
58
|
export const STUCK_WINDOW_SIZE = 6;
|
|
59
59
|
const STUCK_RECOVERY_ATTEMPTS_KEY = "stuck_recovery_attempts";
|
|
60
|
-
const ZERO_TOOL_PROVIDER_ERROR_PREFIX_RE = /^(?:api error(?::|$|\s*\()|provider error(?::|$|\s*\()|request failed\b|(?:http\s*)?(?:429|500|502|503)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|socket hang up\b|fetch failed\b|(?:network|connection|server) error(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|dns\b.*(?:fail|error|timeout)|unexpected eof\b|stream idle timeout\b|partial response received\b|stream_exhausted\b|terminated(?::|$)|(?:connection|stream|request)\b.{0,40}\bterminated\b|other side closed\b|rate.?limit(?:ed| exceeded| reached| error)|too many requests\b|you(?:'ve| have) hit your limit\b|
|
|
60
|
+
const ZERO_TOOL_PROVIDER_ERROR_PREFIX_RE = /^(?:api error(?::|$|\s*\()|provider error(?::|$|\s*\()|request failed\b|(?:http\s*)?(?:429|500|502|503)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|socket hang up\b|fetch failed\b|(?:network|connection|server) error(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|dns\b.*(?:fail|error|timeout)|unexpected eof\b|stream idle timeout\b|partial response received\b|stream_exhausted\b|terminated(?::|$)|(?:connection|stream|request)\b.{0,40}\bterminated\b|other side closed\b|rate.?limit(?:ed| exceeded| reached| error)|too many requests\b|you(?:'ve| have) (?:hit|reached) your (?:\w+ )?limit\b|.*\b(?:usage|session|weekly|daily|monthly|quota) limit\b|limit\b.{0,40}\bresets?\b|out of extra usage\b|service.?unavailable\b|internal(?: server)? error(?::|$)|internal(?:[_-]server)?[_-]error\b|server[_-]error\b|(?:provider|server|api|model|codex|claude|openai|anthropic|gemini)\b.{0,80}\boverloaded\b|overloaded\b.{0,80}\b(?:provider|server|api|model)\b|context (?:window|length) exceed|context window exceed)/i;
|
|
61
61
|
const ZERO_TOOL_PROVIDER_ERROR_SIGNAL_RE = /(?:\b(?:http|status(?: code)?|code|error:)\s*(?:429|500|502|503)\b|\b(?:api|provider) error\s*[:(]?\s*(?:429|500|502|503)\b|\b(?:typeerror|error):\s*(?:fetch failed\b|socket hang up\b|terminated(?::|$)|connection (?:reset|refused)(?::|$|\s+by\b)|(?:network|connection|server) error(?::|$)|stream idle timeout\b|partial response received\b|unexpected eof\b)|\b(?:server_error|api_error|stream_exhausted(?:_without_result)?)\b|\b(?:econnreset|etimedout|econnrefused|epipe)\b|context (?:window|length) exceed|context window exceed)/i;
|
|
62
62
|
function classifyZeroToolProviderMessage(message) {
|
|
63
63
|
const firstLine = message.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
@@ -67,6 +67,7 @@ function classifyZeroToolProviderMessage(message) {
|
|
|
67
67
|
return null;
|
|
68
68
|
return classifyError(firstLine);
|
|
69
69
|
}
|
|
70
|
+
export const _classifyZeroToolProviderMessageForTest = classifyZeroToolProviderMessage;
|
|
70
71
|
export function resolveDispatchRecoveryAttempts(unitRecoveryCount, unitType, unitId) {
|
|
71
72
|
return (unitRecoveryCount.get(`${unitType}/${unitId}`) ?? 0) > 0
|
|
72
73
|
? 0
|
|
@@ -1582,7 +1583,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
1582
1583
|
const dispatchKey = `${unitType}/${unitId}`;
|
|
1583
1584
|
const nextDispatchCount = (s.unitDispatchCount.get(dispatchKey) ?? 0) + 1;
|
|
1584
1585
|
// Status bar (widget + preconditions deferred until after model selection — see #2899)
|
|
1585
|
-
ctx
|
|
1586
|
+
setAutoActiveStatus(ctx, s.stepMode ? "next" : "auto");
|
|
1586
1587
|
if (mid)
|
|
1587
1588
|
deps.updateSliceProgressCache(s.basePath, mid, state.activeSlice?.id);
|
|
1588
1589
|
// ── Safety harness: reset evidence + create checkpoint ──
|
|
@@ -369,8 +369,9 @@ export const hideFooter = (_tui, theme, footerData) => ({
|
|
|
369
369
|
invalidate() { },
|
|
370
370
|
dispose() { },
|
|
371
371
|
});
|
|
372
|
+
export const DEFAULT_WIDGET_MODE = "small";
|
|
372
373
|
const WIDGET_MODES = ["full", "small", "min", "off"];
|
|
373
|
-
let widgetMode =
|
|
374
|
+
let widgetMode = DEFAULT_WIDGET_MODE;
|
|
374
375
|
let widgetModeInitialized = false;
|
|
375
376
|
let widgetModePreferencePath = null;
|
|
376
377
|
function safeReadTextFile(path) {
|
|
@@ -473,10 +474,19 @@ export function getWidgetMode(projectPath, globalPath) {
|
|
|
473
474
|
}
|
|
474
475
|
/** Test-only reset for widget mode caching. */
|
|
475
476
|
export function _resetWidgetModeForTests() {
|
|
476
|
-
widgetMode =
|
|
477
|
+
widgetMode = DEFAULT_WIDGET_MODE;
|
|
477
478
|
widgetModeInitialized = false;
|
|
478
479
|
widgetModePreferencePath = null;
|
|
479
480
|
}
|
|
481
|
+
function clearAutoOutcomeWidget(ctx) {
|
|
482
|
+
if (!ctx.hasUI)
|
|
483
|
+
return;
|
|
484
|
+
ctx.ui.setWidget("gsd-outcome", undefined);
|
|
485
|
+
}
|
|
486
|
+
export function setAutoActiveStatus(ctx, status) {
|
|
487
|
+
ctx.ui.setStatus("gsd-auto", status);
|
|
488
|
+
clearAutoOutcomeWidget(ctx);
|
|
489
|
+
}
|
|
480
490
|
export function updateProgressWidget(ctx, unitType, unitId, state, accessors, tierBadge) {
|
|
481
491
|
if (!ctx.hasUI)
|
|
482
492
|
return;
|
|
@@ -495,7 +505,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
|
|
|
495
505
|
ctx.ui.setStatus("gsd-step", undefined);
|
|
496
506
|
}
|
|
497
507
|
if (!accessors.isSessionSwitching()) {
|
|
498
|
-
ctx
|
|
508
|
+
clearAutoOutcomeWidget(ctx);
|
|
499
509
|
}
|
|
500
510
|
const verb = unitVerb(unitType);
|
|
501
511
|
const phaseLabel = unitPhaseLabel(unitType);
|
|
@@ -548,6 +558,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
|
|
|
548
558
|
logWarning("dashboard", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
549
559
|
}
|
|
550
560
|
}, 15_000);
|
|
561
|
+
progressRefreshTimer.unref?.();
|
|
551
562
|
return {
|
|
552
563
|
render(width) {
|
|
553
564
|
if (cachedLines && cachedWidth === width)
|
|
@@ -789,7 +800,7 @@ export function setCompletionProgressWidget(ctx, snapshot) {
|
|
|
789
800
|
if (!ctx.hasUI)
|
|
790
801
|
return;
|
|
791
802
|
const widgetKey = "gsd-progress";
|
|
792
|
-
ctx
|
|
803
|
+
clearAutoOutcomeWidget(ctx);
|
|
793
804
|
if (typeof ctx.ui?.setHeader === "function") {
|
|
794
805
|
ctx.ui.setHeader(() => ({
|
|
795
806
|
render() { return []; },
|
|
@@ -30,6 +30,7 @@ import { nativeHasChanges, nativeIsRepo, _resetHasChangesCache } from "./native-
|
|
|
30
30
|
import { debugLog, isDebugEnabled } from "./debug-logger.js";
|
|
31
31
|
import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
|
|
32
32
|
import { resolveWorktreeProjectRoot } from "./worktree-root.js";
|
|
33
|
+
import { detectWorktreeName } from "./worktree.js";
|
|
33
34
|
import { probeGitConflictState } from "./git-conflict-state.js";
|
|
34
35
|
import { runTurnGitAction } from "./git-service.js";
|
|
35
36
|
import { parseUnitId } from "./unit-id.js";
|
|
@@ -178,6 +179,24 @@ function missingSliceStop(mid, phase) {
|
|
|
178
179
|
function isRegistryMilestoneComplete(state, mid) {
|
|
179
180
|
return state.registry.some((milestone) => milestone.id === mid && milestone.status === "complete");
|
|
180
181
|
}
|
|
182
|
+
function normalizeMilestoneScope(value) {
|
|
183
|
+
const trimmed = value?.trim();
|
|
184
|
+
if (!trimmed || !MILESTONE_ID_RE.test(trimmed))
|
|
185
|
+
return null;
|
|
186
|
+
return trimmed;
|
|
187
|
+
}
|
|
188
|
+
function resolveDispatchMilestoneScope(ctx) {
|
|
189
|
+
const sessionMilestone = normalizeMilestoneScope(ctx.session?.currentMilestoneId);
|
|
190
|
+
if (sessionMilestone)
|
|
191
|
+
return { id: sessionMilestone, source: "session.currentMilestoneId" };
|
|
192
|
+
const sessionWorktree = normalizeMilestoneScope(ctx.session?.basePath ? detectWorktreeName(ctx.session.basePath) : null);
|
|
193
|
+
if (sessionWorktree)
|
|
194
|
+
return { id: sessionWorktree, source: "session.basePath worktree" };
|
|
195
|
+
const baseWorktree = normalizeMilestoneScope(detectWorktreeName(ctx.basePath));
|
|
196
|
+
if (baseWorktree)
|
|
197
|
+
return { id: baseWorktree, source: "basePath worktree" };
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
181
200
|
function hasMilestonePassedDiscuss(basePath, mid) {
|
|
182
201
|
if (isDbAvailable()) {
|
|
183
202
|
try {
|
|
@@ -1351,6 +1370,17 @@ import { getRegistry } from "./rule-registry.js";
|
|
|
1351
1370
|
* resolveDispatch directly without registry initialization).
|
|
1352
1371
|
*/
|
|
1353
1372
|
export async function resolveDispatch(ctx) {
|
|
1373
|
+
if (ctx.mid && isDbAvailable()) {
|
|
1374
|
+
const milestone = getMilestone(ctx.mid);
|
|
1375
|
+
if (milestone && isClosedStatus(milestone.status)) {
|
|
1376
|
+
return {
|
|
1377
|
+
action: "stop",
|
|
1378
|
+
reason: `Milestone ${ctx.mid} is closed (status: ${milestone.status}); auto-mode will not reopen or recover it implicitly. ` +
|
|
1379
|
+
"Use an explicit reopen command before planning or executing more work for this milestone.",
|
|
1380
|
+
level: "warning",
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1354
1384
|
const activeMid = ctx.state.activeMilestone?.id;
|
|
1355
1385
|
if (activeMid && ctx.mid !== activeMid) {
|
|
1356
1386
|
return {
|
|
@@ -1360,6 +1390,15 @@ export async function resolveDispatch(ctx) {
|
|
|
1360
1390
|
level: "warning",
|
|
1361
1391
|
};
|
|
1362
1392
|
}
|
|
1393
|
+
const scopedMilestone = resolveDispatchMilestoneScope(ctx);
|
|
1394
|
+
if (scopedMilestone && ctx.mid !== scopedMilestone.id) {
|
|
1395
|
+
return {
|
|
1396
|
+
action: "stop",
|
|
1397
|
+
reason: `Dispatch milestone mismatch: context mid "${ctx.mid}" does not match ${scopedMilestone.source} "${scopedMilestone.id}". ` +
|
|
1398
|
+
"The active worktree/session and derived project state disagree; recover, park, or discard the stranded milestone before continuing.",
|
|
1399
|
+
level: "warning",
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1363
1402
|
// Delegate to registry when available
|
|
1364
1403
|
try {
|
|
1365
1404
|
const registry = getRegistry();
|
|
@@ -323,6 +323,41 @@ function stripKnownIdPrefix(value, id) {
|
|
|
323
323
|
return raw.slice(id.length + 1).trim() || undefined;
|
|
324
324
|
return raw;
|
|
325
325
|
}
|
|
326
|
+
function parseReactiveBatchTaskIds(unitId) {
|
|
327
|
+
const { task: batchPart } = parseUnitId(unitId);
|
|
328
|
+
if (!batchPart?.startsWith("reactive+"))
|
|
329
|
+
return [];
|
|
330
|
+
const rawIds = batchPart
|
|
331
|
+
.slice("reactive+".length)
|
|
332
|
+
.split(",")
|
|
333
|
+
.map((taskId) => taskId.trim().toUpperCase())
|
|
334
|
+
.filter(Boolean);
|
|
335
|
+
const unique = new Set();
|
|
336
|
+
for (const taskId of rawIds) {
|
|
337
|
+
unique.add(taskId);
|
|
338
|
+
}
|
|
339
|
+
return [...unique];
|
|
340
|
+
}
|
|
341
|
+
function dedupePaths(values) {
|
|
342
|
+
const seen = new Set();
|
|
343
|
+
const result = [];
|
|
344
|
+
for (const value of values) {
|
|
345
|
+
if (!seen.has(value)) {
|
|
346
|
+
seen.add(value);
|
|
347
|
+
result.push(value);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return result;
|
|
351
|
+
}
|
|
352
|
+
function getPlannedKeyFiles(tasks) {
|
|
353
|
+
return dedupePaths(tasks.flatMap((taskRow) => [
|
|
354
|
+
...(taskRow.expected_output ?? []),
|
|
355
|
+
...(taskRow.files ?? []),
|
|
356
|
+
...(taskRow.key_files ?? []),
|
|
357
|
+
]));
|
|
358
|
+
}
|
|
359
|
+
export const _parseReactiveBatchTaskIdsForTest = parseReactiveBatchTaskIds;
|
|
360
|
+
export const _getPlannedKeyFilesForTest = getPlannedKeyFiles;
|
|
326
361
|
function resolveVerificationFailureMarkerPath(unitType, unitId, basePath) {
|
|
327
362
|
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
328
363
|
switch (unitType) {
|
|
@@ -402,6 +437,34 @@ async function buildTaskCommitContextForUnit(basePath, unitId) {
|
|
|
402
437
|
issueNumber: ghIssueNumber,
|
|
403
438
|
};
|
|
404
439
|
}
|
|
440
|
+
async function buildReactiveTaskCommitContext(_basePath, unitId) {
|
|
441
|
+
const { milestone: mid, slice: sid } = parseUnitId(unitId);
|
|
442
|
+
if (!mid || !sid || !isDbAvailable())
|
|
443
|
+
return undefined;
|
|
444
|
+
const batchTaskIds = parseReactiveBatchTaskIds(unitId);
|
|
445
|
+
if (batchTaskIds.length === 0)
|
|
446
|
+
return undefined;
|
|
447
|
+
const milestone = getMilestone(mid);
|
|
448
|
+
const slice = getSlice(mid, sid);
|
|
449
|
+
const taskRows = batchTaskIds
|
|
450
|
+
.map((tid) => getTask(mid, sid, tid))
|
|
451
|
+
.filter((taskRow) => taskRow !== null);
|
|
452
|
+
const keyFiles = getPlannedKeyFiles(taskRows);
|
|
453
|
+
if (taskRows.length === 0 || keyFiles.length === 0)
|
|
454
|
+
return undefined;
|
|
455
|
+
const taskLabel = taskRows.map((row) => row.id).join(",");
|
|
456
|
+
return {
|
|
457
|
+
taskId: `${sid}/${taskLabel}`,
|
|
458
|
+
taskDisplayId: "reactive-batch",
|
|
459
|
+
taskTitle: `Reactive batch: ${taskLabel}`,
|
|
460
|
+
milestoneId: mid,
|
|
461
|
+
milestoneTitle: stripKnownIdPrefix(milestone?.title, mid),
|
|
462
|
+
sliceId: sid,
|
|
463
|
+
sliceTitle: stripKnownIdPrefix(slice?.title, sid),
|
|
464
|
+
oneLiner: `Reactive execute for ${taskLabel}`,
|
|
465
|
+
keyFiles,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
405
468
|
async function runPostUnitGitHubSyncIfNeeded(basePath, unit) {
|
|
406
469
|
if (unit.type === "complete-milestone")
|
|
407
470
|
return;
|
|
@@ -761,6 +824,9 @@ export async function autoCommitUnit(basePath, unitType, unitId, ctx) {
|
|
|
761
824
|
if (unitType === "execute-task") {
|
|
762
825
|
taskContext = await buildTaskCommitContextForUnit(basePath, unitId);
|
|
763
826
|
}
|
|
827
|
+
else if (unitType === "reactive-execute") {
|
|
828
|
+
taskContext = await buildReactiveTaskCommitContext(basePath, unitId);
|
|
829
|
+
}
|
|
764
830
|
_resetHasChangesCache();
|
|
765
831
|
if (LIFECYCLE_ONLY_UNITS.has(unitType)) {
|
|
766
832
|
return null;
|
|
@@ -812,6 +878,22 @@ async function runCloseoutGitAction(pctx, unit, opts) {
|
|
|
812
878
|
targetRepositories = getTask(mid, sid, tid)?.target_repositories;
|
|
813
879
|
}
|
|
814
880
|
}
|
|
881
|
+
else if (turnAction === "commit" && unit.type === "reactive-execute") {
|
|
882
|
+
taskContext = await buildReactiveTaskCommitContext(s.basePath, unit.id);
|
|
883
|
+
const { milestone: mid, slice: sid } = parseUnitId(unit.id);
|
|
884
|
+
if (mid && sid && isDbAvailable()) {
|
|
885
|
+
const repositories = new Set();
|
|
886
|
+
for (const tid of parseReactiveBatchTaskIds(unit.id)) {
|
|
887
|
+
const taskRow = getTask(mid, sid, tid);
|
|
888
|
+
for (const repoId of taskRow?.target_repositories ?? []) {
|
|
889
|
+
repositories.add(repoId);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (repositories.size > 0) {
|
|
893
|
+
targetRepositories = [...repositories];
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
815
897
|
// Invalidate the nativeHasChanges cache before auto-commit (#1853).
|
|
816
898
|
// The cache has a 10-second TTL and is keyed by basePath. A stale
|
|
817
899
|
// `false` result causes autoCommit to skip staging entirely.
|
|
@@ -1206,12 +1288,19 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
1206
1288
|
if (safetyConfig.enabled) {
|
|
1207
1289
|
const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
|
|
1208
1290
|
// File change validation (execute-task only, after unit execution)
|
|
1209
|
-
if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid
|
|
1291
|
+
if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid) {
|
|
1210
1292
|
try {
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1293
|
+
const sliceTaskRows = isDbAvailable()
|
|
1294
|
+
? getSliceTasks(sMid, sSid).filter((t) => isClosedStatus(t.status) || t.id === sTid)
|
|
1295
|
+
: [];
|
|
1296
|
+
if (sliceTaskRows.length > 0) {
|
|
1297
|
+
const expectedOutput = getPlannedKeyFiles(sliceTaskRows.map((taskRow) => ({
|
|
1298
|
+
expected_output: taskRow.expected_output,
|
|
1299
|
+
files: taskRow.files,
|
|
1300
|
+
})));
|
|
1301
|
+
const plannedFiles = getPlannedKeyFiles(sliceTaskRows.map((taskRow) => ({
|
|
1302
|
+
files: taskRow.files,
|
|
1303
|
+
})));
|
|
1215
1304
|
const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles, safetyConfig.file_change_allowlist);
|
|
1216
1305
|
if (audit && audit.violations.length > 0) {
|
|
1217
1306
|
const warnings = audit.violations.filter(v => v.severity === "warning");
|
|
@@ -1223,6 +1312,23 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
1223
1312
|
}
|
|
1224
1313
|
}
|
|
1225
1314
|
}
|
|
1315
|
+
else {
|
|
1316
|
+
const taskRow = getTask(sMid, sSid, sTid);
|
|
1317
|
+
if (taskRow) {
|
|
1318
|
+
const expectedOutput = taskRow.expected_output ?? [];
|
|
1319
|
+
const plannedFiles = taskRow.files ?? [];
|
|
1320
|
+
const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles, safetyConfig.file_change_allowlist);
|
|
1321
|
+
if (audit && audit.violations.length > 0) {
|
|
1322
|
+
const warnings = audit.violations.filter(v => v.severity === "warning");
|
|
1323
|
+
for (const v of warnings) {
|
|
1324
|
+
logWarning("safety", `file-change: ${v.file} — ${v.reason}`);
|
|
1325
|
+
}
|
|
1326
|
+
if (warnings.length > 0) {
|
|
1327
|
+
ctx.ui.notify(`Safety: ${warnings.length} unexpected file change(s) outside task plan`, "warning");
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1226
1332
|
}
|
|
1227
1333
|
catch (e) {
|
|
1228
1334
|
debugLog("postUnit", { phase: "safety-file-change", error: String(e) });
|
|
@@ -1781,8 +1887,8 @@ export async function postUnitPostVerification(pctx) {
|
|
|
1781
1887
|
}
|
|
1782
1888
|
catch (dbErr) {
|
|
1783
1889
|
// DB unavailable — fail explicitly rather than silently reverting to markdown mutation.
|
|
1784
|
-
// Use 'gsd recover' to
|
|
1785
|
-
logError("engine", `retry state-reset failed (DB unavailable): ${dbErr.message}. Run 'gsd recover' to
|
|
1890
|
+
// Use 'gsd recover --confirm' to import markdown into the DB if needed.
|
|
1891
|
+
logError("engine", `retry state-reset failed (DB unavailable): ${dbErr.message}. Run 'gsd recover --confirm' to import markdown into the DB.`);
|
|
1786
1892
|
}
|
|
1787
1893
|
}
|
|
1788
1894
|
// 2. Delete SUMMARY.md for the task
|
|
@@ -2351,6 +2351,15 @@ export async function buildCompleteSlicePrompt(mid, midTitle, sid, sTitle, base,
|
|
|
2351
2351
|
sliceSummaryPath,
|
|
2352
2352
|
sliceUatPath,
|
|
2353
2353
|
gatesToClose,
|
|
2354
|
+
skillActivation: buildSkillActivationBlock({
|
|
2355
|
+
base,
|
|
2356
|
+
milestoneId: mid,
|
|
2357
|
+
milestoneTitle: midTitle,
|
|
2358
|
+
sliceId: sid,
|
|
2359
|
+
sliceTitle: sTitle,
|
|
2360
|
+
extraContext: [inlinedContext],
|
|
2361
|
+
unitType: "complete-slice",
|
|
2362
|
+
}),
|
|
2354
2363
|
});
|
|
2355
2364
|
}
|
|
2356
2365
|
export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
|
|
@@ -802,7 +802,7 @@ export function buildLoopRemediationSteps(unitType, unitId, base) {
|
|
|
802
802
|
return [
|
|
803
803
|
` 1. Run \`gsd undo-task ${mid}/${sid}/${tid}\` to reset the task state`,
|
|
804
804
|
` 2. Resume auto-mode — it will re-execute the task`,
|
|
805
|
-
` 3. If the task keeps failing
|
|
805
|
+
` 3. If the task keeps failing and markdown should repopulate the DB, run \`gsd recover --confirm\``,
|
|
806
806
|
].join("\n");
|
|
807
807
|
}
|
|
808
808
|
case "plan-slice":
|
|
@@ -814,7 +814,7 @@ export function buildLoopRemediationSteps(unitType, unitId, base) {
|
|
|
814
814
|
: relSliceFile(base, mid, sid, "RESEARCH");
|
|
815
815
|
return [
|
|
816
816
|
` 1. Write ${artifactRel} manually (or with the LLM in interactive mode)`,
|
|
817
|
-
` 2. Run \`gsd recover\` to
|
|
817
|
+
` 2. Run \`gsd recover --confirm\` to import the markdown into the DB`,
|
|
818
818
|
` 3. Resume auto-mode`,
|
|
819
819
|
].join("\n");
|
|
820
820
|
}
|
|
@@ -824,7 +824,7 @@ export function buildLoopRemediationSteps(unitType, unitId, base) {
|
|
|
824
824
|
return [
|
|
825
825
|
` 1. Run \`gsd reset-slice ${mid}/${sid}\` to reset the slice and all its tasks`,
|
|
826
826
|
` 2. Resume auto-mode — it will re-execute incomplete tasks and re-complete the slice`,
|
|
827
|
-
` 3. If the slice keeps failing
|
|
827
|
+
` 3. If the slice keeps failing and markdown should repopulate the DB, run \`gsd recover --confirm\``,
|
|
828
828
|
].join("\n");
|
|
829
829
|
}
|
|
830
830
|
case "validate-milestone": {
|
|
@@ -833,7 +833,7 @@ export function buildLoopRemediationSteps(unitType, unitId, base) {
|
|
|
833
833
|
const artifactRel = relMilestoneFile(base, mid, "VALIDATION");
|
|
834
834
|
return [
|
|
835
835
|
` 1. Write ${artifactRel} with verdict: pass`,
|
|
836
|
-
` 2. Run \`gsd recover\` to
|
|
836
|
+
` 2. Run \`gsd recover --confirm\` to import the markdown into the DB`,
|
|
837
837
|
` 3. Resume auto-mode`,
|
|
838
838
|
].join("\n");
|
|
839
839
|
}
|