gsd-pi 2.72.0-dev.3118184 → 2.72.0-dev.4f3264a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/async-jobs/await-tool.js +4 -7
- package/dist/resources/extensions/async-jobs/job-manager.js +3 -28
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +26 -27
- package/dist/resources/extensions/gsd/auto/loop.js +1 -84
- package/dist/resources/extensions/gsd/auto-post-unit.js +0 -6
- package/dist/resources/extensions/gsd/auto.js +19 -25
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -9
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -4
- package/dist/resources/extensions/gsd/context-injector.js +1 -1
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +7 -3
- package/dist/resources/extensions/gsd/gsd-db.js +5 -47
- package/dist/resources/extensions/gsd/key-manager.js +0 -2
- package/dist/resources/extensions/gsd/preferences-skills.js +34 -2
- package/dist/resources/extensions/gsd/preferences-types.js +0 -15
- package/dist/resources/extensions/gsd/preferences.js +3 -16
- package/dist/resources/extensions/gsd/prompt-loader.js +1 -4
- package/dist/resources/extensions/gsd/state.js +1 -21
- package/dist/resources/extensions/gsd/write-intercept.js +1 -10
- package/dist/resources/extensions/ollama/index.js +5 -4
- package/dist/resources/extensions/ollama/ollama-client.js +6 -35
- package/dist/resources/extensions/ollama/ollama-discovery.js +6 -32
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +3 -3
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/chunks/2331.js +16 -16
- package/dist/web/standalone/.next/server/chunks/4741.js +12 -12
- package/dist/web/standalone/.next/server/chunks/5822.js +2 -2
- package/dist/web/standalone/.next/server/chunks/63.js +8 -8
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/functions-config-manifest.json +9 -0
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +2 -29
- package/dist/web/standalone/.next/server/middleware.js +12 -4
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/env-api-keys.js +0 -1
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/models.custom.d.ts +0 -105
- package/packages/pi-ai/dist/models.custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.custom.js +0 -97
- package/packages/pi-ai/dist/models.custom.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +140 -648
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +364 -861
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.test.js +0 -105
- package/packages/pi-ai/dist/models.test.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +1 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/env-api-keys.ts +0 -1
- package/packages/pi-ai/src/models.custom.ts +0 -98
- package/packages/pi-ai/src/models.generated.ts +364 -861
- package/packages/pi-ai/src/models.test.ts +0 -135
- package/packages/pi-ai/src/types.ts +0 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +0 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/src/core/model-resolver.ts +0 -1
- package/src/resources/extensions/async-jobs/await-tool.test.ts +7 -40
- package/src/resources/extensions/async-jobs/await-tool.ts +4 -7
- package/src/resources/extensions/async-jobs/job-manager.ts +3 -33
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +26 -27
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +2 -20
- package/src/resources/extensions/gsd/auto/loop.ts +1 -89
- package/src/resources/extensions/gsd/auto-post-unit.ts +0 -7
- package/src/resources/extensions/gsd/auto.ts +20 -25
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +10 -8
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -5
- package/src/resources/extensions/gsd/context-injector.ts +1 -1
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +8 -4
- package/src/resources/extensions/gsd/gsd-db.ts +5 -52
- package/src/resources/extensions/gsd/key-manager.ts +0 -2
- package/src/resources/extensions/gsd/preferences-skills.ts +36 -2
- package/src/resources/extensions/gsd/preferences-types.ts +0 -16
- package/src/resources/extensions/gsd/preferences.ts +6 -19
- package/src/resources/extensions/gsd/prompt-loader.ts +1 -6
- package/src/resources/extensions/gsd/state.ts +0 -20
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +0 -74
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +0 -63
- package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -53
- package/src/resources/extensions/gsd/write-intercept.ts +1 -10
- package/src/resources/extensions/ollama/index.ts +5 -4
- package/src/resources/extensions/ollama/ollama-client.ts +6 -35
- package/src/resources/extensions/ollama/ollama-discovery.ts +6 -37
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -54
- package/dist/resources/extensions/gsd/definition-io.js +0 -15
- package/dist/web/standalone/.next/server/edge-runtime-webpack.js +0 -2
- package/packages/pi-ai/dist/models.generated.test.d.ts +0 -2
- package/packages/pi-ai/dist/models.generated.test.d.ts.map +0 -1
- package/packages/pi-ai/dist/models.generated.test.js +0 -334
- package/packages/pi-ai/dist/models.generated.test.js.map +0 -1
- package/packages/pi-ai/src/models.generated.test.ts +0 -373
- package/src/resources/extensions/gsd/definition-io.ts +0 -18
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +0 -27
- package/src/resources/extensions/gsd/tests/block-db-writes.test.ts +0 -63
- package/src/resources/extensions/gsd/tests/definition-io.test.ts +0 -57
- package/src/resources/extensions/gsd/tests/doctor-heal-fixable-warnings.test.ts +0 -14
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +0 -104
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +0 -54
- package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +0 -34
- package/src/resources/extensions/gsd/tests/preferences-formatting.test.ts +0 -87
- package/src/resources/extensions/gsd/tests/prompt-loader-working-directory.test.ts +0 -19
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +0 -97
- package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +0 -41
- /package/dist/web/standalone/.next/static/{NzO79SOz9jHX-VY5-0t2O → vr6Pbde48w4rMUplqDdh_}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{NzO79SOz9jHX-VY5-0t2O → vr6Pbde48w4rMUplqDdh_}/_ssgManifest.js +0 -0
|
@@ -29,69 +29,6 @@ import {
|
|
|
29
29
|
import { debugLog } from "../debug-logger.js";
|
|
30
30
|
import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
|
|
31
31
|
import { resolveEngine } from "../engine-resolver.js";
|
|
32
|
-
import { logWarning } from "../workflow-logger.js";
|
|
33
|
-
import { gsdRoot } from "../paths.js";
|
|
34
|
-
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
35
|
-
import { join } from "node:path";
|
|
36
|
-
|
|
37
|
-
// ── Stuck detection persistence (#3704) ──────────────────────────────────
|
|
38
|
-
// Persist stuck detection state to disk so it survives session restarts.
|
|
39
|
-
// Without this, restarting auto-mode resets all counters, allowing the
|
|
40
|
-
// same blocked unit to burn a full retry budget each session.
|
|
41
|
-
function stuckStatePath(basePath: string): string {
|
|
42
|
-
return join(gsdRoot(basePath), "runtime", "stuck-state.json");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function loadStuckState(basePath: string): { recentUnits: Array<{ key: string }>; stuckRecoveryAttempts: number } {
|
|
46
|
-
try {
|
|
47
|
-
const data = JSON.parse(readFileSync(stuckStatePath(basePath), "utf-8"));
|
|
48
|
-
return {
|
|
49
|
-
recentUnits: Array.isArray(data.recentUnits) ? data.recentUnits : [],
|
|
50
|
-
stuckRecoveryAttempts: typeof data.stuckRecoveryAttempts === "number" ? data.stuckRecoveryAttempts : 0,
|
|
51
|
-
};
|
|
52
|
-
} catch (err) {
|
|
53
|
-
debugLog("autoLoop", { phase: "load-stuck-state-failed", error: err instanceof Error ? err.message : String(err) });
|
|
54
|
-
return { recentUnits: [], stuckRecoveryAttempts: 0 };
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function saveStuckState(basePath: string, state: LoopState): void {
|
|
59
|
-
try {
|
|
60
|
-
const filePath = stuckStatePath(basePath);
|
|
61
|
-
mkdirSync(join(gsdRoot(basePath), "runtime"), { recursive: true });
|
|
62
|
-
writeFileSync(filePath, JSON.stringify({
|
|
63
|
-
recentUnits: state.recentUnits.slice(-20), // keep last 20 entries
|
|
64
|
-
stuckRecoveryAttempts: state.stuckRecoveryAttempts,
|
|
65
|
-
updatedAt: new Date().toISOString(),
|
|
66
|
-
}) + "\n");
|
|
67
|
-
} catch (err) {
|
|
68
|
-
debugLog("autoLoop", { phase: "save-stuck-state-failed", error: err instanceof Error ? err.message : String(err) });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ── Memory pressure monitoring (#3331) ──────────────────────────────────
|
|
73
|
-
// Check heap usage every N iterations and trigger graceful shutdown before
|
|
74
|
-
// the OS OOM killer sends SIGKILL. The threshold is 90% of the V8 heap
|
|
75
|
-
// limit (--max-old-space-size or default ~1.5-4GB depending on platform).
|
|
76
|
-
const MEMORY_CHECK_INTERVAL = 5; // check every 5 iterations
|
|
77
|
-
const MEMORY_PRESSURE_THRESHOLD = 0.85; // 85% of heap limit
|
|
78
|
-
|
|
79
|
-
function checkMemoryPressure(): { pressured: boolean; heapMB: number; limitMB: number; pct: number } {
|
|
80
|
-
const mem = process.memoryUsage();
|
|
81
|
-
// v8.getHeapStatistics() gives heap_size_limit but requires import
|
|
82
|
-
// Use a conservative estimate: RSS > 3GB is danger zone on most systems
|
|
83
|
-
const heapMB = Math.round(mem.heapUsed / 1024 / 1024);
|
|
84
|
-
const rssMB = Math.round(mem.rss / 1024 / 1024);
|
|
85
|
-
// Try to get the actual V8 heap limit
|
|
86
|
-
let limitMB = 4096; // conservative default
|
|
87
|
-
try {
|
|
88
|
-
const v8 = require("node:v8");
|
|
89
|
-
const stats = v8.getHeapStatistics();
|
|
90
|
-
limitMB = Math.round(stats.heap_size_limit / 1024 / 1024);
|
|
91
|
-
} catch { limitMB = 4096; /* v8 stats unavailable — use conservative default */ }
|
|
92
|
-
const pct = heapMB / limitMB;
|
|
93
|
-
return { pressured: pct > MEMORY_PRESSURE_THRESHOLD, heapMB, limitMB, pct };
|
|
94
|
-
}
|
|
95
32
|
|
|
96
33
|
/**
|
|
97
34
|
* Main auto-mode execution loop. Iterates: derive → dispatch → guards →
|
|
@@ -109,13 +46,7 @@ export async function autoLoop(
|
|
|
109
46
|
): Promise<void> {
|
|
110
47
|
debugLog("autoLoop", { phase: "enter" });
|
|
111
48
|
let iteration = 0;
|
|
112
|
-
|
|
113
|
-
const persisted = loadStuckState(s.basePath);
|
|
114
|
-
const loopState: LoopState = {
|
|
115
|
-
recentUnits: persisted.recentUnits,
|
|
116
|
-
stuckRecoveryAttempts: persisted.stuckRecoveryAttempts,
|
|
117
|
-
consecutiveFinalizeTimeouts: 0,
|
|
118
|
-
};
|
|
49
|
+
const loopState: LoopState = { recentUnits: [], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 };
|
|
119
50
|
let consecutiveErrors = 0;
|
|
120
51
|
let consecutiveCooldowns = 0;
|
|
121
52
|
const recentErrorMessages: string[] = [];
|
|
@@ -143,24 +74,6 @@ export async function autoLoop(
|
|
|
143
74
|
break;
|
|
144
75
|
}
|
|
145
76
|
|
|
146
|
-
// ── Memory pressure check (#3331) ──
|
|
147
|
-
// Graceful shutdown before OOM killer sends SIGKILL.
|
|
148
|
-
if (iteration % MEMORY_CHECK_INTERVAL === 0) {
|
|
149
|
-
const mem = checkMemoryPressure();
|
|
150
|
-
debugLog("autoLoop", { phase: "memory-check", ...mem });
|
|
151
|
-
if (mem.pressured) {
|
|
152
|
-
logWarning("dispatch", `Memory pressure: ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%) — stopping auto-mode to prevent OOM kill`);
|
|
153
|
-
await deps.stopAuto(
|
|
154
|
-
ctx,
|
|
155
|
-
pi,
|
|
156
|
-
`Memory pressure: heap at ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%). ` +
|
|
157
|
-
`Stopping gracefully to prevent OOM kill after ${iteration} iterations. ` +
|
|
158
|
-
`Resume with /gsd auto to continue from where you left off.`,
|
|
159
|
-
);
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
77
|
if (!s.cmdCtx) {
|
|
165
78
|
debugLog("autoLoop", { phase: "exit", reason: "no-cmdCtx" });
|
|
166
79
|
break;
|
|
@@ -294,7 +207,6 @@ export async function autoLoop(
|
|
|
294
207
|
consecutiveCooldowns = 0;
|
|
295
208
|
recentErrorMessages.length = 0;
|
|
296
209
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
|
|
297
|
-
saveStuckState(s.basePath, loopState); // persist across session restarts (#3704)
|
|
298
210
|
debugLog("autoLoop", { phase: "iteration-complete", iteration });
|
|
299
211
|
|
|
300
212
|
if (reconcileResult.outcome === "milestone-complete") {
|
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
buildTaskFileName,
|
|
26
26
|
} from "./paths.js";
|
|
27
27
|
import { invalidateAllCaches } from "./cache.js";
|
|
28
|
-
import { rebuildState } from "./doctor.js";
|
|
29
28
|
import { parseUnitId } from "./unit-id.js";
|
|
30
29
|
import { closeoutUnit, type CloseoutOptions } from "./auto-unit-closeout.js";
|
|
31
30
|
import {
|
|
@@ -368,12 +367,6 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
368
367
|
}
|
|
369
368
|
});
|
|
370
369
|
|
|
371
|
-
// Keep the on-disk STATE.md aligned with the live derived state after
|
|
372
|
-
// ordinary unit completion, before any worktree state is synced back.
|
|
373
|
-
await runSafely("postUnit", "state-rebuild", async () => {
|
|
374
|
-
await rebuildState(s.basePath);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
370
|
// Sync worktree state back to project root (skipped for lightweight sidecars)
|
|
378
371
|
if (!opts?.skipWorktreeSync && s.originalBasePath && s.originalBasePath !== s.basePath) {
|
|
379
372
|
await runSafely("postUnit", "worktree-sync", () => {
|
|
@@ -677,13 +677,9 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
|
|
|
677
677
|
logWarning("session", `lock cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
678
678
|
}
|
|
679
679
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
ctx.ui.setStatus("gsd-auto", undefined);
|
|
684
|
-
ctx.ui.setWidget("gsd-progress", undefined);
|
|
685
|
-
ctx.ui.setFooter(undefined);
|
|
686
|
-
}
|
|
680
|
+
ctx.ui.setStatus("gsd-auto", undefined);
|
|
681
|
+
ctx.ui.setWidget("gsd-progress", undefined);
|
|
682
|
+
ctx.ui.setFooter(undefined);
|
|
687
683
|
|
|
688
684
|
// Restore CWD out of worktree back to original project root
|
|
689
685
|
if (s.originalBasePath) {
|
|
@@ -795,22 +791,7 @@ export async function stopAuto(
|
|
|
795
791
|
debugLog("stop-cleanup-worktree", { error: e instanceof Error ? e.message : String(e) });
|
|
796
792
|
}
|
|
797
793
|
|
|
798
|
-
// ── Step 5:
|
|
799
|
-
// rebuildState() calls deriveState() which needs the DB for authoritative
|
|
800
|
-
// state. Previously this ran after closeDatabase(), forcing a filesystem
|
|
801
|
-
// fallback that could disagree with the DB-backed dispatch decisions —
|
|
802
|
-
// a split-brain where dispatch says "blocked" but STATE.md shows work.
|
|
803
|
-
if (s.basePath) {
|
|
804
|
-
try {
|
|
805
|
-
await rebuildState(s.basePath);
|
|
806
|
-
} catch (e) {
|
|
807
|
-
debugLog("stop-rebuild-state-failed", {
|
|
808
|
-
error: e instanceof Error ? e.message : String(e),
|
|
809
|
-
});
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// ── Step 6: DB cleanup ──
|
|
794
|
+
// ── Step 5: DB cleanup ──
|
|
814
795
|
if (isDbAvailable()) {
|
|
815
796
|
try {
|
|
816
797
|
const { closeDatabase } = await import("./gsd-db.js");
|
|
@@ -822,7 +803,7 @@ export async function stopAuto(
|
|
|
822
803
|
}
|
|
823
804
|
}
|
|
824
805
|
|
|
825
|
-
// ── Step
|
|
806
|
+
// ── Step 6: Restore basePath and chdir ──
|
|
826
807
|
try {
|
|
827
808
|
if (s.originalBasePath) {
|
|
828
809
|
s.basePath = s.originalBasePath;
|
|
@@ -837,7 +818,7 @@ export async function stopAuto(
|
|
|
837
818
|
debugLog("stop-cleanup-basepath", { error: e instanceof Error ? e.message : String(e) });
|
|
838
819
|
}
|
|
839
820
|
|
|
840
|
-
// ── Step
|
|
821
|
+
// ── Step 7: Ledger notification ──
|
|
841
822
|
try {
|
|
842
823
|
const ledger = getLedger();
|
|
843
824
|
if (ledger && ledger.units.length > 0) {
|
|
@@ -853,6 +834,17 @@ export async function stopAuto(
|
|
|
853
834
|
debugLog("stop-cleanup-ledger", { error: e instanceof Error ? e.message : String(e) });
|
|
854
835
|
}
|
|
855
836
|
|
|
837
|
+
// ── Step 8: Rebuild state ──
|
|
838
|
+
if (s.basePath) {
|
|
839
|
+
try {
|
|
840
|
+
await rebuildState(s.basePath);
|
|
841
|
+
} catch (e) {
|
|
842
|
+
debugLog("stop-rebuild-state-failed", {
|
|
843
|
+
error: e instanceof Error ? e.message : String(e),
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
856
848
|
// ── Step 9: Cmux sidebar / event log ──
|
|
857
849
|
try {
|
|
858
850
|
clearCmuxSidebar(loadedPreferences);
|
|
@@ -1721,6 +1713,9 @@ export async function dispatchHookUnit(
|
|
|
1721
1713
|
return true;
|
|
1722
1714
|
}
|
|
1723
1715
|
|
|
1716
|
+
// Direct phase dispatch → auto-direct-dispatch.ts
|
|
1717
|
+
export { dispatchDirectPhase } from "./auto-direct-dispatch.js";
|
|
1718
|
+
|
|
1724
1719
|
// Re-export recovery functions for external consumers
|
|
1725
1720
|
export {
|
|
1726
1721
|
buildLoopRemediationSteps,
|
|
@@ -181,10 +181,14 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
181
181
|
// Only gate-shaped ask_user_questions calls should block execution.
|
|
182
182
|
// The gate stays pending until the user selects the approval option.
|
|
183
183
|
if (event.toolName === "ask_user_questions") {
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
187
|
-
|
|
184
|
+
const milestoneId = getDiscussionMilestoneId(discussionBasePath);
|
|
185
|
+
const inDiscussion = milestoneId !== null || isQueuePhaseActive();
|
|
186
|
+
if (inDiscussion) {
|
|
187
|
+
const questions: any[] = (event.input as any)?.questions ?? [];
|
|
188
|
+
const questionId = questions.find((question) => typeof question?.id === "string" && isGateQuestionId(question.id))?.id;
|
|
189
|
+
if (typeof questionId === "string") {
|
|
190
|
+
setPendingGate(questionId);
|
|
191
|
+
}
|
|
188
192
|
}
|
|
189
193
|
}
|
|
190
194
|
|
|
@@ -282,6 +286,7 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
282
286
|
if (event.toolName !== "ask_user_questions") return;
|
|
283
287
|
const milestoneId = getDiscussionMilestoneId(process.cwd());
|
|
284
288
|
const queueActive = isQueuePhaseActive();
|
|
289
|
+
if (!milestoneId && !queueActive) return;
|
|
285
290
|
|
|
286
291
|
const details = event.details as any;
|
|
287
292
|
|
|
@@ -314,16 +319,13 @@ export function registerHooks(pi: ExtensionAPI): void {
|
|
|
314
319
|
// Only unlock the gate if the user selected the first option (confirmation).
|
|
315
320
|
// Cross-references against the question's defined options to reject free-form "Other" text.
|
|
316
321
|
const answer = details.response?.answers?.[question.id];
|
|
317
|
-
const inferredMilestoneId = extractDepthVerificationMilestoneId(question.id) ?? milestoneId;
|
|
318
322
|
if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
|
|
319
|
-
markDepthVerified(
|
|
320
|
-
clearPendingGate();
|
|
323
|
+
markDepthVerified(extractDepthVerificationMilestoneId(question.id) ?? milestoneId);
|
|
321
324
|
}
|
|
322
325
|
break;
|
|
323
326
|
}
|
|
324
327
|
}
|
|
325
328
|
|
|
326
|
-
if (!milestoneId && !queueActive) return;
|
|
327
329
|
if (!milestoneId) return;
|
|
328
330
|
|
|
329
331
|
const basePath = process.cwd();
|
|
@@ -78,10 +78,6 @@ export function parseDoctorArgs(args: string) {
|
|
|
78
78
|
return { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
export function isDoctorHealActionable(issue: { fixable: boolean; severity: string }): boolean {
|
|
82
|
-
return issue.fixable && issue.severity !== "info";
|
|
83
|
-
}
|
|
84
|
-
|
|
85
81
|
export async function handleDoctor(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
|
|
86
82
|
const { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope } = parseDoctorArgs(args);
|
|
87
83
|
const scope = await selectDoctorScope(projectRoot(), requestedScope);
|
|
@@ -113,7 +109,7 @@ export async function handleDoctor(args: string, ctx: ExtensionCommandContext, p
|
|
|
113
109
|
scope: effectiveScope,
|
|
114
110
|
includeWarnings: true,
|
|
115
111
|
});
|
|
116
|
-
const actionable = unresolved.filter(
|
|
112
|
+
const actionable = unresolved.filter(issue => issue.severity === "error");
|
|
117
113
|
if (actionable.length === 0) {
|
|
118
114
|
ctx.ui.notify("Doctor heal found nothing actionable to hand off to the LLM.", "info");
|
|
119
115
|
return;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { readFileSync, existsSync } from "node:fs";
|
|
17
17
|
import { join, resolve, sep } from "node:path";
|
|
18
18
|
import type { StepDefinition } from "./definition-loader.js";
|
|
19
|
-
import { readFrozenDefinition } from "./
|
|
19
|
+
import { readFrozenDefinition } from "./custom-workflow-engine.js";
|
|
20
20
|
|
|
21
21
|
/** Maximum characters per artifact to prevent context window blowout. */
|
|
22
22
|
const MAX_CONTEXT_CHARS = 10_000;
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
} from "./engine-types.js";
|
|
23
23
|
import { readFileSync } from "node:fs";
|
|
24
24
|
import { join } from "node:path";
|
|
25
|
+
import { parse } from "yaml";
|
|
25
26
|
import {
|
|
26
27
|
readGraph,
|
|
27
28
|
writeGraph,
|
|
@@ -31,13 +32,16 @@ import {
|
|
|
31
32
|
type WorkflowGraph,
|
|
32
33
|
} from "./graph.js";
|
|
33
34
|
import { injectContext } from "./context-injector.js";
|
|
34
|
-
import type { StepDefinition } from "./definition-loader.js";
|
|
35
|
-
import { readFrozenDefinition } from "./definition-io.js";
|
|
35
|
+
import type { WorkflowDefinition, StepDefinition } from "./definition-loader.js";
|
|
36
36
|
import { parseUnitId } from "./unit-id.js";
|
|
37
37
|
import { withFileLock } from "./file-lock.js";
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
export
|
|
39
|
+
/** Read and parse the frozen DEFINITION.yaml from a run directory. */
|
|
40
|
+
export function readFrozenDefinition(runDir: string): WorkflowDefinition {
|
|
41
|
+
const defPath = join(runDir, "DEFINITION.yaml");
|
|
42
|
+
const raw = readFileSync(defPath, "utf-8");
|
|
43
|
+
return parse(raw, { schema: "core" }) as WorkflowDefinition;
|
|
44
|
+
}
|
|
41
45
|
|
|
42
46
|
export class CustomWorkflowEngine implements WorkflowEngine {
|
|
43
47
|
readonly engineId = "custom";
|
|
@@ -163,29 +163,6 @@ function openRawDb(path: string): unknown {
|
|
|
163
163
|
|
|
164
164
|
const SCHEMA_VERSION = 14;
|
|
165
165
|
|
|
166
|
-
function indexExists(db: DbAdapter, name: string): boolean {
|
|
167
|
-
return !!db.prepare(
|
|
168
|
-
"SELECT 1 as present FROM sqlite_master WHERE type = 'index' AND name = ?",
|
|
169
|
-
).get(name);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function dedupeVerificationEvidenceRows(db: DbAdapter): void {
|
|
173
|
-
db.exec(`
|
|
174
|
-
DELETE FROM verification_evidence
|
|
175
|
-
WHERE rowid NOT IN (
|
|
176
|
-
SELECT MIN(rowid)
|
|
177
|
-
FROM verification_evidence
|
|
178
|
-
GROUP BY task_id, slice_id, milestone_id, command, verdict
|
|
179
|
-
)
|
|
180
|
-
`);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function ensureVerificationEvidenceDedupIndex(db: DbAdapter): void {
|
|
184
|
-
if (indexExists(db, "idx_verification_evidence_dedup")) return;
|
|
185
|
-
dedupeVerificationEvidenceRows(db);
|
|
186
|
-
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
|
|
187
|
-
}
|
|
188
|
-
|
|
189
166
|
function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
190
167
|
if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
|
|
191
168
|
if (fileBacked) db.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -433,7 +410,7 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
|
433
410
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
434
411
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
435
412
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
436
|
-
|
|
413
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
|
|
437
414
|
|
|
438
415
|
// v14 index — slice dependency lookups
|
|
439
416
|
db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
|
|
@@ -766,7 +743,7 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
766
743
|
db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
|
|
767
744
|
db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
|
|
768
745
|
db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
|
|
769
|
-
|
|
746
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
|
|
770
747
|
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
|
|
771
748
|
":version": 13,
|
|
772
749
|
":applied_at": new Date().toISOString(),
|
|
@@ -1565,30 +1542,6 @@ export interface TaskRow {
|
|
|
1565
1542
|
}
|
|
1566
1543
|
|
|
1567
1544
|
function rowToTask(row: Record<string, unknown>): TaskRow {
|
|
1568
|
-
const parseTaskArray = (value: unknown): string[] => {
|
|
1569
|
-
if (Array.isArray(value)) {
|
|
1570
|
-
return value.filter((entry): entry is string => typeof entry === "string");
|
|
1571
|
-
}
|
|
1572
|
-
if (typeof value !== "string") return [];
|
|
1573
|
-
|
|
1574
|
-
const trimmed = value.trim();
|
|
1575
|
-
if (!trimmed) return [];
|
|
1576
|
-
|
|
1577
|
-
try {
|
|
1578
|
-
const parsed = JSON.parse(trimmed);
|
|
1579
|
-
if (Array.isArray(parsed)) {
|
|
1580
|
-
return parsed.filter((entry): entry is string => typeof entry === "string");
|
|
1581
|
-
}
|
|
1582
|
-
if (typeof parsed === "string" && parsed.trim()) {
|
|
1583
|
-
return [parsed.trim()];
|
|
1584
|
-
}
|
|
1585
|
-
} catch {
|
|
1586
|
-
// Older/corrupt DB rows may contain raw comma-separated paths instead of JSON arrays.
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
return trimmed.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
1590
|
-
};
|
|
1591
|
-
|
|
1592
1545
|
return {
|
|
1593
1546
|
milestone_id: row["milestone_id"] as string,
|
|
1594
1547
|
slice_id: row["slice_id"] as string,
|
|
@@ -1608,10 +1561,10 @@ function rowToTask(row: Record<string, unknown>): TaskRow {
|
|
|
1608
1561
|
full_summary_md: row["full_summary_md"] as string,
|
|
1609
1562
|
description: (row["description"] as string) ?? "",
|
|
1610
1563
|
estimate: (row["estimate"] as string) ?? "",
|
|
1611
|
-
files:
|
|
1564
|
+
files: JSON.parse((row["files"] as string) || "[]"),
|
|
1612
1565
|
verify: (row["verify"] as string) ?? "",
|
|
1613
|
-
inputs:
|
|
1614
|
-
expected_output:
|
|
1566
|
+
inputs: JSON.parse((row["inputs"] as string) || "[]"),
|
|
1567
|
+
expected_output: JSON.parse((row["expected_output"] as string) || "[]"),
|
|
1615
1568
|
observability_impact: (row["observability_impact"] as string) ?? "",
|
|
1616
1569
|
full_plan_md: (row["full_plan_md"] as string) ?? "",
|
|
1617
1570
|
sequence: (row["sequence"] as number) ?? 0,
|
|
@@ -49,8 +49,6 @@ export const PROVIDER_REGISTRY: ProviderInfo[] = [
|
|
|
49
49
|
{ id: "custom-openai", label: "Custom (OpenAI-compat)", category: "llm", envVar: "CUSTOM_OPENAI_API_KEY" },
|
|
50
50
|
{ id: "cerebras", label: "Cerebras", category: "llm", envVar: "CEREBRAS_API_KEY" },
|
|
51
51
|
{ id: "azure-openai-responses", label: "Azure OpenAI", category: "llm", envVar: "AZURE_OPENAI_API_KEY" },
|
|
52
|
-
{ id: "alibaba-coding-plan", label: "Alibaba Coding Plan", category: "llm", envVar: "ALIBABA_API_KEY", dashboardUrl: "bailian.console.aliyun.com" },
|
|
53
|
-
{ id: "alibaba-dashscope", label: "Alibaba DashScope", category: "llm", envVar: "DASHSCOPE_API_KEY", dashboardUrl: "dashscope.console.aliyun.com" },
|
|
54
52
|
|
|
55
53
|
// Tool Keys
|
|
56
54
|
{ id: "context7", label: "Context7 Docs", category: "tool", envVar: "CONTEXT7_API_KEY", dashboardUrl: "context7.com/dashboard" },
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
SkillResolutionReport,
|
|
18
18
|
} from "./preferences-types.js";
|
|
19
19
|
import { validatePreferences } from "./preferences-validation.js";
|
|
20
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
20
21
|
|
|
21
22
|
// Re-export types so existing consumers of ./preferences-skills.js keep working
|
|
22
23
|
export type { GSDSkillRule, SkillDiscoveryMode, SkillResolution, SkillResolutionReport } from "./preferences-types.js";
|
|
@@ -142,5 +143,38 @@ export function resolveAllSkillReferences(preferences: GSDPreferences, cwd: stri
|
|
|
142
143
|
return { resolutions, warnings };
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Format a skill reference for the system prompt.
|
|
148
|
+
* If resolved, shows the path so the agent knows exactly where to read.
|
|
149
|
+
* If unresolved, marks it clearly.
|
|
150
|
+
*/
|
|
151
|
+
export function formatSkillRef(ref: string, resolutions: Map<string, SkillResolution>): string {
|
|
152
|
+
const resolution = resolutions.get(ref);
|
|
153
|
+
if (!resolution || resolution.method === "unresolved") {
|
|
154
|
+
return `${ref} (⚠ not found — check skill name or path)`;
|
|
155
|
+
}
|
|
156
|
+
// For absolute paths where SKILL.md is just appended, don't clutter the output
|
|
157
|
+
if (resolution.method === "absolute-path" || resolution.method === "absolute-dir") {
|
|
158
|
+
return ref;
|
|
159
|
+
}
|
|
160
|
+
// For bare names resolved from skill directories, show the resolved path
|
|
161
|
+
return `${ref} → \`${resolution.resolvedPath}\``;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Resolve the skill discovery mode from effective preferences.
|
|
166
|
+
* Defaults to "suggest" -- skills are identified during research but not installed automatically.
|
|
167
|
+
*/
|
|
168
|
+
export function resolveSkillDiscoveryMode(): SkillDiscoveryMode {
|
|
169
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
170
|
+
return prefs?.preferences.skill_discovery ?? "suggest";
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Resolve the skill staleness threshold in days.
|
|
175
|
+
* Returns 0 if disabled, default 60 if not configured.
|
|
176
|
+
*/
|
|
177
|
+
export function resolveSkillStalenessDays(): number {
|
|
178
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
179
|
+
return prefs?.preferences.skill_staleness_days ?? 60;
|
|
180
|
+
}
|
|
@@ -384,19 +384,3 @@ export interface SkillResolutionReport {
|
|
|
384
384
|
/** References that could not be resolved. */
|
|
385
385
|
warnings: string[];
|
|
386
386
|
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Format a skill reference for the system prompt.
|
|
390
|
-
* If resolved, shows the path so the agent knows exactly where to read.
|
|
391
|
-
* If unresolved, marks it clearly.
|
|
392
|
-
*/
|
|
393
|
-
export function formatSkillRef(ref: string, resolutions: Map<string, SkillResolution>): string {
|
|
394
|
-
const resolution = resolutions.get(ref);
|
|
395
|
-
if (!resolution || resolution.method === "unresolved") {
|
|
396
|
-
return `${ref} (⚠ not found — check skill name or path)`;
|
|
397
|
-
}
|
|
398
|
-
if (resolution.method === "absolute-path" || resolution.method === "absolute-dir") {
|
|
399
|
-
return ref;
|
|
400
|
-
}
|
|
401
|
-
return `${ref} → \`${resolution.resolvedPath}\``;
|
|
402
|
-
}
|
|
@@ -29,10 +29,9 @@ import {
|
|
|
29
29
|
type GSDPreferences,
|
|
30
30
|
type LoadedGSDPreferences,
|
|
31
31
|
type SkillResolution,
|
|
32
|
-
type SkillDiscoveryMode,
|
|
33
|
-
formatSkillRef,
|
|
34
32
|
} from "./preferences-types.js";
|
|
35
33
|
import { validatePreferences } from "./preferences-validation.js";
|
|
34
|
+
import { formatSkillRef } from "./preferences-skills.js";
|
|
36
35
|
|
|
37
36
|
// ─── Re-exports: types ──────────────────────────────────────────────────────
|
|
38
37
|
// Every type/interface that was previously exported from this file is
|
|
@@ -61,20 +60,11 @@ export type {
|
|
|
61
60
|
export { validatePreferences } from "./preferences-validation.js";
|
|
62
61
|
|
|
63
62
|
// ─── Re-exports: skills ─────────────────────────────────────────────────────
|
|
64
|
-
export {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
export function resolveSkillDiscoveryMode(): SkillDiscoveryMode {
|
|
70
|
-
const prefs = loadEffectiveGSDPreferences();
|
|
71
|
-
return prefs?.preferences.skill_discovery ?? "suggest";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function resolveSkillStalenessDays(): number {
|
|
75
|
-
const prefs = loadEffectiveGSDPreferences();
|
|
76
|
-
return prefs?.preferences.skill_staleness_days ?? 60;
|
|
77
|
-
}
|
|
63
|
+
export {
|
|
64
|
+
resolveAllSkillReferences,
|
|
65
|
+
resolveSkillDiscoveryMode,
|
|
66
|
+
resolveSkillStalenessDays,
|
|
67
|
+
} from "./preferences-skills.js";
|
|
78
68
|
|
|
79
69
|
// ─── Re-exports: models ─────────────────────────────────────────────────────
|
|
80
70
|
export {
|
|
@@ -399,9 +389,6 @@ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPr
|
|
|
399
389
|
github: (base.github || override.github)
|
|
400
390
|
? { ...(base.github ?? {}), ...(override.github ?? {}) } as import("../github-sync/types.js").GitHubSyncConfig
|
|
401
391
|
: undefined,
|
|
402
|
-
experimental: (base.experimental || override.experimental)
|
|
403
|
-
? { ...(base.experimental ?? {}), ...(override.experimental ?? {}) }
|
|
404
|
-
: undefined,
|
|
405
392
|
service_tier: override.service_tier ?? base.service_tier,
|
|
406
393
|
forensics_dedup: override.forensics_dedup ?? base.forensics_dedup,
|
|
407
394
|
show_token_cost: override.show_token_cost ?? base.show_token_cost,
|
|
@@ -143,15 +143,10 @@ export function loadPrompt(name: string, vars: Record<string, string> = {}): str
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
for (const [key, value] of Object.entries(effectiveVars)) {
|
|
146
|
-
const safeValue =
|
|
147
|
-
key === "workingDirectory" && typeof value === "string"
|
|
148
|
-
? value.replaceAll("\\", "/")
|
|
149
|
-
: value;
|
|
150
|
-
|
|
151
146
|
// Use split/join instead of replaceAll to avoid JavaScript's special
|
|
152
147
|
// replacement patterns ($', $`, $&) being interpreted in the value.
|
|
153
148
|
// See: https://github.com/gsd-build/gsd-2/issues/2968
|
|
154
|
-
content = content.split(`{{${key}}}`).join(
|
|
149
|
+
content = content.split(`{{${key}}}`).join(value);
|
|
155
150
|
}
|
|
156
151
|
|
|
157
152
|
return content.trim();
|
|
@@ -57,7 +57,6 @@ import {
|
|
|
57
57
|
insertMilestone,
|
|
58
58
|
insertSlice,
|
|
59
59
|
insertTask,
|
|
60
|
-
updateSliceStatus,
|
|
61
60
|
updateTaskStatus,
|
|
62
61
|
getPendingGateCountForTurn,
|
|
63
62
|
type MilestoneRow,
|
|
@@ -359,25 +358,6 @@ function reconcileDiskToDb(basePath: string): MilestoneRow[] {
|
|
|
359
358
|
depends: s.depends, demo: s.demo,
|
|
360
359
|
});
|
|
361
360
|
}
|
|
362
|
-
|
|
363
|
-
// Reconcile stale *existing* slice rows (#3599): a slice row may exist in
|
|
364
|
-
// the DB with status "pending" even though disk artifacts (SUMMARY) prove
|
|
365
|
-
// completion — the same class of desync that task-level reconciliation
|
|
366
|
-
// (further below) already handles. Without this, the dependency resolver
|
|
367
|
-
// builds doneSliceIds from stale DB rows and downstream slices stay blocked
|
|
368
|
-
// forever with "No slice eligible".
|
|
369
|
-
for (const dbSlice of dbSlices) {
|
|
370
|
-
if (isStatusDone(dbSlice.status)) continue;
|
|
371
|
-
const summaryPath = resolveSliceFile(basePath, mid, dbSlice.id, "SUMMARY");
|
|
372
|
-
if (summaryPath) {
|
|
373
|
-
try {
|
|
374
|
-
updateSliceStatus(mid, dbSlice.id, "complete");
|
|
375
|
-
logWarning("reconcile", `slice ${mid}/${dbSlice.id} status reconciled from "${dbSlice.status}" to "complete" (#3599)`, { mid, sid: dbSlice.id });
|
|
376
|
-
} catch (e) {
|
|
377
|
-
logError("reconcile", `failed to update slice ${dbSlice.id}`, { sid: dbSlice.id, error: (e as Error).message });
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
361
|
}
|
|
382
362
|
return allMilestones;
|
|
383
363
|
}
|