gsd-pi 2.44.0-dev.848dd4c → 2.44.0-dev.8dd0d8e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/gsd/activity-log.js +7 -0
- package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +37 -36
- package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
- package/dist/resources/extensions/gsd/auto-start.js +21 -2
- package/dist/resources/extensions/gsd/auto-timers.js +57 -3
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
- package/dist/resources/extensions/gsd/auto.js +30 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
- package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +34 -16
- package/dist/resources/extensions/gsd/doctor.js +8 -0
- package/dist/resources/extensions/gsd/git-service.js +8 -3
- package/dist/resources/extensions/gsd/gsd-db.js +12 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/preferences.js +9 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
- package/dist/resources/extensions/gsd/repo-identity.js +45 -7
- package/dist/resources/extensions/gsd/rethink.js +115 -0
- package/dist/resources/extensions/gsd/state.js +41 -3
- package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +32 -2
- package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/page.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +1 -1
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
- package/dist/web/standalone/.next/static/chunks/app/{page-b9367c5ae13b99c6.js → page-6654a8cca61a3d1c.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
- package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
- package/package.json +1 -1
- package/packages/native/dist/stream-process/index.js +2 -2
- package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
- package/packages/native/src/stream-process/index.ts +2 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -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 +20 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.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 +7 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
- package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
- package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
- package/packages/pi-coding-agent/src/main.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
- package/src/resources/extensions/gsd/activity-log.ts +1 -0
- package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
- package/src/resources/extensions/gsd/auto/phases.ts +46 -48
- package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
- package/src/resources/extensions/gsd/auto-start.ts +25 -2
- package/src/resources/extensions/gsd/auto-timers.ts +64 -3
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
- package/src/resources/extensions/gsd/auto.ts +37 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
- package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
- package/src/resources/extensions/gsd/db-writer.ts +39 -17
- package/src/resources/extensions/gsd/doctor.ts +7 -1
- package/src/resources/extensions/gsd/git-service.ts +6 -2
- package/src/resources/extensions/gsd/gsd-db.ts +16 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/preferences.ts +11 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
- package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
- package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
- package/src/resources/extensions/gsd/repo-identity.ts +46 -7
- package/src/resources/extensions/gsd/rethink.ts +154 -0
- package/src/resources/extensions/gsd/state.ts +41 -1
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
- package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
- package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
- package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
- package/src/resources/extensions/mcp-client/index.ts +20 -0
- package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
- package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
- package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → enTIm32JJtw--VTtLPSC3}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → enTIm32JJtw--VTtLPSC3}/_ssgManifest.js +0 -0
|
@@ -134,6 +134,13 @@ export function pruneActivityLogs(activityDir, retentionDays) {
|
|
|
134
134
|
for (const entry of entries) {
|
|
135
135
|
if (entry.seq === maxSeq)
|
|
136
136
|
continue; // always preserve highest-seq
|
|
137
|
+
if (retentionDays === 0) {
|
|
138
|
+
try {
|
|
139
|
+
unlinkSync(entry.filePath);
|
|
140
|
+
}
|
|
141
|
+
catch { /* skip */ }
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
137
144
|
try {
|
|
138
145
|
const mtime = statSync(entry.filePath).mtimeMs;
|
|
139
146
|
if (Math.floor(mtime) <= cutoff)
|
|
@@ -17,6 +17,9 @@ export const INFRA_ERROR_CODES = new Set([
|
|
|
17
17
|
"EDQUOT", // disk quota exceeded
|
|
18
18
|
"EMFILE", // too many open files (process)
|
|
19
19
|
"ENFILE", // too many open files (system)
|
|
20
|
+
"ECONNREFUSED", // connection refused (offline / local server down)
|
|
21
|
+
"ENOTFOUND", // DNS lookup failed (offline / no network)
|
|
22
|
+
"ENETUNREACH", // network unreachable (offline / no route)
|
|
20
23
|
]);
|
|
21
24
|
/**
|
|
22
25
|
* Detect whether an error is an unrecoverable infrastructure failure.
|
|
@@ -14,7 +14,9 @@ import { debugLog } from "../debug-logger.js";
|
|
|
14
14
|
import { gsdRoot } from "../paths.js";
|
|
15
15
|
import { atomicWriteSync } from "../atomic-write.js";
|
|
16
16
|
import { PROJECT_FILES } from "../detection.js";
|
|
17
|
+
import { MergeConflictError } from "../git-service.js";
|
|
17
18
|
import { join } from "node:path";
|
|
19
|
+
import { existsSync, cpSync } from "node:fs";
|
|
18
20
|
// ─── generateMilestoneReport ──────────────────────────────────────────────────
|
|
19
21
|
/**
|
|
20
22
|
* Generate and write an HTML milestone report snapshot.
|
|
@@ -145,20 +147,19 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
145
147
|
loopState.recentUnits.length = 0;
|
|
146
148
|
loopState.stuckRecoveryAttempts = 0;
|
|
147
149
|
// Worktree lifecycle on milestone transition — merge current, enter next
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// Non-fatal — PR creation is best-effort
|
|
150
|
+
try {
|
|
151
|
+
deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
|
|
152
|
+
}
|
|
153
|
+
catch (mergeErr) {
|
|
154
|
+
if (mergeErr instanceof MergeConflictError) {
|
|
155
|
+
// Real code conflicts — stop the loop instead of retrying forever (#2330)
|
|
156
|
+
ctx.ui.notify(`Merge conflict: ${mergeErr.conflictedFiles.join(", ")}. Resolve conflicts manually and run /gsd auto to resume.`, "error");
|
|
157
|
+
await deps.stopAuto(ctx, pi, `Merge conflict on milestone ${s.currentMilestoneId}`);
|
|
158
|
+
return { action: "break", reason: "merge-conflict" };
|
|
160
159
|
}
|
|
160
|
+
// Non-conflict errors — log and continue
|
|
161
161
|
}
|
|
162
|
+
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
162
163
|
deps.invalidateAllCaches();
|
|
163
164
|
state = await deps.deriveState(s.basePath);
|
|
164
165
|
mid = state.activeMilestone?.id;
|
|
@@ -179,9 +180,14 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
179
180
|
// Reset completed-units tracking for the new milestone — stale entries
|
|
180
181
|
// from the previous milestone cause the dispatch loop to skip units
|
|
181
182
|
// that haven't actually been completed in the new milestone's context.
|
|
183
|
+
// Archive the old completed-units.json instead of wiping it (#2313).
|
|
182
184
|
s.completedUnits = [];
|
|
183
185
|
try {
|
|
184
186
|
const completedKeysPath = join(gsdRoot(s.basePath), "completed-units.json");
|
|
187
|
+
if (existsSync(completedKeysPath) && s.currentMilestoneId) {
|
|
188
|
+
const archivePath = join(gsdRoot(s.basePath), `completed-units-${s.currentMilestoneId}.json`);
|
|
189
|
+
cpSync(completedKeysPath, archivePath);
|
|
190
|
+
}
|
|
185
191
|
atomicWriteSync(completedKeysPath, JSON.stringify([], null, 2));
|
|
186
192
|
}
|
|
187
193
|
catch { /* non-fatal */ }
|
|
@@ -209,20 +215,17 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
209
215
|
if (incomplete.length === 0 && state.registry.length > 0) {
|
|
210
216
|
// All milestones complete — merge milestone branch before stopping
|
|
211
217
|
if (s.currentMilestoneId) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
// Non-fatal — PR creation is best-effort
|
|
218
|
+
try {
|
|
219
|
+
deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
|
|
220
|
+
}
|
|
221
|
+
catch (mergeErr) {
|
|
222
|
+
if (mergeErr instanceof MergeConflictError) {
|
|
223
|
+
ctx.ui.notify(`Merge conflict: ${mergeErr.conflictedFiles.join(", ")}. Resolve conflicts manually and run /gsd auto to resume.`, "error");
|
|
224
|
+
await deps.stopAuto(ctx, pi, `Merge conflict on milestone ${s.currentMilestoneId}`);
|
|
225
|
+
return { action: "break", reason: "merge-conflict" };
|
|
224
226
|
}
|
|
225
227
|
}
|
|
228
|
+
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
226
229
|
}
|
|
227
230
|
deps.sendDesktopNotification("GSD", "All milestones complete!", "success", "milestone");
|
|
228
231
|
deps.logCmuxEvent(prefs, "All milestones complete.", "success");
|
|
@@ -277,20 +280,17 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
277
280
|
if (state.phase === "complete") {
|
|
278
281
|
// Milestone merge on complete (before closeout so branch state is clean)
|
|
279
282
|
if (s.currentMilestoneId) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
catch {
|
|
291
|
-
// Non-fatal — PR creation is best-effort
|
|
283
|
+
try {
|
|
284
|
+
deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
|
|
285
|
+
}
|
|
286
|
+
catch (mergeErr) {
|
|
287
|
+
if (mergeErr instanceof MergeConflictError) {
|
|
288
|
+
ctx.ui.notify(`Merge conflict: ${mergeErr.conflictedFiles.join(", ")}. Resolve conflicts manually and run /gsd auto to resume.`, "error");
|
|
289
|
+
await deps.stopAuto(ctx, pi, `Merge conflict on milestone ${s.currentMilestoneId}`);
|
|
290
|
+
return { action: "break", reason: "merge-conflict" };
|
|
292
291
|
}
|
|
293
292
|
}
|
|
293
|
+
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
294
294
|
}
|
|
295
295
|
deps.sendDesktopNotification("GSD", `Milestone ${mid} complete!`, "success", "milestone");
|
|
296
296
|
deps.logCmuxEvent(prefs, `Milestone ${mid} complete.`, "success");
|
|
@@ -591,6 +591,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
591
591
|
lastProgressAt: s.currentUnit.startedAt,
|
|
592
592
|
progressCount: 0,
|
|
593
593
|
lastProgressKind: "dispatch",
|
|
594
|
+
recoveryAttempts: 0, // Reset so re-dispatched units get full recovery budget (#2322)
|
|
594
595
|
});
|
|
595
596
|
// Status bar + progress widget
|
|
596
597
|
ctx.ui.setStatus("gsd-auto", "auto");
|
|
@@ -1156,6 +1156,12 @@ export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
|
|
|
1156
1156
|
roadmapPath: roadmapRel,
|
|
1157
1157
|
inlinedContext,
|
|
1158
1158
|
milestoneSummaryPath,
|
|
1159
|
+
skillActivation: buildSkillActivationBlock({
|
|
1160
|
+
base,
|
|
1161
|
+
milestoneId: mid,
|
|
1162
|
+
milestoneTitle: midTitle,
|
|
1163
|
+
extraContext: [inlinedContext],
|
|
1164
|
+
}),
|
|
1159
1165
|
});
|
|
1160
1166
|
}
|
|
1161
1167
|
export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
|
|
@@ -1236,6 +1242,12 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
|
|
|
1236
1242
|
inlinedContext,
|
|
1237
1243
|
validationPath: validationOutputPath,
|
|
1238
1244
|
remediationRound: String(remediationRound),
|
|
1245
|
+
skillActivation: buildSkillActivationBlock({
|
|
1246
|
+
base,
|
|
1247
|
+
milestoneId: mid,
|
|
1248
|
+
milestoneTitle: midTitle,
|
|
1249
|
+
extraContext: [inlinedContext],
|
|
1250
|
+
}),
|
|
1239
1251
|
});
|
|
1240
1252
|
}
|
|
1241
1253
|
export async function buildReplanSlicePrompt(mid, midTitle, sid, sTitle, base) {
|
|
@@ -1332,6 +1344,12 @@ export async function buildRunUatPrompt(mid, sliceId, uatPath, uatContent, base)
|
|
|
1332
1344
|
uatResultPath,
|
|
1333
1345
|
uatType,
|
|
1334
1346
|
inlinedContext,
|
|
1347
|
+
skillActivation: buildSkillActivationBlock({
|
|
1348
|
+
base,
|
|
1349
|
+
milestoneId: mid,
|
|
1350
|
+
sliceId,
|
|
1351
|
+
extraContext: [inlinedContext],
|
|
1352
|
+
}),
|
|
1335
1353
|
});
|
|
1336
1354
|
}
|
|
1337
1355
|
export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId, base, level) {
|
|
@@ -1378,11 +1396,16 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
|
|
|
1378
1396
|
milestoneTitle: midTitle,
|
|
1379
1397
|
completedSliceId,
|
|
1380
1398
|
roadmapPath: roadmapRel,
|
|
1381
|
-
completedSliceSummaryPath: summaryRel,
|
|
1382
1399
|
assessmentPath,
|
|
1383
1400
|
inlinedContext,
|
|
1384
1401
|
deferredCaptures,
|
|
1385
1402
|
commitInstruction: reassessCommitInstruction,
|
|
1403
|
+
skillActivation: buildSkillActivationBlock({
|
|
1404
|
+
base,
|
|
1405
|
+
milestoneId: mid,
|
|
1406
|
+
milestoneTitle: midTitle,
|
|
1407
|
+
extraContext: [inlinedContext, deferredCaptures],
|
|
1408
|
+
}),
|
|
1386
1409
|
});
|
|
1387
1410
|
}
|
|
1388
1411
|
// ─── Reactive Execute Prompt ──────────────────────────────────────────────
|
|
@@ -197,10 +197,13 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
197
197
|
state = await deriveState(wtPath);
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
|
-
// Milestone branch recovery (#601)
|
|
200
|
+
// Milestone branch recovery (#601, #2358)
|
|
201
|
+
// Detect survivor milestone branches in both pre-planning and complete phases.
|
|
202
|
+
// In phase=complete, the milestone artifacts exist but finalization (merge,
|
|
203
|
+
// worktree cleanup) was never run — the survivor branch must be merged.
|
|
201
204
|
let hasSurvivorBranch = false;
|
|
202
205
|
if (state.activeMilestone &&
|
|
203
|
-
state.phase === "pre-planning" &&
|
|
206
|
+
(state.phase === "pre-planning" || state.phase === "complete") &&
|
|
204
207
|
shouldUseWorktreeIsolation() &&
|
|
205
208
|
!detectWorktreeName(base) &&
|
|
206
209
|
!base.includes(`${pathSep}.gsd${pathSep}worktrees${pathSep}`)) {
|
|
@@ -231,6 +234,22 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
231
234
|
return releaseLockAndReturn();
|
|
232
235
|
}
|
|
233
236
|
}
|
|
237
|
+
// Survivor branch exists and milestone is complete (#2358):
|
|
238
|
+
// The milestone artifacts were written but finalization (merge, worktree
|
|
239
|
+
// cleanup) never ran. Run mergeAndExit to finalize, then re-derive state
|
|
240
|
+
// so the normal "all milestones complete" or "next milestone" path runs.
|
|
241
|
+
if (hasSurvivorBranch && state.phase === "complete") {
|
|
242
|
+
const mid = state.activeMilestone.id;
|
|
243
|
+
ctx.ui.notify(`Milestone ${mid} is complete but branch/worktree was not finalized. Running merge now.`, "info");
|
|
244
|
+
const resolver = buildResolver();
|
|
245
|
+
resolver.mergeAndExit(mid, {
|
|
246
|
+
notify: ctx.ui.notify.bind(ctx.ui),
|
|
247
|
+
});
|
|
248
|
+
invalidateAllCaches();
|
|
249
|
+
state = await deriveState(base);
|
|
250
|
+
// Clear survivor flag — finalization is done
|
|
251
|
+
hasSurvivorBranch = false;
|
|
252
|
+
}
|
|
234
253
|
if (!hasSurvivorBranch) {
|
|
235
254
|
// No active work — start a new milestone via discuss flow
|
|
236
255
|
if (!state.activeMilestone || state.phase === "complete") {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* via startUnitSupervision() and torn down by the caller via clearUnitTimeout().
|
|
7
7
|
*/
|
|
8
8
|
import { readUnitRuntimeRecord, writeUnitRuntimeRecord } from "./unit-runtime.js";
|
|
9
|
+
import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
|
|
9
10
|
import { resolveAutoSupervisorConfig } from "./preferences.js";
|
|
10
11
|
import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.js";
|
|
11
12
|
import { getInFlightToolCount, getOldestInFlightToolStart, } from "./auto-tool-tracking.js";
|
|
@@ -21,12 +22,65 @@ import { resolveAgentEndCancelled } from "./auto/resolve.js";
|
|
|
21
22
|
* 3. Hard timeout (pause + recovery)
|
|
22
23
|
* 4. Context-pressure monitor (continue-here)
|
|
23
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* Parse a task estimate string (e.g. "30m", "2h", "1h30m") into minutes.
|
|
27
|
+
* Returns null if the string cannot be parsed.
|
|
28
|
+
*/
|
|
29
|
+
export function parseEstimateMinutes(estimate) {
|
|
30
|
+
if (!estimate || typeof estimate !== "string")
|
|
31
|
+
return null;
|
|
32
|
+
const trimmed = estimate.trim();
|
|
33
|
+
if (!trimmed)
|
|
34
|
+
return null;
|
|
35
|
+
let totalMinutes = 0;
|
|
36
|
+
let matched = false;
|
|
37
|
+
// Match hours component
|
|
38
|
+
const hoursMatch = trimmed.match(/(\d+)\s*h/i);
|
|
39
|
+
if (hoursMatch) {
|
|
40
|
+
totalMinutes += Number(hoursMatch[1]) * 60;
|
|
41
|
+
matched = true;
|
|
42
|
+
}
|
|
43
|
+
// Match minutes component
|
|
44
|
+
const minutesMatch = trimmed.match(/(\d+)\s*m/i);
|
|
45
|
+
if (minutesMatch) {
|
|
46
|
+
totalMinutes += Number(minutesMatch[1]);
|
|
47
|
+
matched = true;
|
|
48
|
+
}
|
|
49
|
+
return matched ? totalMinutes : null;
|
|
50
|
+
}
|
|
24
51
|
export function startUnitSupervision(sctx) {
|
|
25
52
|
const { s, ctx, pi, unitType, unitId, prefs, buildSnapshotOpts, buildRecoveryContext, pauseAuto } = sctx;
|
|
26
53
|
const supervisor = resolveAutoSupervisorConfig();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
54
|
+
// Scale timeouts based on task estimate annotations (#2243).
|
|
55
|
+
// If the task has an est: annotation, use it to extend the hard and soft timeouts
|
|
56
|
+
// so longer tasks don't get prematurely timed out.
|
|
57
|
+
let taskEstimate = sctx.taskEstimate;
|
|
58
|
+
if (!taskEstimate && unitType === "task" && isDbAvailable()) {
|
|
59
|
+
// Look up the task estimate from the DB (#2243).
|
|
60
|
+
try {
|
|
61
|
+
if (s.currentMilestoneId) {
|
|
62
|
+
const slices = getMilestoneSlices(s.currentMilestoneId);
|
|
63
|
+
for (const slice of slices) {
|
|
64
|
+
const tasks = getSliceTasks(s.currentMilestoneId, slice.id);
|
|
65
|
+
const task = tasks.find(t => t.id === unitId);
|
|
66
|
+
if (task?.estimate) {
|
|
67
|
+
taskEstimate = task.estimate;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Non-fatal — fall through with no estimate
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const estimateMinutes = taskEstimate ? parseEstimateMinutes(taskEstimate) : null;
|
|
78
|
+
const timeoutScale = estimateMinutes && estimateMinutes > 0
|
|
79
|
+
? Math.max(1, estimateMinutes / 10) // 10min task = 1x, 30min = 3x, 2h = 12x
|
|
80
|
+
: 1;
|
|
81
|
+
const softTimeoutMs = (supervisor.soft_timeout_minutes ?? 0) * 60 * 1000 * timeoutScale;
|
|
82
|
+
const idleTimeoutMs = (supervisor.idle_timeout_minutes ?? 0) * 60 * 1000; // idle not scaled — idle is idle
|
|
83
|
+
const hardTimeoutMs = (supervisor.hard_timeout_minutes ?? 0) * 60 * 1000 * timeoutScale;
|
|
30
84
|
// ── 1. Soft timeout warning ──
|
|
31
85
|
s.wrapupWarningHandle = setTimeout(() => {
|
|
32
86
|
s.wrapupWarningHandle = null;
|
|
@@ -63,6 +63,10 @@ export function syncStateToProjectRoot(worktreePath, projectRoot, milestoneId) {
|
|
|
63
63
|
// 2. Milestone directory — ROADMAP, slice PLANs, task summaries
|
|
64
64
|
// Copy the entire milestone .gsd subtree so deriveState reads current checkboxes
|
|
65
65
|
safeCopyRecursive(join(wtGsd, "milestones", milestoneId), join(prGsd, "milestones", milestoneId), { force: true });
|
|
66
|
+
// 3. metrics.json — session cost/token tracking (#2313).
|
|
67
|
+
// Without this, metrics accumulated in the worktree are invisible from the
|
|
68
|
+
// project root and never appear in the dashboard or skill-health reports.
|
|
69
|
+
safeCopy(join(wtGsd, "metrics.json"), join(prGsd, "metrics.json"), { force: true });
|
|
66
70
|
// 4. Runtime records — unit dispatch state used by selfHealRuntimeRecords().
|
|
67
71
|
// Without this, a crash during a unit leaves the runtime record only in the
|
|
68
72
|
// worktree. If the next session resolves basePath before worktree re-entry,
|
|
@@ -109,6 +109,7 @@ export function syncGsdStateToWorktree(mainBasePath, worktreePath_) {
|
|
|
109
109
|
"OVERRIDES.md",
|
|
110
110
|
"QUEUE.md",
|
|
111
111
|
"completed-units.json",
|
|
112
|
+
"metrics.json",
|
|
112
113
|
];
|
|
113
114
|
for (const f of rootFiles) {
|
|
114
115
|
const src = join(mainGsd, f);
|
|
@@ -269,8 +270,9 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
|
269
270
|
// ── 1. Sync root-level .gsd/ files back ──────────────────────────────
|
|
270
271
|
// The worktree is authoritative — complete-milestone updates REQUIREMENTS,
|
|
271
272
|
// PROJECT, etc. These must overwrite main's copies so they survive teardown.
|
|
272
|
-
// Also includes QUEUE.md
|
|
273
|
-
// milestone closeout and lost on teardown without explicit sync
|
|
273
|
+
// Also includes QUEUE.md, completed-units.json, and metrics.json which are
|
|
274
|
+
// written during milestone closeout and lost on teardown without explicit sync
|
|
275
|
+
// (#1787, #2313).
|
|
274
276
|
const rootFiles = [
|
|
275
277
|
"DECISIONS.md",
|
|
276
278
|
"REQUIREMENTS.md",
|
|
@@ -279,6 +281,7 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
|
|
|
279
281
|
"OVERRIDES.md",
|
|
280
282
|
"QUEUE.md",
|
|
281
283
|
"completed-units.json",
|
|
284
|
+
"metrics.json",
|
|
282
285
|
];
|
|
283
286
|
for (const f of rootFiles) {
|
|
284
287
|
const src = join(wtGsd, f);
|
|
@@ -1133,9 +1136,9 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1133
1136
|
// Push failure is non-fatal
|
|
1134
1137
|
}
|
|
1135
1138
|
}
|
|
1136
|
-
// 9b. Auto-create PR if enabled (
|
|
1139
|
+
// 9b. Auto-create PR if enabled (#2302: no longer gated on pushed/auto_push)
|
|
1137
1140
|
let prCreated = false;
|
|
1138
|
-
if (prefs.auto_pr === true &&
|
|
1141
|
+
if (prefs.auto_pr === true && !nothingToCommit) {
|
|
1139
1142
|
const remote = prefs.remote ?? "origin";
|
|
1140
1143
|
const prTarget = prefs.pr_target_branch ?? mainBranch;
|
|
1141
1144
|
try {
|
|
@@ -1145,9 +1148,9 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1145
1148
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1146
1149
|
encoding: "utf-8",
|
|
1147
1150
|
});
|
|
1148
|
-
// Create PR via gh CLI
|
|
1151
|
+
// Create PR via gh CLI with explicit --head and --base (#2302)
|
|
1149
1152
|
execFileSync("gh", [
|
|
1150
|
-
"pr", "create",
|
|
1153
|
+
"pr", "create", "--draft",
|
|
1151
1154
|
"--base", prTarget,
|
|
1152
1155
|
"--head", milestoneBranch,
|
|
1153
1156
|
"--title", `Milestone ${milestoneId} complete`,
|
|
@@ -369,14 +369,41 @@ export async function stopAuto(ctx, pi, reason) {
|
|
|
369
369
|
debugLog("stop-cleanup-sigterm", { error: e instanceof Error ? e.message : String(e) });
|
|
370
370
|
}
|
|
371
371
|
// ── Step 4: Auto-worktree exit ──
|
|
372
|
+
// When the milestone is complete (has a SUMMARY), merge the worktree branch
|
|
373
|
+
// back to main so code isn't stranded on the worktree branch (#2317).
|
|
374
|
+
// For incomplete milestones, preserve the branch for later resumption.
|
|
372
375
|
try {
|
|
373
376
|
if (s.currentMilestoneId) {
|
|
374
377
|
const notifyCtx = ctx
|
|
375
378
|
? { notify: ctx.ui.notify.bind(ctx.ui) }
|
|
376
379
|
: { notify: () => { } };
|
|
377
|
-
buildResolver()
|
|
378
|
-
|
|
379
|
-
|
|
380
|
+
const resolver = buildResolver();
|
|
381
|
+
// Check if the milestone is complete — SUMMARY file is the authoritative signal.
|
|
382
|
+
let milestoneComplete = false;
|
|
383
|
+
try {
|
|
384
|
+
const summaryPath = resolveMilestoneFile(s.originalBasePath || s.basePath, s.currentMilestoneId, "SUMMARY");
|
|
385
|
+
if (!summaryPath) {
|
|
386
|
+
// Also check in the worktree path (SUMMARY may not be synced yet)
|
|
387
|
+
const wtSummaryPath = resolveMilestoneFile(s.basePath, s.currentMilestoneId, "SUMMARY");
|
|
388
|
+
milestoneComplete = wtSummaryPath !== null;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
milestoneComplete = true;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
// Non-fatal — fall through to preserveBranch path
|
|
396
|
+
}
|
|
397
|
+
if (milestoneComplete) {
|
|
398
|
+
// Milestone is complete — merge worktree branch back to main
|
|
399
|
+
resolver.mergeAndExit(s.currentMilestoneId, notifyCtx);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
// Milestone still in progress — preserve branch for later resumption
|
|
403
|
+
resolver.exitMilestone(s.currentMilestoneId, notifyCtx, {
|
|
404
|
+
preserveBranch: true,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
380
407
|
}
|
|
381
408
|
}
|
|
382
409
|
catch (e) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { Text } from "@gsd/pi-tui";
|
|
2
3
|
import { findMilestoneIds, nextMilestoneId, claimReservedId, getReservedMilestoneIds } from "../guided-flow.js";
|
|
3
4
|
import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|
4
5
|
import { ensureDbOpen } from "./dynamic-tools.js";
|
|
6
|
+
import { StringEnum } from "@gsd/pi-ai";
|
|
5
7
|
/**
|
|
6
8
|
* Register an alias tool that shares the same execute function as its canonical counterpart.
|
|
7
9
|
* The alias description and promptGuidelines direct the LLM to prefer the canonical name.
|
|
@@ -77,6 +79,26 @@ export function registerDbTools(pi) {
|
|
|
77
79
|
], { description: "Who made this decision: 'human' (user directed), 'agent' (LLM decided autonomously), or 'collaborative' (discussed and agreed). Default: 'agent'" })),
|
|
78
80
|
}),
|
|
79
81
|
execute: decisionSaveExecute,
|
|
82
|
+
renderCall(args, theme) {
|
|
83
|
+
let text = theme.fg("toolTitle", theme.bold("decision_save "));
|
|
84
|
+
if (args.scope)
|
|
85
|
+
text += theme.fg("accent", `[${args.scope}] `);
|
|
86
|
+
if (args.decision)
|
|
87
|
+
text += theme.fg("muted", args.decision);
|
|
88
|
+
if (args.choice)
|
|
89
|
+
text += theme.fg("dim", ` — ${args.choice}`);
|
|
90
|
+
return new Text(text, 0, 0);
|
|
91
|
+
},
|
|
92
|
+
renderResult(result, _options, theme) {
|
|
93
|
+
const d = result.details;
|
|
94
|
+
if (result.isError || d?.error) {
|
|
95
|
+
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
96
|
+
}
|
|
97
|
+
let text = theme.fg("success", `Decision ${d?.id ?? ""} saved`);
|
|
98
|
+
if (d?.id)
|
|
99
|
+
text += theme.fg("dim", ` → DECISIONS.md`);
|
|
100
|
+
return new Text(text, 0, 0);
|
|
101
|
+
},
|
|
80
102
|
};
|
|
81
103
|
pi.registerTool(decisionSaveTool);
|
|
82
104
|
registerAlias(pi, decisionSaveTool, "gsd_save_decision", "gsd_decision_save");
|
|
@@ -149,6 +171,24 @@ export function registerDbTools(pi) {
|
|
|
149
171
|
supporting_slices: Type.Optional(Type.String({ description: "Supporting slices" })),
|
|
150
172
|
}),
|
|
151
173
|
execute: requirementUpdateExecute,
|
|
174
|
+
renderCall(args, theme) {
|
|
175
|
+
let text = theme.fg("toolTitle", theme.bold("requirement_update "));
|
|
176
|
+
if (args.id)
|
|
177
|
+
text += theme.fg("accent", args.id);
|
|
178
|
+
const fields = ["status", "validation", "notes", "description"].filter((f) => args[f]);
|
|
179
|
+
if (fields.length > 0)
|
|
180
|
+
text += theme.fg("dim", ` (${fields.join(", ")})`);
|
|
181
|
+
return new Text(text, 0, 0);
|
|
182
|
+
},
|
|
183
|
+
renderResult(result, _options, theme) {
|
|
184
|
+
const d = result.details;
|
|
185
|
+
if (result.isError || d?.error) {
|
|
186
|
+
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
187
|
+
}
|
|
188
|
+
let text = theme.fg("success", `Requirement ${d?.id ?? ""} updated`);
|
|
189
|
+
text += theme.fg("dim", ` → REQUIREMENTS.md`);
|
|
190
|
+
return new Text(text, 0, 0);
|
|
191
|
+
},
|
|
152
192
|
};
|
|
153
193
|
pi.registerTool(requirementUpdateTool);
|
|
154
194
|
registerAlias(pi, requirementUpdateTool, "gsd_update_requirement", "gsd_requirement_update");
|
|
@@ -222,6 +262,25 @@ export function registerDbTools(pi) {
|
|
|
222
262
|
content: Type.String({ description: "The full markdown content of the artifact" }),
|
|
223
263
|
}),
|
|
224
264
|
execute: summarySaveExecute,
|
|
265
|
+
renderCall(args, theme) {
|
|
266
|
+
let text = theme.fg("toolTitle", theme.bold("summary_save "));
|
|
267
|
+
if (args.artifact_type)
|
|
268
|
+
text += theme.fg("accent", args.artifact_type);
|
|
269
|
+
const path = [args.milestone_id, args.slice_id, args.task_id].filter(Boolean).join("/");
|
|
270
|
+
if (path)
|
|
271
|
+
text += theme.fg("dim", ` ${path}`);
|
|
272
|
+
return new Text(text, 0, 0);
|
|
273
|
+
},
|
|
274
|
+
renderResult(result, _options, theme) {
|
|
275
|
+
const d = result.details;
|
|
276
|
+
if (result.isError || d?.error) {
|
|
277
|
+
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
278
|
+
}
|
|
279
|
+
let text = theme.fg("success", `${d?.artifact_type ?? "Artifact"} saved`);
|
|
280
|
+
if (d?.path)
|
|
281
|
+
text += theme.fg("dim", ` → ${d.path}`);
|
|
282
|
+
return new Text(text, 0, 0);
|
|
283
|
+
},
|
|
225
284
|
};
|
|
226
285
|
pi.registerTool(summarySaveTool);
|
|
227
286
|
registerAlias(pi, summarySaveTool, "gsd_save_summary", "gsd_summary_save");
|
|
@@ -232,6 +291,7 @@ export function registerDbTools(pi) {
|
|
|
232
291
|
// This guarantees the ID shown in the UI matches the one materialised on disk.
|
|
233
292
|
const reserved = claimReservedId();
|
|
234
293
|
if (reserved) {
|
|
294
|
+
await ensureMilestoneDbRow(reserved);
|
|
235
295
|
return {
|
|
236
296
|
content: [{ type: "text", text: reserved }],
|
|
237
297
|
details: { operation: "generate_milestone_id", id: reserved, source: "reserved" },
|
|
@@ -242,6 +302,7 @@ export function registerDbTools(pi) {
|
|
|
242
302
|
const uniqueEnabled = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
243
303
|
const allIds = [...new Set([...existingIds, ...getReservedMilestoneIds()])];
|
|
244
304
|
const newId = nextMilestoneId(allIds, uniqueEnabled);
|
|
305
|
+
await ensureMilestoneDbRow(newId);
|
|
245
306
|
return {
|
|
246
307
|
content: [{ type: "text", text: newId }],
|
|
247
308
|
details: { operation: "generate_milestone_id", id: newId, existingCount: existingIds.length, uniqueEnabled },
|
|
@@ -255,6 +316,24 @@ export function registerDbTools(pi) {
|
|
|
255
316
|
};
|
|
256
317
|
}
|
|
257
318
|
};
|
|
319
|
+
/**
|
|
320
|
+
* Insert a minimal DB row for a milestone ID so it's visible to the state
|
|
321
|
+
* machine. Uses INSERT OR IGNORE — safe to call even if gsd_plan_milestone
|
|
322
|
+
* later writes the full row. Silently skips if the DB isn't available yet
|
|
323
|
+
* (pre-migration).
|
|
324
|
+
*/
|
|
325
|
+
async function ensureMilestoneDbRow(milestoneId) {
|
|
326
|
+
const dbAvailable = await ensureDbOpen();
|
|
327
|
+
if (!dbAvailable)
|
|
328
|
+
return;
|
|
329
|
+
try {
|
|
330
|
+
const { insertMilestone } = await import("../gsd-db.js");
|
|
331
|
+
insertMilestone({ id: milestoneId, status: "queued" });
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// Non-fatal — the safety-net in deriveStateFromDb will catch this
|
|
335
|
+
}
|
|
336
|
+
}
|
|
258
337
|
const milestoneGenerateIdTool = {
|
|
259
338
|
name: "gsd_milestone_generate_id",
|
|
260
339
|
label: "Generate Milestone ID",
|
|
@@ -270,6 +349,19 @@ export function registerDbTools(pi) {
|
|
|
270
349
|
],
|
|
271
350
|
parameters: Type.Object({}),
|
|
272
351
|
execute: milestoneGenerateIdExecute,
|
|
352
|
+
renderCall(_args, theme) {
|
|
353
|
+
return new Text(theme.fg("toolTitle", theme.bold("milestone_generate_id")), 0, 0);
|
|
354
|
+
},
|
|
355
|
+
renderResult(result, _options, theme) {
|
|
356
|
+
const d = result.details;
|
|
357
|
+
if (result.isError || d?.error) {
|
|
358
|
+
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
359
|
+
}
|
|
360
|
+
let text = theme.fg("success", `Generated ${d?.id ?? "ID"}`);
|
|
361
|
+
if (d?.source === "reserved")
|
|
362
|
+
text += theme.fg("dim", " (reserved)");
|
|
363
|
+
return new Text(text, 0, 0);
|
|
364
|
+
},
|
|
273
365
|
};
|
|
274
366
|
pi.registerTool(milestoneGenerateIdTool);
|
|
275
367
|
registerAlias(pi, milestoneGenerateIdTool, "gsd_generate_milestone_id", "gsd_milestone_generate_id");
|
|
@@ -732,6 +824,70 @@ export function registerDbTools(pi) {
|
|
|
732
824
|
};
|
|
733
825
|
pi.registerTool(milestoneCompleteTool);
|
|
734
826
|
registerAlias(pi, milestoneCompleteTool, "gsd_milestone_complete", "gsd_complete_milestone");
|
|
827
|
+
// ─── gsd_validate_milestone (gsd_milestone_validate alias) ─────────────
|
|
828
|
+
const milestoneValidateExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
829
|
+
const dbAvailable = await ensureDbOpen();
|
|
830
|
+
if (!dbAvailable) {
|
|
831
|
+
return {
|
|
832
|
+
content: [{ type: "text", text: "Error: GSD database is not available. Cannot validate milestone." }],
|
|
833
|
+
details: { operation: "validate_milestone", error: "db_unavailable" },
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
try {
|
|
837
|
+
const { handleValidateMilestone } = await import("../tools/validate-milestone.js");
|
|
838
|
+
const result = await handleValidateMilestone(params, process.cwd());
|
|
839
|
+
if ("error" in result) {
|
|
840
|
+
return {
|
|
841
|
+
content: [{ type: "text", text: `Error validating milestone: ${result.error}` }],
|
|
842
|
+
details: { operation: "validate_milestone", error: result.error },
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
return {
|
|
846
|
+
content: [{ type: "text", text: `Validated milestone ${result.milestoneId} — verdict: ${result.verdict}. Written to ${result.validationPath}` }],
|
|
847
|
+
details: {
|
|
848
|
+
operation: "validate_milestone",
|
|
849
|
+
milestoneId: result.milestoneId,
|
|
850
|
+
verdict: result.verdict,
|
|
851
|
+
validationPath: result.validationPath,
|
|
852
|
+
},
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
catch (err) {
|
|
856
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
857
|
+
process.stderr.write(`gsd-db: validate_milestone tool failed: ${msg}\n`);
|
|
858
|
+
return {
|
|
859
|
+
content: [{ type: "text", text: `Error validating milestone: ${msg}` }],
|
|
860
|
+
details: { operation: "validate_milestone", error: msg },
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
const milestoneValidateTool = {
|
|
865
|
+
name: "gsd_validate_milestone",
|
|
866
|
+
label: "Validate Milestone",
|
|
867
|
+
description: "Validate a milestone before completion — persist validation results to the DB, render VALIDATION.md to disk. " +
|
|
868
|
+
"Records verdict (pass/needs-attention/needs-remediation) and rationale.",
|
|
869
|
+
promptSnippet: "Validate a GSD milestone (DB write + VALIDATION.md render)",
|
|
870
|
+
promptGuidelines: [
|
|
871
|
+
"Use gsd_validate_milestone when all slices are done and the milestone needs validation before completion.",
|
|
872
|
+
"Parameters: milestoneId, verdict, remediationRound, successCriteriaChecklist, sliceDeliveryAudit, crossSliceIntegration, requirementCoverage, verdictRationale, remediationPlan (optional).",
|
|
873
|
+
"If verdict is 'needs-remediation', also provide remediationPlan and use gsd_reassess_roadmap to add remediation slices to the roadmap.",
|
|
874
|
+
"On success, returns validationPath where VALIDATION.md was written.",
|
|
875
|
+
],
|
|
876
|
+
parameters: Type.Object({
|
|
877
|
+
milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
|
|
878
|
+
verdict: StringEnum(["pass", "needs-attention", "needs-remediation"], { description: "Validation verdict" }),
|
|
879
|
+
remediationRound: Type.Number({ description: "Remediation round (0 for first validation)" }),
|
|
880
|
+
successCriteriaChecklist: Type.String({ description: "Markdown checklist of success criteria with pass/fail and evidence" }),
|
|
881
|
+
sliceDeliveryAudit: Type.String({ description: "Markdown table auditing each slice's claimed vs delivered output" }),
|
|
882
|
+
crossSliceIntegration: Type.String({ description: "Markdown describing any cross-slice boundary mismatches" }),
|
|
883
|
+
requirementCoverage: Type.String({ description: "Markdown describing any unaddressed requirements" }),
|
|
884
|
+
verdictRationale: Type.String({ description: "Why this verdict was chosen" }),
|
|
885
|
+
remediationPlan: Type.Optional(Type.String({ description: "Remediation plan (required if verdict is needs-remediation)" })),
|
|
886
|
+
}),
|
|
887
|
+
execute: milestoneValidateExecute,
|
|
888
|
+
};
|
|
889
|
+
pi.registerTool(milestoneValidateTool);
|
|
890
|
+
registerAlias(pi, milestoneValidateTool, "gsd_milestone_validate", "gsd_validate_milestone");
|
|
735
891
|
// ─── gsd_replan_slice (gsd_slice_replan alias) ─────────────────────────
|
|
736
892
|
const replanSliceExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
737
893
|
const dbAvailable = await ensureDbOpen();
|