gsd-pi 2.76.0-dev.82e249f7b → 2.76.0-dev.97807402
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/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/phases.js +14 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +27 -0
- package/dist/resources/extensions/gsd/auto-model-selection.js +1 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +13 -0
- package/dist/resources/extensions/gsd/auto-start.js +27 -18
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -48
- package/dist/resources/extensions/gsd/auto.js +13 -17
- 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 +40 -4
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +12 -1
- 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/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/gsd-db.js +115 -7
- package/dist/resources/extensions/gsd/guided-flow.js +189 -0
- 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 +6 -0
- package/dist/resources/extensions/gsd/model-router.js +36 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -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/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/file-change-validator.js +10 -4
- package/dist/resources/extensions/gsd/safety/safety-harness.js +4 -0
- package/dist/resources/extensions/gsd/token-counter.js +22 -5
- 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/workflow-mcp.js +3 -0
- 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 +7 -7
- 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 +7 -7
- 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/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.map +1 -1
- package/packages/mcp-server/dist/server.js +18 -1
- package/packages/mcp-server/dist/server.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/remote-questions.test.ts +294 -0
- package/packages/mcp-server/src/remote-questions.ts +916 -0
- package/packages/mcp-server/src/server.ts +19 -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/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +2 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.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/src/providers/anthropic-shared.ts +3 -1
- package/packages/pi-ai/src/providers/simple-options.ts +17 -1
- package/packages/pi-ai/tsconfig.tsbuildinfo +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.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +14 -0
- 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 +9 -5
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +25 -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/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/model-registry-custom-caps.test.ts +245 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +16 -0
- 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 +36 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +9 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
- 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/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/phases.ts +14 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +29 -0
- package/src/resources/extensions/gsd/auto-model-selection.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +1 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +15 -0
- package/src/resources/extensions/gsd/auto-start.ts +29 -19
- package/src/resources/extensions/gsd/auto-worktree.ts +34 -52
- package/src/resources/extensions/gsd/auto.ts +12 -17
- 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 +42 -4
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +13 -1
- 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/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/gsd-db.ts +122 -7
- package/src/resources/extensions/gsd/guided-flow.ts +221 -0
- 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 +6 -0
- package/src/resources/extensions/gsd/model-router.ts +42 -1
- package/src/resources/extensions/gsd/pre-execution-checks.ts +36 -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/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/file-change-validator.ts +14 -3
- package/src/resources/extensions/gsd/safety/safety-harness.ts +6 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
- 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/doctor-providers.test.ts +31 -0
- 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 +124 -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 +152 -1
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -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/key-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- 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/pre-exec-backtick-strip.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +234 -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/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/save-gate-result-render.test.ts +95 -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/workflow-tool-executors.test.ts +65 -2
- 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/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/workflow-logger.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- 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/{ecSsu49rxxcpbNmVP4mLD → pI48IF3dgfs0CBrYi2bh_}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ecSsu49rxxcpbNmVP4mLD → pI48IF3dgfs0CBrYi2bh_}/_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
|
|
@@ -55,7 +55,7 @@ import { getErrorMessage } from "./error-utils.js";
|
|
|
55
55
|
import { recoverFailedMigration } from "./migrate-external.js";
|
|
56
56
|
import { initRegistry, convertDispatchRules } from "./rule-registry.js";
|
|
57
57
|
import { emitJournalEvent as _emitJournalEvent } from "./journal.js";
|
|
58
|
-
import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache,
|
|
58
|
+
import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, } from "./auto-dashboard.js";
|
|
59
59
|
import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
|
|
60
60
|
import { isDbAvailable, getMilestone } from "./gsd-db.js";
|
|
61
61
|
import { countPendingCaptures } from "./captures.js";
|
|
@@ -145,8 +145,8 @@ export function startAutoDetached(ctx, pi, base, verboseMode, options) {
|
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
147
|
/** Returns true if the project is configured for `isolation:worktree` mode. */
|
|
148
|
-
export function shouldUseWorktreeIsolation() {
|
|
149
|
-
const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
148
|
+
export function shouldUseWorktreeIsolation(basePath) {
|
|
149
|
+
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
|
|
150
150
|
if (prefs?.isolation === "worktree")
|
|
151
151
|
return true;
|
|
152
152
|
// Default is false — worktree isolation requires explicit opt-in
|
|
@@ -215,7 +215,7 @@ export function getAutoDashboardData() {
|
|
|
215
215
|
const rtkSavings = sessionId && s.basePath
|
|
216
216
|
? getRtkSessionSavings(s.basePath, sessionId)
|
|
217
217
|
: null;
|
|
218
|
-
const rtkEnabled = loadEffectiveGSDPreferences()?.preferences.experimental?.rtk === true;
|
|
218
|
+
const rtkEnabled = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences.experimental?.rtk === true;
|
|
219
219
|
// Pending capture count — lazy check, non-fatal
|
|
220
220
|
let pendingCaptureCount = 0;
|
|
221
221
|
try {
|
|
@@ -393,7 +393,7 @@ function clearUnitTimeout() {
|
|
|
393
393
|
}
|
|
394
394
|
/** Build snapshot metric opts. */
|
|
395
395
|
function buildSnapshotOpts(_unitType, _unitId) {
|
|
396
|
-
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
396
|
+
const prefs = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
|
|
397
397
|
const uokFlags = resolveUokFlags(prefs);
|
|
398
398
|
return {
|
|
399
399
|
...(s.autoStartTime > 0 ? { autoSessionKey: String(s.autoStartTime) } : {}),
|
|
@@ -427,7 +427,7 @@ function handleLostSessionLock(ctx, lockStatus) {
|
|
|
427
427
|
restoreProjectRootEnv();
|
|
428
428
|
restoreMilestoneLockEnv();
|
|
429
429
|
deregisterSigtermHandler();
|
|
430
|
-
clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
|
|
430
|
+
clearCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences);
|
|
431
431
|
const base = lockBase();
|
|
432
432
|
const lockFilePath = base ? join(gsdRoot(base), "auto.lock") : "unknown";
|
|
433
433
|
const recoverySuggestion = "\nTo recover, run: gsd doctor --fix";
|
|
@@ -443,7 +443,6 @@ function handleLostSessionLock(ctx, lockStatus) {
|
|
|
443
443
|
ctx?.ui.notify(message, "error");
|
|
444
444
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
445
445
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
446
|
-
ctx?.ui.setFooter(undefined);
|
|
447
446
|
if (ctx)
|
|
448
447
|
initHealthWidget(ctx);
|
|
449
448
|
}
|
|
@@ -480,7 +479,6 @@ function cleanupAfterLoopExit(ctx) {
|
|
|
480
479
|
if (!s.paused) {
|
|
481
480
|
ctx.ui.setStatus("gsd-auto", undefined);
|
|
482
481
|
ctx.ui.setWidget("gsd-progress", undefined);
|
|
483
|
-
ctx.ui.setFooter(undefined);
|
|
484
482
|
initHealthWidget(ctx);
|
|
485
483
|
}
|
|
486
484
|
// Restore CWD out of worktree back to original project root
|
|
@@ -498,7 +496,7 @@ function cleanupAfterLoopExit(ctx) {
|
|
|
498
496
|
export async function stopAuto(ctx, pi, reason) {
|
|
499
497
|
if (!s.active && !s.paused)
|
|
500
498
|
return;
|
|
501
|
-
const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
|
|
499
|
+
const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
|
|
502
500
|
const reasonSuffix = reason ? ` — ${reason}` : "";
|
|
503
501
|
try {
|
|
504
502
|
// ── Step 1: Timers and locks ──
|
|
@@ -743,7 +741,6 @@ export async function stopAuto(ctx, pi, reason) {
|
|
|
743
741
|
// UI cleanup
|
|
744
742
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
745
743
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
746
|
-
ctx?.ui.setFooter(undefined);
|
|
747
744
|
if (ctx)
|
|
748
745
|
initHealthWidget(ctx);
|
|
749
746
|
restoreProjectRootEnv();
|
|
@@ -832,7 +829,6 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
|
|
|
832
829
|
s.verificationRetryCount.clear();
|
|
833
830
|
ctx?.ui.setStatus("gsd-auto", "paused");
|
|
834
831
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
835
|
-
ctx?.ui.setFooter(undefined);
|
|
836
832
|
if (ctx)
|
|
837
833
|
initHealthWidget(ctx);
|
|
838
834
|
const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
|
|
@@ -1164,7 +1160,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1164
1160
|
});
|
|
1165
1161
|
// ── Auto-worktree / branch-mode: re-enter on resume ──
|
|
1166
1162
|
if (s.currentMilestoneId &&
|
|
1167
|
-
getIsolationMode() !== "none" &&
|
|
1163
|
+
getIsolationMode(s.originalBasePath || s.basePath) !== "none" &&
|
|
1168
1164
|
s.originalBasePath &&
|
|
1169
1165
|
!isInAutoWorktree(s.basePath) &&
|
|
1170
1166
|
!detectWorktreeName(s.basePath) &&
|
|
@@ -1175,7 +1171,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1175
1171
|
}
|
|
1176
1172
|
registerSigtermHandler(lockBase());
|
|
1177
1173
|
ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
|
|
1178
|
-
ctx.ui.
|
|
1174
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
1179
1175
|
ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
|
|
1180
1176
|
restoreHookState(s.basePath);
|
|
1181
1177
|
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
|
@@ -1197,7 +1193,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1197
1193
|
await openProjectDbIfPresent(s.basePath);
|
|
1198
1194
|
try {
|
|
1199
1195
|
await rebuildState(s.basePath);
|
|
1200
|
-
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1196
|
+
syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
|
|
1201
1197
|
}
|
|
1202
1198
|
catch (e) {
|
|
1203
1199
|
debugLog("resume-rebuild-state-failed", {
|
|
@@ -1227,7 +1223,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1227
1223
|
}
|
|
1228
1224
|
updateSessionLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
|
|
1229
1225
|
writeLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
|
|
1230
|
-
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1226
|
+
logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1231
1227
|
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1232
1228
|
startAutoCommandPolling(s.basePath);
|
|
1233
1229
|
await runAutoLoopWithUok({
|
|
@@ -1253,13 +1249,13 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1253
1249
|
return;
|
|
1254
1250
|
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1255
1251
|
try {
|
|
1256
|
-
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1252
|
+
syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
|
|
1257
1253
|
}
|
|
1258
1254
|
catch (err) {
|
|
1259
1255
|
// Best-effort only — sidebar sync must never block auto-mode startup
|
|
1260
1256
|
logWarning("engine", `cmux sync failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1261
1257
|
}
|
|
1262
|
-
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
|
|
1258
|
+
logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
|
|
1263
1259
|
startAutoCommandPolling(s.basePath);
|
|
1264
1260
|
// Dispatch the first unit
|
|
1265
1261
|
await runAutoLoopWithUok({
|
|
@@ -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);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// GSD2 — Exec (context-mode) tool registration.
|
|
2
|
+
//
|
|
3
|
+
// Exposes the `gsd_exec` tool over MCP. Opt-in: disabled unless
|
|
4
|
+
// `context_mode.enabled: true` is set in preferences.
|
|
5
|
+
import { Type } from "@sinclair/typebox";
|
|
6
|
+
import { executeGsdExec } from "../tools/exec-tool.js";
|
|
7
|
+
import { executeExecSearch } from "../tools/exec-search-tool.js";
|
|
8
|
+
import { executeResume } from "../tools/resume-tool.js";
|
|
9
|
+
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
10
|
+
import { logWarning } from "../workflow-logger.js";
|
|
11
|
+
export function registerExecTools(pi) {
|
|
12
|
+
pi.registerTool({
|
|
13
|
+
name: "gsd_exec",
|
|
14
|
+
label: "Exec (Sandboxed)",
|
|
15
|
+
description: "Run a short script (bash/node/python) in a subprocess. Full stdout/stderr persist to " +
|
|
16
|
+
".gsd/exec/<id>.{stdout,stderr,meta.json}; only a short digest returns in context. Use " +
|
|
17
|
+
"this instead of reading many files or emitting large tool outputs — e.g. have the script " +
|
|
18
|
+
"count/grep/summarize and log the finding. Enabled by default; opt out via " +
|
|
19
|
+
"preferences.context_mode.enabled=false.",
|
|
20
|
+
promptSnippet: "Run a bash/node/python script in a sandbox; full output is saved to disk and only a digest returns",
|
|
21
|
+
promptGuidelines: [
|
|
22
|
+
"Prefer gsd_exec for analyses that would otherwise read >3 files or produce large tool output.",
|
|
23
|
+
"Write scripts that log the finding (counts, matches, summaries) rather than raw dumps.",
|
|
24
|
+
"The digest is the last ~300 chars of stdout — size your log output accordingly.",
|
|
25
|
+
"Need the full output? Read the stdout_path returned in details (file on local disk).",
|
|
26
|
+
],
|
|
27
|
+
parameters: Type.Object({
|
|
28
|
+
runtime: Type.Union([Type.Literal("bash"), Type.Literal("node"), Type.Literal("python")], { description: "Interpreter: bash (-c), node (-e), or python3 (-c)." }),
|
|
29
|
+
script: Type.String({ description: "Script body. Keep output small (log the finding, not the data)." }),
|
|
30
|
+
purpose: Type.Optional(Type.String({ description: "Short label recorded in meta.json for later review." })),
|
|
31
|
+
timeout_ms: Type.Optional(Type.Number({
|
|
32
|
+
description: "Per-invocation timeout (ms). Capped at 600000. Default from preferences.",
|
|
33
|
+
minimum: 1_000,
|
|
34
|
+
maximum: 600_000,
|
|
35
|
+
})),
|
|
36
|
+
}),
|
|
37
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
38
|
+
let prefs = null;
|
|
39
|
+
try {
|
|
40
|
+
prefs = loadEffectiveGSDPreferences();
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
logWarning("tool", `gsd_exec could not load preferences: ${err instanceof Error ? err.message : String(err)}`);
|
|
44
|
+
}
|
|
45
|
+
return executeGsdExec(params, {
|
|
46
|
+
baseDir: process.cwd(),
|
|
47
|
+
preferences: prefs?.preferences ?? null,
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
pi.registerTool({
|
|
52
|
+
name: "gsd_exec_search",
|
|
53
|
+
label: "Search gsd_exec History",
|
|
54
|
+
description: "List prior gsd_exec runs (most recent first) from .gsd/exec/*.meta.json. Useful for " +
|
|
55
|
+
"rediscovering the stdout_path of an earlier run without re-executing it. Read-only.",
|
|
56
|
+
promptSnippet: "Search prior gsd_exec runs by substring, runtime, or failing-only filter",
|
|
57
|
+
promptGuidelines: [
|
|
58
|
+
"Use this before re-running an expensive analysis — the prior run's stdout file may still answer.",
|
|
59
|
+
"The preview shows the trailing ~300 chars of stdout; read stdout_path for the full transcript.",
|
|
60
|
+
],
|
|
61
|
+
parameters: Type.Object({
|
|
62
|
+
query: Type.Optional(Type.String({ description: "Substring matched against id and purpose (case-insensitive)." })),
|
|
63
|
+
runtime: Type.Optional(Type.Union([Type.Literal("bash"), Type.Literal("node"), Type.Literal("python")], {
|
|
64
|
+
description: "Restrict to one runtime.",
|
|
65
|
+
})),
|
|
66
|
+
failing_only: Type.Optional(Type.Boolean({ description: "Only non-zero exit codes and timeouts." })),
|
|
67
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 20, cap 200)", minimum: 1, maximum: 200 })),
|
|
68
|
+
}),
|
|
69
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
70
|
+
return executeExecSearch(params, {
|
|
71
|
+
baseDir: process.cwd(),
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
pi.registerTool({
|
|
76
|
+
name: "gsd_resume",
|
|
77
|
+
label: "Resume (Read Snapshot)",
|
|
78
|
+
description: "Return the contents of .gsd/last-snapshot.md — a ≤2 KB digest of top memories, recent " +
|
|
79
|
+
"gsd_exec runs, and active context, written automatically on session_before_compact. Use " +
|
|
80
|
+
"this after compaction or session resume to re-orient quickly.",
|
|
81
|
+
promptSnippet: "Read the pre-compaction snapshot to re-orient after context loss",
|
|
82
|
+
promptGuidelines: [
|
|
83
|
+
"Call this right after a session resumes if you feel you've lost durable context.",
|
|
84
|
+
"The snapshot is a summary — use memory_query or gsd_exec_search for detail.",
|
|
85
|
+
],
|
|
86
|
+
parameters: Type.Object({}),
|
|
87
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
88
|
+
return executeResume(params, {
|
|
89
|
+
baseDir: process.cwd(),
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -4,6 +4,7 @@ import { registerWorktreeCommand } from "../worktree-command.js";
|
|
|
4
4
|
import { loadEcosystemExtensions } from "../ecosystem/loader.js";
|
|
5
5
|
import { registerDbTools } from "./db-tools.js";
|
|
6
6
|
import { registerDynamicTools } from "./dynamic-tools.js";
|
|
7
|
+
import { registerExecTools } from "./exec-tools.js";
|
|
7
8
|
import { registerJournalTools } from "./journal-tools.js";
|
|
8
9
|
import { registerMemoryTools } from "./memory-tools.js";
|
|
9
10
|
import { registerQueryTools } from "./query-tools.js";
|
|
@@ -86,6 +87,7 @@ export function registerGsdExtension(pi) {
|
|
|
86
87
|
["journal-tools", () => registerJournalTools(pi)],
|
|
87
88
|
["query-tools", () => registerQueryTools(pi)],
|
|
88
89
|
["memory-tools", () => registerMemoryTools(pi)],
|
|
90
|
+
["exec-tools", () => registerExecTools(pi)],
|
|
89
91
|
["shortcuts", () => registerShortcuts(pi)],
|
|
90
92
|
["hooks", () => registerHooks(pi, ecosystemHandlers)],
|
|
91
93
|
["ecosystem", () => {
|
|
@@ -13,7 +13,6 @@ import { loadToolApiKeys } from "../commands-config.js";
|
|
|
13
13
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
14
14
|
import { deriveState } from "../state.js";
|
|
15
15
|
import { getAutoDashboardData, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto.js";
|
|
16
|
-
import { hideFooter } from "../auto-dashboard.js";
|
|
17
16
|
import { isParallelActive, shutdownParallel } from "../parallel-orchestrator.js";
|
|
18
17
|
import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
19
18
|
import { saveActivityLog } from "../activity-log.js";
|
|
@@ -37,7 +36,9 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
37
36
|
initNotificationStore(process.cwd());
|
|
38
37
|
installNotifyInterceptor(ctx);
|
|
39
38
|
initNotificationWidget(ctx);
|
|
40
|
-
|
|
39
|
+
if (!isAutoActive()) {
|
|
40
|
+
initHealthWidget(ctx);
|
|
41
|
+
}
|
|
41
42
|
resetWriteGateState();
|
|
42
43
|
resetToolCallLoopGuard();
|
|
43
44
|
resetAskUserQuestionsCache();
|
|
@@ -79,7 +80,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
79
80
|
}
|
|
80
81
|
loadToolApiKeys();
|
|
81
82
|
if (isAutoActive()) {
|
|
82
|
-
ctx.ui.
|
|
83
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
83
84
|
}
|
|
84
85
|
});
|
|
85
86
|
pi.on("session_switch", async (_event, ctx) => {
|
|
@@ -101,7 +102,7 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
101
102
|
}
|
|
102
103
|
loadToolApiKeys();
|
|
103
104
|
if (isAutoActive()) {
|
|
104
|
-
ctx.ui.
|
|
105
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
105
106
|
}
|
|
106
107
|
});
|
|
107
108
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
@@ -204,6 +205,41 @@ export function registerHooks(pi, ecosystemHandlers) {
|
|
|
204
205
|
nextAction: `Resume task ${state.activeTask.id}: ${state.activeTask.title}.`,
|
|
205
206
|
}));
|
|
206
207
|
});
|
|
208
|
+
// Context-mode snapshot: write .gsd/last-snapshot.md before compaction so
|
|
209
|
+
// agents can call gsd_resume (or Read the file) to re-orient. Opt-in via
|
|
210
|
+
// preferences.context_mode.enabled. Runs after the auto-cancel handler
|
|
211
|
+
// above — if that one returned cancel:true, pi still fires us but the
|
|
212
|
+
// compaction won't actually happen; the snapshot is still useful then,
|
|
213
|
+
// since auto may pause and resume later.
|
|
214
|
+
pi.on("session_before_compact", async () => {
|
|
215
|
+
try {
|
|
216
|
+
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
217
|
+
const { isContextModeEnabled } = await import("../preferences-types.js");
|
|
218
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
219
|
+
if (!isContextModeEnabled(prefs?.preferences))
|
|
220
|
+
return;
|
|
221
|
+
const { writeCompactionSnapshot } = await import("../compaction-snapshot.js");
|
|
222
|
+
const { ensureDbOpen } = await import("./dynamic-tools.js");
|
|
223
|
+
await ensureDbOpen();
|
|
224
|
+
const basePath = process.cwd();
|
|
225
|
+
let activeContext = null;
|
|
226
|
+
try {
|
|
227
|
+
const state = await deriveState(basePath);
|
|
228
|
+
if (state.activeMilestone && state.activeSlice && state.activeTask) {
|
|
229
|
+
activeContext =
|
|
230
|
+
`Active: ${state.activeMilestone.id} / ${state.activeSlice.id} / ${state.activeTask.id}` +
|
|
231
|
+
(state.activeTask.title ? ` — ${state.activeTask.title}` : "");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
/* non-fatal */
|
|
236
|
+
}
|
|
237
|
+
writeCompactionSnapshot(basePath, { activeContext });
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
safetyLogWarning("context-mode", `failed to write compaction snapshot: ${err instanceof Error ? err.message : String(err)}`);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
207
243
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
208
244
|
if (isParallelActive()) {
|
|
209
245
|
try {
|
|
@@ -99,10 +99,21 @@ function normalizeWriteGateSnapshot(value) {
|
|
|
99
99
|
pendingGateId: typeof record.pendingGateId === "string" ? record.pendingGateId : null,
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
+
const EMPTY_SNAPSHOT = {
|
|
103
|
+
verifiedDepthMilestones: [],
|
|
104
|
+
activeQueuePhase: false,
|
|
105
|
+
pendingGateId: null,
|
|
106
|
+
};
|
|
102
107
|
export function loadWriteGateSnapshot(basePath = process.cwd()) {
|
|
103
108
|
const path = writeGateSnapshotPath(basePath);
|
|
104
|
-
if (!existsSync(path))
|
|
109
|
+
if (!existsSync(path)) {
|
|
110
|
+
// When persist mode is active and the file has been deleted, treat it as a
|
|
111
|
+
// full state reset so deleting the file clears the HARD BLOCK gate.
|
|
112
|
+
// In non-persist mode the file is never written, so fall back to in-memory.
|
|
113
|
+
if (shouldPersistWriteGateSnapshot())
|
|
114
|
+
return EMPTY_SNAPSHOT;
|
|
105
115
|
return currentWriteGateSnapshot();
|
|
116
|
+
}
|
|
106
117
|
try {
|
|
107
118
|
return normalizeWriteGateSnapshot(JSON.parse(readFileSync(path, "utf-8")));
|
|
108
119
|
}
|