gsd-pi 2.38.0-dev.96dc7fb → 2.38.0-dev.add4f78
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/extension-registry.js +2 -2
- package/dist/remote-questions-config.js +2 -2
- package/dist/resources/extensions/env-utils.js +29 -0
- package/dist/resources/extensions/get-secrets-from-user.js +5 -24
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +7 -8
- package/dist/resources/extensions/gsd/auto-loop.js +54 -30
- package/dist/resources/extensions/gsd/auto-post-unit.js +75 -71
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +2 -1
- package/dist/resources/extensions/gsd/auto.js +10 -26
- package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
- package/dist/resources/extensions/gsd/commands.js +2 -1
- package/dist/resources/extensions/gsd/detection.js +1 -2
- 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/preferences-validation.js +1 -1
- package/dist/resources/extensions/gsd/preferences.js +4 -3
- package/dist/resources/extensions/gsd/repo-identity.js +2 -1
- 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/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/isolation.js +2 -1
- package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
- package/package.json +1 -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 +5 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +6 -8
- package/src/resources/extensions/gsd/auto-loop.ts +70 -63
- package/src/resources/extensions/gsd/auto-post-unit.ts +52 -42
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +3 -1
- package/src/resources/extensions/gsd/auto.ts +14 -29
- package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
- package/src/resources/extensions/gsd/commands.ts +3 -1
- package/src/resources/extensions/gsd/detection.ts +2 -2
- 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-validation.ts +1 -1
- package/src/resources/extensions/gsd/preferences.ts +5 -3
- package/src/resources/extensions/gsd/repo-identity.ts +3 -1
- package/src/resources/extensions/gsd/resource-version.ts +3 -1
- package/src/resources/extensions/gsd/state.ts +1 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
- 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/isolation.ts +3 -1
- package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
|
@@ -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
|
|
|
@@ -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"));
|
|
@@ -622,43 +622,28 @@ export async function stopAuto(
|
|
|
622
622
|
if (existsSync(pausedPath)) unlinkSync(pausedPath);
|
|
623
623
|
} catch { /* non-fatal */ }
|
|
624
624
|
|
|
625
|
-
|
|
626
|
-
s.paused = false;
|
|
627
|
-
s.stepMode = false;
|
|
628
|
-
s.unitDispatchCount.clear();
|
|
629
|
-
s.unitRecoveryCount.clear();
|
|
630
|
-
clearInFlightTools();
|
|
631
|
-
s.lastBudgetAlertLevel = 0;
|
|
632
|
-
s.lastStateRebuildAt = 0;
|
|
633
|
-
s.unitLifetimeDispatches.clear();
|
|
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);
|
|
650
|
-
|
|
625
|
+
// Restore original model before reset() clears the IDs
|
|
651
626
|
if (pi && ctx && s.originalModelId && s.originalModelProvider) {
|
|
652
627
|
const original = ctx.modelRegistry.find(
|
|
653
628
|
s.originalModelProvider,
|
|
654
629
|
s.originalModelId,
|
|
655
630
|
);
|
|
656
631
|
if (original) await pi.setModel(original);
|
|
657
|
-
s.originalModelId = null;
|
|
658
|
-
s.originalModelProvider = null;
|
|
659
632
|
}
|
|
660
633
|
|
|
661
|
-
|
|
634
|
+
// External cleanup (not covered by session reset)
|
|
635
|
+
clearInFlightTools();
|
|
636
|
+
clearSliceProgressCache();
|
|
637
|
+
clearActivityLogState();
|
|
638
|
+
resetProactiveHealing();
|
|
639
|
+
|
|
640
|
+
// UI cleanup
|
|
641
|
+
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
642
|
+
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
643
|
+
ctx?.ui.setFooter(undefined);
|
|
644
|
+
|
|
645
|
+
// Reset all session state in one call
|
|
646
|
+
s.reset();
|
|
662
647
|
}
|
|
663
648
|
|
|
664
649
|
/**
|
|
@@ -11,6 +11,8 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFile
|
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
|
|
14
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
15
|
+
|
|
14
16
|
// ─── Types (mirrored from extension-registry.ts) ────────────────────────────
|
|
15
17
|
|
|
16
18
|
interface ExtensionManifest {
|
|
@@ -48,11 +50,11 @@ interface ExtensionRegistry {
|
|
|
48
50
|
// ─── Registry I/O ───────────────────────────────────────────────────────────
|
|
49
51
|
|
|
50
52
|
function getRegistryPath(): string {
|
|
51
|
-
return join(
|
|
53
|
+
return join(gsdHome, "extensions", "registry.json");
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
function getAgentExtensionsDir(): string {
|
|
55
|
-
return join(
|
|
57
|
+
return join(gsdHome, "agent", "extensions");
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
function loadRegistry(): ExtensionRegistry {
|
|
@@ -10,6 +10,8 @@ import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { gsdRoot } from "./paths.js";
|
|
13
|
+
|
|
14
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
13
15
|
import { enableDebug } from "./debug-logger.js";
|
|
14
16
|
import { deriveState } from "./state.js";
|
|
15
17
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
@@ -482,7 +484,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
482
484
|
if (parts.length === 3 && ["enable", "disable", "info"].includes(parts[1])) {
|
|
483
485
|
const idPrefix = parts[2] ?? "";
|
|
484
486
|
try {
|
|
485
|
-
const extDir = join(
|
|
487
|
+
const extDir = join(gsdHome, "agent", "extensions");
|
|
486
488
|
const ids: { id: string; name: string }[] = [];
|
|
487
489
|
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
|
|
488
490
|
if (!entry.isDirectory()) continue;
|
|
@@ -11,6 +11,8 @@ import { join } from "node:path";
|
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { gsdRoot } from "./paths.js";
|
|
13
13
|
|
|
14
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
15
|
+
|
|
14
16
|
// ─── Types ──────────────────────────────────────────────────────────────────────
|
|
15
17
|
|
|
16
18
|
export interface ProjectDetection {
|
|
@@ -400,7 +402,6 @@ function detectVerificationCommands(
|
|
|
400
402
|
* Check if global GSD setup exists (has ~/.gsd/ with preferences).
|
|
401
403
|
*/
|
|
402
404
|
export function hasGlobalSetup(): boolean {
|
|
403
|
-
const gsdHome = join(homedir(), ".gsd");
|
|
404
405
|
return (
|
|
405
406
|
existsSync(join(gsdHome, "preferences.md")) ||
|
|
406
407
|
existsSync(join(gsdHome, "PREFERENCES.md"))
|
|
@@ -412,7 +413,6 @@ export function hasGlobalSetup(): boolean {
|
|
|
412
413
|
* Returns true if ~/.gsd/ doesn't exist or has no preferences or auth.
|
|
413
414
|
*/
|
|
414
415
|
export function isFirstEverLaunch(): boolean {
|
|
415
|
-
const gsdHome = join(homedir(), ".gsd");
|
|
416
416
|
if (!existsSync(gsdHome)) return true;
|
|
417
417
|
|
|
418
418
|
// If we have preferences, not first launch
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "./metrics.js";
|
|
12
12
|
import type { UnitMetrics } from "./metrics.js";
|
|
13
13
|
import { gsdRoot } from "./paths.js";
|
|
14
|
-
import { formatDuration, fileLink } from "../shared/
|
|
14
|
+
import { formatDuration, fileLink } from "../shared/format-utils.js";
|
|
15
15
|
import { getErrorMessage } from "./error-utils.js";
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -7,7 +7,7 @@ import { promises as fs } from 'node:fs';
|
|
|
7
7
|
import { resolve } from 'node:path';
|
|
8
8
|
import { atomicWriteAsync } from './atomic-write.js';
|
|
9
9
|
import { resolveMilestoneFile, relMilestoneFile, resolveGsdRootFile } from './paths.js';
|
|
10
|
-
import { milestoneIdSort, findMilestoneIds } from './
|
|
10
|
+
import { milestoneIdSort, findMilestoneIds } from './milestone-ids.js';
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
13
|
Roadmap, BoundaryMapEntry,
|
|
@@ -20,7 +20,7 @@ import type {
|
|
|
20
20
|
ManifestStatus,
|
|
21
21
|
} from './types.js';
|
|
22
22
|
|
|
23
|
-
import { checkExistingEnvKeys } from '../
|
|
23
|
+
import { checkExistingEnvKeys } from '../env-utils.js';
|
|
24
24
|
import { parseRoadmapSlices } from './roadmap-slices.js';
|
|
25
25
|
import { nativeParseRoadmap, nativeExtractSection, nativeParsePlanFile, nativeParseSummaryFile, NATIVE_UNAVAILABLE } from './native-parser-bridge.js';
|
|
26
26
|
import { debugTime, debugCount } from './debug-logger.js';
|
|
@@ -27,7 +27,7 @@ import { deriveState } from "./state.js";
|
|
|
27
27
|
import { isAutoActive } from "./auto.js";
|
|
28
28
|
import { loadPrompt } from "./prompt-loader.js";
|
|
29
29
|
import { gsdRoot } from "./paths.js";
|
|
30
|
-
import { formatDuration } from "../shared/
|
|
30
|
+
import { formatDuration } from "../shared/format-utils.js";
|
|
31
31
|
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
32
32
|
|
|
33
33
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
@@ -60,6 +60,8 @@ import { join } from "node:path";
|
|
|
60
60
|
import { existsSync, readFileSync } from "node:fs";
|
|
61
61
|
import { homedir } from "node:os";
|
|
62
62
|
import { shortcutDesc } from "../shared/mod.js";
|
|
63
|
+
|
|
64
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
63
65
|
import { Text } from "@gsd/pi-tui";
|
|
64
66
|
import { pauseAutoForProviderError, classifyProviderError } from "./provider-error-pause.js";
|
|
65
67
|
import { toPosixPath } from "../shared/mod.js";
|
|
@@ -73,7 +75,7 @@ import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../cmux/index.js"
|
|
|
73
75
|
|
|
74
76
|
function warnDeprecatedAgentInstructions(): void {
|
|
75
77
|
const paths = [
|
|
76
|
-
join(
|
|
78
|
+
join(gsdHome, "agent-instructions.md"),
|
|
77
79
|
join(process.cwd(), ".gsd", "agent-instructions.md"),
|
|
78
80
|
];
|
|
79
81
|
for (const p of paths) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Zero Pi dependencies — uses only exported helpers from files.ts.
|
|
4
4
|
|
|
5
5
|
import { splitFrontmatter, parseFrontmatterMap, extractBoldField } from '../files.js';
|
|
6
|
-
import { normalizeStringArray } from '../../shared/
|
|
6
|
+
import { normalizeStringArray } from '../../shared/format-utils.js';
|
|
7
7
|
|
|
8
8
|
import type {
|
|
9
9
|
PlanningRoadmap,
|
|
@@ -10,7 +10,7 @@ import type { GitPreferences } from "./git-service.js";
|
|
|
10
10
|
import type { PostUnitHookConfig, PreDispatchHookConfig, TokenProfile, PhaseSkipPreferences } from "./types.js";
|
|
11
11
|
import type { DynamicRoutingConfig } from "./model-router.js";
|
|
12
12
|
import { VALID_BRANCH_NAME } from "./git-service.js";
|
|
13
|
-
import { normalizeStringArray } from "../shared/
|
|
13
|
+
import { normalizeStringArray } from "../shared/format-utils.js";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
KNOWN_PREFERENCE_KEYS,
|
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
import { existsSync, readFileSync } from "node:fs";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
16
18
|
import { gsdRoot } from "./paths.js";
|
|
17
19
|
import { parse as parseYaml } from "yaml";
|
|
18
20
|
import type { PostUnitHookConfig, PreDispatchHookConfig, TokenProfile } from "./types.js";
|
|
19
21
|
import type { DynamicRoutingConfig } from "./model-router.js";
|
|
20
|
-
import { normalizeStringArray } from "../shared/
|
|
22
|
+
import { normalizeStringArray } from "../shared/format-utils.js";
|
|
21
23
|
import { resolveProfileDefaults as _resolveProfileDefaults } from "./preferences-models.js";
|
|
22
24
|
|
|
23
25
|
import {
|
|
@@ -82,14 +84,14 @@ export {
|
|
|
82
84
|
|
|
83
85
|
// ─── Path Constants & Getters ───────────────────────────────────────────────
|
|
84
86
|
|
|
85
|
-
const GLOBAL_PREFERENCES_PATH = join(
|
|
87
|
+
const GLOBAL_PREFERENCES_PATH = join(gsdHome, "preferences.md");
|
|
86
88
|
const LEGACY_GLOBAL_PREFERENCES_PATH = join(homedir(), ".pi", "agent", "gsd-preferences.md");
|
|
87
89
|
function projectPreferencesPath(): string {
|
|
88
90
|
return join(gsdRoot(process.cwd()), "preferences.md");
|
|
89
91
|
}
|
|
90
92
|
// Bootstrap in gitignore.ts historically created PREFERENCES.md (uppercase) by mistake.
|
|
91
93
|
// Check uppercase as a fallback so those files aren't silently ignored.
|
|
92
|
-
const GLOBAL_PREFERENCES_PATH_UPPERCASE = join(
|
|
94
|
+
const GLOBAL_PREFERENCES_PATH_UPPERCASE = join(gsdHome, "PREFERENCES.md");
|
|
93
95
|
function projectPreferencesPathUppercase(): string {
|
|
94
96
|
return join(gsdRoot(process.cwd()), "PREFERENCES.md");
|
|
95
97
|
}
|
|
@@ -12,6 +12,8 @@ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, s
|
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
import { join, resolve } from "node:path";
|
|
14
14
|
|
|
15
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
16
|
+
|
|
15
17
|
// ─── Repo Identity ──────────────────────────────────────────────────────────
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -113,7 +115,7 @@ export function repoIdentity(basePath: string): string {
|
|
|
113
115
|
* otherwise `~/.gsd/projects/<hash>`.
|
|
114
116
|
*/
|
|
115
117
|
export function externalGsdRoot(basePath: string): string {
|
|
116
|
-
const base = process.env.GSD_STATE_DIR ||
|
|
118
|
+
const base = process.env.GSD_STATE_DIR || gsdHome;
|
|
117
119
|
return join(base, "projects", repoIdentity(basePath));
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -11,6 +11,8 @@ import { join } from "node:path";
|
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { resolveProjectRoot } from "./worktree.js";
|
|
13
13
|
|
|
14
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
15
|
+
|
|
14
16
|
// ─── Resource Staleness ───────────────────────────────────────────────────
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -23,7 +25,7 @@ function isManifestWithVersion(data: unknown): data is { gsdVersion: string } {
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export function readResourceVersion(): string | null {
|
|
26
|
-
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(
|
|
28
|
+
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
|
|
27
29
|
const manifestPath = join(agentDir, "managed-resources.json");
|
|
28
30
|
const manifest = loadJsonFileOrNull(manifestPath, isManifestWithVersion);
|
|
29
31
|
return manifest?.gsdVersion ?? null;
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
gsdRoot,
|
|
32
32
|
} from './paths.js';
|
|
33
33
|
|
|
34
|
-
import { milestoneIdSort, findMilestoneIds } from './
|
|
34
|
+
import { milestoneIdSort, findMilestoneIds } from './milestone-ids.js';
|
|
35
35
|
import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
|
|
36
36
|
|
|
37
37
|
import { join, resolve } from 'path';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
4
4
|
import { deriveState } from './state.js';
|
|
5
5
|
import { parseRoadmap, parsePlan, parseSummary, loadFile } from './files.js';
|
|
6
|
-
import { findMilestoneIds } from './
|
|
6
|
+
import { findMilestoneIds } from './milestone-ids.js';
|
|
7
7
|
import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile } from './paths.js';
|
|
8
8
|
import {
|
|
9
9
|
getLedger,
|
|
@@ -7,6 +7,8 @@ import { join } from "node:path";
|
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { readPromptRecord } from "./store.js";
|
|
9
9
|
|
|
10
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
11
|
+
|
|
10
12
|
export interface LatestPromptSummary {
|
|
11
13
|
id: string;
|
|
12
14
|
status: string;
|
|
@@ -14,7 +16,7 @@ export interface LatestPromptSummary {
|
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function getLatestPromptSummary(): LatestPromptSummary | null {
|
|
17
|
-
const runtimeDir = join(
|
|
19
|
+
const runtimeDir = join(gsdHome, "runtime", "remote-questions");
|
|
18
20
|
if (!existsSync(runtimeDir)) return null;
|
|
19
21
|
const files = readdirSync(runtimeDir).filter((f) => f.endsWith(".json"));
|
|
20
22
|
if (files.length === 0) return null;
|
|
@@ -7,8 +7,10 @@ import { join } from "node:path";
|
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import type { RemotePrompt, RemotePromptRecord, RemotePromptRef, RemoteAnswer, RemotePromptStatus } from "./types.js";
|
|
9
9
|
|
|
10
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
11
|
+
|
|
10
12
|
function runtimeDir(): string {
|
|
11
|
-
return join(
|
|
13
|
+
return join(gsdHome, "runtime", "remote-questions");
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
function recordPath(id: string): string {
|
|
@@ -17,7 +17,8 @@ import { resolveSearchProviderFromPreferences } from '../gsd/preferences.js'
|
|
|
17
17
|
// Compute authFilePath locally instead of importing from app-paths.ts,
|
|
18
18
|
// because extensions are copied to ~/.gsd/agent/extensions/ at runtime
|
|
19
19
|
// where the relative import '../../../app-paths.ts' doesn't resolve.
|
|
20
|
-
const
|
|
20
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), '.gsd')
|
|
21
|
+
const authFilePath = join(gsdHome, 'agent', 'auth.json')
|
|
21
22
|
|
|
22
23
|
export type SearchProvider = 'tavily' | 'brave' | 'ollama'
|
|
23
24
|
export type SearchProviderPreference = SearchProvider | 'auto'
|
|
@@ -57,8 +57,10 @@ function encodeCwd(cwd: string): string {
|
|
|
57
57
|
return cwd.replace(/\//g, "--");
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
const gsdHome = process.env.GSD_HOME || path.join(os.homedir(), ".gsd");
|
|
61
|
+
|
|
60
62
|
function getIsolationBaseDir(cwd: string, taskId: string): string {
|
|
61
|
-
return path.join(
|
|
63
|
+
return path.join(gsdHome, "wt", encodeCwd(cwd), taskId);
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
// Track active isolation dirs for cleanup on exit
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
|
9
9
|
import { join, basename } from "node:path";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
|
+
|
|
12
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
11
13
|
import type { Rule } from "./ttsr-manager.js";
|
|
12
14
|
import { splitFrontmatter, parseFrontmatterMap } from "../shared/frontmatter.js";
|
|
13
15
|
|
|
@@ -59,7 +61,7 @@ function scanDir(dir: string): Rule[] {
|
|
|
59
61
|
* Project rules override global rules with the same name.
|
|
60
62
|
*/
|
|
61
63
|
export function loadRules(cwd: string): Rule[] {
|
|
62
|
-
const globalDir = join(
|
|
64
|
+
const globalDir = join(gsdHome, "agent", "rules");
|
|
63
65
|
const projectDir = join(cwd, ".gsd", "rules");
|
|
64
66
|
|
|
65
67
|
const globalRules = scanDir(globalDir);
|