gsd-pi 2.37.1-dev.d3ace49 → 2.38.0-dev.63ad7e5
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/app-paths.js +1 -1
- package/dist/cli.js +9 -0
- package/dist/extension-discovery.d.ts +5 -3
- package/dist/extension-discovery.js +14 -9
- package/dist/extension-registry.js +2 -2
- package/dist/remote-questions-config.js +2 -2
- package/dist/resources/extensions/browser-tools/package.json +3 -1
- package/dist/resources/extensions/cmux/index.js +55 -1
- package/dist/resources/extensions/context7/package.json +1 -1
- package/dist/resources/extensions/env-utils.js +29 -0
- package/dist/resources/extensions/get-secrets-from-user.js +5 -24
- package/dist/resources/extensions/google-search/package.json +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +6 -23
- package/dist/resources/extensions/gsd/auto-dispatch.js +7 -8
- package/dist/resources/extensions/gsd/auto-loop.js +68 -97
- package/dist/resources/extensions/gsd/auto-post-unit.js +75 -71
- package/dist/resources/extensions/gsd/auto-prompts.js +7 -31
- package/dist/resources/extensions/gsd/auto-start.js +13 -2
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
- package/dist/resources/extensions/gsd/auto.js +143 -96
- package/dist/resources/extensions/gsd/captures.js +9 -1
- package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
- package/dist/resources/extensions/gsd/commands-handlers.js +16 -3
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +22 -2
- package/dist/resources/extensions/gsd/context-budget.js +2 -10
- package/dist/resources/extensions/gsd/detection.js +1 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
- package/dist/resources/extensions/gsd/doctor-format.js +15 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +27 -11
- package/dist/resources/extensions/gsd/doctor.js +184 -11
- package/dist/resources/extensions/gsd/export.js +1 -1
- package/dist/resources/extensions/gsd/files.js +2 -2
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/index.js +2 -1
- package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
- package/dist/resources/extensions/gsd/package.json +1 -1
- package/dist/resources/extensions/gsd/preferences-models.js +0 -12
- package/dist/resources/extensions/gsd/preferences-types.js +0 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +1 -11
- package/dist/resources/extensions/gsd/preferences.js +5 -5
- package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -10
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/dist/resources/extensions/gsd/repo-identity.js +21 -4
- package/dist/resources/extensions/gsd/resource-version.js +2 -1
- package/dist/resources/extensions/gsd/state.js +1 -1
- package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
- package/dist/resources/extensions/gsd/worktree.js +35 -16
- package/dist/resources/extensions/remote-questions/status.js +2 -1
- package/dist/resources/extensions/remote-questions/store.js +2 -1
- package/dist/resources/extensions/search-the-web/provider.js +2 -1
- package/dist/resources/extensions/subagent/index.js +12 -3
- package/dist/resources/extensions/subagent/isolation.js +2 -1
- package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
- package/dist/resources/extensions/universal-config/package.json +1 -1
- package/dist/welcome-screen.d.ts +12 -0
- package/dist/welcome-screen.js +53 -0
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
- package/pkg/package.json +1 -1
- package/src/resources/extensions/cmux/index.ts +57 -1
- package/src/resources/extensions/env-utils.ts +31 -0
- package/src/resources/extensions/get-secrets-from-user.ts +5 -24
- package/src/resources/extensions/gsd/auto/session.ts +7 -25
- package/src/resources/extensions/gsd/auto-dispatch.ts +6 -8
- package/src/resources/extensions/gsd/auto-loop.ts +88 -133
- package/src/resources/extensions/gsd/auto-post-unit.ts +52 -42
- package/src/resources/extensions/gsd/auto-prompts.ts +7 -33
- package/src/resources/extensions/gsd/auto-start.ts +18 -2
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
- package/src/resources/extensions/gsd/auto.ts +139 -101
- package/src/resources/extensions/gsd/captures.ts +10 -1
- package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
- package/src/resources/extensions/gsd/commands-handlers.ts +17 -2
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +24 -2
- package/src/resources/extensions/gsd/context-budget.ts +2 -12
- package/src/resources/extensions/gsd/detection.ts +2 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
- package/src/resources/extensions/gsd/doctor-format.ts +20 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +26 -9
- package/src/resources/extensions/gsd/doctor-types.ts +16 -1
- package/src/resources/extensions/gsd/doctor.ts +177 -13
- package/src/resources/extensions/gsd/export.ts +1 -1
- package/src/resources/extensions/gsd/files.ts +2 -2
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/index.ts +3 -1
- package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
- package/src/resources/extensions/gsd/preferences-models.ts +0 -12
- package/src/resources/extensions/gsd/preferences-types.ts +0 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +1 -11
- package/src/resources/extensions/gsd/preferences.ts +5 -5
- package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -8
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/src/resources/extensions/gsd/prompts/run-uat.md +25 -10
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/src/resources/extensions/gsd/repo-identity.ts +23 -4
- package/src/resources/extensions/gsd/resource-version.ts +3 -1
- package/src/resources/extensions/gsd/state.ts +1 -1
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +11 -31
- package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +11 -3
- package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
- package/src/resources/extensions/gsd/types.ts +0 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
- package/src/resources/extensions/gsd/worktree.ts +35 -15
- package/src/resources/extensions/remote-questions/status.ts +3 -1
- package/src/resources/extensions/remote-questions/store.ts +3 -1
- package/src/resources/extensions/search-the-web/provider.ts +2 -1
- package/src/resources/extensions/subagent/index.ts +12 -3
- package/src/resources/extensions/subagent/isolation.ts +3 -1
- package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
- package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
- package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
- package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
- package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
- package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
- package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
- package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
- package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
- package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
- package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
- package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
|
@@ -33,7 +33,6 @@ import { writeUnitRuntimeRecord, clearUnitRuntimeRecord } from "./unit-runtime.j
|
|
|
33
33
|
import { runGSDDoctor, rebuildState, summarizeDoctorIssues } from "./doctor.js";
|
|
34
34
|
import { recordHealthSnapshot, checkHealEscalation } from "./doctor-proactive.js";
|
|
35
35
|
import { syncStateToProjectRoot } from "./auto-worktree-sync.js";
|
|
36
|
-
import { resetRewriteCircuitBreaker } from "./auto-dispatch.js";
|
|
37
36
|
import { isDbAvailable } from "./gsd-db.js";
|
|
38
37
|
import { consumeSignal } from "./session-status-io.js";
|
|
39
38
|
import {
|
|
@@ -56,6 +55,13 @@ import { join } from "node:path";
|
|
|
56
55
|
/** Throttle STATE.md rebuilds — at most once per 30 seconds */
|
|
57
56
|
const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
58
57
|
|
|
58
|
+
export interface PreVerificationOpts {
|
|
59
|
+
skipSettleDelay?: boolean;
|
|
60
|
+
skipDoctor?: boolean;
|
|
61
|
+
skipStateRebuild?: boolean;
|
|
62
|
+
skipWorktreeSync?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
export interface PostUnitContext {
|
|
60
66
|
s: AutoSession;
|
|
61
67
|
ctx: ExtensionContext;
|
|
@@ -73,7 +79,7 @@ export interface PostUnitContext {
|
|
|
73
79
|
*
|
|
74
80
|
* Returns "dispatched" if a signal caused stop/pause, "continue" to proceed.
|
|
75
81
|
*/
|
|
76
|
-
export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"dispatched" | "continue"> {
|
|
82
|
+
export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreVerificationOpts): Promise<"dispatched" | "continue"> {
|
|
77
83
|
const { s, ctx, pi, buildSnapshotOpts, stopAuto, pauseAuto } = pctx;
|
|
78
84
|
|
|
79
85
|
// ── Parallel worker signal check ──
|
|
@@ -95,8 +101,10 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
95
101
|
// Invalidate all caches
|
|
96
102
|
invalidateAllCaches();
|
|
97
103
|
|
|
98
|
-
// Small delay to let files settle
|
|
99
|
-
|
|
104
|
+
// Small delay to let files settle (skipped for sidecars where latency matters more)
|
|
105
|
+
if (!opts?.skipSettleDelay) {
|
|
106
|
+
await new Promise(r => setTimeout(r, 100));
|
|
107
|
+
}
|
|
100
108
|
|
|
101
109
|
// Auto-commit
|
|
102
110
|
if (s.currentUnit) {
|
|
@@ -120,8 +128,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
120
128
|
keyFiles: summary.frontmatter.key_files?.filter(f => !f.includes("{{")) || undefined,
|
|
121
129
|
};
|
|
122
130
|
}
|
|
123
|
-
} catch {
|
|
124
|
-
|
|
131
|
+
} catch (e) {
|
|
132
|
+
debugLog("postUnit", { phase: "task-summary-parse", error: String(e) });
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
}
|
|
@@ -131,12 +139,12 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
131
139
|
if (commitMsg) {
|
|
132
140
|
ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
|
|
133
141
|
}
|
|
134
|
-
} catch {
|
|
135
|
-
|
|
142
|
+
} catch (e) {
|
|
143
|
+
debugLog("postUnit", { phase: "auto-commit", error: String(e) });
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
// Doctor: fix mechanical bookkeeping
|
|
139
|
-
try {
|
|
146
|
+
// Doctor: fix mechanical bookkeeping (skipped for lightweight sidecars)
|
|
147
|
+
if (!opts?.skipDoctor) try {
|
|
140
148
|
const scopeParts = s.currentUnit.id.split("/").slice(0, 2);
|
|
141
149
|
const doctorScope = scopeParts.join("/");
|
|
142
150
|
const sliceTerminalUnits = new Set(["complete-slice", "run-uat"]);
|
|
@@ -168,24 +176,26 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
168
176
|
const reportText = formatDoctorReport(report, { scope: doctorScope, includeWarnings: true });
|
|
169
177
|
const structuredIssues = formatDoctorIssuesForPrompt(actionable);
|
|
170
178
|
dispatchDoctorHeal(pi, doctorScope, reportText, structuredIssues);
|
|
171
|
-
} catch {
|
|
172
|
-
|
|
179
|
+
} catch (e) {
|
|
180
|
+
debugLog("postUnit", { phase: "doctor-heal-dispatch", error: String(e) });
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
}
|
|
176
|
-
} catch {
|
|
177
|
-
|
|
184
|
+
} catch (e) {
|
|
185
|
+
debugLog("postUnit", { phase: "doctor", error: String(e) });
|
|
178
186
|
}
|
|
179
187
|
|
|
180
|
-
// Throttled STATE.md rebuild
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
// Throttled STATE.md rebuild (skipped for lightweight sidecars)
|
|
189
|
+
if (!opts?.skipStateRebuild) {
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
if (now - s.lastStateRebuildAt >= STATE_REBUILD_MIN_INTERVAL_MS) {
|
|
192
|
+
try {
|
|
193
|
+
await rebuildState(s.basePath);
|
|
194
|
+
s.lastStateRebuildAt = now;
|
|
195
|
+
autoCommitCurrentBranch(s.basePath, "state-rebuild", s.currentUnit.id);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
debugLog("postUnit", { phase: "state-rebuild", error: String(e) });
|
|
198
|
+
}
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
|
|
@@ -193,16 +203,16 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
193
203
|
try {
|
|
194
204
|
const { pruneDeadProcesses } = await import("../bg-shell/process-manager.js");
|
|
195
205
|
pruneDeadProcesses();
|
|
196
|
-
} catch {
|
|
197
|
-
|
|
206
|
+
} catch (e) {
|
|
207
|
+
debugLog("postUnit", { phase: "prune-bg-shell", error: String(e) });
|
|
198
208
|
}
|
|
199
209
|
|
|
200
|
-
// Sync worktree state back to project root
|
|
201
|
-
if (s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
210
|
+
// Sync worktree state back to project root (skipped for lightweight sidecars)
|
|
211
|
+
if (!opts?.skipWorktreeSync && s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
202
212
|
try {
|
|
203
213
|
syncStateToProjectRoot(s.basePath, s.originalBasePath, s.currentMilestoneId);
|
|
204
|
-
} catch {
|
|
205
|
-
|
|
214
|
+
} catch (e) {
|
|
215
|
+
debugLog("postUnit", { phase: "worktree-sync", error: String(e) });
|
|
206
216
|
}
|
|
207
217
|
}
|
|
208
218
|
|
|
@@ -210,10 +220,10 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
210
220
|
if (s.currentUnit.type === "rewrite-docs") {
|
|
211
221
|
try {
|
|
212
222
|
await resolveAllOverrides(s.basePath);
|
|
213
|
-
|
|
223
|
+
s.rewriteAttemptCount = 0;
|
|
214
224
|
ctx.ui.notify("Override(s) resolved — rewrite-docs completed.", "info");
|
|
215
|
-
} catch {
|
|
216
|
-
|
|
225
|
+
} catch (e) {
|
|
226
|
+
debugLog("postUnit", { phase: "rewrite-docs-resolve", error: String(e) });
|
|
217
227
|
}
|
|
218
228
|
}
|
|
219
229
|
|
|
@@ -226,8 +236,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
226
236
|
const { clearReactiveState } = await import("./reactive-graph.js");
|
|
227
237
|
clearReactiveState(s.basePath, mid, sid);
|
|
228
238
|
}
|
|
229
|
-
} catch {
|
|
230
|
-
|
|
239
|
+
} catch (e) {
|
|
240
|
+
debugLog("postUnit", { phase: "reactive-state-cleanup", error: String(e) });
|
|
231
241
|
}
|
|
232
242
|
}
|
|
233
243
|
|
|
@@ -280,8 +290,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
280
290
|
if (triggerArtifactVerified) {
|
|
281
291
|
invalidateAllCaches();
|
|
282
292
|
}
|
|
283
|
-
} catch {
|
|
284
|
-
|
|
293
|
+
} catch (e) {
|
|
294
|
+
debugLog("postUnit", { phase: "artifact-verify", error: String(e) });
|
|
285
295
|
}
|
|
286
296
|
} else {
|
|
287
297
|
// Hook unit completed — finalize its runtime record
|
|
@@ -292,8 +302,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext): Promise<"d
|
|
|
292
302
|
lastProgressKind: "hook-completed",
|
|
293
303
|
});
|
|
294
304
|
clearUnitRuntimeRecord(s.basePath, s.currentUnit.type, s.currentUnit.id);
|
|
295
|
-
} catch {
|
|
296
|
-
|
|
305
|
+
} catch (e) {
|
|
306
|
+
debugLog("postUnit", { phase: "hook-finalize", error: String(e) });
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
}
|
|
@@ -429,8 +439,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
429
439
|
}
|
|
430
440
|
}
|
|
431
441
|
}
|
|
432
|
-
} catch {
|
|
433
|
-
|
|
442
|
+
} catch (e) {
|
|
443
|
+
debugLog("postUnit", { phase: "triage-check", error: String(e) });
|
|
434
444
|
}
|
|
435
445
|
}
|
|
436
446
|
|
|
@@ -475,8 +485,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
475
485
|
);
|
|
476
486
|
|
|
477
487
|
return "continue";
|
|
478
|
-
} catch {
|
|
479
|
-
|
|
488
|
+
} catch (e) {
|
|
489
|
+
debugLog("postUnit", { phase: "quick-task-dispatch", error: String(e) });
|
|
480
490
|
}
|
|
481
491
|
}
|
|
482
492
|
|
|
@@ -21,10 +21,7 @@ import type { GSDPreferences } from "./preferences.js";
|
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { existsSync } from "node:fs";
|
|
23
23
|
import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.js";
|
|
24
|
-
import { compressToTarget } from "./prompt-compressor.js";
|
|
25
|
-
import { distillSummaries } from "./summary-distiller.js";
|
|
26
24
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
27
|
-
import { chunkByRelevance, formatChunks } from "./semantic-chunker.js";
|
|
28
25
|
|
|
29
26
|
// ─── Executor Constraints ─────────────────────────────────────────────────────
|
|
30
27
|
|
|
@@ -159,16 +156,10 @@ export async function inlineFileSmart(
|
|
|
159
156
|
return `### ${label}\nSource: \`${relPath}\`\n\n${content.trim()}`;
|
|
160
157
|
}
|
|
161
158
|
|
|
162
|
-
//
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (result.savingsPercent < 20) {
|
|
167
|
-
return `### ${label}\nSource: \`${relPath}\`\n\n${content.trim()}`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const formatted = formatChunks(result, relPath);
|
|
171
|
-
return `### ${label} (${result.omittedChunks} sections omitted for relevance)\nSource: \`${relPath}\`\n\n${formatted}`;
|
|
159
|
+
// For large files, truncate at section boundary
|
|
160
|
+
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
161
|
+
const truncated = truncateAtSectionBoundary(content, threshold).content;
|
|
162
|
+
return `### ${label}\nSource: \`${relPath}\`\n\n${truncated}`;
|
|
172
163
|
}
|
|
173
164
|
|
|
174
165
|
/**
|
|
@@ -202,20 +193,6 @@ export async function inlineDependencySummaries(
|
|
|
202
193
|
|
|
203
194
|
const result = sections.join("\n\n");
|
|
204
195
|
if (budgetChars !== undefined && result.length > budgetChars) {
|
|
205
|
-
// For 3+ summaries, try distillation first (preserves more information)
|
|
206
|
-
if (sections.length >= 3) {
|
|
207
|
-
const rawSummaries = sections.map(s => {
|
|
208
|
-
// Extract content after the header line
|
|
209
|
-
const lines = s.split("\n");
|
|
210
|
-
const contentStart = lines.findIndex(l => l.startsWith("Source:"));
|
|
211
|
-
return contentStart >= 0 ? lines.slice(contentStart + 1).join("\n").trim() : s;
|
|
212
|
-
});
|
|
213
|
-
const distilled = distillSummaries(rawSummaries, budgetChars);
|
|
214
|
-
if (distilled.content.length <= budgetChars) {
|
|
215
|
-
return distilled.content;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// Fall back to section-boundary truncation
|
|
219
196
|
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
220
197
|
return truncateAtSectionBoundary(result, budgetChars).content;
|
|
221
198
|
}
|
|
@@ -900,15 +877,12 @@ export async function buildExecuteTaskPrompt(
|
|
|
900
877
|
const budgets = computeBudgets(contextWindow);
|
|
901
878
|
const verificationBudget = `~${Math.round(budgets.verificationBudgetChars / 1000)}K chars`;
|
|
902
879
|
|
|
903
|
-
//
|
|
904
|
-
// Only compress when compression_strategy is "compress" (budget/balanced profiles).
|
|
880
|
+
// Truncate carry-forward section when it exceeds 40% of inline context budget.
|
|
905
881
|
const carryForwardBudget = Math.floor(budgets.inlineContextBudgetChars * 0.4);
|
|
906
882
|
let finalCarryForward = carryForwardSection;
|
|
907
883
|
if (carryForwardSection.length > carryForwardBudget) {
|
|
908
|
-
const {
|
|
909
|
-
|
|
910
|
-
finalCarryForward = compressToTarget(carryForwardSection, carryForwardBudget).content;
|
|
911
|
-
}
|
|
884
|
+
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
885
|
+
finalCarryForward = truncateAtSectionBoundary(carryForwardSection, carryForwardBudget).content;
|
|
912
886
|
}
|
|
913
887
|
|
|
914
888
|
return loadPrompt("execute-task", {
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
resolveSkillDiscoveryMode,
|
|
21
21
|
getIsolationMode,
|
|
22
22
|
} from "./preferences.js";
|
|
23
|
-
import { ensureGsdSymlink } from "./repo-identity.js";
|
|
23
|
+
import { ensureGsdSymlink, validateProjectId } from "./repo-identity.js";
|
|
24
24
|
import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
|
|
25
25
|
import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
|
|
26
26
|
import { gsdRoot, resolveMilestoneFile, milestonesDir } from "./paths.js";
|
|
@@ -130,6 +130,16 @@ export async function bootstrapAutoSession(
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
try {
|
|
133
|
+
// Validate GSD_PROJECT_ID early so the user gets immediate feedback
|
|
134
|
+
const customProjectId = process.env.GSD_PROJECT_ID;
|
|
135
|
+
if (customProjectId && !validateProjectId(customProjectId)) {
|
|
136
|
+
ctx.ui.notify(
|
|
137
|
+
`GSD_PROJECT_ID must contain only alphanumeric characters, hyphens, and underscores. Got: "${customProjectId}"`,
|
|
138
|
+
"error",
|
|
139
|
+
);
|
|
140
|
+
return releaseLockAndReturn();
|
|
141
|
+
}
|
|
142
|
+
|
|
133
143
|
// Ensure git repo exists
|
|
134
144
|
if (!nativeIsRepo(base)) {
|
|
135
145
|
const mainBranch =
|
|
@@ -429,10 +439,16 @@ export async function bootstrapAutoSession(
|
|
|
429
439
|
s.originalBasePath = base;
|
|
430
440
|
|
|
431
441
|
const isUnderGsdWorktrees = (p: string): boolean => {
|
|
442
|
+
// Direct layout: /.gsd/worktrees/
|
|
432
443
|
const marker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
|
|
433
444
|
if (p.includes(marker)) return true;
|
|
434
445
|
const worktreesSuffix = `${pathSep}.gsd${pathSep}worktrees`;
|
|
435
|
-
|
|
446
|
+
if (p.endsWith(worktreesSuffix)) return true;
|
|
447
|
+
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
|
|
448
|
+
const symlinkRe = new RegExp(
|
|
449
|
+
`\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees(?:\\${pathSep}|$)`,
|
|
450
|
+
);
|
|
451
|
+
return symlinkRe.test(p);
|
|
436
452
|
};
|
|
437
453
|
|
|
438
454
|
if (
|
|
@@ -22,6 +22,8 @@ import { join, sep as pathSep } from "node:path";
|
|
|
22
22
|
import { homedir } from "node:os";
|
|
23
23
|
import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
|
|
24
24
|
|
|
25
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
26
|
+
|
|
25
27
|
// ─── Project Root → Worktree Sync ─────────────────────────────────────────
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -111,7 +113,7 @@ export function syncStateToProjectRoot(
|
|
|
111
113
|
*/
|
|
112
114
|
export function readResourceVersion(): string | null {
|
|
113
115
|
const agentDir =
|
|
114
|
-
process.env.GSD_CODING_AGENT_DIR || join(
|
|
116
|
+
process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
|
|
115
117
|
const manifestPath = join(agentDir, "managed-resources.json");
|
|
116
118
|
try {
|
|
117
119
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
@@ -153,9 +155,18 @@ export function checkResourcesStale(
|
|
|
153
155
|
* Returns the corrected base path.
|
|
154
156
|
*/
|
|
155
157
|
export function escapeStaleWorktree(base: string): string {
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
158
|
+
// Direct layout: /.gsd/worktrees/
|
|
159
|
+
const directMarker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
|
|
160
|
+
let idx = base.indexOf(directMarker);
|
|
161
|
+
if (idx === -1) {
|
|
162
|
+
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
|
|
163
|
+
const symlinkRe = new RegExp(
|
|
164
|
+
`\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees\\${pathSep}`,
|
|
165
|
+
);
|
|
166
|
+
const match = base.match(symlinkRe);
|
|
167
|
+
if (!match || match.index === undefined) return base;
|
|
168
|
+
idx = match.index;
|
|
169
|
+
}
|
|
159
170
|
|
|
160
171
|
// base is inside .gsd/worktrees/<something> — extract the project root
|
|
161
172
|
const projectRoot = base.slice(0, idx);
|
|
@@ -536,129 +536,167 @@ export async function stopAuto(
|
|
|
536
536
|
if (!s.active && !s.paused) return;
|
|
537
537
|
const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
|
|
538
538
|
const reasonSuffix = reason ? ` — ${reason}` : "";
|
|
539
|
-
clearUnitTimeout();
|
|
540
|
-
if (lockBase()) clearLock(lockBase());
|
|
541
|
-
if (lockBase()) releaseSessionLock(lockBase());
|
|
542
|
-
clearSkillSnapshot();
|
|
543
|
-
resetSkillTelemetry();
|
|
544
539
|
|
|
545
|
-
|
|
546
|
-
|
|
540
|
+
try {
|
|
541
|
+
// ── Step 1: Timers and locks ──
|
|
542
|
+
try {
|
|
543
|
+
clearUnitTimeout();
|
|
544
|
+
if (lockBase()) clearLock(lockBase());
|
|
545
|
+
if (lockBase()) releaseSessionLock(lockBase());
|
|
546
|
+
} catch (e) {
|
|
547
|
+
debugLog("stop-cleanup-locks", { error: e instanceof Error ? e.message : String(e) });
|
|
548
|
+
}
|
|
547
549
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
});
|
|
556
|
-
}
|
|
550
|
+
// ── Step 2: Skill state ──
|
|
551
|
+
try {
|
|
552
|
+
clearSkillSnapshot();
|
|
553
|
+
resetSkillTelemetry();
|
|
554
|
+
} catch (e) {
|
|
555
|
+
debugLog("stop-cleanup-skills", { error: e instanceof Error ? e.message : String(e) });
|
|
556
|
+
}
|
|
557
557
|
|
|
558
|
-
|
|
559
|
-
if (isDbAvailable()) {
|
|
558
|
+
// ── Step 3: SIGTERM handler ──
|
|
560
559
|
try {
|
|
561
|
-
|
|
562
|
-
closeDatabase();
|
|
560
|
+
deregisterSigtermHandler();
|
|
563
561
|
} catch (e) {
|
|
564
|
-
debugLog("
|
|
565
|
-
error: e instanceof Error ? e.message : String(e),
|
|
566
|
-
});
|
|
562
|
+
debugLog("stop-cleanup-sigterm", { error: e instanceof Error ? e.message : String(e) });
|
|
567
563
|
}
|
|
568
|
-
}
|
|
569
564
|
|
|
570
|
-
|
|
571
|
-
s.basePath = s.originalBasePath;
|
|
565
|
+
// ── Step 4: Auto-worktree exit ──
|
|
572
566
|
try {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
567
|
+
if (s.currentMilestoneId) {
|
|
568
|
+
const notifyCtx = ctx
|
|
569
|
+
? { notify: ctx.ui.notify.bind(ctx.ui) }
|
|
570
|
+
: { notify: () => {} };
|
|
571
|
+
buildResolver().exitMilestone(s.currentMilestoneId, notifyCtx, {
|
|
572
|
+
preserveBranch: true,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
} catch (e) {
|
|
576
|
+
debugLog("stop-cleanup-worktree", { error: e instanceof Error ? e.message : String(e) });
|
|
576
577
|
}
|
|
577
|
-
}
|
|
578
578
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
579
|
+
// ── Step 5: DB cleanup ──
|
|
580
|
+
if (isDbAvailable()) {
|
|
581
|
+
try {
|
|
582
|
+
const { closeDatabase } = await import("./gsd-db.js");
|
|
583
|
+
closeDatabase();
|
|
584
|
+
} catch (e) {
|
|
585
|
+
debugLog("db-close-failed", {
|
|
586
|
+
error: e instanceof Error ? e.message : String(e),
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
589
590
|
|
|
590
|
-
|
|
591
|
+
// ── Step 6: Restore basePath and chdir ──
|
|
591
592
|
try {
|
|
592
|
-
|
|
593
|
+
if (s.originalBasePath) {
|
|
594
|
+
s.basePath = s.originalBasePath;
|
|
595
|
+
try {
|
|
596
|
+
process.chdir(s.basePath);
|
|
597
|
+
} catch {
|
|
598
|
+
/* best-effort */
|
|
599
|
+
}
|
|
600
|
+
}
|
|
593
601
|
} catch (e) {
|
|
594
|
-
debugLog("stop-
|
|
595
|
-
error: e instanceof Error ? e.message : String(e),
|
|
596
|
-
});
|
|
602
|
+
debugLog("stop-cleanup-basepath", { error: e instanceof Error ? e.message : String(e) });
|
|
597
603
|
}
|
|
598
|
-
}
|
|
599
604
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
605
|
+
// ── Step 7: Ledger notification ──
|
|
606
|
+
try {
|
|
607
|
+
const ledger = getLedger();
|
|
608
|
+
if (ledger && ledger.units.length > 0) {
|
|
609
|
+
const totals = getProjectTotals(ledger.units);
|
|
610
|
+
ctx?.ui.notify(
|
|
611
|
+
`Auto-mode stopped${reasonSuffix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`,
|
|
612
|
+
"info",
|
|
613
|
+
);
|
|
614
|
+
} else {
|
|
615
|
+
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}.`, "info");
|
|
616
|
+
}
|
|
617
|
+
} catch (e) {
|
|
618
|
+
debugLog("stop-cleanup-ledger", { error: e instanceof Error ? e.message : String(e) });
|
|
619
|
+
}
|
|
606
620
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
621
|
+
// ── Step 8: Rebuild state ──
|
|
622
|
+
if (s.basePath) {
|
|
623
|
+
try {
|
|
624
|
+
await rebuildState(s.basePath);
|
|
625
|
+
} catch (e) {
|
|
626
|
+
debugLog("stop-rebuild-state-failed", {
|
|
627
|
+
error: e instanceof Error ? e.message : String(e),
|
|
628
|
+
});
|
|
629
|
+
}
|
|
611
630
|
}
|
|
612
|
-
}
|
|
613
631
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
632
|
+
// ── Step 9: Cmux sidebar / event log ──
|
|
633
|
+
try {
|
|
634
|
+
clearCmuxSidebar(loadedPreferences);
|
|
635
|
+
logCmuxEvent(
|
|
636
|
+
loadedPreferences,
|
|
637
|
+
`Auto-mode stopped${reasonSuffix || ""}.`,
|
|
638
|
+
reason?.startsWith("Blocked:") ? "warning" : "info",
|
|
639
|
+
);
|
|
640
|
+
} catch (e) {
|
|
641
|
+
debugLog("stop-cleanup-cmux", { error: e instanceof Error ? e.message : String(e) });
|
|
642
|
+
}
|
|
618
643
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
644
|
+
// ── Step 10: Debug summary ──
|
|
645
|
+
try {
|
|
646
|
+
if (isDebugEnabled()) {
|
|
647
|
+
const logPath = writeDebugSummary();
|
|
648
|
+
if (logPath) {
|
|
649
|
+
ctx?.ui.notify(`Debug log written → ${logPath}`, "info");
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
} catch (e) {
|
|
653
|
+
debugLog("stop-cleanup-debug", { error: e instanceof Error ? e.message : String(e) });
|
|
654
|
+
}
|
|
624
655
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
s.currentUnit = null;
|
|
635
|
-
s.autoModeStartModel = null;
|
|
636
|
-
s.currentMilestoneId = null;
|
|
637
|
-
s.originalBasePath = "";
|
|
638
|
-
s.completedUnits = [];
|
|
639
|
-
s.pendingQuickTasks = [];
|
|
640
|
-
clearSliceProgressCache();
|
|
641
|
-
clearActivityLogState();
|
|
642
|
-
resetProactiveHealing();
|
|
643
|
-
s.pendingCrashRecovery = null;
|
|
644
|
-
s.pendingVerificationRetry = null;
|
|
645
|
-
s.verificationRetryCount.clear();
|
|
646
|
-
s.pausedSessionFile = null;
|
|
647
|
-
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
648
|
-
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
649
|
-
ctx?.ui.setFooter(undefined);
|
|
656
|
+
// ── Step 11: Reset metrics, routing, hooks ──
|
|
657
|
+
try {
|
|
658
|
+
resetMetrics();
|
|
659
|
+
resetRoutingHistory();
|
|
660
|
+
resetHookState();
|
|
661
|
+
if (s.basePath) clearPersistedHookState(s.basePath);
|
|
662
|
+
} catch (e) {
|
|
663
|
+
debugLog("stop-cleanup-metrics", { error: e instanceof Error ? e.message : String(e) });
|
|
664
|
+
}
|
|
650
665
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
s.
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
if (original) await pi.setModel(original);
|
|
657
|
-
s.originalModelId = null;
|
|
658
|
-
s.originalModelProvider = null;
|
|
659
|
-
}
|
|
666
|
+
// ── Step 12: Remove paused-session metadata (#1383) ──
|
|
667
|
+
try {
|
|
668
|
+
const pausedPath = join(gsdRoot(s.originalBasePath || s.basePath), "runtime", "paused-session.json");
|
|
669
|
+
if (existsSync(pausedPath)) unlinkSync(pausedPath);
|
|
670
|
+
} catch { /* non-fatal */ }
|
|
660
671
|
|
|
661
|
-
|
|
672
|
+
// ── Step 13: Restore original model (before reset clears IDs) ──
|
|
673
|
+
try {
|
|
674
|
+
if (pi && ctx && s.originalModelId && s.originalModelProvider) {
|
|
675
|
+
const original = ctx.modelRegistry.find(
|
|
676
|
+
s.originalModelProvider,
|
|
677
|
+
s.originalModelId,
|
|
678
|
+
);
|
|
679
|
+
if (original) await pi.setModel(original);
|
|
680
|
+
}
|
|
681
|
+
} catch (e) {
|
|
682
|
+
debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
|
|
683
|
+
}
|
|
684
|
+
} finally {
|
|
685
|
+
// ── Critical invariants: these MUST execute regardless of errors ──
|
|
686
|
+
// External cleanup (not covered by session reset)
|
|
687
|
+
clearInFlightTools();
|
|
688
|
+
clearSliceProgressCache();
|
|
689
|
+
clearActivityLogState();
|
|
690
|
+
resetProactiveHealing();
|
|
691
|
+
|
|
692
|
+
// UI cleanup
|
|
693
|
+
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
694
|
+
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
695
|
+
ctx?.ui.setFooter(undefined);
|
|
696
|
+
|
|
697
|
+
// Reset all session state in one call
|
|
698
|
+
s.reset();
|
|
699
|
+
}
|
|
662
700
|
}
|
|
663
701
|
|
|
664
702
|
/**
|
|
@@ -59,8 +59,17 @@ const VALID_CLASSIFICATIONS: readonly string[] = [
|
|
|
59
59
|
*/
|
|
60
60
|
export function resolveCapturesPath(basePath: string): string {
|
|
61
61
|
const resolved = resolve(basePath);
|
|
62
|
+
// Direct layout: /.gsd/worktrees/
|
|
62
63
|
const worktreeMarker = `${sep}.gsd${sep}worktrees${sep}`;
|
|
63
|
-
|
|
64
|
+
let idx = resolved.indexOf(worktreeMarker);
|
|
65
|
+
if (idx === -1) {
|
|
66
|
+
// Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
|
|
67
|
+
const symlinkRe = new RegExp(
|
|
68
|
+
`\\${sep}\\.gsd\\${sep}projects\\${sep}[a-f0-9]+\\${sep}worktrees\\${sep}`,
|
|
69
|
+
);
|
|
70
|
+
const match = resolved.match(symlinkRe);
|
|
71
|
+
if (match && match.index !== undefined) idx = match.index;
|
|
72
|
+
}
|
|
64
73
|
if (idx !== -1) {
|
|
65
74
|
// basePath is inside a worktree — resolve to project root
|
|
66
75
|
const projectRoot = resolved.slice(0, idx);
|