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
|
@@ -238,14 +238,6 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
function isProjectGsdSymlink(basePath: string): boolean {
|
|
242
|
-
try {
|
|
243
|
-
return lstatSyncFn(join(basePath, ".gsd")).isSymbolicLink();
|
|
244
|
-
} catch {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
241
|
// ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
|
|
250
242
|
|
|
251
243
|
/** Patterns for machine-generated build artifacts that can be safely
|
|
@@ -1664,54 +1656,15 @@ export function mergeMilestoneToMain(
|
|
|
1664
1656
|
}
|
|
1665
1657
|
}
|
|
1666
1658
|
|
|
1667
|
-
// 7.
|
|
1668
|
-
// blocked by unrelated local changes (#2151). clearProjectRootStateFiles
|
|
1669
|
-
// only removes untracked .gsd/ files; tracked dirty files elsewhere (e.g.
|
|
1670
|
-
// .planning/work-state.json with stash conflict markers) are invisible to
|
|
1671
|
-
// that cleanup but will cause `git merge --squash` to reject.
|
|
1672
|
-
let stashed = false;
|
|
1673
|
-
try {
|
|
1674
|
-
const status = execFileSync("git", ["status", "--porcelain"], {
|
|
1675
|
-
cwd: originalBasePath_,
|
|
1676
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1677
|
-
encoding: "utf-8",
|
|
1678
|
-
}).trim();
|
|
1679
|
-
if (status) {
|
|
1680
|
-
// Use --include-untracked to stash untracked files that would block
|
|
1681
|
-
// the squash merge, but EXCLUDE .gsd/milestones/ (#2505).
|
|
1682
|
-
// --include-untracked without exclusion sweeps queued milestone
|
|
1683
|
-
// CONTEXT files into the stash. If stash pop later fails, those files
|
|
1684
|
-
// are permanently trapped in the stash entry and lost on the next
|
|
1685
|
-
// stash push or drop.
|
|
1686
|
-
//
|
|
1687
|
-
// When `.gsd` itself is a symlink, Git rejects pathspecs below it
|
|
1688
|
-
// ("beyond a symbolic link"). In that layout, exclude the whole symlink
|
|
1689
|
-
// and keep stashing real project files that could block the merge.
|
|
1690
|
-
const stashPathspecs = isProjectGsdSymlink(originalBasePath_)
|
|
1691
|
-
? [".", ":(exclude).gsd"]
|
|
1692
|
-
: [":(exclude).gsd/milestones"];
|
|
1693
|
-
execFileSync(
|
|
1694
|
-
"git",
|
|
1695
|
-
[
|
|
1696
|
-
"stash", "push", "--include-untracked",
|
|
1697
|
-
"-m", `gsd: pre-merge stash for ${milestoneId}`,
|
|
1698
|
-
"--", ...stashPathspecs,
|
|
1699
|
-
],
|
|
1700
|
-
{ cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
|
|
1701
|
-
);
|
|
1702
|
-
stashed = true;
|
|
1703
|
-
}
|
|
1704
|
-
} catch (err) {
|
|
1705
|
-
// Stash failure is non-fatal — proceed without stash and let the merge
|
|
1706
|
-
// report the dirty tree if it fails.
|
|
1707
|
-
logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
// 7a. Shelter queued milestone directories before the squash merge (#2505).
|
|
1659
|
+
// 7. Shelter queued milestone directories before the squash merge (#2505).
|
|
1711
1660
|
// The milestone branch may contain copies of queued milestone dirs (via
|
|
1712
1661
|
// copyPlanningArtifacts), so `git merge --squash` rejects when those same
|
|
1713
1662
|
// files exist as untracked in the working tree. Temporarily move them to
|
|
1714
1663
|
// a backup location, then restore after the merge+commit.
|
|
1664
|
+
//
|
|
1665
|
+
// MUST run BEFORE the pre-merge stash (step 7a) so `--include-untracked`
|
|
1666
|
+
// does not sweep queued CONTEXT files into the stash. If stash pop later
|
|
1667
|
+
// fails, files trapped inside the stash are permanently lost (#2505).
|
|
1715
1668
|
const milestonesDir = join(gsdRoot(originalBasePath_), "milestones");
|
|
1716
1669
|
const shelterDir = join(gsdRoot(originalBasePath_), ".milestone-shelter");
|
|
1717
1670
|
const shelteredDirs: string[] = [];
|
|
@@ -1759,6 +1712,35 @@ export function mergeMilestoneToMain(
|
|
|
1759
1712
|
logWarning("worktree", `milestone shelter operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1760
1713
|
}
|
|
1761
1714
|
|
|
1715
|
+
// 7a. Stash pre-existing dirty files so the squash merge is not blocked by
|
|
1716
|
+
// unrelated local changes (#2151). Includes untracked files to handle
|
|
1717
|
+
// locally-added files that conflict with tracked files on the milestone
|
|
1718
|
+
// branch. Passing NO pathspec lets git skip gitignored paths silently;
|
|
1719
|
+
// adding an explicit pathspec trips a `git add`-style fatal on ignored
|
|
1720
|
+
// entries (e.g. a gitignored `.gsd` symlink under ADR-002) (#4573).
|
|
1721
|
+
// Queued CONTEXT files under `.gsd/milestones/*` are already sheltered
|
|
1722
|
+
// in step 7 above, so they won't be swept into the stash.
|
|
1723
|
+
let stashed = false;
|
|
1724
|
+
try {
|
|
1725
|
+
const status = execFileSync("git", ["status", "--porcelain"], {
|
|
1726
|
+
cwd: originalBasePath_,
|
|
1727
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1728
|
+
encoding: "utf-8",
|
|
1729
|
+
}).trim();
|
|
1730
|
+
if (status) {
|
|
1731
|
+
execFileSync(
|
|
1732
|
+
"git",
|
|
1733
|
+
["stash", "push", "--include-untracked", "-m", `gsd: pre-merge stash for ${milestoneId}`],
|
|
1734
|
+
{ cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
|
|
1735
|
+
);
|
|
1736
|
+
stashed = true;
|
|
1737
|
+
}
|
|
1738
|
+
} catch (err) {
|
|
1739
|
+
// Stash failure is non-fatal — proceed without stash and let the merge
|
|
1740
|
+
// report the dirty tree if it fails.
|
|
1741
|
+
logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1762
1744
|
// 7b. Clean up stale merge state before attempting squash merge (#2912).
|
|
1763
1745
|
// A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
|
|
1764
1746
|
// or interrupted operation) causes `git merge --squash` to refuse with
|
|
@@ -182,7 +182,6 @@ import {
|
|
|
182
182
|
unitVerb,
|
|
183
183
|
formatAutoElapsed as _formatAutoElapsed,
|
|
184
184
|
formatWidgetTokens,
|
|
185
|
-
hideFooter,
|
|
186
185
|
type WidgetStateAccessors,
|
|
187
186
|
} from "./auto-dashboard.js";
|
|
188
187
|
import {
|
|
@@ -330,8 +329,8 @@ export function startAutoDetached(
|
|
|
330
329
|
}
|
|
331
330
|
|
|
332
331
|
/** Returns true if the project is configured for `isolation:worktree` mode. */
|
|
333
|
-
export function shouldUseWorktreeIsolation(): boolean {
|
|
334
|
-
const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
332
|
+
export function shouldUseWorktreeIsolation(basePath?: string): boolean {
|
|
333
|
+
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
|
|
335
334
|
if (prefs?.isolation === "worktree") return true;
|
|
336
335
|
// Default is false — worktree isolation requires explicit opt-in
|
|
337
336
|
return false;
|
|
@@ -424,7 +423,7 @@ export function getAutoDashboardData(): AutoDashboardData {
|
|
|
424
423
|
const rtkSavings = sessionId && s.basePath
|
|
425
424
|
? getRtkSessionSavings(s.basePath, sessionId)
|
|
426
425
|
: null;
|
|
427
|
-
const rtkEnabled = loadEffectiveGSDPreferences()?.preferences.experimental?.rtk === true;
|
|
426
|
+
const rtkEnabled = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences.experimental?.rtk === true;
|
|
428
427
|
// Pending capture count — lazy check, non-fatal
|
|
429
428
|
let pendingCaptureCount = 0;
|
|
430
429
|
try {
|
|
@@ -648,7 +647,7 @@ function buildSnapshotOpts(
|
|
|
648
647
|
gitStatus?: "ok" | "failed";
|
|
649
648
|
gitError?: string;
|
|
650
649
|
} & Record<string, unknown> {
|
|
651
|
-
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
650
|
+
const prefs = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
|
|
652
651
|
const uokFlags = resolveUokFlags(prefs);
|
|
653
652
|
return {
|
|
654
653
|
...(s.autoStartTime > 0 ? { autoSessionKey: String(s.autoStartTime) } : {}),
|
|
@@ -686,7 +685,7 @@ function handleLostSessionLock(
|
|
|
686
685
|
restoreProjectRootEnv();
|
|
687
686
|
restoreMilestoneLockEnv();
|
|
688
687
|
deregisterSigtermHandler();
|
|
689
|
-
clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
|
|
688
|
+
clearCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences);
|
|
690
689
|
const base = lockBase();
|
|
691
690
|
const lockFilePath = base ? join(gsdRoot(base), "auto.lock") : "unknown";
|
|
692
691
|
const recoverySuggestion = "\nTo recover, run: gsd doctor --fix";
|
|
@@ -706,7 +705,6 @@ function handleLostSessionLock(
|
|
|
706
705
|
);
|
|
707
706
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
708
707
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
709
|
-
ctx?.ui.setFooter(undefined);
|
|
710
708
|
if (ctx) initHealthWidget(ctx);
|
|
711
709
|
}
|
|
712
710
|
|
|
@@ -742,7 +740,6 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
|
|
|
742
740
|
if (!s.paused) {
|
|
743
741
|
ctx.ui.setStatus("gsd-auto", undefined);
|
|
744
742
|
ctx.ui.setWidget("gsd-progress", undefined);
|
|
745
|
-
ctx.ui.setFooter(undefined);
|
|
746
743
|
initHealthWidget(ctx);
|
|
747
744
|
}
|
|
748
745
|
|
|
@@ -764,7 +761,7 @@ export async function stopAuto(
|
|
|
764
761
|
reason?: string,
|
|
765
762
|
): Promise<void> {
|
|
766
763
|
if (!s.active && !s.paused) return;
|
|
767
|
-
const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
|
|
764
|
+
const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
|
|
768
765
|
const reasonSuffix = reason ? ` — ${reason}` : "";
|
|
769
766
|
|
|
770
767
|
try {
|
|
@@ -1018,7 +1015,6 @@ export async function stopAuto(
|
|
|
1018
1015
|
// UI cleanup
|
|
1019
1016
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
1020
1017
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
1021
|
-
ctx?.ui.setFooter(undefined);
|
|
1022
1018
|
if (ctx) initHealthWidget(ctx);
|
|
1023
1019
|
restoreProjectRootEnv();
|
|
1024
1020
|
restoreMilestoneLockEnv();
|
|
@@ -1121,7 +1117,6 @@ export async function pauseAuto(
|
|
|
1121
1117
|
s.verificationRetryCount.clear();
|
|
1122
1118
|
ctx?.ui.setStatus("gsd-auto", "paused");
|
|
1123
1119
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
1124
|
-
ctx?.ui.setFooter(undefined);
|
|
1125
1120
|
if (ctx) initHealthWidget(ctx);
|
|
1126
1121
|
const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
|
|
1127
1122
|
ctx?.ui.notify(
|
|
@@ -1495,7 +1490,7 @@ export async function startAuto(
|
|
|
1495
1490
|
// ── Auto-worktree / branch-mode: re-enter on resume ──
|
|
1496
1491
|
if (
|
|
1497
1492
|
s.currentMilestoneId &&
|
|
1498
|
-
getIsolationMode() !== "none" &&
|
|
1493
|
+
getIsolationMode(s.originalBasePath || s.basePath) !== "none" &&
|
|
1499
1494
|
s.originalBasePath &&
|
|
1500
1495
|
!isInAutoWorktree(s.basePath) &&
|
|
1501
1496
|
!detectWorktreeName(s.basePath) &&
|
|
@@ -1509,7 +1504,7 @@ export async function startAuto(
|
|
|
1509
1504
|
registerSigtermHandler(lockBase());
|
|
1510
1505
|
|
|
1511
1506
|
ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
|
|
1512
|
-
ctx.ui.
|
|
1507
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
1513
1508
|
ctx.ui.notify(
|
|
1514
1509
|
s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.",
|
|
1515
1510
|
"info",
|
|
@@ -1534,7 +1529,7 @@ export async function startAuto(
|
|
|
1534
1529
|
await openProjectDbIfPresent(s.basePath);
|
|
1535
1530
|
try {
|
|
1536
1531
|
await rebuildState(s.basePath);
|
|
1537
|
-
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1532
|
+
syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
|
|
1538
1533
|
} catch (e) {
|
|
1539
1534
|
debugLog("resume-rebuild-state-failed", {
|
|
1540
1535
|
error: e instanceof Error ? e.message : String(e),
|
|
@@ -1584,7 +1579,7 @@ export async function startAuto(
|
|
|
1584
1579
|
"resuming",
|
|
1585
1580
|
s.currentMilestoneId ?? "unknown",
|
|
1586
1581
|
);
|
|
1587
|
-
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1582
|
+
logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1588
1583
|
|
|
1589
1584
|
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1590
1585
|
startAutoCommandPolling(s.basePath);
|
|
@@ -1622,12 +1617,12 @@ export async function startAuto(
|
|
|
1622
1617
|
|
|
1623
1618
|
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1624
1619
|
try {
|
|
1625
|
-
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1620
|
+
syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
|
|
1626
1621
|
} catch (err) {
|
|
1627
1622
|
// Best-effort only — sidebar sync must never block auto-mode startup
|
|
1628
1623
|
logWarning("engine", `cmux sync failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
1629
1624
|
}
|
|
1630
|
-
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
|
|
1625
|
+
logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
|
|
1631
1626
|
|
|
1632
1627
|
startAutoCommandPolling(s.basePath);
|
|
1633
1628
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
2
2
|
|
|
3
3
|
import { logWarning } from "../workflow-logger.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
checkAutoStartAfterDiscuss,
|
|
6
|
+
maybeHandleReadyPhraseWithoutFiles,
|
|
7
|
+
maybeHandleEmptyIntentTurn,
|
|
8
|
+
resetEmptyTurnCounter,
|
|
9
|
+
} from "../guided-flow.js";
|
|
5
10
|
import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
|
|
6
11
|
import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
|
|
7
12
|
import { pauseAutoForProviderError } from "../provider-error-pause.js";
|
|
@@ -75,6 +80,20 @@ export async function handleAgentEnd(
|
|
|
75
80
|
clearDiscussionFlowState();
|
|
76
81
|
return;
|
|
77
82
|
}
|
|
83
|
+
|
|
84
|
+
// #4573 — When the LLM emits "Milestone X ready." but the required files
|
|
85
|
+
// are missing, `checkAutoStartAfterDiscuss` returns false silently. Surface
|
|
86
|
+
// that and nudge the LLM to complete the writes before the user hits the
|
|
87
|
+
// downstream "All milestones complete" warning loop.
|
|
88
|
+
if (maybeHandleReadyPhraseWithoutFiles(event)) return;
|
|
89
|
+
|
|
90
|
+
// #4573 — Empty-turn recovery: if the LLM announced intent in prose but
|
|
91
|
+
// emitted no tool calls, nudge it to execute. Fires only when auto-mode is
|
|
92
|
+
// active or a discussion autostart is pending (non-auto interactive discuss
|
|
93
|
+
// is user-driven). Runs before `isAutoActive` early return so pending
|
|
94
|
+
// discussions (where isAutoActive may be false) still get recovered.
|
|
95
|
+
if (maybeHandleEmptyIntentTurn(event, isAutoActive())) return;
|
|
96
|
+
|
|
78
97
|
if (!isAutoActive()) return;
|
|
79
98
|
if (isSessionSwitchInFlight()) return;
|
|
80
99
|
|
|
@@ -336,6 +355,9 @@ export async function handleAgentEnd(
|
|
|
336
355
|
// ── Success path ─────────────────────────────────────────────────────────
|
|
337
356
|
try {
|
|
338
357
|
resetRetryState(retryState);
|
|
358
|
+
// #4573 — Reset the empty-turn counter on any successful agent turn so
|
|
359
|
+
// transient stalls don't accumulate across independent units.
|
|
360
|
+
resetEmptyTurnCounter();
|
|
339
361
|
resolveAgentEnd(event);
|
|
340
362
|
} catch (err) {
|
|
341
363
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -35,6 +35,19 @@ function registerAlias(pi: ExtensionAPI, toolDef: any, aliasName: string, canoni
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Read a tool result's structured payload, accommodating MCP's `details` →
|
|
40
|
+
* `structuredContent` rename (#4472, #4477). In-process executions still
|
|
41
|
+
* deliver the payload on `result.details`; MCP-routed executions deliver it
|
|
42
|
+
* on `result.structuredContent` (post `adaptExecutorResult` transform). All
|
|
43
|
+
* `renderResult` callbacks in this file route through this helper so a future
|
|
44
|
+
* field rename only needs to be applied in one place.
|
|
45
|
+
*/
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- result shape varies by tool
|
|
47
|
+
function readDetails(result: any): any {
|
|
48
|
+
return result?.details ?? result?.structuredContent;
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
export function registerDbTools(pi: ExtensionAPI): void {
|
|
39
52
|
// ─── gsd_decision_save (formerly gsd_save_decision) ─────────────────────
|
|
40
53
|
|
|
@@ -110,7 +123,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
110
123
|
return new Text(text, 0, 0);
|
|
111
124
|
},
|
|
112
125
|
renderResult(result: any, _options: any, theme: any) {
|
|
113
|
-
const d = result
|
|
126
|
+
const d = readDetails(result);
|
|
114
127
|
if (result.isError || d?.error) {
|
|
115
128
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
116
129
|
}
|
|
@@ -188,7 +201,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
188
201
|
return new Text(text, 0, 0);
|
|
189
202
|
},
|
|
190
203
|
renderResult(result: any, _options: any, theme: any) {
|
|
191
|
-
const d = result
|
|
204
|
+
const d = readDetails(result);
|
|
192
205
|
if (result.isError || d?.error) {
|
|
193
206
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
194
207
|
}
|
|
@@ -273,7 +286,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
273
286
|
return new Text(text, 0, 0);
|
|
274
287
|
},
|
|
275
288
|
renderResult(result: any, _options: any, theme: any) {
|
|
276
|
-
const d = result
|
|
289
|
+
const d = readDetails(result);
|
|
277
290
|
if (result.isError || d?.error) {
|
|
278
291
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
279
292
|
}
|
|
@@ -322,7 +335,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
322
335
|
return new Text(text, 0, 0);
|
|
323
336
|
},
|
|
324
337
|
renderResult(result: any, _options: any, theme: any) {
|
|
325
|
-
const d = result
|
|
338
|
+
const d = readDetails(result);
|
|
326
339
|
if (result.isError || d?.error) {
|
|
327
340
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
328
341
|
}
|
|
@@ -406,7 +419,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
406
419
|
return new Text(theme.fg("toolTitle", theme.bold("milestone_generate_id")), 0, 0);
|
|
407
420
|
},
|
|
408
421
|
renderResult(result: any, _options: any, theme: any) {
|
|
409
|
-
const d = result
|
|
422
|
+
const d = readDetails(result);
|
|
410
423
|
if (result.isError || d?.error) {
|
|
411
424
|
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
412
425
|
}
|
|
@@ -1074,13 +1087,31 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
1074
1087
|
text += theme.fg("dim", ` → ${args.verdict ?? ""}`);
|
|
1075
1088
|
return new Text(text, 0, 0);
|
|
1076
1089
|
},
|
|
1090
|
+
/**
|
|
1091
|
+
* Render the save_gate_result tool output for the TUI.
|
|
1092
|
+
*
|
|
1093
|
+
* Prefers structured fields, but falls back to `content[0].text` when the
|
|
1094
|
+
* structured payload is empty. Defensive: the structural fix on this
|
|
1095
|
+
* branch plumbs `details` through MCP via `structuredContent`, but older
|
|
1096
|
+
* hosts, a future handler that forgets `structuredContent`, or any drop
|
|
1097
|
+
* of non-standard return fields would otherwise render as
|
|
1098
|
+
* "undefined: undefined". Same fallback applies to error rendering, and
|
|
1099
|
+
* we strip a leading `Error:` from the fallback text to avoid producing
|
|
1100
|
+
* `Error: Error: ...`.
|
|
1101
|
+
*/
|
|
1077
1102
|
renderResult(result: any, _options: any, theme: any) {
|
|
1078
|
-
const d = result
|
|
1103
|
+
const d = readDetails(result);
|
|
1079
1104
|
if (result.isError || d?.error) {
|
|
1080
|
-
|
|
1105
|
+
const rawMsg = d?.error ?? result.content?.[0]?.text ?? "unknown";
|
|
1106
|
+
const msg = rawMsg.replace(/^\s*Error:\s*/i, "");
|
|
1107
|
+
return new Text(theme.fg("error", `Error: ${msg}`), 0, 0);
|
|
1108
|
+
}
|
|
1109
|
+
if (!d?.gateId || !d?.verdict) {
|
|
1110
|
+
const text = result.content?.[0]?.text ?? "Gate result saved";
|
|
1111
|
+
return new Text(theme.fg("success", text), 0, 0);
|
|
1081
1112
|
}
|
|
1082
|
-
const color = d
|
|
1083
|
-
return new Text(theme.fg(color, `${d
|
|
1113
|
+
const color = d.verdict === "flag" ? "warning" : "success";
|
|
1114
|
+
return new Text(theme.fg(color, `${d.gateId}: ${d.verdict}`), 0, 0);
|
|
1084
1115
|
},
|
|
1085
1116
|
};
|
|
1086
1117
|
|
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
|
|
6
|
+
import { Type } from "@sinclair/typebox";
|
|
7
|
+
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
8
|
+
|
|
9
|
+
import { executeGsdExec } from "../tools/exec-tool.js";
|
|
10
|
+
import { executeExecSearch } from "../tools/exec-search-tool.js";
|
|
11
|
+
import { executeResume } from "../tools/resume-tool.js";
|
|
12
|
+
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
13
|
+
import { logWarning } from "../workflow-logger.js";
|
|
14
|
+
|
|
15
|
+
export function registerExecTools(pi: ExtensionAPI): void {
|
|
16
|
+
pi.registerTool({
|
|
17
|
+
name: "gsd_exec",
|
|
18
|
+
label: "Exec (Sandboxed)",
|
|
19
|
+
description:
|
|
20
|
+
"Run a short script (bash/node/python) in a subprocess. Full stdout/stderr persist to " +
|
|
21
|
+
".gsd/exec/<id>.{stdout,stderr,meta.json}; only a short digest returns in context. Use " +
|
|
22
|
+
"this instead of reading many files or emitting large tool outputs — e.g. have the script " +
|
|
23
|
+
"count/grep/summarize and log the finding. Enabled by default; opt out via " +
|
|
24
|
+
"preferences.context_mode.enabled=false.",
|
|
25
|
+
promptSnippet:
|
|
26
|
+
"Run a bash/node/python script in a sandbox; full output is saved to disk and only a digest returns",
|
|
27
|
+
promptGuidelines: [
|
|
28
|
+
"Prefer gsd_exec for analyses that would otherwise read >3 files or produce large tool output.",
|
|
29
|
+
"Write scripts that log the finding (counts, matches, summaries) rather than raw dumps.",
|
|
30
|
+
"The digest is the last ~300 chars of stdout — size your log output accordingly.",
|
|
31
|
+
"Need the full output? Read the stdout_path returned in details (file on local disk).",
|
|
32
|
+
],
|
|
33
|
+
parameters: Type.Object({
|
|
34
|
+
runtime: Type.Union(
|
|
35
|
+
[Type.Literal("bash"), Type.Literal("node"), Type.Literal("python")],
|
|
36
|
+
{ description: "Interpreter: bash (-c), node (-e), or python3 (-c)." },
|
|
37
|
+
),
|
|
38
|
+
script: Type.String({ description: "Script body. Keep output small (log the finding, not the data)." }),
|
|
39
|
+
purpose: Type.Optional(Type.String({ description: "Short label recorded in meta.json for later review." })),
|
|
40
|
+
timeout_ms: Type.Optional(
|
|
41
|
+
Type.Number({
|
|
42
|
+
description: "Per-invocation timeout (ms). Capped at 600000. Default from preferences.",
|
|
43
|
+
minimum: 1_000,
|
|
44
|
+
maximum: 600_000,
|
|
45
|
+
}),
|
|
46
|
+
),
|
|
47
|
+
}),
|
|
48
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
49
|
+
let prefs: Awaited<ReturnType<typeof loadEffectiveGSDPreferences>> | null = null;
|
|
50
|
+
try {
|
|
51
|
+
prefs = loadEffectiveGSDPreferences();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
logWarning("tool", `gsd_exec could not load preferences: ${err instanceof Error ? err.message : String(err)}`);
|
|
54
|
+
}
|
|
55
|
+
return executeGsdExec(params as Parameters<typeof executeGsdExec>[0], {
|
|
56
|
+
baseDir: process.cwd(),
|
|
57
|
+
preferences: prefs?.preferences ?? null,
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
pi.registerTool({
|
|
63
|
+
name: "gsd_exec_search",
|
|
64
|
+
label: "Search gsd_exec History",
|
|
65
|
+
description:
|
|
66
|
+
"List prior gsd_exec runs (most recent first) from .gsd/exec/*.meta.json. Useful for " +
|
|
67
|
+
"rediscovering the stdout_path of an earlier run without re-executing it. Read-only.",
|
|
68
|
+
promptSnippet: "Search prior gsd_exec runs by substring, runtime, or failing-only filter",
|
|
69
|
+
promptGuidelines: [
|
|
70
|
+
"Use this before re-running an expensive analysis — the prior run's stdout file may still answer.",
|
|
71
|
+
"The preview shows the trailing ~300 chars of stdout; read stdout_path for the full transcript.",
|
|
72
|
+
],
|
|
73
|
+
parameters: Type.Object({
|
|
74
|
+
query: Type.Optional(Type.String({ description: "Substring matched against id and purpose (case-insensitive)." })),
|
|
75
|
+
runtime: Type.Optional(
|
|
76
|
+
Type.Union([Type.Literal("bash"), Type.Literal("node"), Type.Literal("python")], {
|
|
77
|
+
description: "Restrict to one runtime.",
|
|
78
|
+
}),
|
|
79
|
+
),
|
|
80
|
+
failing_only: Type.Optional(Type.Boolean({ description: "Only non-zero exit codes and timeouts." })),
|
|
81
|
+
limit: Type.Optional(Type.Number({ description: "Max results (default 20, cap 200)", minimum: 1, maximum: 200 })),
|
|
82
|
+
}),
|
|
83
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
84
|
+
return executeExecSearch(params as Parameters<typeof executeExecSearch>[0], {
|
|
85
|
+
baseDir: process.cwd(),
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
pi.registerTool({
|
|
91
|
+
name: "gsd_resume",
|
|
92
|
+
label: "Resume (Read Snapshot)",
|
|
93
|
+
description:
|
|
94
|
+
"Return the contents of .gsd/last-snapshot.md — a ≤2 KB digest of top memories, recent " +
|
|
95
|
+
"gsd_exec runs, and active context, written automatically on session_before_compact. Use " +
|
|
96
|
+
"this after compaction or session resume to re-orient quickly.",
|
|
97
|
+
promptSnippet: "Read the pre-compaction snapshot to re-orient after context loss",
|
|
98
|
+
promptGuidelines: [
|
|
99
|
+
"Call this right after a session resumes if you feel you've lost durable context.",
|
|
100
|
+
"The snapshot is a summary — use memory_query or gsd_exec_search for detail.",
|
|
101
|
+
],
|
|
102
|
+
parameters: Type.Object({}),
|
|
103
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
104
|
+
return executeResume(params as Parameters<typeof executeResume>[0], {
|
|
105
|
+
baseDir: process.cwd(),
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
@@ -8,6 +8,7 @@ import type { GSDEcosystemBeforeAgentStartHandler } from "../ecosystem/gsd-exten
|
|
|
8
8
|
import { loadEcosystemExtensions } from "../ecosystem/loader.js";
|
|
9
9
|
import { registerDbTools } from "./db-tools.js";
|
|
10
10
|
import { registerDynamicTools } from "./dynamic-tools.js";
|
|
11
|
+
import { registerExecTools } from "./exec-tools.js";
|
|
11
12
|
import { registerJournalTools } from "./journal-tools.js";
|
|
12
13
|
import { registerMemoryTools } from "./memory-tools.js";
|
|
13
14
|
import { registerQueryTools } from "./query-tools.js";
|
|
@@ -100,6 +101,7 @@ export function registerGsdExtension(pi: ExtensionAPI): void {
|
|
|
100
101
|
["journal-tools", () => registerJournalTools(pi)],
|
|
101
102
|
["query-tools", () => registerQueryTools(pi)],
|
|
102
103
|
["memory-tools", () => registerMemoryTools(pi)],
|
|
104
|
+
["exec-tools", () => registerExecTools(pi)],
|
|
103
105
|
["shortcuts", () => registerShortcuts(pi)],
|
|
104
106
|
["hooks", () => registerHooks(pi, ecosystemHandlers)],
|
|
105
107
|
["ecosystem", () => {
|
|
@@ -18,7 +18,7 @@ import { loadToolApiKeys } from "../commands-config.js";
|
|
|
18
18
|
import { loadFile, saveFile, formatContinue } from "../files.js";
|
|
19
19
|
import { deriveState } from "../state.js";
|
|
20
20
|
import { getAutoDashboardData, isAutoActive, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto.js";
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
import { isParallelActive, shutdownParallel } from "../parallel-orchestrator.js";
|
|
23
23
|
import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
|
|
24
24
|
import { saveActivityLog } from "../activity-log.js";
|
|
@@ -48,7 +48,9 @@ export function registerHooks(
|
|
|
48
48
|
initNotificationStore(process.cwd());
|
|
49
49
|
installNotifyInterceptor(ctx);
|
|
50
50
|
initNotificationWidget(ctx);
|
|
51
|
-
|
|
51
|
+
if (!isAutoActive()) {
|
|
52
|
+
initHealthWidget(ctx);
|
|
53
|
+
}
|
|
52
54
|
resetWriteGateState();
|
|
53
55
|
resetToolCallLoopGuard();
|
|
54
56
|
resetAskUserQuestionsCache();
|
|
@@ -90,7 +92,7 @@ export function registerHooks(
|
|
|
90
92
|
}
|
|
91
93
|
loadToolApiKeys();
|
|
92
94
|
if (isAutoActive()) {
|
|
93
|
-
ctx.ui.
|
|
95
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
94
96
|
}
|
|
95
97
|
});
|
|
96
98
|
|
|
@@ -113,7 +115,7 @@ export function registerHooks(
|
|
|
113
115
|
}
|
|
114
116
|
loadToolApiKeys();
|
|
115
117
|
if (isAutoActive()) {
|
|
116
|
-
ctx.ui.
|
|
118
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
117
119
|
}
|
|
118
120
|
});
|
|
119
121
|
|
|
@@ -225,6 +227,42 @@ export function registerHooks(
|
|
|
225
227
|
}));
|
|
226
228
|
});
|
|
227
229
|
|
|
230
|
+
// Context-mode snapshot: write .gsd/last-snapshot.md before compaction so
|
|
231
|
+
// agents can call gsd_resume (or Read the file) to re-orient. Opt-in via
|
|
232
|
+
// preferences.context_mode.enabled. Runs after the auto-cancel handler
|
|
233
|
+
// above — if that one returned cancel:true, pi still fires us but the
|
|
234
|
+
// compaction won't actually happen; the snapshot is still useful then,
|
|
235
|
+
// since auto may pause and resume later.
|
|
236
|
+
pi.on("session_before_compact", async () => {
|
|
237
|
+
try {
|
|
238
|
+
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
239
|
+
const { isContextModeEnabled } = await import("../preferences-types.js");
|
|
240
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
241
|
+
if (!isContextModeEnabled(prefs?.preferences)) return;
|
|
242
|
+
const { writeCompactionSnapshot } = await import("../compaction-snapshot.js");
|
|
243
|
+
const { ensureDbOpen } = await import("./dynamic-tools.js");
|
|
244
|
+
await ensureDbOpen();
|
|
245
|
+
const basePath = process.cwd();
|
|
246
|
+
let activeContext: string | null = null;
|
|
247
|
+
try {
|
|
248
|
+
const state = await deriveState(basePath);
|
|
249
|
+
if (state.activeMilestone && state.activeSlice && state.activeTask) {
|
|
250
|
+
activeContext =
|
|
251
|
+
`Active: ${state.activeMilestone.id} / ${state.activeSlice.id} / ${state.activeTask.id}` +
|
|
252
|
+
(state.activeTask.title ? ` — ${state.activeTask.title}` : "");
|
|
253
|
+
}
|
|
254
|
+
} catch {
|
|
255
|
+
/* non-fatal */
|
|
256
|
+
}
|
|
257
|
+
writeCompactionSnapshot(basePath, { activeContext });
|
|
258
|
+
} catch (err) {
|
|
259
|
+
safetyLogWarning(
|
|
260
|
+
"context-mode",
|
|
261
|
+
`failed to write compaction snapshot: ${err instanceof Error ? err.message : String(err)}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
228
266
|
pi.on("session_shutdown", async (_event, ctx: ExtensionContext) => {
|
|
229
267
|
if (isParallelActive()) {
|
|
230
268
|
try {
|
|
@@ -117,9 +117,21 @@ function normalizeWriteGateSnapshot(value: unknown): WriteGateSnapshot {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
const EMPTY_SNAPSHOT: WriteGateSnapshot = {
|
|
121
|
+
verifiedDepthMilestones: [],
|
|
122
|
+
activeQueuePhase: false,
|
|
123
|
+
pendingGateId: null,
|
|
124
|
+
};
|
|
125
|
+
|
|
120
126
|
export function loadWriteGateSnapshot(basePath: string = process.cwd()): WriteGateSnapshot {
|
|
121
127
|
const path = writeGateSnapshotPath(basePath);
|
|
122
|
-
if (!existsSync(path))
|
|
128
|
+
if (!existsSync(path)) {
|
|
129
|
+
// When persist mode is active and the file has been deleted, treat it as a
|
|
130
|
+
// full state reset so deleting the file clears the HARD BLOCK gate.
|
|
131
|
+
// In non-persist mode the file is never written, so fall back to in-memory.
|
|
132
|
+
if (shouldPersistWriteGateSnapshot()) return EMPTY_SNAPSHOT;
|
|
133
|
+
return currentWriteGateSnapshot();
|
|
134
|
+
}
|
|
123
135
|
try {
|
|
124
136
|
return normalizeWriteGateSnapshot(JSON.parse(readFileSync(path, "utf-8")));
|
|
125
137
|
} catch {
|