gsd-pi 2.49.0-dev.de3d9f6 → 2.50.0-dev.9476db8
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/headless-ui.js +12 -2
- package/dist/headless.js +29 -13
- package/dist/resources/extensions/gsd/auto/infra-errors.js +1 -0
- package/dist/resources/extensions/gsd/auto/phases.js +11 -11
- package/dist/resources/extensions/gsd/auto/resolve.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +2 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +8 -10
- package/dist/resources/extensions/gsd/auto-dashboard.js +6 -3
- package/dist/resources/extensions/gsd/auto-dispatch.js +33 -21
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -24
- package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +62 -184
- package/dist/resources/extensions/gsd/auto-start.js +4 -31
- package/dist/resources/extensions/gsd/auto-timers.js +2 -2
- package/dist/resources/extensions/gsd/auto-verification.js +4 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +257 -113
- package/dist/resources/extensions/gsd/auto.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +89 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -1
- package/dist/resources/extensions/gsd/branch-patterns.js +13 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +5 -1234
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +168 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +28 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +405 -0
- package/dist/resources/extensions/gsd/doctor-global-checks.js +74 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +600 -0
- package/dist/resources/extensions/gsd/doctor.js +9 -1
- package/dist/resources/extensions/gsd/extension-manifest.json +1 -1
- package/dist/resources/extensions/gsd/git-service.js +9 -10
- package/dist/resources/extensions/gsd/gsd-db.js +124 -1
- package/dist/resources/extensions/gsd/guided-flow-queue.js +10 -11
- package/dist/resources/extensions/gsd/markdown-renderer.js +33 -5
- package/dist/resources/extensions/gsd/preferences-types.js +2 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/dist/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +29 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +2 -2
- package/dist/resources/extensions/gsd/session-forensics.js +6 -11
- package/dist/resources/extensions/gsd/session-lock.js +67 -56
- package/dist/resources/extensions/gsd/state.js +34 -7
- package/dist/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/dist/resources/extensions/gsd/templates/plan.md +16 -0
- package/dist/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/dist/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -3
- package/dist/resources/extensions/gsd/verdict-parser.js +84 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +24 -0
- package/dist/resources/extensions/gsd/worktree.js +3 -2
- package/dist/resources/extensions/remote-questions/config.js +3 -5
- package/dist/resources/extensions/search-the-web/native-search.js +8 -3
- package/dist/resources/extensions/search-the-web/tool-search.js +19 -2
- package/dist/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- 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 +1 -1
- package/dist/web/standalone/.next/required-server-files.json +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.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 +15 -15
- package/dist/web/standalone/.next/server/chunks/229.js +2 -2
- 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.7c75ac378de0f2b5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-0a4cd455ec4197d2.js → webpack-2473ce2c3879fff4.js} +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +39 -10
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +39 -8
- package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/blob-store.js +8 -3
- package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +9 -2
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.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 -32
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +0 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/blob-store.ts +6 -3
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +9 -2
- package/packages/pi-coding-agent/src/core/retry-handler.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +7 -32
- package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +6 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/infra-errors.ts +1 -0
- package/src/resources/extensions/gsd/auto/phases.ts +10 -11
- package/src/resources/extensions/gsd/auto/resolve.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +2 -2
- package/src/resources/extensions/gsd/auto/session.ts +5 -0
- package/src/resources/extensions/gsd/auto/types.ts +13 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +19 -21
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +39 -21
- package/src/resources/extensions/gsd/auto-loop.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -28
- package/src/resources/extensions/gsd/auto-prompts.ts +113 -19
- package/src/resources/extensions/gsd/auto-recovery.ts +65 -199
- package/src/resources/extensions/gsd/auto-start.ts +7 -27
- package/src/resources/extensions/gsd/auto-timers.ts +2 -2
- package/src/resources/extensions/gsd/auto-verification.ts +4 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +305 -108
- package/src/resources/extensions/gsd/auto.ts +11 -10
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +93 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/branch-patterns.ts +16 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +5 -1291
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +182 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +30 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +415 -0
- package/src/resources/extensions/gsd/doctor-global-checks.ts +84 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +626 -0
- package/src/resources/extensions/gsd/doctor.ts +9 -1
- package/src/resources/extensions/gsd/extension-manifest.json +1 -1
- package/src/resources/extensions/gsd/git-service.ts +7 -15
- package/src/resources/extensions/gsd/gsd-db.ts +150 -2
- package/src/resources/extensions/gsd/guided-flow-queue.ts +11 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +37 -4
- package/src/resources/extensions/gsd/preferences-types.ts +5 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +37 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/src/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +28 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +2 -2
- package/src/resources/extensions/gsd/session-forensics.ts +6 -11
- package/src/resources/extensions/gsd/session-lock.ts +92 -64
- package/src/resources/extensions/gsd/state.ts +38 -5
- package/src/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/src/resources/extensions/gsd/templates/plan.md +16 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +1 -81
- 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/completed-units-metrics-sync.test.ts +9 -12
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +115 -1
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +65 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +39 -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/quality-gates.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/queue-completed-milestone-perf.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +20 -16
- package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +44 -4
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +0 -16
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +204 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +16 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -3
- package/src/resources/extensions/gsd/types.ts +30 -0
- package/src/resources/extensions/gsd/verdict-parser.ts +95 -0
- package/src/resources/extensions/gsd/verification-gate.ts +0 -2
- package/src/resources/extensions/gsd/worktree-resolver.ts +31 -0
- package/src/resources/extensions/gsd/worktree.ts +3 -2
- package/src/resources/extensions/remote-questions/config.ts +3 -5
- package/src/resources/extensions/search-the-web/native-search.ts +8 -3
- package/src/resources/extensions/search-the-web/tool-search.ts +22 -2
- package/src/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -191
- package/dist/resources/extensions/gsd/resource-version.js +0 -97
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +0 -9
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -234
- package/src/resources/extensions/gsd/resource-version.ts +0 -101
- /package/dist/web/standalone/.next/static/{ceckLbAMjhzHaQ3RPtJnT → MkE9kzqUGny3-cSE0GNnm}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ceckLbAMjhzHaQ3RPtJnT → MkE9kzqUGny3-cSE0GNnm}/_ssgManifest.js +0 -0
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
unlinkSync,
|
|
18
18
|
lstatSync as lstatSyncFn,
|
|
19
19
|
} from "node:fs";
|
|
20
|
-
import { isAbsolute, join } from "node:path";
|
|
20
|
+
import { isAbsolute, join, sep as pathSep } from "node:path";
|
|
21
|
+
import { homedir } from "node:os";
|
|
21
22
|
import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
|
|
22
23
|
import {
|
|
23
24
|
reconcileWorktreeDb,
|
|
@@ -63,6 +64,38 @@ import {
|
|
|
63
64
|
nativeIsAncestor,
|
|
64
65
|
} from "./native-git-bridge.js";
|
|
65
66
|
|
|
67
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
68
|
+
|
|
69
|
+
// ─── Shared Constants & Helpers ─────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Root-level .gsd/ state files synced between worktree and project root.
|
|
73
|
+
* Single source of truth — used by syncGsdStateToWorktree, syncWorktreeStateBack,
|
|
74
|
+
* and the dispatch-level sync functions.
|
|
75
|
+
*/
|
|
76
|
+
const ROOT_STATE_FILES = [
|
|
77
|
+
"DECISIONS.md",
|
|
78
|
+
"REQUIREMENTS.md",
|
|
79
|
+
"PROJECT.md",
|
|
80
|
+
"KNOWLEDGE.md",
|
|
81
|
+
"OVERRIDES.md",
|
|
82
|
+
"QUEUE.md",
|
|
83
|
+
"completed-units.json",
|
|
84
|
+
"metrics.json",
|
|
85
|
+
] as const;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if two filesystem paths resolve to the same real location.
|
|
89
|
+
* Returns false if either path cannot be resolved (e.g. doesn't exist).
|
|
90
|
+
*/
|
|
91
|
+
function isSamePath(a: string, b: string): boolean {
|
|
92
|
+
try {
|
|
93
|
+
return realpathSync(a) === realpathSync(b);
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
66
99
|
// ─── Module State ──────────────────────────────────────────────────────────
|
|
67
100
|
|
|
68
101
|
/** Original project root before chdir into auto-worktree. */
|
|
@@ -119,6 +152,227 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
|
|
|
119
152
|
}
|
|
120
153
|
}
|
|
121
154
|
}
|
|
155
|
+
|
|
156
|
+
// ─── Dispatch-Level Sync (project root ↔ worktree) ──────────────────────────
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sync milestone artifacts from project root INTO worktree before deriveState.
|
|
160
|
+
* Covers the case where the LLM wrote artifacts to the main repo filesystem
|
|
161
|
+
* (e.g. via absolute paths) but the worktree has stale data. Also deletes
|
|
162
|
+
* gsd.db in the worktree so it rebuilds from fresh disk state (#853).
|
|
163
|
+
* Non-fatal — sync failure should never block dispatch.
|
|
164
|
+
*/
|
|
165
|
+
export function syncProjectRootToWorktree(
|
|
166
|
+
projectRoot: string,
|
|
167
|
+
worktreePath_: string,
|
|
168
|
+
milestoneId: string | null,
|
|
169
|
+
): void {
|
|
170
|
+
if (!worktreePath_ || !projectRoot || worktreePath_ === projectRoot) return;
|
|
171
|
+
if (!milestoneId) return;
|
|
172
|
+
|
|
173
|
+
const prGsd = join(projectRoot, ".gsd");
|
|
174
|
+
const wtGsd = join(worktreePath_, ".gsd");
|
|
175
|
+
|
|
176
|
+
// Copy milestone directory from project root to worktree — additive only.
|
|
177
|
+
// force:false prevents cpSync from overwriting existing worktree files.
|
|
178
|
+
// Without this, worktree-authoritative files (e.g. VALIDATION.md written
|
|
179
|
+
// by validate-milestone) get clobbered by stale project root copies,
|
|
180
|
+
// causing an infinite re-validation loop (#1886).
|
|
181
|
+
safeCopyRecursive(
|
|
182
|
+
join(prGsd, "milestones", milestoneId),
|
|
183
|
+
join(wtGsd, "milestones", milestoneId),
|
|
184
|
+
{ force: false },
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// Forward-sync completed-units.json from project root to worktree.
|
|
188
|
+
// Project root is authoritative for completion state after crash recovery;
|
|
189
|
+
// without this, the worktree re-dispatches already-completed units (#1886).
|
|
190
|
+
safeCopy(
|
|
191
|
+
join(prGsd, "completed-units.json"),
|
|
192
|
+
join(wtGsd, "completed-units.json"),
|
|
193
|
+
{ force: true },
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Delete worktree gsd.db so it rebuilds from the freshly synced files.
|
|
197
|
+
// Stale DB rows are the root cause of the infinite skip loop (#853).
|
|
198
|
+
try {
|
|
199
|
+
const wtDb = join(wtGsd, "gsd.db");
|
|
200
|
+
if (existsSync(wtDb)) {
|
|
201
|
+
unlinkSync(wtDb);
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
/* non-fatal */
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Sync dispatch-critical .gsd/ state files from worktree to project root.
|
|
210
|
+
* Only runs when inside an auto-worktree (worktreePath differs from projectRoot).
|
|
211
|
+
* Copies: STATE.md + active milestone directory (roadmap, slice plans, task summaries).
|
|
212
|
+
* Non-fatal — sync failure should never block dispatch.
|
|
213
|
+
*/
|
|
214
|
+
export function syncStateToProjectRoot(
|
|
215
|
+
worktreePath_: string,
|
|
216
|
+
projectRoot: string,
|
|
217
|
+
milestoneId: string | null,
|
|
218
|
+
): void {
|
|
219
|
+
if (!worktreePath_ || !projectRoot || worktreePath_ === projectRoot) return;
|
|
220
|
+
if (!milestoneId) return;
|
|
221
|
+
|
|
222
|
+
const wtGsd = join(worktreePath_, ".gsd");
|
|
223
|
+
const prGsd = join(projectRoot, ".gsd");
|
|
224
|
+
|
|
225
|
+
// 1. STATE.md — the quick-glance status used by initial deriveState()
|
|
226
|
+
safeCopy(join(wtGsd, "STATE.md"), join(prGsd, "STATE.md"), { force: true });
|
|
227
|
+
|
|
228
|
+
// 2. Milestone directory — ROADMAP, slice PLANs, task summaries
|
|
229
|
+
// Copy the entire milestone .gsd subtree so deriveState reads current checkboxes
|
|
230
|
+
safeCopyRecursive(
|
|
231
|
+
join(wtGsd, "milestones", milestoneId),
|
|
232
|
+
join(prGsd, "milestones", milestoneId),
|
|
233
|
+
{ force: true },
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// 3. metrics.json — session cost/token tracking (#2313).
|
|
237
|
+
// Without this, metrics accumulated in the worktree are invisible from the
|
|
238
|
+
// project root and never appear in the dashboard or skill-health reports.
|
|
239
|
+
safeCopy(join(wtGsd, "metrics.json"), join(prGsd, "metrics.json"), { force: true });
|
|
240
|
+
|
|
241
|
+
// 4. Runtime records — unit dispatch state used by selfHealRuntimeRecords().
|
|
242
|
+
// Without this, a crash during a unit leaves the runtime record only in the
|
|
243
|
+
// worktree. If the next session resolves basePath before worktree re-entry,
|
|
244
|
+
// selfHeal can't find or clear the stale record (#769).
|
|
245
|
+
safeCopyRecursive(
|
|
246
|
+
join(wtGsd, "runtime", "units"),
|
|
247
|
+
join(prGsd, "runtime", "units"),
|
|
248
|
+
{ force: true },
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ─── Resource Staleness ───────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Read the resource version (semver) from the managed-resources manifest.
|
|
256
|
+
* Uses gsdVersion instead of syncedAt so that launching a second session
|
|
257
|
+
* doesn't falsely trigger staleness (#804).
|
|
258
|
+
*/
|
|
259
|
+
export function readResourceVersion(): string | null {
|
|
260
|
+
const agentDir =
|
|
261
|
+
process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
|
|
262
|
+
const manifestPath = join(agentDir, "managed-resources.json");
|
|
263
|
+
try {
|
|
264
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
265
|
+
return typeof manifest?.gsdVersion === "string"
|
|
266
|
+
? manifest.gsdVersion
|
|
267
|
+
: null;
|
|
268
|
+
} catch {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Check if managed resources have been updated since session start.
|
|
275
|
+
* Returns a warning message if stale, null otherwise.
|
|
276
|
+
*/
|
|
277
|
+
export function checkResourcesStale(
|
|
278
|
+
versionOnStart: string | null,
|
|
279
|
+
): string | null {
|
|
280
|
+
if (versionOnStart === null) return null;
|
|
281
|
+
const current = readResourceVersion();
|
|
282
|
+
if (current === null) return null;
|
|
283
|
+
if (current !== versionOnStart) {
|
|
284
|
+
return "GSD resources were updated since this session started. Restart gsd to load the new code.";
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ─── Stale Worktree Escape ────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Detect and escape a stale worktree cwd (#608).
|
|
293
|
+
*
|
|
294
|
+
* After milestone completion + merge, the worktree directory is removed but
|
|
295
|
+
* the process cwd may still point inside `.gsd/worktrees/<MID>/`.
|
|
296
|
+
* When a new session starts, `process.cwd()` is passed as `base` to startAuto
|
|
297
|
+
* and all subsequent writes land in the wrong directory. This function detects
|
|
298
|
+
* that scenario and chdir back to the project root.
|
|
299
|
+
*
|
|
300
|
+
* Returns the corrected base path.
|
|
301
|
+
*/
|
|
302
|
+
export function escapeStaleWorktree(base: string): string {
|
|
303
|
+
// Direct layout: /.gsd/worktrees/
|
|
304
|
+
const directMarker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
|
|
305
|
+
let idx = base.indexOf(directMarker);
|
|
306
|
+
if (idx === -1) {
|
|
307
|
+
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
|
|
308
|
+
const symlinkRe = new RegExp(
|
|
309
|
+
`\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees\\${pathSep}`,
|
|
310
|
+
);
|
|
311
|
+
const match = base.match(symlinkRe);
|
|
312
|
+
if (!match || match.index === undefined) return base;
|
|
313
|
+
idx = match.index;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// base is inside .gsd/worktrees/<something> — extract the project root
|
|
317
|
+
const projectRoot = base.slice(0, idx);
|
|
318
|
+
|
|
319
|
+
// Guard: If the candidate project root's .gsd IS the user-level ~/.gsd,
|
|
320
|
+
// the string-slice heuristic matched the wrong /.gsd/ boundary. This happens
|
|
321
|
+
// when .gsd is a symlink into ~/.gsd/projects/<hash> and process.cwd()
|
|
322
|
+
// resolved through the symlink. Returning ~ would be catastrophic (#1676).
|
|
323
|
+
const candidateGsd = join(projectRoot, ".gsd").replaceAll("\\", "/");
|
|
324
|
+
const gsdHomePath = gsdHome.replaceAll("\\", "/");
|
|
325
|
+
if (candidateGsd === gsdHomePath || candidateGsd.startsWith(gsdHomePath + "/")) {
|
|
326
|
+
// Don't chdir to home — return base unchanged.
|
|
327
|
+
// resolveProjectRoot() in worktree.ts has the full git-file-based recovery
|
|
328
|
+
// and will be called by the caller (startAuto → projectRoot()).
|
|
329
|
+
return base;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
process.chdir(projectRoot);
|
|
334
|
+
} catch {
|
|
335
|
+
// If chdir fails, return the original — caller will handle errors downstream
|
|
336
|
+
return base;
|
|
337
|
+
}
|
|
338
|
+
return projectRoot;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Clean stale runtime unit files for completed milestones.
|
|
343
|
+
*
|
|
344
|
+
* After restart, stale runtime/units/*.json from prior milestones can
|
|
345
|
+
* cause deriveState to resume the wrong milestone (#887). Removes files
|
|
346
|
+
* for milestones that have a SUMMARY (fully complete).
|
|
347
|
+
*/
|
|
348
|
+
export function cleanStaleRuntimeUnits(
|
|
349
|
+
gsdRootPath: string,
|
|
350
|
+
hasMilestoneSummary: (mid: string) => boolean,
|
|
351
|
+
): number {
|
|
352
|
+
const runtimeUnitsDir = join(gsdRootPath, "runtime", "units");
|
|
353
|
+
if (!existsSync(runtimeUnitsDir)) return 0;
|
|
354
|
+
|
|
355
|
+
let cleaned = 0;
|
|
356
|
+
try {
|
|
357
|
+
for (const file of readdirSync(runtimeUnitsDir)) {
|
|
358
|
+
if (!file.endsWith(".json")) continue;
|
|
359
|
+
const midMatch = file.match(/(M\d+(?:-[a-z0-9]{6})?)/);
|
|
360
|
+
if (!midMatch) continue;
|
|
361
|
+
if (hasMilestoneSummary(midMatch[1])) {
|
|
362
|
+
try {
|
|
363
|
+
unlinkSync(join(runtimeUnitsDir, file));
|
|
364
|
+
cleaned++;
|
|
365
|
+
} catch {
|
|
366
|
+
/* non-fatal */
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
} catch {
|
|
371
|
+
/* non-fatal */
|
|
372
|
+
}
|
|
373
|
+
return cleaned;
|
|
374
|
+
}
|
|
375
|
+
|
|
122
376
|
// ─── Worktree ↔ Main Repo Sync (#1311) ──────────────────────────────────────
|
|
123
377
|
|
|
124
378
|
/**
|
|
@@ -144,28 +398,12 @@ export function syncGsdStateToWorktree(
|
|
|
144
398
|
const synced: string[] = [];
|
|
145
399
|
|
|
146
400
|
// If both resolve to the same directory (symlink), no sync needed
|
|
147
|
-
|
|
148
|
-
const mainResolved = realpathSync(mainGsd);
|
|
149
|
-
const wtResolved = realpathSync(wtGsd);
|
|
150
|
-
if (mainResolved === wtResolved) return { synced };
|
|
151
|
-
} catch {
|
|
152
|
-
// Can't resolve — proceed with sync as a safety measure
|
|
153
|
-
}
|
|
401
|
+
if (isSamePath(mainGsd, wtGsd)) return { synced };
|
|
154
402
|
|
|
155
403
|
if (!existsSync(mainGsd) || !existsSync(wtGsd)) return { synced };
|
|
156
404
|
|
|
157
405
|
// Sync root-level .gsd/ files (DECISIONS, REQUIREMENTS, PROJECT, KNOWLEDGE, etc.)
|
|
158
|
-
const
|
|
159
|
-
"DECISIONS.md",
|
|
160
|
-
"REQUIREMENTS.md",
|
|
161
|
-
"PROJECT.md",
|
|
162
|
-
"KNOWLEDGE.md",
|
|
163
|
-
"OVERRIDES.md",
|
|
164
|
-
"QUEUE.md",
|
|
165
|
-
"completed-units.json",
|
|
166
|
-
"metrics.json",
|
|
167
|
-
];
|
|
168
|
-
for (const f of rootFiles) {
|
|
406
|
+
for (const f of ROOT_STATE_FILES) {
|
|
169
407
|
const src = join(mainGsd, f);
|
|
170
408
|
const dst = join(wtGsd, f);
|
|
171
409
|
if (existsSync(src) && !existsSync(dst)) {
|
|
@@ -298,13 +536,7 @@ export function syncWorktreeStateBack(
|
|
|
298
536
|
const synced: string[] = [];
|
|
299
537
|
|
|
300
538
|
// If both resolve to the same directory (symlink), no sync needed
|
|
301
|
-
|
|
302
|
-
const mainResolved = realpathSync(mainGsd);
|
|
303
|
-
const wtResolved = realpathSync(wtGsd);
|
|
304
|
-
if (mainResolved === wtResolved) return { synced };
|
|
305
|
-
} catch {
|
|
306
|
-
// Can't resolve — proceed with sync
|
|
307
|
-
}
|
|
539
|
+
if (isSamePath(mainGsd, wtGsd)) return { synced };
|
|
308
540
|
|
|
309
541
|
if (!existsSync(wtGsd) || !existsSync(mainGsd)) return { synced };
|
|
310
542
|
|
|
@@ -330,17 +562,7 @@ export function syncWorktreeStateBack(
|
|
|
330
562
|
// Also includes QUEUE.md, completed-units.json, and metrics.json which are
|
|
331
563
|
// written during milestone closeout and lost on teardown without explicit sync
|
|
332
564
|
// (#1787, #2313).
|
|
333
|
-
const
|
|
334
|
-
"DECISIONS.md",
|
|
335
|
-
"REQUIREMENTS.md",
|
|
336
|
-
"PROJECT.md",
|
|
337
|
-
"KNOWLEDGE.md",
|
|
338
|
-
"OVERRIDES.md",
|
|
339
|
-
"QUEUE.md",
|
|
340
|
-
"completed-units.json",
|
|
341
|
-
"metrics.json",
|
|
342
|
-
];
|
|
343
|
-
for (const f of rootFiles) {
|
|
565
|
+
for (const f of ROOT_STATE_FILES) {
|
|
344
566
|
const src = join(wtGsd, f);
|
|
345
567
|
const dst = join(mainGsd, f);
|
|
346
568
|
if (existsSync(src)) {
|
|
@@ -379,6 +601,29 @@ export function syncWorktreeStateBack(
|
|
|
379
601
|
* Sync a single milestone directory from worktree to main.
|
|
380
602
|
* Copies milestone-level .md files, slice-level files, and task summaries.
|
|
381
603
|
*/
|
|
604
|
+
/** Copy matching files from srcDir to dstDir (non-fatal per file). */
|
|
605
|
+
function syncDirFiles(
|
|
606
|
+
srcDir: string,
|
|
607
|
+
dstDir: string,
|
|
608
|
+
filter: (name: string) => boolean,
|
|
609
|
+
synced: string[],
|
|
610
|
+
prefix: string,
|
|
611
|
+
): void {
|
|
612
|
+
try {
|
|
613
|
+
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
614
|
+
if (!entry.isFile() || !filter(entry.name)) continue;
|
|
615
|
+
try {
|
|
616
|
+
cpSync(join(srcDir, entry.name), join(dstDir, entry.name), { force: true });
|
|
617
|
+
synced.push(`${prefix}${entry.name}`);
|
|
618
|
+
} catch {
|
|
619
|
+
/* non-fatal */
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
} catch {
|
|
623
|
+
/* non-fatal — srcDir may not be readable */
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
382
627
|
function syncMilestoneDir(
|
|
383
628
|
wtGsd: string,
|
|
384
629
|
mainGsd: string,
|
|
@@ -391,83 +636,35 @@ function syncMilestoneDir(
|
|
|
391
636
|
if (!existsSync(wtMilestoneDir)) return;
|
|
392
637
|
mkdirSync(mainMilestoneDir, { recursive: true });
|
|
393
638
|
|
|
639
|
+
const isMd = (name: string): boolean => name.endsWith(".md");
|
|
640
|
+
|
|
394
641
|
// Sync milestone-level files (SUMMARY, VALIDATION, ROADMAP, CONTEXT)
|
|
395
|
-
|
|
396
|
-
for (const entry of readdirSync(wtMilestoneDir, { withFileTypes: true })) {
|
|
397
|
-
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
398
|
-
const src = join(wtMilestoneDir, entry.name);
|
|
399
|
-
const dst = join(mainMilestoneDir, entry.name);
|
|
400
|
-
try {
|
|
401
|
-
cpSync(src, dst, { force: true });
|
|
402
|
-
synced.push(`milestones/${mid}/${entry.name}`);
|
|
403
|
-
} catch {
|
|
404
|
-
/* non-fatal */
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
} catch {
|
|
409
|
-
/* non-fatal */
|
|
410
|
-
}
|
|
642
|
+
syncDirFiles(wtMilestoneDir, mainMilestoneDir, isMd, synced, `milestones/${mid}/`);
|
|
411
643
|
|
|
412
|
-
// Sync slice-level files (summaries, UATs)
|
|
644
|
+
// Sync slice-level files (summaries, UATs) and task summaries (#1678)
|
|
413
645
|
const wtSlicesDir = join(wtMilestoneDir, "slices");
|
|
414
646
|
const mainSlicesDir = join(mainMilestoneDir, "slices");
|
|
415
|
-
if (existsSync(wtSlicesDir))
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
try {
|
|
433
|
-
cpSync(src, dst, { force: true });
|
|
434
|
-
synced.push(
|
|
435
|
-
`milestones/${mid}/slices/${sid}/${fileEntry.name}`,
|
|
436
|
-
);
|
|
437
|
-
} catch {
|
|
438
|
-
/* non-fatal */
|
|
439
|
-
}
|
|
440
|
-
} else if (fileEntry.isDirectory() && fileEntry.name === "tasks") {
|
|
441
|
-
// Recurse into tasks/ subdirectory to sync task summaries (#1678).
|
|
442
|
-
// Without this, T01-SUMMARY.md etc. are silently dropped on
|
|
443
|
-
// worktree teardown because the loop only processes isFile() entries.
|
|
444
|
-
const wtTasksDir = join(wtSliceDir, "tasks");
|
|
445
|
-
const mainTasksDir = join(mainSliceDir, "tasks");
|
|
446
|
-
mkdirSync(mainTasksDir, { recursive: true });
|
|
447
|
-
try {
|
|
448
|
-
for (const taskEntry of readdirSync(wtTasksDir, { withFileTypes: true })) {
|
|
449
|
-
if (taskEntry.isFile() && taskEntry.name.endsWith(".md")) {
|
|
450
|
-
const taskSrc = join(wtTasksDir, taskEntry.name);
|
|
451
|
-
const taskDst = join(mainTasksDir, taskEntry.name);
|
|
452
|
-
try {
|
|
453
|
-
cpSync(taskSrc, taskDst, { force: true });
|
|
454
|
-
synced.push(
|
|
455
|
-
`milestones/${mid}/slices/${sid}/tasks/${taskEntry.name}`,
|
|
456
|
-
);
|
|
457
|
-
} catch {
|
|
458
|
-
/* non-fatal */
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
} catch {
|
|
463
|
-
/* non-fatal: tasks dir read failure */
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
}
|
|
647
|
+
if (!existsSync(wtSlicesDir)) return;
|
|
648
|
+
|
|
649
|
+
try {
|
|
650
|
+
for (const sliceEntry of readdirSync(wtSlicesDir, { withFileTypes: true })) {
|
|
651
|
+
if (!sliceEntry.isDirectory()) continue;
|
|
652
|
+
const sid = sliceEntry.name;
|
|
653
|
+
const wtSliceDir = join(wtSlicesDir, sid);
|
|
654
|
+
const mainSliceDir = join(mainSlicesDir, sid);
|
|
655
|
+
mkdirSync(mainSliceDir, { recursive: true });
|
|
656
|
+
|
|
657
|
+
syncDirFiles(wtSliceDir, mainSliceDir, isMd, synced, `milestones/${mid}/slices/${sid}/`);
|
|
658
|
+
|
|
659
|
+
const wtTasksDir = join(wtSliceDir, "tasks");
|
|
660
|
+
const mainTasksDir = join(mainSliceDir, "tasks");
|
|
661
|
+
if (existsSync(wtTasksDir)) {
|
|
662
|
+
mkdirSync(mainTasksDir, { recursive: true });
|
|
663
|
+
syncDirFiles(wtTasksDir, mainTasksDir, isMd, synced, `milestones/${mid}/slices/${sid}/tasks/`);
|
|
467
664
|
}
|
|
468
|
-
} catch {
|
|
469
|
-
/* non-fatal */
|
|
470
665
|
}
|
|
666
|
+
} catch {
|
|
667
|
+
/* non-fatal */
|
|
471
668
|
}
|
|
472
669
|
}
|
|
473
670
|
// ─── Worktree Post-Create Hook (#597) ────────────────────────────────────────
|
|
@@ -38,6 +38,7 @@ import { clearActivityLogState } from "./activity-log.js";
|
|
|
38
38
|
import {
|
|
39
39
|
synthesizeCrashRecovery,
|
|
40
40
|
getDeepDiagnostic,
|
|
41
|
+
readActiveMilestoneId,
|
|
41
42
|
} from "./session-forensics.js";
|
|
42
43
|
import {
|
|
43
44
|
writeLock,
|
|
@@ -76,13 +77,6 @@ import {
|
|
|
76
77
|
import { closeoutUnit } from "./auto-unit-closeout.js";
|
|
77
78
|
import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
|
|
78
79
|
import { selectAndApplyModel, resolveModelId } from "./auto-model-selection.js";
|
|
79
|
-
import {
|
|
80
|
-
syncProjectRootToWorktree,
|
|
81
|
-
syncStateToProjectRoot,
|
|
82
|
-
readResourceVersion,
|
|
83
|
-
checkResourcesStale,
|
|
84
|
-
escapeStaleWorktree,
|
|
85
|
-
} from "./auto-worktree-sync.js";
|
|
86
80
|
import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
|
|
87
81
|
import {
|
|
88
82
|
checkPostUnitHooks,
|
|
@@ -143,6 +137,11 @@ import {
|
|
|
143
137
|
mergeMilestoneToMain,
|
|
144
138
|
autoWorktreeBranch,
|
|
145
139
|
syncWorktreeStateBack,
|
|
140
|
+
syncProjectRootToWorktree,
|
|
141
|
+
syncStateToProjectRoot,
|
|
142
|
+
readResourceVersion,
|
|
143
|
+
checkResourcesStale,
|
|
144
|
+
escapeStaleWorktree,
|
|
146
145
|
} from "./auto-worktree.js";
|
|
147
146
|
import { pruneQueueOrder } from "./queue-order.js";
|
|
148
147
|
|
|
@@ -190,8 +189,6 @@ import {
|
|
|
190
189
|
} from "./worktree-resolver.js";
|
|
191
190
|
import { reorderForCaching } from "./prompt-ordering.js";
|
|
192
191
|
|
|
193
|
-
// Worktree sync, resource staleness, stale worktree escape → auto-worktree-sync.ts
|
|
194
|
-
|
|
195
192
|
// ─── Session State ─────────────────────────────────────────────────────────
|
|
196
193
|
|
|
197
194
|
import {
|
|
@@ -980,7 +977,11 @@ function buildLoopDeps(): LoopDeps {
|
|
|
980
977
|
startUnitSupervision,
|
|
981
978
|
|
|
982
979
|
// Prompt helpers
|
|
983
|
-
getDeepDiagnostic
|
|
980
|
+
getDeepDiagnostic: (basePath: string) => {
|
|
981
|
+
const mid = readActiveMilestoneId(basePath);
|
|
982
|
+
const wtPath = mid ? getAutoWorktreePath(basePath, mid) : undefined;
|
|
983
|
+
return getDeepDiagnostic(basePath, wtPath ?? undefined);
|
|
984
|
+
},
|
|
984
985
|
isDbAvailable,
|
|
985
986
|
reorderForCaching,
|
|
986
987
|
|
|
@@ -1112,4 +1112,97 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
1112
1112
|
|
|
1113
1113
|
pi.registerTool(reassessRoadmapTool);
|
|
1114
1114
|
registerAlias(pi, reassessRoadmapTool, "gsd_roadmap_reassess", "gsd_reassess_roadmap");
|
|
1115
|
+
|
|
1116
|
+
// ─── gsd_save_gate_result ──────────────────────────────────────────────
|
|
1117
|
+
|
|
1118
|
+
const saveGateResultExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
|
|
1119
|
+
const dbAvailable = await ensureDbOpen();
|
|
1120
|
+
if (!dbAvailable) {
|
|
1121
|
+
return {
|
|
1122
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available." }],
|
|
1123
|
+
details: { operation: "save_gate_result", error: "db_unavailable" } as any,
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"];
|
|
1127
|
+
if (!validGates.includes(params.gateId)) {
|
|
1128
|
+
return {
|
|
1129
|
+
content: [{ type: "text" as const, text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
|
|
1130
|
+
details: { operation: "save_gate_result", error: "invalid_gate_id" } as any,
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
const validVerdicts = ["pass", "flag", "omitted"];
|
|
1134
|
+
if (!validVerdicts.includes(params.verdict)) {
|
|
1135
|
+
return {
|
|
1136
|
+
content: [{ type: "text" as const, text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }],
|
|
1137
|
+
details: { operation: "save_gate_result", error: "invalid_verdict" } as any,
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
try {
|
|
1141
|
+
const { saveGateResult } = await import("../gsd-db.js");
|
|
1142
|
+
const { invalidateStateCache } = await import("../state.js");
|
|
1143
|
+
saveGateResult({
|
|
1144
|
+
milestoneId: params.milestoneId,
|
|
1145
|
+
sliceId: params.sliceId,
|
|
1146
|
+
gateId: params.gateId,
|
|
1147
|
+
taskId: params.taskId ?? "",
|
|
1148
|
+
verdict: params.verdict,
|
|
1149
|
+
rationale: params.rationale,
|
|
1150
|
+
findings: params.findings ?? "",
|
|
1151
|
+
});
|
|
1152
|
+
invalidateStateCache();
|
|
1153
|
+
return {
|
|
1154
|
+
content: [{ type: "text" as const, text: `Gate ${params.gateId} result saved: verdict=${params.verdict}` }],
|
|
1155
|
+
details: { operation: "save_gate_result", gateId: params.gateId, verdict: params.verdict } as any,
|
|
1156
|
+
};
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1159
|
+
logError("tool", `gsd_save_gate_result failed: ${msg}`, { tool: "gsd_save_gate_result", error: String(err) });
|
|
1160
|
+
return {
|
|
1161
|
+
content: [{ type: "text" as const, text: `Error saving gate result: ${msg}` }],
|
|
1162
|
+
details: { operation: "save_gate_result", error: msg } as any,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
const saveGateResultTool = {
|
|
1168
|
+
name: "gsd_save_gate_result",
|
|
1169
|
+
label: "Save Gate Result",
|
|
1170
|
+
description:
|
|
1171
|
+
"Save the result of a quality gate evaluation (Q3-Q8) to the GSD database. " +
|
|
1172
|
+
"Called by gate evaluation sub-agents after analyzing a specific quality question.",
|
|
1173
|
+
promptSnippet: "Save quality gate evaluation result (verdict, rationale, findings)",
|
|
1174
|
+
promptGuidelines: [
|
|
1175
|
+
"Use gsd_save_gate_result after evaluating a quality gate question.",
|
|
1176
|
+
"gateId must be one of: Q3, Q4, Q5, Q6, Q7, Q8.",
|
|
1177
|
+
"verdict must be: pass (no concerns), flag (concerns found), or omitted (not applicable).",
|
|
1178
|
+
"rationale should be a one-sentence justification for the verdict.",
|
|
1179
|
+
"findings should contain detailed markdown analysis (or empty string if omitted).",
|
|
1180
|
+
],
|
|
1181
|
+
parameters: Type.Object({
|
|
1182
|
+
milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
|
|
1183
|
+
sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
|
|
1184
|
+
gateId: Type.String({ description: "Gate ID: Q3, Q4, Q5, Q6, Q7, or Q8" }),
|
|
1185
|
+
taskId: Type.Optional(Type.String({ description: "Task ID for task-scoped gates (Q5/Q6/Q7)" })),
|
|
1186
|
+
verdict: Type.String({ description: "pass, flag, or omitted" }),
|
|
1187
|
+
rationale: Type.String({ description: "One-sentence justification" }),
|
|
1188
|
+
findings: Type.Optional(Type.String({ description: "Detailed markdown findings" })),
|
|
1189
|
+
}),
|
|
1190
|
+
execute: saveGateResultExecute,
|
|
1191
|
+
renderCall(args: any, theme: any) {
|
|
1192
|
+
let text = theme.fg("toolTitle", theme.bold("save_gate_result "));
|
|
1193
|
+
text += theme.fg("accent", args.gateId ?? "");
|
|
1194
|
+
text += theme.fg("dim", ` → ${args.verdict ?? ""}`);
|
|
1195
|
+
return new Text(text, 0, 0);
|
|
1196
|
+
},
|
|
1197
|
+
renderResult(result: any, _options: any, theme: any) {
|
|
1198
|
+
const d = result.details;
|
|
1199
|
+
if (result.isError || d?.error) {
|
|
1200
|
+
return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
|
|
1201
|
+
}
|
|
1202
|
+
const color = d?.verdict === "flag" ? "warning" : "success";
|
|
1203
|
+
return new Text(theme.fg(color, `${d?.gateId}: ${d?.verdict}`), 0, 0);
|
|
1204
|
+
},
|
|
1205
|
+
};
|
|
1206
|
+
|
|
1207
|
+
pi.registerTool(saveGateResultTool);
|
|
1115
1208
|
}
|
|
@@ -69,6 +69,14 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
pi.on("session_switch", async (_event, ctx) => {
|
|
73
|
+
resetWriteGateState();
|
|
74
|
+
resetToolCallLoopGuard();
|
|
75
|
+
clearDiscussionFlowState();
|
|
76
|
+
await syncServiceTierStatus(ctx);
|
|
77
|
+
loadToolApiKeys();
|
|
78
|
+
});
|
|
79
|
+
|
|
72
80
|
pi.on("before_agent_start", async (event, ctx: ExtensionContext) => {
|
|
73
81
|
return buildBeforeAgentStartResult(event, ctx);
|
|
74
82
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD branch naming patterns — single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* gsd/<worktree>/<milestone>/<slice> → SLICE_BRANCH_RE
|
|
5
|
+
* gsd/quick/<id>-<slug> → QUICK_BRANCH_RE
|
|
6
|
+
* gsd/<workflow>/<...> → WORKFLOW_BRANCH_RE (non-milestone gsd/ branches)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Matches gsd/ slice branches: gsd/[worktree/]M001[-hash]/S01 */
|
|
10
|
+
export const SLICE_BRANCH_RE = /^gsd\/(?:([a-zA-Z0-9_-]+)\/)?(M\d+(?:-[a-z0-9]{6})?)\/(S\d+)$/;
|
|
11
|
+
|
|
12
|
+
/** Matches gsd/quick/ task branches */
|
|
13
|
+
export const QUICK_BRANCH_RE = /^gsd\/quick\//;
|
|
14
|
+
|
|
15
|
+
/** Matches gsd/ workflow branches (non-milestone, e.g. gsd/workflow-name/...) */
|
|
16
|
+
export const WORKFLOW_BRANCH_RE = /^gsd\/(?!M\d)[\w-]+\//;
|