ralphctl 0.17.1 → 0.18.0
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.
|
@@ -1229,16 +1229,16 @@ var killWithEscalation = (child, graceMs = DEFAULT_KILL_GRACE_MS) => {
|
|
|
1229
1229
|
var DEFAULT_GIT_TIMEOUT_MS = 3e4;
|
|
1230
1230
|
var GIT_MAX_OUTPUT_BYTES = 50 * 1024 * 1024;
|
|
1231
1231
|
var createGitRunner = (deps = {}) => {
|
|
1232
|
-
const
|
|
1232
|
+
const spawn2 = deps.spawn ?? defaultSpawn;
|
|
1233
1233
|
const defaultTimeoutMs = deps.defaultTimeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
|
|
1234
1234
|
const maxOutputBytes = deps.maxOutputBytes ?? GIT_MAX_OUTPUT_BYTES;
|
|
1235
|
-
const run = (cwd, args, opts = {}) => runGitOnce(
|
|
1235
|
+
const run = (cwd, args, opts = {}) => runGitOnce(spawn2, cwd, args, opts.timeoutMs ?? defaultTimeoutMs, maxOutputBytes);
|
|
1236
1236
|
return { run };
|
|
1237
1237
|
};
|
|
1238
|
-
var spawnGitChild = (
|
|
1238
|
+
var spawnGitChild = (spawn2, cwd, args) => {
|
|
1239
1239
|
try {
|
|
1240
1240
|
return Result.ok(
|
|
1241
|
-
|
|
1241
|
+
spawn2("git", args, {
|
|
1242
1242
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1243
1243
|
cwd: String(cwd)
|
|
1244
1244
|
})
|
|
@@ -1310,8 +1310,8 @@ var buildCloseResult = (collector, timedOut, code, timeoutMs, args, maxOutputByt
|
|
|
1310
1310
|
);
|
|
1311
1311
|
}
|
|
1312
1312
|
};
|
|
1313
|
-
var runGitOnce = (
|
|
1314
|
-
const spawnResult = spawnGitChild(
|
|
1313
|
+
var runGitOnce = (spawn2, cwd, args, timeoutMs, maxOutputBytes) => new Promise((resolve) => {
|
|
1314
|
+
const spawnResult = spawnGitChild(spawn2, cwd, args);
|
|
1315
1315
|
if (!spawnResult.ok) {
|
|
1316
1316
|
resolve(Result.error(spawnResult.error));
|
|
1317
1317
|
return;
|
|
@@ -1398,9 +1398,9 @@ var buildChildEnv = (overrideEnv) => ({
|
|
|
1398
1398
|
...process.env,
|
|
1399
1399
|
...overrideEnv
|
|
1400
1400
|
});
|
|
1401
|
-
var trySpawnChild = (
|
|
1401
|
+
var trySpawnChild = (spawn2, cwd, script, env) => {
|
|
1402
1402
|
try {
|
|
1403
|
-
const child =
|
|
1403
|
+
const child = spawn2(script, [], {
|
|
1404
1404
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1405
1405
|
cwd: String(cwd),
|
|
1406
1406
|
shell: true,
|
|
@@ -1462,14 +1462,14 @@ var wireAbortLadder = (child, signal, abortKillGraceMs, isSettled2) => {
|
|
|
1462
1462
|
};
|
|
1463
1463
|
};
|
|
1464
1464
|
var runChildProcess = (cwd, script, opts, deps, resolve) => {
|
|
1465
|
-
const { spawn:
|
|
1465
|
+
const { spawn: spawn2, defaultTimeoutMs, now, abortKillGraceMs } = deps;
|
|
1466
1466
|
const start = now();
|
|
1467
1467
|
const timeoutMs = opts.timeoutMs ?? defaultTimeoutMs;
|
|
1468
1468
|
if (opts.signal?.aborted === true) {
|
|
1469
1469
|
resolve(abortResult());
|
|
1470
1470
|
return;
|
|
1471
1471
|
}
|
|
1472
|
-
const spawnAttempt = trySpawnChild(
|
|
1472
|
+
const spawnAttempt = trySpawnChild(spawn2, cwd, script, buildChildEnv(opts.env));
|
|
1473
1473
|
if (!spawnAttempt.ok) {
|
|
1474
1474
|
resolve(Result.error(spawnAttempt.error));
|
|
1475
1475
|
return;
|
|
@@ -1989,33 +1989,38 @@ var reconcileStaleProjectSiblings = async (root, id, canonicalFile) => {
|
|
|
1989
1989
|
await removeFile(fullPath);
|
|
1990
1990
|
}
|
|
1991
1991
|
};
|
|
1992
|
-
var
|
|
1993
|
-
const
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
const byId = /* @__PURE__ */ new Map();
|
|
2000
|
-
const canonicalIds = /* @__PURE__ */ new Set();
|
|
2001
|
-
for (const file of jsonFiles) {
|
|
1992
|
+
var listProjects = async (root) => {
|
|
1993
|
+
const dir = projectsDir(root);
|
|
1994
|
+
const entries = await listDir(dir);
|
|
1995
|
+
if (!entries.ok) return Result.error(entries.error);
|
|
1996
|
+
const jsonFiles = entries.value.filter((f) => f.endsWith(".json")).sort();
|
|
1997
|
+
const reads = await Promise.all(
|
|
1998
|
+
jsonFiles.map(async (file) => {
|
|
2002
1999
|
const path2 = `${dir}/${file}`;
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
byId.set(id, decoded.value);
|
|
2014
|
-
if (isCanonical) canonicalIds.add(id);
|
|
2000
|
+
return { file, path: path2, json: await readJson(path2) };
|
|
2001
|
+
})
|
|
2002
|
+
);
|
|
2003
|
+
const items = [];
|
|
2004
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2005
|
+
const canonicalIds = /* @__PURE__ */ new Set();
|
|
2006
|
+
for (const { file, path: path2, json } of reads) {
|
|
2007
|
+
if (!json.ok) {
|
|
2008
|
+
if (json.error instanceof NotFoundError) continue;
|
|
2009
|
+
return Result.error(json.error);
|
|
2015
2010
|
}
|
|
2016
|
-
|
|
2017
|
-
return Result.
|
|
2018
|
-
|
|
2011
|
+
const decoded = decode(fromJsonProject, json.value, { entity: "project", path: path2 });
|
|
2012
|
+
if (!decoded.ok) return Result.error(decoded.error);
|
|
2013
|
+
const id = String(decoded.value.id);
|
|
2014
|
+
const isCanonical = file.includes("--");
|
|
2015
|
+
if (!isCanonical && canonicalIds.has(id)) continue;
|
|
2016
|
+
byId.set(id, decoded.value);
|
|
2017
|
+
if (isCanonical) canonicalIds.add(id);
|
|
2018
|
+
}
|
|
2019
|
+
items.push(...byId.values());
|
|
2020
|
+
return Result.ok(items);
|
|
2021
|
+
};
|
|
2022
|
+
var createFsProjectRepository = (deps) => {
|
|
2023
|
+
const list = () => listProjects(deps.root);
|
|
2019
2024
|
return {
|
|
2020
2025
|
async findById(id) {
|
|
2021
2026
|
const path2 = await resolveProjectPath(deps.root, id);
|
|
@@ -2352,33 +2357,38 @@ var reconcileSprintDir = async (root, id, canonicalDir) => {
|
|
|
2352
2357
|
reconciledDirs.add(memoKey);
|
|
2353
2358
|
}
|
|
2354
2359
|
};
|
|
2355
|
-
var
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
const byId = /* @__PURE__ */ new Map();
|
|
2363
|
-
const canonicalIds = /* @__PURE__ */ new Set();
|
|
2364
|
-
for (const entry of sortedEntries) {
|
|
2360
|
+
var listSprints = async (root) => {
|
|
2361
|
+
const dir = sprintsDir(root);
|
|
2362
|
+
const entries = await listDir(dir);
|
|
2363
|
+
if (!entries.ok) return Result.error(entries.error);
|
|
2364
|
+
const sortedEntries = [...entries.value].sort((a, b) => parseIdFromName(a).localeCompare(parseIdFromName(b)));
|
|
2365
|
+
const reads = await Promise.all(
|
|
2366
|
+
sortedEntries.map(async (entry) => {
|
|
2365
2367
|
const path2 = `${dir}/${entry}/sprint.json`;
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
byId.set(id, decoded.value);
|
|
2377
|
-
if (isCanonical) canonicalIds.add(id);
|
|
2368
|
+
return { entry, path: path2, json: await readJson(path2) };
|
|
2369
|
+
})
|
|
2370
|
+
);
|
|
2371
|
+
const items = [];
|
|
2372
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2373
|
+
const canonicalIds = /* @__PURE__ */ new Set();
|
|
2374
|
+
for (const { entry, path: path2, json } of reads) {
|
|
2375
|
+
if (!json.ok) {
|
|
2376
|
+
if (json.error instanceof NotFoundError) continue;
|
|
2377
|
+
return Result.error(json.error);
|
|
2378
2378
|
}
|
|
2379
|
-
|
|
2380
|
-
return Result.
|
|
2381
|
-
|
|
2379
|
+
const decoded = decode((input) => fromJsonSprint(input, path2), json.value, { entity: "sprint", path: path2 });
|
|
2380
|
+
if (!decoded.ok) return Result.error(decoded.error);
|
|
2381
|
+
const id = String(decoded.value.id);
|
|
2382
|
+
const isCanonical = entry.includes("--");
|
|
2383
|
+
if (!isCanonical && canonicalIds.has(id)) continue;
|
|
2384
|
+
byId.set(id, decoded.value);
|
|
2385
|
+
if (isCanonical) canonicalIds.add(id);
|
|
2386
|
+
}
|
|
2387
|
+
items.push(...byId.values());
|
|
2388
|
+
return Result.ok(items);
|
|
2389
|
+
};
|
|
2390
|
+
var createFsSprintRepository = (deps) => {
|
|
2391
|
+
const list = () => listSprints(deps.root);
|
|
2382
2392
|
return {
|
|
2383
2393
|
async findById(id) {
|
|
2384
2394
|
const dir = await resolveSprintDir(deps.root, id);
|
|
@@ -2495,6 +2505,8 @@ var unblockTask = (task) => {
|
|
|
2495
2505
|
// The raised effort is a per-run remedy like the model bump — a clean restart drops it so the
|
|
2496
2506
|
// fresh run begins on the configured effort again.
|
|
2497
2507
|
escalatedToEffort: _effort,
|
|
2508
|
+
// Evaluator-side counterpart of the effort remedy above — same clean-restart rationale.
|
|
2509
|
+
escalatedToEvaluatorEffort: _evaluatorEffort,
|
|
2498
2510
|
// A clean restart drops the prior run's per-criterion verdicts too — a freshly-planned task
|
|
2499
2511
|
// carries no k-of-N history; stale verdicts would mislead the next run's checklist.
|
|
2500
2512
|
criteriaVerdicts: _verdicts,
|
|
@@ -2505,6 +2517,7 @@ var unblockTask = (task) => {
|
|
|
2505
2517
|
void _from;
|
|
2506
2518
|
void _to;
|
|
2507
2519
|
void _effort;
|
|
2520
|
+
void _evaluatorEffort;
|
|
2508
2521
|
void _verdicts;
|
|
2509
2522
|
return Result.ok({ ...rest, status: "todo", attempts: [] });
|
|
2510
2523
|
};
|
|
@@ -2761,7 +2774,10 @@ var TaskBaseShape = {
|
|
|
2761
2774
|
escalatedToModel: z13.string().optional(),
|
|
2762
2775
|
// Same-model effort-rung override. Optional on read so `tasks.json` files written before the
|
|
2763
2776
|
// field existed load unchanged (a missing value heals to `undefined`). Never planner-authored.
|
|
2764
|
-
escalatedToEffort: z13.string().optional()
|
|
2777
|
+
escalatedToEffort: z13.string().optional(),
|
|
2778
|
+
// Evaluator-side counterpart of escalatedToEffort — same tolerant-read rationale. The evaluator
|
|
2779
|
+
// MODEL never escalates, so there is no evaluator equivalent of escalatedToModel.
|
|
2780
|
+
escalatedToEvaluatorEffort: z13.string().optional()
|
|
2765
2781
|
};
|
|
2766
2782
|
var TodoTaskSchema = z13.object({ ...TaskBaseShape, status: z13.literal("todo") });
|
|
2767
2783
|
var InProgressTaskSchema = z13.object({ ...TaskBaseShape, status: z13.literal("in_progress") });
|
|
@@ -3061,6 +3077,31 @@ var isSuspendedModel = (s) => SUSPENDED_MODELS.includes(s);
|
|
|
3061
3077
|
var SUSPENSION_NOTE = "suspended";
|
|
3062
3078
|
var suspendedModelMessage = (model) => `'${model}' is temporarily suspended by its provider \u2014 pick another model until access is restored`;
|
|
3063
3079
|
|
|
3080
|
+
// src/integration/ai/providers/_engine/validate-model.ts
|
|
3081
|
+
var validateModel = (model, isKnownModel, opts) => {
|
|
3082
|
+
if (!isKnownModel(model)) {
|
|
3083
|
+
return Result.error(
|
|
3084
|
+
new InvalidStateError({
|
|
3085
|
+
entity: opts.entity,
|
|
3086
|
+
currentState: "model-validation",
|
|
3087
|
+
attemptedAction: opts.attemptedAction,
|
|
3088
|
+
message: opts.notKnownMessage
|
|
3089
|
+
})
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
if (isSuspendedModel(model)) {
|
|
3093
|
+
return Result.error(
|
|
3094
|
+
new InvalidStateError({
|
|
3095
|
+
entity: opts.entity,
|
|
3096
|
+
currentState: "model-suspended",
|
|
3097
|
+
attemptedAction: opts.attemptedAction,
|
|
3098
|
+
message: suspendedModelMessage(model)
|
|
3099
|
+
})
|
|
3100
|
+
);
|
|
3101
|
+
}
|
|
3102
|
+
return Result.ok(void 0);
|
|
3103
|
+
};
|
|
3104
|
+
|
|
3064
3105
|
// src/integration/ai/providers/_engine/bounded-tail.ts
|
|
3065
3106
|
var STDERR_TAIL_CAP = 16384;
|
|
3066
3107
|
var RATE_LIMIT_SCAN_TAIL_CAP = 8192;
|
|
@@ -3211,7 +3252,7 @@ var defaultProviderSpawn = (command, args, options) => crossPlatformSpawn(comman
|
|
|
3211
3252
|
// src/integration/ai/providers/_engine/truncate-debug-field.ts
|
|
3212
3253
|
var truncateField = (value, max = 120) => {
|
|
3213
3254
|
if (value === void 0 || value === null) return void 0;
|
|
3214
|
-
if (value.length === 0) return void 0;
|
|
3255
|
+
if (value.trim().length === 0) return void 0;
|
|
3215
3256
|
if (value.length <= max) return value;
|
|
3216
3257
|
return `${value.slice(0, max)}\u2026`;
|
|
3217
3258
|
};
|
|
@@ -3360,6 +3401,12 @@ var delayForRetry = (retryIndex, schedule = DEFAULT_BACKOFF_SCHEDULE) => {
|
|
|
3360
3401
|
const clamped = Math.min(retryIndex - 1, schedule.length - 1);
|
|
3361
3402
|
return schedule[clamped] ?? 0;
|
|
3362
3403
|
};
|
|
3404
|
+
var JITTER_RATIO = 0.2;
|
|
3405
|
+
var applyJitter = (delayMs, random = Math.random) => {
|
|
3406
|
+
if (delayMs <= 0) return delayMs;
|
|
3407
|
+
const spread = delayMs * JITTER_RATIO;
|
|
3408
|
+
return Math.round(delayMs - spread + random() * spread * 2);
|
|
3409
|
+
};
|
|
3363
3410
|
var sleepCancellable = (ms, signal) => new Promise((resolve) => {
|
|
3364
3411
|
if (signal?.aborted === true) {
|
|
3365
3412
|
resolve();
|
|
@@ -3394,7 +3441,8 @@ var handleRateLimitOutcome = async ({
|
|
|
3394
3441
|
attemptIdx,
|
|
3395
3442
|
maxAttempts,
|
|
3396
3443
|
schedule,
|
|
3397
|
-
error
|
|
3444
|
+
error,
|
|
3445
|
+
random
|
|
3398
3446
|
}) => {
|
|
3399
3447
|
const bannerId = `rate-limit-${providerSlug}-${error.sessionId ?? String(attemptIdx + 1)}`;
|
|
3400
3448
|
eventBus.publish({
|
|
@@ -3408,7 +3456,7 @@ var handleRateLimitOutcome = async ({
|
|
|
3408
3456
|
if (attemptIdx >= maxAttempts - 1) {
|
|
3409
3457
|
return { type: "continue", session: nextSession };
|
|
3410
3458
|
}
|
|
3411
|
-
const delayMs = delayForRetry(attemptIdx + 1, schedule);
|
|
3459
|
+
const delayMs = applyJitter(delayForRetry(attemptIdx + 1, schedule), random ?? Math.random);
|
|
3412
3460
|
if (delayMs <= 0) {
|
|
3413
3461
|
return { type: "continue", session: nextSession };
|
|
3414
3462
|
}
|
|
@@ -3472,7 +3520,8 @@ var runWithRateLimitRetry = async (opts) => {
|
|
|
3472
3520
|
attemptIdx,
|
|
3473
3521
|
maxAttempts,
|
|
3474
3522
|
schedule,
|
|
3475
|
-
error: outcome.error
|
|
3523
|
+
error: outcome.error,
|
|
3524
|
+
random: opts.random
|
|
3476
3525
|
});
|
|
3477
3526
|
if (result.type === "aborted") {
|
|
3478
3527
|
return Result.error(result.error);
|
|
@@ -3918,26 +3967,12 @@ var disallowedToolsFor = (p) => {
|
|
|
3918
3967
|
return denied;
|
|
3919
3968
|
};
|
|
3920
3969
|
var buildClaudeArgs = (session) => {
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
message: `claude-provider: '${session.model}' is not a known Claude model`
|
|
3928
|
-
})
|
|
3929
|
-
);
|
|
3930
|
-
}
|
|
3931
|
-
if (isSuspendedModel(session.model)) {
|
|
3932
|
-
return Result.error(
|
|
3933
|
-
new InvalidStateError({
|
|
3934
|
-
entity: PROVIDER_NAME,
|
|
3935
|
-
currentState: "model-suspended",
|
|
3936
|
-
attemptedAction: "build argv",
|
|
3937
|
-
message: suspendedModelMessage(session.model)
|
|
3938
|
-
})
|
|
3939
|
-
);
|
|
3940
|
-
}
|
|
3970
|
+
const validated = validateModel(session.model, isClaudeModel, {
|
|
3971
|
+
entity: PROVIDER_NAME,
|
|
3972
|
+
attemptedAction: "build argv",
|
|
3973
|
+
notKnownMessage: `claude-provider: '${session.model}' is not a known Claude model`
|
|
3974
|
+
});
|
|
3975
|
+
if (!validated.ok) return Result.error(validated.error);
|
|
3941
3976
|
const args = ["-p", "--verbose", "--output-format", "stream-json", "--model", session.model];
|
|
3942
3977
|
args.push("--permission-mode", "bypassPermissions");
|
|
3943
3978
|
const denied = disallowedToolsFor(session.permissions);
|
|
@@ -4039,16 +4074,12 @@ var sandboxFor = (_p) => {
|
|
|
4039
4074
|
return Result.ok({ sandbox: "workspace-write" });
|
|
4040
4075
|
};
|
|
4041
4076
|
var buildCodexArgs = (session, opts) => {
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
message: `codex-provider: '${session.model}' is not a known Codex model`
|
|
4049
|
-
})
|
|
4050
|
-
);
|
|
4051
|
-
}
|
|
4077
|
+
const validated = validateModel(session.model, isCodexModel, {
|
|
4078
|
+
entity: PROVIDER_NAME2,
|
|
4079
|
+
attemptedAction: "build argv",
|
|
4080
|
+
notKnownMessage: `codex-provider: '${session.model}' is not a known Codex model`
|
|
4081
|
+
});
|
|
4082
|
+
if (!validated.ok) return Result.error(validated.error);
|
|
4052
4083
|
const perms = sandboxFor(session.permissions);
|
|
4053
4084
|
if (!perms.ok) return Result.error(perms.error);
|
|
4054
4085
|
const args = ["exec"];
|
|
@@ -4409,26 +4440,12 @@ var RESUME_STALE_RE3 = /(session|conversation)[^\n]*not found|no (session|conver
|
|
|
4409
4440
|
var isFullAuto = (p) => p.autoApprove && p.canModifyRepoFiles && p.canRunShell;
|
|
4410
4441
|
var PROVIDER_NAME3 = "copilot-provider";
|
|
4411
4442
|
var buildCopilotArgs = (session) => {
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
message: `copilot-provider: '${session.model}' is not a known Copilot model`
|
|
4419
|
-
})
|
|
4420
|
-
);
|
|
4421
|
-
}
|
|
4422
|
-
if (isSuspendedModel(session.model)) {
|
|
4423
|
-
return Result.error(
|
|
4424
|
-
new InvalidStateError({
|
|
4425
|
-
entity: PROVIDER_NAME3,
|
|
4426
|
-
currentState: "model-suspended",
|
|
4427
|
-
attemptedAction: "build argv",
|
|
4428
|
-
message: suspendedModelMessage(session.model)
|
|
4429
|
-
})
|
|
4430
|
-
);
|
|
4431
|
-
}
|
|
4443
|
+
const validated = validateModel(session.model, isCopilotModel, {
|
|
4444
|
+
entity: PROVIDER_NAME3,
|
|
4445
|
+
attemptedAction: "build argv",
|
|
4446
|
+
notKnownMessage: `copilot-provider: '${session.model}' is not a known Copilot model`
|
|
4447
|
+
});
|
|
4448
|
+
if (!validated.ok) return Result.error(validated.error);
|
|
4432
4449
|
const args = [
|
|
4433
4450
|
"--output-format=json",
|
|
4434
4451
|
"--autopilot",
|
|
@@ -4657,16 +4674,12 @@ var createInteractiveClaudeProvider = (deps) => {
|
|
|
4657
4674
|
const newSessionId = deps.newSessionId ?? uuidv7;
|
|
4658
4675
|
return {
|
|
4659
4676
|
async run(input) {
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
message: `interactive-claude: '${input.model}' is not a known Claude model`
|
|
4667
|
-
})
|
|
4668
|
-
);
|
|
4669
|
-
}
|
|
4677
|
+
const validated = validateModel(input.model, isClaudeModel, {
|
|
4678
|
+
entity: PROVIDER,
|
|
4679
|
+
attemptedAction: "run",
|
|
4680
|
+
notKnownMessage: `interactive-claude: '${input.model}' is not a known Claude model`
|
|
4681
|
+
});
|
|
4682
|
+
if (!validated.ok) return Result.error(validated.error);
|
|
4670
4683
|
let prompt;
|
|
4671
4684
|
try {
|
|
4672
4685
|
prompt = await readFile5(String(input.promptFile));
|
|
@@ -4777,16 +4790,12 @@ var createInteractiveCodexProvider = (deps) => {
|
|
|
4777
4790
|
const readFile5 = deps.readFile ?? defaultReadFile;
|
|
4778
4791
|
return {
|
|
4779
4792
|
async run(input) {
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
message: `interactive-codex: '${input.model}' is not a known Codex model`
|
|
4787
|
-
})
|
|
4788
|
-
);
|
|
4789
|
-
}
|
|
4793
|
+
const validated = validateModel(input.model, isCodexModel, {
|
|
4794
|
+
entity: PROVIDER2,
|
|
4795
|
+
attemptedAction: "run",
|
|
4796
|
+
notKnownMessage: `interactive-codex: '${input.model}' is not a known Codex model`
|
|
4797
|
+
});
|
|
4798
|
+
if (!validated.ok) return Result.error(validated.error);
|
|
4790
4799
|
let prompt;
|
|
4791
4800
|
try {
|
|
4792
4801
|
prompt = await readFile5(String(input.promptFile));
|
|
@@ -4887,16 +4896,12 @@ var createInteractiveCopilotProvider = (deps) => {
|
|
|
4887
4896
|
const newSessionId = deps.newSessionId ?? uuidv7;
|
|
4888
4897
|
return {
|
|
4889
4898
|
async run(input) {
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
message: `interactive-copilot: '${input.model}' is not a known Copilot model`
|
|
4897
|
-
})
|
|
4898
|
-
);
|
|
4899
|
-
}
|
|
4899
|
+
const validated = validateModel(input.model, isCopilotModel, {
|
|
4900
|
+
entity: PROVIDER3,
|
|
4901
|
+
attemptedAction: "run",
|
|
4902
|
+
notKnownMessage: `interactive-copilot: '${input.model}' is not a known Copilot model`
|
|
4903
|
+
});
|
|
4904
|
+
if (!validated.ok) return Result.error(validated.error);
|
|
4900
4905
|
let prompt;
|
|
4901
4906
|
try {
|
|
4902
4907
|
prompt = await readFile5(String(input.promptFile));
|
|
@@ -5016,10 +5021,10 @@ var createInteractiveAiProvider = (deps) => {
|
|
|
5016
5021
|
};
|
|
5017
5022
|
|
|
5018
5023
|
// src/integration/io/run-cli.ts
|
|
5019
|
-
var runCli = (
|
|
5024
|
+
var runCli = (spawn2, command, args, opts) => new Promise((resolve) => {
|
|
5020
5025
|
let child;
|
|
5021
5026
|
try {
|
|
5022
|
-
child =
|
|
5027
|
+
child = spawn2(command, args, {
|
|
5023
5028
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5024
5029
|
...opts.cwd !== void 0 ? { cwd: opts.cwd } : {}
|
|
5025
5030
|
});
|
|
@@ -5133,9 +5138,9 @@ var looksLikeNotFound = (stderr) => {
|
|
|
5133
5138
|
const s = stderr.toLowerCase();
|
|
5134
5139
|
return s.includes("not found") || s.includes("404") || s.includes("could not resolve") || s.includes("does not exist");
|
|
5135
5140
|
};
|
|
5136
|
-
var fetchGitHub = async (
|
|
5141
|
+
var fetchGitHub = async (spawn2, parsed, url) => {
|
|
5137
5142
|
const result = await runCli(
|
|
5138
|
-
|
|
5143
|
+
spawn2,
|
|
5139
5144
|
"gh",
|
|
5140
5145
|
[
|
|
5141
5146
|
"issue",
|
|
@@ -5182,9 +5187,9 @@ var fetchGitHub = async (spawn3, parsed, url) => {
|
|
|
5182
5187
|
comments
|
|
5183
5188
|
});
|
|
5184
5189
|
};
|
|
5185
|
-
var fetchGitLabNotes = async (
|
|
5190
|
+
var fetchGitLabNotes = async (spawn2, parsed) => {
|
|
5186
5191
|
const result = await runCli(
|
|
5187
|
-
|
|
5192
|
+
spawn2,
|
|
5188
5193
|
"glab",
|
|
5189
5194
|
["issue", "note", "list", String(parsed.number), "--repo", glabRepoArg(parsed), "--output", "json"],
|
|
5190
5195
|
{ timeoutMs: CLI_TIMEOUT_MS }
|
|
@@ -5213,9 +5218,9 @@ var fetchGitLabNotes = async (spawn3, parsed) => {
|
|
|
5213
5218
|
}));
|
|
5214
5219
|
return { comments };
|
|
5215
5220
|
};
|
|
5216
|
-
var fetchGitLab = async (
|
|
5221
|
+
var fetchGitLab = async (spawn2, parsed, url, logger) => {
|
|
5217
5222
|
const result = await runCli(
|
|
5218
|
-
|
|
5223
|
+
spawn2,
|
|
5219
5224
|
"glab",
|
|
5220
5225
|
["issue", "view", String(parsed.number), "--repo", glabRepoArg(parsed), "--output", "json"],
|
|
5221
5226
|
{ timeoutMs: CLI_TIMEOUT_MS }
|
|
@@ -5242,7 +5247,7 @@ var fetchGitLab = async (spawn3, parsed, url, logger) => {
|
|
|
5242
5247
|
})
|
|
5243
5248
|
);
|
|
5244
5249
|
}
|
|
5245
|
-
const notes = await fetchGitLabNotes(
|
|
5250
|
+
const notes = await fetchGitLabNotes(spawn2, parsed);
|
|
5246
5251
|
if (notes.failure !== void 0) {
|
|
5247
5252
|
logger?.warn(`glab issue note list failed for ${url}: ${notes.failure} \u2014 proceeding without comments`);
|
|
5248
5253
|
}
|
|
@@ -5263,9 +5268,9 @@ var createIssueFetcher = (deps) => async (url) => {
|
|
|
5263
5268
|
|
|
5264
5269
|
// src/integration/scm/issue-pusher.ts
|
|
5265
5270
|
var CLI_TIMEOUT_MS2 = 3e4;
|
|
5266
|
-
var commentGitHub = async (
|
|
5271
|
+
var commentGitHub = async (spawn2, url, body, parsed) => {
|
|
5267
5272
|
const r = await runCli(
|
|
5268
|
-
|
|
5273
|
+
spawn2,
|
|
5269
5274
|
"gh",
|
|
5270
5275
|
["issue", "comment", String(parsed.number), "--repo", `${parsed.owner}/${parsed.repo}`, "--body-file", "-"],
|
|
5271
5276
|
{ stdin: body, timeoutMs: CLI_TIMEOUT_MS2 }
|
|
@@ -5281,9 +5286,9 @@ var commentGitHub = async (spawn3, url, body, parsed) => {
|
|
|
5281
5286
|
}
|
|
5282
5287
|
return Result.ok(void 0);
|
|
5283
5288
|
};
|
|
5284
|
-
var commentGitLab = async (
|
|
5289
|
+
var commentGitLab = async (spawn2, url, body, parsed) => {
|
|
5285
5290
|
const r = await runCli(
|
|
5286
|
-
|
|
5291
|
+
spawn2,
|
|
5287
5292
|
"glab",
|
|
5288
5293
|
[
|
|
5289
5294
|
"issue",
|
|
@@ -5408,11 +5413,11 @@ var buildGlabArgs = (input) => {
|
|
|
5408
5413
|
if (input.draft) args.push("--draft");
|
|
5409
5414
|
return args;
|
|
5410
5415
|
};
|
|
5411
|
-
var runPlatformCli = async (
|
|
5416
|
+
var runPlatformCli = async (spawn2, platform, input) => {
|
|
5412
5417
|
const command = platform === "github" ? "gh" : "glab";
|
|
5413
5418
|
const args = platform === "github" ? buildGhArgs(input) : buildGlabArgs(input);
|
|
5414
5419
|
const noun = platform === "github" ? "gh pr create" : "glab mr create";
|
|
5415
|
-
const result = await runCli(
|
|
5420
|
+
const result = await runCli(spawn2, command, args, { cwd: String(input.cwd), timeoutMs: CLI_TIMEOUT_MS3 });
|
|
5416
5421
|
if (!result.ok) return Result.error(result.error);
|
|
5417
5422
|
if (result.value.exitCode !== 0) {
|
|
5418
5423
|
const stderr = result.value.stderr.trim();
|
|
@@ -6024,7 +6029,7 @@ var createNpmVersionChecker = (deps) => {
|
|
|
6024
6029
|
// package.json
|
|
6025
6030
|
var package_default = {
|
|
6026
6031
|
name: "ralphctl",
|
|
6027
|
-
version: "0.
|
|
6032
|
+
version: "0.18.0",
|
|
6028
6033
|
description: "Agent harness for long-running AI coding tasks \u2014 orchestrates Claude Code, GitHub Copilot, and OpenAI Codex across repositories",
|
|
6029
6034
|
homepage: "https://github.com/lukas-grigis/ralphctl",
|
|
6030
6035
|
type: "module",
|
|
@@ -6526,18 +6531,7 @@ var BUNDLED_SKILLS = [
|
|
|
6526
6531
|
];
|
|
6527
6532
|
var skillsForFlow = (flowId) => BUNDLED_SKILLS.filter((entry) => entry.defaultFor.includes(flowId)).map((entry) => entry.name);
|
|
6528
6533
|
|
|
6529
|
-
// src/integration/ai/skills/_engine/
|
|
6530
|
-
import { z as z16 } from "zod";
|
|
6531
|
-
var SkillNameSchema = z16.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u, "lowercase alphanumeric with hyphens, no leading/trailing/consecutive hyphens");
|
|
6532
|
-
var SkillFrontmatterSchema = z16.object({
|
|
6533
|
-
name: SkillNameSchema,
|
|
6534
|
-
description: z16.string().min(1).max(1024),
|
|
6535
|
-
license: z16.string().optional(),
|
|
6536
|
-
compatibility: z16.string().max(500).optional(),
|
|
6537
|
-
"allowed-tools": z16.string().optional()
|
|
6538
|
-
});
|
|
6539
|
-
|
|
6540
|
-
// src/integration/ai/skills/_engine/parse-skill.ts
|
|
6534
|
+
// src/integration/ai/skills/_engine/frontmatter.ts
|
|
6541
6535
|
var splitFrontmatter = (raw) => {
|
|
6542
6536
|
const trimmed = raw.replace(/^\uFEFF/u, "");
|
|
6543
6537
|
if (!trimmed.startsWith("---")) return { frontmatter: "", body: trimmed };
|
|
@@ -6564,6 +6558,19 @@ var parseSimpleYaml = (input) => {
|
|
|
6564
6558
|
return result;
|
|
6565
6559
|
};
|
|
6566
6560
|
var errorCode = (cause) => typeof cause === "object" && cause !== null && "code" in cause && typeof cause.code === "string" ? cause.code : void 0;
|
|
6561
|
+
|
|
6562
|
+
// src/integration/ai/skills/_engine/skill.ts
|
|
6563
|
+
import { z as z16 } from "zod";
|
|
6564
|
+
var SkillNameSchema = z16.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u, "lowercase alphanumeric with hyphens, no leading/trailing/consecutive hyphens");
|
|
6565
|
+
var SkillFrontmatterSchema = z16.object({
|
|
6566
|
+
name: SkillNameSchema,
|
|
6567
|
+
description: z16.string().min(1).max(1024),
|
|
6568
|
+
license: z16.string().optional(),
|
|
6569
|
+
compatibility: z16.string().max(500).optional(),
|
|
6570
|
+
"allowed-tools": z16.string().optional()
|
|
6571
|
+
});
|
|
6572
|
+
|
|
6573
|
+
// src/integration/ai/skills/_engine/parse-skill.ts
|
|
6567
6574
|
var parseSkill = (label, path2, name, raw) => {
|
|
6568
6575
|
const { frontmatter, body } = splitFrontmatter(raw);
|
|
6569
6576
|
const fm = parseSimpleYaml(frontmatter);
|
|
@@ -7293,35 +7300,9 @@ import { readFile as readFile4 } from "fs/promises";
|
|
|
7293
7300
|
var BUNDLED_AGENT_DEFINITIONS = ["ralphctl-evaluator", "ralphctl-generator"];
|
|
7294
7301
|
|
|
7295
7302
|
// src/integration/ai/agents/_engine/parse-agent-definition.ts
|
|
7296
|
-
var splitFrontmatter2 = (raw) => {
|
|
7297
|
-
const trimmed = raw.replace(/^\uFEFF/u, "");
|
|
7298
|
-
if (!trimmed.startsWith("---")) return { frontmatter: "", body: trimmed };
|
|
7299
|
-
const closing = trimmed.indexOf("\n---", 3);
|
|
7300
|
-
if (closing === -1) return { frontmatter: "", body: trimmed };
|
|
7301
|
-
const frontmatter = trimmed.slice(3, closing).trim();
|
|
7302
|
-
const afterClose = trimmed.slice(closing + 4);
|
|
7303
|
-
const body = afterClose.replace(/^(?:\r?\n)+/, "");
|
|
7304
|
-
return { frontmatter, body };
|
|
7305
|
-
};
|
|
7306
|
-
var parseSimpleYaml2 = (input) => {
|
|
7307
|
-
const result = {};
|
|
7308
|
-
for (const line of input.split("\n")) {
|
|
7309
|
-
const stripped = line.trim();
|
|
7310
|
-
if (stripped.length === 0 || stripped.startsWith("#")) continue;
|
|
7311
|
-
const colon = stripped.indexOf(":");
|
|
7312
|
-
if (colon === -1) continue;
|
|
7313
|
-
const key = stripped.slice(0, colon).trim();
|
|
7314
|
-
let value = stripped.slice(colon + 1).trim();
|
|
7315
|
-
if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
|
|
7316
|
-
else if (value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
7317
|
-
result[key] = value;
|
|
7318
|
-
}
|
|
7319
|
-
return result;
|
|
7320
|
-
};
|
|
7321
|
-
var errorCode2 = (cause) => typeof cause === "object" && cause !== null && "code" in cause && typeof cause.code === "string" ? cause.code : void 0;
|
|
7322
7303
|
var parseAgentDefinition = (label, path2, name, raw) => {
|
|
7323
|
-
const { frontmatter, body } =
|
|
7324
|
-
const fm =
|
|
7304
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
7305
|
+
const fm = parseSimpleYaml(frontmatter);
|
|
7325
7306
|
const parsed = AgentDefinitionFrontmatterSchema.safeParse(fm);
|
|
7326
7307
|
if (!parsed.success) {
|
|
7327
7308
|
return Result.error(
|
|
@@ -7375,7 +7356,7 @@ var readDefinitionOptional = async (root, name) => {
|
|
|
7375
7356
|
try {
|
|
7376
7357
|
raw = await readFile4(path2, "utf-8");
|
|
7377
7358
|
} catch (cause) {
|
|
7378
|
-
if (
|
|
7359
|
+
if (errorCode(cause) === "ENOENT") return Result.ok(void 0);
|
|
7379
7360
|
return Result.error(
|
|
7380
7361
|
new StorageError({ subCode: "io", message: `bundled agent definition not readable: ${path2}`, path: path2, cause })
|
|
7381
7362
|
);
|
|
@@ -7423,7 +7404,7 @@ var loadOperatorAgentDefinitions = async (deps) => {
|
|
|
7423
7404
|
try {
|
|
7424
7405
|
entries = await fs14.readdir(root, { withFileTypes: true });
|
|
7425
7406
|
} catch (cause) {
|
|
7426
|
-
if (
|
|
7407
|
+
if (errorCode(cause) === "ENOENT") return [];
|
|
7427
7408
|
log.warn("operator agent definitions dir not readable", { path: root, cause });
|
|
7428
7409
|
return [];
|
|
7429
7410
|
}
|
|
@@ -7656,7 +7637,7 @@ var buildWireAgentDefinitionSource = (storage2, logger) => composeAgentDefinitio
|
|
|
7656
7637
|
})
|
|
7657
7638
|
);
|
|
7658
7639
|
var wire = (opts) => {
|
|
7659
|
-
const
|
|
7640
|
+
const spawn2 = opts.spawn ?? defaultPipeSpawn;
|
|
7660
7641
|
const env = opts.env ?? process.env;
|
|
7661
7642
|
const debugTrace = isTruthyEnvFlag(env[RALPHCTL_DEBUG_TRACE_ENV]);
|
|
7662
7643
|
const appendFile = createAppendFile();
|
|
@@ -7716,9 +7697,9 @@ var wire = (opts) => {
|
|
|
7716
7697
|
availableModelsFor,
|
|
7717
7698
|
eventBus,
|
|
7718
7699
|
logger,
|
|
7719
|
-
pullRequestCreator: createPullRequestCreator({ gitRunner: createGitRunner(), spawn:
|
|
7720
|
-
issueFetcher: createIssueFetcher({ spawn:
|
|
7721
|
-
issuePusher: createIssuePusher({ spawn:
|
|
7700
|
+
pullRequestCreator: createPullRequestCreator({ gitRunner: createGitRunner(), spawn: spawn2 }),
|
|
7701
|
+
issueFetcher: createIssueFetcher({ spawn: spawn2, logger }),
|
|
7702
|
+
issuePusher: createIssuePusher({ spawn: spawn2 }),
|
|
7722
7703
|
versionChecker: createNpmVersionChecker({
|
|
7723
7704
|
stateRoot: opts.storage.stateRoot,
|
|
7724
7705
|
currentVersion: CLI_METADATA.currentVersion,
|
|
@@ -8954,9 +8935,8 @@ var useSelection = () => {
|
|
|
8954
8935
|
import { createContext as createContext10, useCallback as useCallback5, useContext as useContext10, useEffect as useEffect4, useState as useState6 } from "react";
|
|
8955
8936
|
|
|
8956
8937
|
// src/integration/io/command-exists.ts
|
|
8957
|
-
import { spawn as spawn2 } from "child_process";
|
|
8958
8938
|
var commandExists = (name) => new Promise((resolve) => {
|
|
8959
|
-
const child = process.platform === "win32" ?
|
|
8939
|
+
const child = process.platform === "win32" ? crossPlatformSpawn("where", [name], { stdio: "ignore" }) : crossPlatformSpawn("sh", ["-c", 'command -v "$0"', name], { stdio: "ignore" });
|
|
8960
8940
|
let settled = false;
|
|
8961
8941
|
const settle = (value) => {
|
|
8962
8942
|
if (settled) return;
|
|
@@ -10033,7 +10013,7 @@ var Breadcrumb = () => {
|
|
|
10033
10013
|
/* @__PURE__ */ jsx14(Text5, { color: inkColors.primary, bold: true, children: right[1] }),
|
|
10034
10014
|
/* @__PURE__ */ jsx14(Text5, { dimColor: true, children: " " }),
|
|
10035
10015
|
/* @__PURE__ */ jsx14(Text5, { color: inkColors.highlight, bold: true, children: "[S]" }),
|
|
10036
|
-
atLeast("md") && effectiveSprintStatus !== void 0 && /* @__PURE__ */ jsx14(Box3, { marginLeft:
|
|
10016
|
+
atLeast("md") && effectiveSprintStatus !== void 0 && /* @__PURE__ */ jsx14(Box3, { marginLeft: spacing.gutter, children: /* @__PURE__ */ jsx14(StatusChip, { label: effectiveSprintStatus, kind: sprintStatusKind(effectiveSprintStatus) }) })
|
|
10037
10017
|
] })
|
|
10038
10018
|
] })
|
|
10039
10019
|
]
|
|
@@ -11104,7 +11084,7 @@ var ScrollableMessage = ({ message, ownsArrows = true }) => {
|
|
|
11104
11084
|
" ",
|
|
11105
11085
|
header
|
|
11106
11086
|
] }),
|
|
11107
|
-
body.length > 0 && /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", marginTop:
|
|
11087
|
+
body.length > 0 && /* @__PURE__ */ jsxs12(Box11, { flexDirection: "column", marginTop: spacing.section, children: [
|
|
11108
11088
|
/* @__PURE__ */ jsx22(Box11, { flexDirection: "column", borderStyle: "single", borderColor: inkColors.rule, paddingX: spacing.cardPadX, children: visible.map((line, i) => /* @__PURE__ */ jsx22(Text13, { children: line.length === 0 ? " " : line }, `body-${String(offset + i)}`)) }),
|
|
11109
11089
|
overflows && /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
11110
11090
|
"lines ",
|
|
@@ -11146,7 +11126,7 @@ var ConfirmPrompt = ({
|
|
|
11146
11126
|
const Pill = ({ on, label }) => /* @__PURE__ */ jsx23(Text14, { color: on ? inkColors.primary : inkColors.muted, bold: on, children: on ? `[ ${label} ]` : ` ${label} ` });
|
|
11147
11127
|
return /* @__PURE__ */ jsxs13(Box12, { flexDirection: "column", paddingX: spacing.indent, children: [
|
|
11148
11128
|
/* @__PURE__ */ jsx23(ScrollableMessage, { message }),
|
|
11149
|
-
/* @__PURE__ */ jsxs13(Box12, { marginTop:
|
|
11129
|
+
/* @__PURE__ */ jsxs13(Box12, { marginTop: spacing.section, children: [
|
|
11150
11130
|
/* @__PURE__ */ jsx23(Pill, { on: yes, label: "Yes" }),
|
|
11151
11131
|
/* @__PURE__ */ jsx23(Text14, { children: " " }),
|
|
11152
11132
|
/* @__PURE__ */ jsx23(Pill, { on: !yes, label: "No" })
|
|
@@ -11209,7 +11189,7 @@ var SelectPrompt = ({
|
|
|
11209
11189
|
const end = Math.min(options.length, start + VISIBLE_ROWS);
|
|
11210
11190
|
return /* @__PURE__ */ jsxs14(Box13, { flexDirection: "column", paddingX: spacing.indent, children: [
|
|
11211
11191
|
/* @__PURE__ */ jsx24(ScrollableMessage, { message, ownsArrows: false }),
|
|
11212
|
-
/* @__PURE__ */ jsx24(Box13, { flexDirection: "column", marginTop:
|
|
11192
|
+
/* @__PURE__ */ jsx24(Box13, { flexDirection: "column", marginTop: spacing.section, children: options.slice(start, end).map((opt, localIdx) => {
|
|
11213
11193
|
const i = start + localIdx;
|
|
11214
11194
|
const focused = i === cursor;
|
|
11215
11195
|
const disabled = opt.disabled === true;
|
|
@@ -11352,7 +11332,7 @@ var MultiSelectPrompt = ({
|
|
|
11352
11332
|
" ",
|
|
11353
11333
|
message
|
|
11354
11334
|
] }),
|
|
11355
|
-
/* @__PURE__ */ jsx25(Box14, { flexDirection: "column", marginTop:
|
|
11335
|
+
/* @__PURE__ */ jsx25(Box14, { flexDirection: "column", marginTop: spacing.section, children: options.slice(start, end).map((opt, localIdx) => {
|
|
11356
11336
|
const i = start + localIdx;
|
|
11357
11337
|
const focused = i === cursor && opt.disabled !== true;
|
|
11358
11338
|
return /* @__PURE__ */ jsx25(OptionRow, { opt, focused, checked: picked.has(i) }, `opt-${String(i)}`);
|
|
@@ -11585,6 +11565,9 @@ var OverflowRow = ({ direction, count }) => {
|
|
|
11585
11565
|
// src/application/ui/tui/components/action-menu.tsx
|
|
11586
11566
|
import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
11587
11567
|
var isEnabled3 = (item) => item.disabledReason === void 0;
|
|
11568
|
+
var RESERVED_NAV_KEYS = new Set(
|
|
11569
|
+
[...listKeys.up.keys, ...listKeys.down.keys].filter((k) => /^[a-z]$/.test(k))
|
|
11570
|
+
);
|
|
11588
11571
|
var findInitialCursorId = (items, initialIndex, enabledItems) => {
|
|
11589
11572
|
for (let i = initialIndex; i < items.length; i++) {
|
|
11590
11573
|
const it = items[i];
|
|
@@ -11592,7 +11575,10 @@ var findInitialCursorId = (items, initialIndex, enabledItems) => {
|
|
|
11592
11575
|
}
|
|
11593
11576
|
return enabledItems[0]?.id ?? "";
|
|
11594
11577
|
};
|
|
11595
|
-
var matchHotkey = (items, input) =>
|
|
11578
|
+
var matchHotkey = (items, input) => {
|
|
11579
|
+
if (RESERVED_NAV_KEYS.has(input)) return void 0;
|
|
11580
|
+
return items.find((it) => it.hotkey === input && it.globalHotkey !== true && isEnabled3(it));
|
|
11581
|
+
};
|
|
11596
11582
|
var isRowVisible = (it, enabled, visibleEnabledIds, windowedEnabled) => {
|
|
11597
11583
|
if (enabled) return visibleEnabledIds.has(it.id);
|
|
11598
11584
|
if (windowedEnabled.length === 0) return true;
|
|
@@ -14001,14 +13987,20 @@ var warnIfContractViolated = (logger, skillName, content) => {
|
|
|
14001
13987
|
};
|
|
14002
13988
|
|
|
14003
13989
|
// src/business/settings/resolve-effort.ts
|
|
13990
|
+
var FLOW_DEFAULT_EFFORT = {
|
|
13991
|
+
plan: "high",
|
|
13992
|
+
ideate: "high"
|
|
13993
|
+
};
|
|
14004
13994
|
var resolveEffort = (flow, settings) => {
|
|
14005
13995
|
const row = primaryFlowRow(settings.ai, flow);
|
|
14006
|
-
return resolveEffortForRow(row, settings.ai.effort);
|
|
13996
|
+
return resolveEffortForRow(row, settings.ai.effort, flow);
|
|
14007
13997
|
};
|
|
14008
|
-
var resolveEffortForRow = (row, globalEffort) => {
|
|
13998
|
+
var resolveEffortForRow = (row, globalEffort, flow) => {
|
|
14009
13999
|
if (row.effort !== void 0) return row.effort;
|
|
14010
|
-
if (globalEffort
|
|
14011
|
-
|
|
14000
|
+
if (globalEffort !== void 0) return _floorForProvider(globalEffort, row.provider);
|
|
14001
|
+
const flowDefault = flow !== void 0 ? FLOW_DEFAULT_EFFORT[flow] : void 0;
|
|
14002
|
+
if (flowDefault !== void 0) return clampEffortToProvider(flowDefault, row.provider);
|
|
14003
|
+
return void 0;
|
|
14012
14004
|
};
|
|
14013
14005
|
var clampEffortToProvider = (effort, provider) => {
|
|
14014
14006
|
if (provider === "openai-codex" && effort === "max") return "xhigh";
|
|
@@ -14585,6 +14577,28 @@ var interactiveSprintNameLeaf = (deps) => leaf("interactive-sprint-name", {
|
|
|
14585
14577
|
// src/application/flows/create-sprint/leaves/init-progress-journal.ts
|
|
14586
14578
|
import { join as join31 } from "path";
|
|
14587
14579
|
|
|
14580
|
+
// src/business/_shared/format-duration.ts
|
|
14581
|
+
var EM_DASH = "\u2014";
|
|
14582
|
+
var MS_PER_SECOND = 1e3;
|
|
14583
|
+
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
14584
|
+
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
14585
|
+
var formatDuration = (ms) => {
|
|
14586
|
+
if (ms === void 0) return EM_DASH;
|
|
14587
|
+
if (ms < 0) return `${String(ms)}ms`;
|
|
14588
|
+
if (ms < MS_PER_SECOND) return `${String(ms)}ms`;
|
|
14589
|
+
if (ms < MS_PER_MINUTE) {
|
|
14590
|
+
return `${String(Math.floor(ms / MS_PER_SECOND))}s`;
|
|
14591
|
+
}
|
|
14592
|
+
if (ms < MS_PER_HOUR) {
|
|
14593
|
+
const minutes2 = Math.floor(ms / MS_PER_MINUTE);
|
|
14594
|
+
const seconds = Math.floor(ms % MS_PER_MINUTE / MS_PER_SECOND);
|
|
14595
|
+
return seconds > 0 ? `${String(minutes2)}m ${String(seconds)}s` : `${String(minutes2)}m`;
|
|
14596
|
+
}
|
|
14597
|
+
const hours = Math.floor(ms / MS_PER_HOUR);
|
|
14598
|
+
const minutes = Math.floor(ms % MS_PER_HOUR / MS_PER_MINUTE);
|
|
14599
|
+
return minutes > 0 ? `${String(hours)}h ${String(minutes)}m` : `${String(hours)}h`;
|
|
14600
|
+
};
|
|
14601
|
+
|
|
14588
14602
|
// src/business/sprint/journal-sanitize.ts
|
|
14589
14603
|
var sanitizeInline = (text) => text.replace(/[\r\n\t\v\f]+/g, " ").trim();
|
|
14590
14604
|
var neutralizeProseHeadings = (text) => text.split("\n").map((line) => line.startsWith("#") ? ` ${line}` : line).join("\n");
|
|
@@ -14628,26 +14642,7 @@ _Task ${sanitizeInline(taskName)}: rejected diff quarantined to git stash \u2014
|
|
|
14628
14642
|
`;
|
|
14629
14643
|
|
|
14630
14644
|
// src/business/sprint/render-journal-entry.ts
|
|
14631
|
-
var
|
|
14632
|
-
var MS_PER_SECOND = 1e3;
|
|
14633
|
-
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
14634
|
-
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
14635
|
-
var formatDuration = (ms) => {
|
|
14636
|
-
if (ms === void 0) return EM_DASH;
|
|
14637
|
-
if (ms < 0) return `${String(ms)}ms`;
|
|
14638
|
-
if (ms < MS_PER_SECOND) return `${String(ms)}ms`;
|
|
14639
|
-
if (ms < MS_PER_MINUTE) {
|
|
14640
|
-
return `${String(Math.floor(ms / MS_PER_SECOND))}s`;
|
|
14641
|
-
}
|
|
14642
|
-
if (ms < MS_PER_HOUR) {
|
|
14643
|
-
const minutes2 = Math.floor(ms / MS_PER_MINUTE);
|
|
14644
|
-
const seconds = Math.floor(ms % MS_PER_MINUTE / MS_PER_SECOND);
|
|
14645
|
-
return seconds > 0 ? `${String(minutes2)}m ${String(seconds)}s` : `${String(minutes2)}m`;
|
|
14646
|
-
}
|
|
14647
|
-
const hours = Math.floor(ms / MS_PER_HOUR);
|
|
14648
|
-
const minutes = Math.floor(ms % MS_PER_HOUR / MS_PER_MINUTE);
|
|
14649
|
-
return minutes > 0 ? `${String(hours)}h ${String(minutes)}m` : `${String(hours)}h`;
|
|
14650
|
-
};
|
|
14645
|
+
var EM_DASH2 = "\u2014";
|
|
14651
14646
|
var SHA_DISPLAY_LENGTH = 7;
|
|
14652
14647
|
var appendSubsection = (lines, heading, entries) => {
|
|
14653
14648
|
if (entries.length === 0) return;
|
|
@@ -14734,7 +14729,7 @@ var appendOutcomeDetail = (lines, input) => {
|
|
|
14734
14729
|
lines.push("");
|
|
14735
14730
|
};
|
|
14736
14731
|
var renderJournalEntry = (input) => {
|
|
14737
|
-
const sha = input.commitSha !== void 0 ? input.commitSha.slice(0, SHA_DISPLAY_LENGTH) :
|
|
14732
|
+
const sha = input.commitSha !== void 0 ? input.commitSha.slice(0, SHA_DISPLAY_LENGTH) : EM_DASH2;
|
|
14738
14733
|
const outcome = input.outcome.trim().length > 0 ? neutralizeProseHeadings(input.outcome.trim()) : "_(no outcome paragraph)_";
|
|
14739
14734
|
const lines = [];
|
|
14740
14735
|
lines.push("");
|
|
@@ -15676,11 +15671,29 @@ var assertTemplateKeysFilled = (rendered, template, partialBodies, values, where
|
|
|
15676
15671
|
};
|
|
15677
15672
|
|
|
15678
15673
|
// src/integration/ai/prompts/_engine/build-prompt.ts
|
|
15674
|
+
var findPartialParameterCollision = (def, partialPlaceholders) => {
|
|
15675
|
+
for (const [field, rawSpec] of Object.entries(def.parameters)) {
|
|
15676
|
+
if (partialPlaceholders.has(rawSpec.placeholder)) return [field, rawSpec.placeholder];
|
|
15677
|
+
}
|
|
15678
|
+
return void 0;
|
|
15679
|
+
};
|
|
15679
15680
|
var buildPrompt = async (loader, def, input) => {
|
|
15680
15681
|
const template = await loader.load(def.templateName);
|
|
15681
15682
|
if (!template.ok) return Result.error(template.error);
|
|
15682
15683
|
const values = {};
|
|
15683
15684
|
const partialBodies = [];
|
|
15685
|
+
const partialPlaceholders = new Set(def.partials !== void 0 ? Object.keys(def.partials) : []);
|
|
15686
|
+
const collision = findPartialParameterCollision(def, partialPlaceholders);
|
|
15687
|
+
if (collision !== void 0) {
|
|
15688
|
+
const [field, placeholder] = collision;
|
|
15689
|
+
return Result.error(
|
|
15690
|
+
new ValidationError({
|
|
15691
|
+
field,
|
|
15692
|
+
value: placeholder,
|
|
15693
|
+
message: `buildPrompt(${def.templateName}): parameter '${field}' declares placeholder {{${placeholder}}}, which collides with an auto-loaded partial slot of the same name \u2014 rename one of them.`
|
|
15694
|
+
})
|
|
15695
|
+
);
|
|
15696
|
+
}
|
|
15684
15697
|
if (def.partials !== void 0) {
|
|
15685
15698
|
for (const [placeholder, name] of Object.entries(def.partials)) {
|
|
15686
15699
|
const partial = await loader.load(name);
|
|
@@ -18546,7 +18559,6 @@ var branchPreflightLeaf = (deps, opts, name = "branch-preflight") => leaf(name,
|
|
|
18546
18559
|
});
|
|
18547
18560
|
|
|
18548
18561
|
// src/application/flows/implement/leaves/build-task-workspace.ts
|
|
18549
|
-
import { promises as fs21 } from "fs";
|
|
18550
18562
|
import { join as join44 } from "path";
|
|
18551
18563
|
|
|
18552
18564
|
// src/business/task/compose-criteria-history.ts
|
|
@@ -18849,22 +18861,6 @@ ${body}
|
|
|
18849
18861
|
};
|
|
18850
18862
|
|
|
18851
18863
|
// src/application/flows/implement/leaves/build-task-workspace.ts
|
|
18852
|
-
var writeOrError = async (path2, content) => {
|
|
18853
|
-
try {
|
|
18854
|
-
await fs21.mkdir(path2.slice(0, path2.lastIndexOf("/")), { recursive: true });
|
|
18855
|
-
await fs21.writeFile(path2, content, "utf8");
|
|
18856
|
-
return Result.ok(void 0);
|
|
18857
|
-
} catch (cause) {
|
|
18858
|
-
return Result.error(
|
|
18859
|
-
new StorageError({
|
|
18860
|
-
subCode: "io",
|
|
18861
|
-
message: `failed to write task workspace file: ${path2}`,
|
|
18862
|
-
path: path2,
|
|
18863
|
-
cause
|
|
18864
|
-
})
|
|
18865
|
-
);
|
|
18866
|
-
}
|
|
18867
|
-
};
|
|
18868
18864
|
var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-${String(taskId)}`, {
|
|
18869
18865
|
useCase: {
|
|
18870
18866
|
execute: async (input) => {
|
|
@@ -18873,6 +18869,10 @@ var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-
|
|
|
18873
18869
|
const previewOutputDir = AbsolutePath.parse(join44(workspaceRoot, "rounds", "1", "generator"));
|
|
18874
18870
|
if (!previewOutputDir.ok) return Result.error(previewOutputDir.error);
|
|
18875
18871
|
const contractPath = join44(workspaceRoot, "contract.md");
|
|
18872
|
+
const parsedContractPath = AbsolutePath.parse(contractPath);
|
|
18873
|
+
if (!parsedContractPath.ok) return Result.error(parsedContractPath.error);
|
|
18874
|
+
const parsedPromptPath = AbsolutePath.parse(join44(workspaceRoot, "prompt.md"));
|
|
18875
|
+
if (!parsedPromptPath.ok) return Result.error(parsedPromptPath.error);
|
|
18876
18876
|
const prompt = await buildImplementPrompt(deps.templateLoader, {
|
|
18877
18877
|
task: input.task,
|
|
18878
18878
|
projectPath: String(opts.cwd),
|
|
@@ -18883,9 +18883,9 @@ var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-
|
|
|
18883
18883
|
...opts.verifyScript !== void 0 ? { verifyScript: opts.verifyScript } : {}
|
|
18884
18884
|
});
|
|
18885
18885
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
18886
|
-
const wrotePrompt = await
|
|
18886
|
+
const wrotePrompt = await deps.writeFile(parsedPromptPath.value, String(prompt.value));
|
|
18887
18887
|
if (!wrotePrompt.ok) return Result.error(wrotePrompt.error);
|
|
18888
|
-
const wroteContract = await
|
|
18888
|
+
const wroteContract = await deps.writeFile(parsedContractPath.value, renderContractMd(input.task));
|
|
18889
18889
|
if (!wroteContract.ok) return Result.error(wroteContract.error);
|
|
18890
18890
|
log.debug("task workspace built", { taskId: input.task.id, workspaceRoot });
|
|
18891
18891
|
const parsedRoot = AbsolutePath.parse(workspaceRoot);
|
|
@@ -19306,6 +19306,7 @@ var markTaskDone = (task, now) => {
|
|
|
19306
19306
|
...guard2.value.escalatedFromModel !== void 0 ? { escalatedFromModel: guard2.value.escalatedFromModel } : {},
|
|
19307
19307
|
...guard2.value.escalatedToModel !== void 0 ? { escalatedToModel: guard2.value.escalatedToModel } : {},
|
|
19308
19308
|
...guard2.value.escalatedToEffort !== void 0 ? { escalatedToEffort: guard2.value.escalatedToEffort } : {},
|
|
19309
|
+
...guard2.value.escalatedToEvaluatorEffort !== void 0 ? { escalatedToEvaluatorEffort: guard2.value.escalatedToEvaluatorEffort } : {},
|
|
19309
19310
|
status: "done",
|
|
19310
19311
|
attempts,
|
|
19311
19312
|
finalAttemptN: verified.n
|
|
@@ -19350,6 +19351,11 @@ var recordTaskEffortEscalation = (task, toEffort) => {
|
|
|
19350
19351
|
if (!to.ok) return Result.error(to.error);
|
|
19351
19352
|
return Result.ok({ ...task, escalatedToEffort: to.value });
|
|
19352
19353
|
};
|
|
19354
|
+
var recordTaskEvaluatorEffortEscalation = (task, toEffort) => {
|
|
19355
|
+
const to = parseRequiredString("task.escalatedToEvaluatorEffort", toEffort);
|
|
19356
|
+
if (!to.ok) return Result.error(to.error);
|
|
19357
|
+
return Result.ok({ ...task, escalatedToEvaluatorEffort: to.value });
|
|
19358
|
+
};
|
|
19353
19359
|
|
|
19354
19360
|
// src/business/task/escalation-policy.ts
|
|
19355
19361
|
var BANNER_SHOW_EVENT = "banner-show";
|
|
@@ -19373,11 +19379,14 @@ var decideEscalation = (props) => {
|
|
|
19373
19379
|
if (nudgedAtTop) return { kind: "topped-out", model: props.generatorModel };
|
|
19374
19380
|
const effortTarget = nextEffortRung(props.generatorProvider, props.generatorModel, props.generatorEffort);
|
|
19375
19381
|
if (effortTarget !== void 0) {
|
|
19382
|
+
const evaluatorEffortTarget = props.evaluatorModel !== void 0 ? nextEffortRung(props.evaluatorProvider, props.evaluatorModel, props.evaluatorEffort) : void 0;
|
|
19383
|
+
const evaluatorCarry = evaluatorEffortTarget !== void 0 ? { evaluator: { from: props.evaluatorEffort ?? "default", to: evaluatorEffortTarget } } : {};
|
|
19376
19384
|
return {
|
|
19377
19385
|
kind: "escalate-effort",
|
|
19378
19386
|
model: props.generatorModel,
|
|
19379
19387
|
from: props.generatorEffort ?? "default",
|
|
19380
|
-
to: effortTarget
|
|
19388
|
+
to: effortTarget,
|
|
19389
|
+
...evaluatorCarry
|
|
19381
19390
|
};
|
|
19382
19391
|
}
|
|
19383
19392
|
return { kind: "nudge", currentModel: props.generatorModel };
|
|
@@ -19449,11 +19458,12 @@ var applyEscalation = (props) => {
|
|
|
19449
19458
|
return Result.ok({ task: stamped.value });
|
|
19450
19459
|
}
|
|
19451
19460
|
case "escalate-effort": {
|
|
19461
|
+
const evaluatorClause = decision.evaluator !== void 0 ? `; evaluator effort: ${decision.evaluator.from} \u2192 ${decision.evaluator.to}` : "";
|
|
19452
19462
|
eventBus.publish({
|
|
19453
19463
|
type: BANNER_SHOW_EVENT,
|
|
19454
19464
|
id: bannerId,
|
|
19455
19465
|
tier: "info",
|
|
19456
|
-
message: `raised generator effort on '${decision.model}': ${decision.from} \u2192 ${decision.to}`,
|
|
19466
|
+
message: `raised generator effort on '${decision.model}': ${decision.from} \u2192 ${decision.to}${evaluatorClause}`,
|
|
19457
19467
|
cause,
|
|
19458
19468
|
at: now
|
|
19459
19469
|
});
|
|
@@ -19462,7 +19472,8 @@ var applyEscalation = (props) => {
|
|
|
19462
19472
|
model: decision.model,
|
|
19463
19473
|
from: decision.from,
|
|
19464
19474
|
to: decision.to,
|
|
19465
|
-
reason: trigger
|
|
19475
|
+
reason: trigger,
|
|
19476
|
+
...decision.evaluator !== void 0 ? { evaluatorFrom: decision.evaluator.from, evaluatorTo: decision.evaluator.to } : {}
|
|
19466
19477
|
});
|
|
19467
19478
|
return Result.ok({ task });
|
|
19468
19479
|
}
|
|
@@ -19563,7 +19574,12 @@ var resolveEscalatableRemedy = (exit, props, cfg) => {
|
|
|
19563
19574
|
// provider (or the row carries no effort dimension the caller could resolve) the policy never
|
|
19564
19575
|
// returns `escalate-effort` and behaves exactly as before.
|
|
19565
19576
|
generatorProvider: props.generatorProvider,
|
|
19566
|
-
generatorEffort: props.generatorEffort
|
|
19577
|
+
generatorEffort: props.generatorEffort,
|
|
19578
|
+
// Evaluator lockstep context — same optionality: absent context means the policy never
|
|
19579
|
+
// returns an `evaluator` field on the decision.
|
|
19580
|
+
evaluatorProvider: props.evaluatorProvider,
|
|
19581
|
+
evaluatorModel: props.evaluatorModel,
|
|
19582
|
+
evaluatorEffort: props.evaluatorEffort
|
|
19567
19583
|
});
|
|
19568
19584
|
const applied = applyEscalation({
|
|
19569
19585
|
task: props.task,
|
|
@@ -19583,6 +19599,11 @@ var resolveEscalatableRemedy = (exit, props, cfg) => {
|
|
|
19583
19599
|
const stamped = recordTaskEffortEscalation(task, decision.to);
|
|
19584
19600
|
if (!stamped.ok) return Result.error(stamped.error);
|
|
19585
19601
|
task = stamped.value;
|
|
19602
|
+
if (decision.evaluator !== void 0) {
|
|
19603
|
+
const evaluatorStamped = recordTaskEvaluatorEffortEscalation(task, decision.evaluator.to);
|
|
19604
|
+
if (!evaluatorStamped.ok) return Result.error(evaluatorStamped.error);
|
|
19605
|
+
task = evaluatorStamped.value;
|
|
19606
|
+
}
|
|
19586
19607
|
}
|
|
19587
19608
|
const shouldFailAttempt = decision.kind === "escalate" || decision.kind === "escalate-effort" || decision.kind === "nudge";
|
|
19588
19609
|
return Result.ok({
|
|
@@ -19679,7 +19700,10 @@ var finalizeGenEvalLeaf = (deps, taskId) => leaf(`finalize-gen-eval-${String(tas
|
|
|
19679
19700
|
clock: deps.clock,
|
|
19680
19701
|
generatorModel: input.generatorModel,
|
|
19681
19702
|
...input.generatorProvider !== void 0 ? { generatorProvider: input.generatorProvider } : {},
|
|
19682
|
-
...input.generatorEffort !== void 0 ? { generatorEffort: input.generatorEffort } : {}
|
|
19703
|
+
...input.generatorEffort !== void 0 ? { generatorEffort: input.generatorEffort } : {},
|
|
19704
|
+
...input.evaluatorProvider !== void 0 ? { evaluatorProvider: input.evaluatorProvider } : {},
|
|
19705
|
+
...input.evaluatorModel !== void 0 ? { evaluatorModel: input.evaluatorModel } : {},
|
|
19706
|
+
...input.evaluatorEffort !== void 0 ? { evaluatorEffort: input.evaluatorEffort } : {}
|
|
19683
19707
|
})
|
|
19684
19708
|
},
|
|
19685
19709
|
input: (ctx) => {
|
|
@@ -19701,6 +19725,7 @@ var finalizeGenEvalLeaf = (deps, taskId) => leaf(`finalize-gen-eval-${String(tas
|
|
|
19701
19725
|
}
|
|
19702
19726
|
const generatorModel = ctx.currentTask.escalatedToModel ?? deps.configuredGeneratorModel;
|
|
19703
19727
|
const generatorEffort = ctx.currentTask.escalatedToEffort ?? deps.configuredGeneratorEffort;
|
|
19728
|
+
const evaluatorEffort = ctx.currentTask.escalatedToEvaluatorEffort ?? deps.configuredEvaluatorEffort;
|
|
19704
19729
|
return {
|
|
19705
19730
|
task: ctx.currentTask,
|
|
19706
19731
|
sprintId: ctx.sprintId,
|
|
@@ -19708,7 +19733,10 @@ var finalizeGenEvalLeaf = (deps, taskId) => leaf(`finalize-gen-eval-${String(tas
|
|
|
19708
19733
|
turnsUsed: ctx.genEvalTurn ?? 0,
|
|
19709
19734
|
generatorModel,
|
|
19710
19735
|
...deps.configuredGeneratorProvider !== void 0 ? { generatorProvider: deps.configuredGeneratorProvider } : {},
|
|
19711
|
-
...generatorEffort !== void 0 ? { generatorEffort } : {}
|
|
19736
|
+
...generatorEffort !== void 0 ? { generatorEffort } : {},
|
|
19737
|
+
...deps.configuredEvaluatorProvider !== void 0 ? { evaluatorProvider: deps.configuredEvaluatorProvider } : {},
|
|
19738
|
+
...deps.configuredEvaluatorModel !== void 0 ? { evaluatorModel: deps.configuredEvaluatorModel } : {},
|
|
19739
|
+
...evaluatorEffort !== void 0 ? { evaluatorEffort } : {}
|
|
19712
19740
|
};
|
|
19713
19741
|
},
|
|
19714
19742
|
output: (ctx, out) => {
|
|
@@ -19846,7 +19874,7 @@ var computePlateauVerdict = (priorTurns, current, options) => {
|
|
|
19846
19874
|
|
|
19847
19875
|
// src/application/flows/implement/leaves/evaluator.ts
|
|
19848
19876
|
import { dirname as dirname17, join as join46 } from "path";
|
|
19849
|
-
import { promises as
|
|
19877
|
+
import { promises as fs22 } from "fs";
|
|
19850
19878
|
|
|
19851
19879
|
// src/business/task/turn-error-policy.ts
|
|
19852
19880
|
var isRecoverableTurnError = (err) => err.code !== ErrorCode.Aborted && err.code !== ErrorCode.RateLimit;
|
|
@@ -20184,7 +20212,7 @@ var buildEvaluateContinuationPrompt = async (deps, input) => buildPrompt(deps, e
|
|
|
20184
20212
|
|
|
20185
20213
|
// src/integration/ai/contract/_engine/corrective-retry.ts
|
|
20186
20214
|
import { ZodError } from "zod";
|
|
20187
|
-
var isCorrectableContractError = (err) => err
|
|
20215
|
+
var isCorrectableContractError = (err) => err instanceof InvalidStateError || err instanceof ParseError;
|
|
20188
20216
|
var MAX_ENUMERATED_ISSUES = 10;
|
|
20189
20217
|
var correctiveBody = (err, signalsPath, selfContainedContext) => {
|
|
20190
20218
|
const lines = [];
|
|
@@ -20255,6 +20283,14 @@ var validateSignalsFileWithCorrectiveRetry = async (deps, contract) => {
|
|
|
20255
20283
|
return Result.ok({ signals: revalidated.value, nudgeCount: attempt });
|
|
20256
20284
|
}
|
|
20257
20285
|
lastErr = revalidated.error;
|
|
20286
|
+
if (!isCorrectableContractError(lastErr)) {
|
|
20287
|
+
log.warn("corrective retry produced a non-correctable error \u2014 self-blocking without a further nudge", {
|
|
20288
|
+
outputDir: String(deps.outputDir),
|
|
20289
|
+
attempt,
|
|
20290
|
+
error: lastErr.message
|
|
20291
|
+
});
|
|
20292
|
+
return Result.error(lastErr);
|
|
20293
|
+
}
|
|
20258
20294
|
}
|
|
20259
20295
|
log.warn("corrective retries exhausted \u2014 self-blocking", {
|
|
20260
20296
|
outputDir: String(deps.outputDir),
|
|
@@ -20526,7 +20562,7 @@ var evaluatorOutputContract = {
|
|
|
20526
20562
|
};
|
|
20527
20563
|
|
|
20528
20564
|
// src/application/flows/implement/leaves/round-artifacts.ts
|
|
20529
|
-
import { promises as
|
|
20565
|
+
import { promises as fs21 } from "fs";
|
|
20530
20566
|
import { join as join45 } from "path";
|
|
20531
20567
|
var nextRoundNum = async (workspaceRoot) => {
|
|
20532
20568
|
const entries = await listDir(join45(String(workspaceRoot), "rounds"));
|
|
@@ -20545,7 +20581,7 @@ var readRoundSessionId = async (workspaceRoot, round, role) => {
|
|
|
20545
20581
|
const path2 = join45(String(workspaceRoot), "rounds", String(round), role, "session-id.txt");
|
|
20546
20582
|
let content;
|
|
20547
20583
|
try {
|
|
20548
|
-
content = await
|
|
20584
|
+
content = await fs21.readFile(path2, "utf8");
|
|
20549
20585
|
} catch {
|
|
20550
20586
|
return void 0;
|
|
20551
20587
|
}
|
|
@@ -20617,7 +20653,7 @@ var positiveCountCarry = (field, delta, prior) => delta > 0 ? { [field]: (prior
|
|
|
20617
20653
|
// src/application/flows/implement/leaves/evaluator.ts
|
|
20618
20654
|
var readCappedProgress = async (path2, currentTaskId, model) => {
|
|
20619
20655
|
try {
|
|
20620
|
-
return capProgressBody(await
|
|
20656
|
+
return capProgressBody(await fs22.readFile(path2, "utf8"), {
|
|
20621
20657
|
currentTaskId,
|
|
20622
20658
|
recentBudgetTokens: progressCapBudgetForModel(model)
|
|
20623
20659
|
});
|
|
@@ -20676,7 +20712,7 @@ var makeEvaluatorReinvoke = (deps, args) => async (corrective, attempt) => {
|
|
|
20676
20712
|
args.signalsFile,
|
|
20677
20713
|
"evaluator",
|
|
20678
20714
|
resume,
|
|
20679
|
-
|
|
20715
|
+
args.effectiveEffort,
|
|
20680
20716
|
args.signal,
|
|
20681
20717
|
bodyFile.ok ? bodyFile.value : void 0
|
|
20682
20718
|
)
|
|
@@ -20704,7 +20740,8 @@ var makeEvaluatorCallEvaluate = (deps, args) => async (task) => {
|
|
|
20704
20740
|
const initialBodyFile = AbsolutePath.parse(
|
|
20705
20741
|
roundBodyPath(args.input.workspaceRoot, args.input.roundNum, "evaluator")
|
|
20706
20742
|
);
|
|
20707
|
-
const
|
|
20743
|
+
const effectiveEffort = task.escalatedToEvaluatorEffort ?? deps.effort;
|
|
20744
|
+
const spawn2 = await deps.provider.generate(
|
|
20708
20745
|
implementSession(
|
|
20709
20746
|
args.input.workspaceRoot,
|
|
20710
20747
|
deps.cwd,
|
|
@@ -20714,12 +20751,12 @@ var makeEvaluatorCallEvaluate = (deps, args) => async (task) => {
|
|
|
20714
20751
|
args.signalsFile,
|
|
20715
20752
|
"evaluator",
|
|
20716
20753
|
args.input.priorEvaluatorSessionId,
|
|
20717
|
-
|
|
20754
|
+
effectiveEffort,
|
|
20718
20755
|
args.signal,
|
|
20719
20756
|
initialBodyFile.ok ? initialBodyFile.value : void 0
|
|
20720
20757
|
)
|
|
20721
20758
|
);
|
|
20722
|
-
if (!
|
|
20759
|
+
if (!spawn2.ok) return Result.error(spawn2.error);
|
|
20723
20760
|
const validated = await validateSignalsFileWithCorrectiveRetry(
|
|
20724
20761
|
{
|
|
20725
20762
|
outputDir: args.outputDir,
|
|
@@ -20741,6 +20778,7 @@ var makeEvaluatorCallEvaluate = (deps, args) => async (task) => {
|
|
|
20741
20778
|
workspaceRoot: args.input.workspaceRoot,
|
|
20742
20779
|
roundNum: args.input.roundNum,
|
|
20743
20780
|
signalsFile: args.signalsFile,
|
|
20781
|
+
effectiveEffort,
|
|
20744
20782
|
priorEvaluatorSessionId: args.input.priorEvaluatorSessionId,
|
|
20745
20783
|
signal: args.signal
|
|
20746
20784
|
})
|
|
@@ -20860,7 +20898,7 @@ var evaluatorLeaf = (deps, taskId) => leaf(`evaluator-${String(taskId)}`, {
|
|
|
20860
20898
|
|
|
20861
20899
|
// src/application/flows/implement/leaves/generator.ts
|
|
20862
20900
|
import { dirname as dirname18, join as join47 } from "path";
|
|
20863
|
-
import { promises as
|
|
20901
|
+
import { promises as fs24 } from "fs";
|
|
20864
20902
|
|
|
20865
20903
|
// src/business/task/run-generator-turn.ts
|
|
20866
20904
|
var findTaskBlocked = (signals) => signals.find((s) => s.type === "task-blocked")?.reason;
|
|
@@ -21027,7 +21065,8 @@ var composeDimensionTrajectory = (input) => {
|
|
|
21027
21065
|
const priorFailed = failedDimensions(prior.evaluation);
|
|
21028
21066
|
const fixed = sortedLimited([...priorFailed].filter((d) => !latestFailed.has(d)));
|
|
21029
21067
|
const newlyFailing = sortedLimited([...latestFailed].filter((d) => !priorFailed.has(d)));
|
|
21030
|
-
const
|
|
21068
|
+
const stillFailingAll = [...latestFailed].filter((d) => priorFailed.has(d));
|
|
21069
|
+
const stillFailing = sortedLimited(stillFailingAll);
|
|
21031
21070
|
const lines = [];
|
|
21032
21071
|
for (const d of fixed) {
|
|
21033
21072
|
lines.push(`- ${d}: fixed since last round \u2014 keep it passing.`);
|
|
@@ -21041,7 +21080,7 @@ var composeDimensionTrajectory = (input) => {
|
|
|
21041
21080
|
}
|
|
21042
21081
|
if (lines.length === 0) return "";
|
|
21043
21082
|
const threshold = Math.max(2, Math.trunc(input.plateauThreshold));
|
|
21044
|
-
const longestStall =
|
|
21083
|
+
const longestStall = stillFailingAll.reduce((max, d) => Math.max(max, consecutiveFailing(history, d)), 0);
|
|
21045
21084
|
const pressure = longestStall >= threshold - 1 ? [
|
|
21046
21085
|
"",
|
|
21047
21086
|
`Round ${String(input.roundNum)} of ${String(input.maxTurns)}; ${String(longestStall)} stalled round(s) \u2014 the harness exits this loop at ${String(threshold)} consecutive stalled rounds and escalates. Do NOT repeat the previous approach on a still-failing dimension; step back and try a fundamentally different fix.`
|
|
@@ -21160,7 +21199,7 @@ var formatRetryFeedback = (task, logTail) => {
|
|
|
21160
21199
|
};
|
|
21161
21200
|
|
|
21162
21201
|
// src/integration/io/read-log-tail.ts
|
|
21163
|
-
import { promises as
|
|
21202
|
+
import { promises as fs23 } from "fs";
|
|
21164
21203
|
var DEFAULT_LOG_TAIL_BYTES = 4096;
|
|
21165
21204
|
var createFsLogTailReader = () => {
|
|
21166
21205
|
return async (path2, maxBytes = DEFAULT_LOG_TAIL_BYTES) => {
|
|
@@ -21168,7 +21207,7 @@ var createFsLogTailReader = () => {
|
|
|
21168
21207
|
if (cap === 0) return "";
|
|
21169
21208
|
let handle;
|
|
21170
21209
|
try {
|
|
21171
|
-
handle = await
|
|
21210
|
+
handle = await fs23.open(String(path2), "r");
|
|
21172
21211
|
const stat = await handle.stat();
|
|
21173
21212
|
const size = stat.size;
|
|
21174
21213
|
if (size <= 0) return "";
|
|
@@ -21199,7 +21238,7 @@ var countTurnActionKinds = (out) => {
|
|
|
21199
21238
|
};
|
|
21200
21239
|
var readCappedProgress2 = async (path2, currentTaskId, model) => {
|
|
21201
21240
|
try {
|
|
21202
|
-
return capProgressBody(await
|
|
21241
|
+
return capProgressBody(await fs24.readFile(path2, "utf8"), {
|
|
21203
21242
|
currentTaskId,
|
|
21204
21243
|
recentBudgetTokens: progressCapBudgetForModel(model)
|
|
21205
21244
|
});
|
|
@@ -21364,7 +21403,7 @@ var makeGeneratorCallImplement = (deps, taskId, args) => async (task) => {
|
|
|
21364
21403
|
const effectiveModel = task.escalatedToModel ?? deps.model;
|
|
21365
21404
|
const effectiveEffort = task.escalatedToEffort ?? deps.effort;
|
|
21366
21405
|
const initialBodyFile = AbsolutePath.parse(roundBodyPath(args.input.workspaceRoot, args.roundNum, "generator"));
|
|
21367
|
-
const
|
|
21406
|
+
const spawn2 = await deps.provider.generate(
|
|
21368
21407
|
implementSession(
|
|
21369
21408
|
args.input.workspaceRoot,
|
|
21370
21409
|
deps.cwd,
|
|
@@ -21379,7 +21418,7 @@ var makeGeneratorCallImplement = (deps, taskId, args) => async (task) => {
|
|
|
21379
21418
|
initialBodyFile.ok ? initialBodyFile.value : void 0
|
|
21380
21419
|
)
|
|
21381
21420
|
);
|
|
21382
|
-
if (!
|
|
21421
|
+
if (!spawn2.ok) return Result.error(spawn2.error);
|
|
21383
21422
|
const validated = await validateSignalsFileWithCorrectiveRetry(
|
|
21384
21423
|
{
|
|
21385
21424
|
outputDir: args.outputDir,
|
|
@@ -22725,13 +22764,13 @@ var quarantineRetryDiffLeaf = (deps, opts, taskId) => leaf(`quarantine-retry-dif
|
|
|
22725
22764
|
var isRedVerifyRetry = (ctx) => ctx.lastShouldFailAttempt === true && ctx.lastBlockReason !== void 0;
|
|
22726
22765
|
|
|
22727
22766
|
// src/application/flows/implement/leaves/progress-journal.ts
|
|
22728
|
-
import { promises as
|
|
22767
|
+
import { promises as fs25 } from "fs";
|
|
22729
22768
|
|
|
22730
22769
|
// src/business/sprint/render-sprint-state-header.ts
|
|
22731
|
-
var
|
|
22770
|
+
var EM_DASH3 = "\u2014";
|
|
22732
22771
|
var cell = (text) => neutralizeProseHeadings(sanitizeInline(text));
|
|
22733
22772
|
var isStale = (task) => task.status === "in_progress" && task.attemptCount > 0;
|
|
22734
|
-
var orEmDash = (value) => value !== null && value.length > 0 ? value :
|
|
22773
|
+
var orEmDash = (value) => value !== null && value.length > 0 ? value : EM_DASH3;
|
|
22735
22774
|
var renderIdentity = (input) => [
|
|
22736
22775
|
`# Sprint: ${cell(input.sprintName)}`,
|
|
22737
22776
|
"",
|
|
@@ -22751,7 +22790,7 @@ var renderBlockers = (tasks) => {
|
|
|
22751
22790
|
const blockers = tasks.filter((t) => t.status === "blocked");
|
|
22752
22791
|
if (blockers.length === 0) return [];
|
|
22753
22792
|
const bullets = blockers.map((t) => {
|
|
22754
|
-
const reason = t.blockedReason !== void 0 && t.blockedReason.trim().length > 0 ? cell(t.blockedReason) :
|
|
22793
|
+
const reason = t.blockedReason !== void 0 && t.blockedReason.trim().length > 0 ? cell(t.blockedReason) : EM_DASH3;
|
|
22755
22794
|
return `- ${cell(t.name)}: ${reason}`;
|
|
22756
22795
|
});
|
|
22757
22796
|
return ["## Blockers", "", ...bullets, ""];
|
|
@@ -22764,7 +22803,7 @@ var renderStale = (tasks) => {
|
|
|
22764
22803
|
);
|
|
22765
22804
|
return ["## Stale tasks", "", ...bullets, ""];
|
|
22766
22805
|
};
|
|
22767
|
-
var passesCell = (task) => task.criteriaTotal === 0 ?
|
|
22806
|
+
var passesCell = (task) => task.criteriaTotal === 0 ? EM_DASH3 : `${String(task.criteriaPassed)}/${String(task.criteriaTotal)}`;
|
|
22768
22807
|
var renderTaskTable = (tasks) => {
|
|
22769
22808
|
if (tasks.length === 0) return ["## Tasks", "", "_No tasks planned yet._", ""];
|
|
22770
22809
|
const rows = tasks.map((t) => `| ${cell(t.name)} | ${t.status} | ${passesCell(t)} |`);
|
|
@@ -22902,7 +22941,7 @@ var buildAttemptSection = (input, totalRounds, clock) => {
|
|
|
22902
22941
|
};
|
|
22903
22942
|
var readExisting = async (path2) => {
|
|
22904
22943
|
try {
|
|
22905
|
-
return await
|
|
22944
|
+
return await fs25.readFile(String(path2), "utf8");
|
|
22906
22945
|
} catch {
|
|
22907
22946
|
return "";
|
|
22908
22947
|
}
|
|
@@ -23142,16 +23181,16 @@ ${output.slice(output.length - tail)}`;
|
|
|
23142
23181
|
};
|
|
23143
23182
|
|
|
23144
23183
|
// src/business/task/render-round-outcome.ts
|
|
23145
|
-
var
|
|
23184
|
+
var EM_DASH4 = "\u2014";
|
|
23146
23185
|
var renderRoundOutcome = (input) => {
|
|
23147
23186
|
const lines = [];
|
|
23148
23187
|
lines.push(`# Round ${String(input.roundN)} \xB7 attempt ${String(input.attemptN)}`);
|
|
23149
23188
|
lines.push("");
|
|
23150
|
-
lines.push(`- generator session: ${input.generatorSessionId ??
|
|
23151
|
-
lines.push(`- evaluator session: ${input.evaluatorSessionId ??
|
|
23152
|
-
lines.push(`- duration: ${
|
|
23189
|
+
lines.push(`- generator session: ${input.generatorSessionId ?? EM_DASH4}`);
|
|
23190
|
+
lines.push(`- evaluator session: ${input.evaluatorSessionId ?? EM_DASH4}`);
|
|
23191
|
+
lines.push(`- duration: ${formatDuration(input.durationMs)}`);
|
|
23153
23192
|
lines.push(`- verdict: ${input.verdict}`);
|
|
23154
|
-
lines.push(`- commit: ${input.attempt.commitSha !== void 0 ? String(input.attempt.commitSha) :
|
|
23193
|
+
lines.push(`- commit: ${input.attempt.commitSha !== void 0 ? String(input.attempt.commitSha) : EM_DASH4}`);
|
|
23155
23194
|
const dimensionsBlock = renderDimensions(input.evaluation, input.attempt.evaluation);
|
|
23156
23195
|
if (dimensionsBlock !== void 0) {
|
|
23157
23196
|
lines.push("");
|
|
@@ -23223,25 +23262,6 @@ var formatFailedDimensions = (evaluation) => {
|
|
|
23223
23262
|
};
|
|
23224
23263
|
var SHA_DISPLAY_LENGTH2 = 7;
|
|
23225
23264
|
var shortSha = (sha) => sha.slice(0, SHA_DISPLAY_LENGTH2);
|
|
23226
|
-
var MS_PER_SECOND2 = 1e3;
|
|
23227
|
-
var MS_PER_MINUTE2 = 60 * MS_PER_SECOND2;
|
|
23228
|
-
var MS_PER_HOUR2 = 60 * MS_PER_MINUTE2;
|
|
23229
|
-
var formatDuration2 = (ms) => {
|
|
23230
|
-
if (ms === void 0) return EM_DASH3;
|
|
23231
|
-
if (ms < 0) return `${String(ms)}ms`;
|
|
23232
|
-
if (ms < MS_PER_SECOND2) return `${String(ms)}ms`;
|
|
23233
|
-
if (ms < MS_PER_MINUTE2) {
|
|
23234
|
-
return `${String(Math.floor(ms / MS_PER_SECOND2))}s`;
|
|
23235
|
-
}
|
|
23236
|
-
if (ms < MS_PER_HOUR2) {
|
|
23237
|
-
const minutes2 = Math.floor(ms / MS_PER_MINUTE2);
|
|
23238
|
-
const seconds = Math.floor(ms % MS_PER_MINUTE2 / MS_PER_SECOND2);
|
|
23239
|
-
return seconds > 0 ? `${String(minutes2)}m ${String(seconds)}s` : `${String(minutes2)}m`;
|
|
23240
|
-
}
|
|
23241
|
-
const hours = Math.floor(ms / MS_PER_HOUR2);
|
|
23242
|
-
const minutes = Math.floor(ms % MS_PER_HOUR2 / MS_PER_MINUTE2);
|
|
23243
|
-
return minutes > 0 ? `${String(hours)}h ${String(minutes)}m` : `${String(hours)}h`;
|
|
23244
|
-
};
|
|
23245
23265
|
|
|
23246
23266
|
// src/application/flows/implement/leaves/settle-attempt.ts
|
|
23247
23267
|
var settleAttemptLeaf = (deps, opts, taskId) => {
|
|
@@ -23651,7 +23671,7 @@ var createPerTaskSubchain = (deps, opts, task, repo, readConfig) => {
|
|
|
23651
23671
|
)
|
|
23652
23672
|
] : [],
|
|
23653
23673
|
buildTaskWorkspaceLeaf(
|
|
23654
|
-
{ templateLoader: deps.templateLoader, logger: deps.logger },
|
|
23674
|
+
{ templateLoader: deps.templateLoader, logger: deps.logger, writeFile: deps.writeFile },
|
|
23655
23675
|
{
|
|
23656
23676
|
sprintDir: opts.sprintDir,
|
|
23657
23677
|
cwd: repo.path,
|
|
@@ -23759,7 +23779,14 @@ var createPerTaskSubchain = (deps, opts, task, repo, readConfig) => {
|
|
|
23759
23779
|
// before spending the nudge. `providerId` is the resolved provider enum string;
|
|
23760
23780
|
// `nextEffortRung` skips gracefully for any non-effort provider or model.
|
|
23761
23781
|
configuredGeneratorProvider: opts.generator.providerId,
|
|
23762
|
-
...opts.generator.effort !== void 0 ? { configuredGeneratorEffort: opts.generator.effort } : {}
|
|
23782
|
+
...opts.generator.effort !== void 0 ? { configuredGeneratorEffort: opts.generator.effort } : {},
|
|
23783
|
+
// Same lockstep wiring for the EVALUATOR role: the policy computes its bump
|
|
23784
|
+
// independently against this triple, never copying the generator's target. The
|
|
23785
|
+
// evaluator MODEL is never escalated, but it still rides through so `nextEffortRung`
|
|
23786
|
+
// can classify the evaluator's own effort ladder.
|
|
23787
|
+
configuredEvaluatorProvider: opts.evaluator.providerId,
|
|
23788
|
+
configuredEvaluatorModel: opts.evaluator.model,
|
|
23789
|
+
...opts.evaluator.effort !== void 0 ? { configuredEvaluatorEffort: opts.evaluator.effort } : {}
|
|
23763
23790
|
},
|
|
23764
23791
|
taskId
|
|
23765
23792
|
),
|
|
@@ -24892,6 +24919,16 @@ var createPublishSignal = (eventBus, source, taskId) => (signal) => {
|
|
|
24892
24919
|
eventBus.publish({ type: "ai-signal", signal, source, ...taskId !== void 0 ? { taskId } : {} });
|
|
24893
24920
|
};
|
|
24894
24921
|
|
|
24922
|
+
// src/application/flows/implement/sprint-scoped-projection.ts
|
|
24923
|
+
var projectSprintScopedFields = (ctx) => ({
|
|
24924
|
+
sprintId: ctx.sprintId,
|
|
24925
|
+
sprint: ctx.sprint,
|
|
24926
|
+
execution: ctx.execution,
|
|
24927
|
+
progressFile: ctx.progressFile,
|
|
24928
|
+
setupVerifiedRepoIdsThisRun: ctx.setupVerifiedRepoIdsThisRun,
|
|
24929
|
+
priorLearnings: ctx.priorLearnings
|
|
24930
|
+
});
|
|
24931
|
+
|
|
24895
24932
|
// src/application/flows/implement/merge-wave.ts
|
|
24896
24933
|
var branchSettled = (outcome) => outcome.status === "completed" || outcome.error !== void 0;
|
|
24897
24934
|
var mergeImplementWave = (base, outcomes) => {
|
|
@@ -24902,13 +24939,7 @@ var mergeImplementWave = (base, outcomes) => {
|
|
|
24902
24939
|
}
|
|
24903
24940
|
const tasks = base.tasks?.map((t) => byId.get(t.id) ?? t);
|
|
24904
24941
|
return {
|
|
24905
|
-
|
|
24906
|
-
sprintId: base.sprintId,
|
|
24907
|
-
...base.sprint !== void 0 ? { sprint: base.sprint } : {},
|
|
24908
|
-
...base.execution !== void 0 ? { execution: base.execution } : {},
|
|
24909
|
-
...base.progressFile !== void 0 ? { progressFile: base.progressFile } : {},
|
|
24910
|
-
...base.setupVerifiedRepoIdsThisRun !== void 0 ? { setupVerifiedRepoIdsThisRun: base.setupVerifiedRepoIdsThisRun } : {},
|
|
24911
|
-
...base.priorLearnings !== void 0 ? { priorLearnings: base.priorLearnings } : {},
|
|
24942
|
+
...projectSprintScopedFields(base),
|
|
24912
24943
|
// task list → overlay
|
|
24913
24944
|
...tasks !== void 0 ? { tasks } : {}
|
|
24914
24945
|
// per-task + signal-accum classes intentionally omitted → undefined in the merged ctx.
|
|
@@ -24916,17 +24947,8 @@ var mergeImplementWave = (base, outcomes) => {
|
|
|
24916
24947
|
};
|
|
24917
24948
|
var forkCtx = (base, repo, worktreePath) => {
|
|
24918
24949
|
const ctx = {
|
|
24919
|
-
|
|
24920
|
-
|
|
24921
|
-
...base.sprint !== void 0 ? { sprint: base.sprint } : {},
|
|
24922
|
-
...base.execution !== void 0 ? { execution: base.execution } : {},
|
|
24923
|
-
...base.progressFile !== void 0 ? { progressFile: base.progressFile } : {},
|
|
24924
|
-
...base.tasks !== void 0 ? { tasks: base.tasks } : {},
|
|
24925
|
-
// run-scoped (like `execution`): the set of repos this launch's setup verified survives the
|
|
24926
|
-
// fork so a parallel branch's first pre-task-verify can also take the fresh-setup skip.
|
|
24927
|
-
...base.setupVerifiedRepoIdsThisRun !== void 0 ? { setupVerifiedRepoIdsThisRun: base.setupVerifiedRepoIdsThisRun } : {},
|
|
24928
|
-
// run-scoped cross-sprint memory: every branch's generator reads the same loaded ledger.
|
|
24929
|
-
...base.priorLearnings !== void 0 ? { priorLearnings: base.priorLearnings } : {}
|
|
24950
|
+
...projectSprintScopedFields(base),
|
|
24951
|
+
...base.tasks !== void 0 ? { tasks: base.tasks } : {}
|
|
24930
24952
|
// per-task + signal-accum classes cleared (omitted → undefined); `priorPostVerifyOutcome`
|
|
24931
24953
|
// dropped (accepted cost). `expectedBranch` is intentionally NOT set here — see the docstring:
|
|
24932
24954
|
// the branch element omits `branch-preflight`, so leaving it `undefined` is correct.
|
|
@@ -25776,7 +25798,7 @@ var launchImplement = async (ctx) => {
|
|
|
25776
25798
|
import { join as join60 } from "path";
|
|
25777
25799
|
|
|
25778
25800
|
// src/application/flows/review/leaves/ensure-feedback-file.ts
|
|
25779
|
-
import { promises as
|
|
25801
|
+
import { promises as fs26 } from "fs";
|
|
25780
25802
|
|
|
25781
25803
|
// src/business/feedback/md-parser.ts
|
|
25782
25804
|
var ROUND_HEADING_RE = /^##\s+Round\s+(\d+)\s*$/;
|
|
@@ -25851,12 +25873,12 @@ var ensureFeedbackFileLeaf = (feedbackFile) => leaf("ensure-feedback-file", {
|
|
|
25851
25873
|
useCase: {
|
|
25852
25874
|
execute: async (path2) => {
|
|
25853
25875
|
try {
|
|
25854
|
-
await
|
|
25876
|
+
await fs26.access(String(path2));
|
|
25855
25877
|
return Result.ok(void 0);
|
|
25856
25878
|
} catch {
|
|
25857
25879
|
}
|
|
25858
25880
|
try {
|
|
25859
|
-
await
|
|
25881
|
+
await fs26.writeFile(String(path2), TEMPLATE, { flag: "wx" });
|
|
25860
25882
|
return Result.ok(void 0);
|
|
25861
25883
|
} catch (cause) {
|
|
25862
25884
|
if (typeof cause === "object" && cause !== null && cause.code === "EEXIST") {
|
|
@@ -25878,7 +25900,7 @@ var ensureFeedbackFileLeaf = (feedbackFile) => leaf("ensure-feedback-file", {
|
|
|
25878
25900
|
});
|
|
25879
25901
|
|
|
25880
25902
|
// src/application/flows/review/leaves/review-round.ts
|
|
25881
|
-
import { promises as
|
|
25903
|
+
import { promises as fs27 } from "fs";
|
|
25882
25904
|
import { join as join56 } from "path";
|
|
25883
25905
|
|
|
25884
25906
|
// src/business/feedback/apply-feedback.ts
|
|
@@ -26061,7 +26083,7 @@ var reviewRoundOutputContract = {
|
|
|
26061
26083
|
var readProgressSnippet = async (path2) => {
|
|
26062
26084
|
if (path2 === void 0) return "_(no progress file)_";
|
|
26063
26085
|
try {
|
|
26064
|
-
const content = await
|
|
26086
|
+
const content = await fs27.readFile(String(path2), "utf8");
|
|
26065
26087
|
return content.length > 4e3 ? `${content.slice(0, 4e3)}
|
|
26066
26088
|
[truncated]` : content;
|
|
26067
26089
|
} catch {
|
|
@@ -26077,7 +26099,7 @@ ${renderEmptyRound(nextIndex)}${ROUND_SEPARATOR}
|
|
|
26077
26099
|
};
|
|
26078
26100
|
var deriveActiveRoundIndex = async (feedbackFile) => {
|
|
26079
26101
|
try {
|
|
26080
|
-
const raw = await
|
|
26102
|
+
const raw = await fs27.readFile(String(feedbackFile), "utf8");
|
|
26081
26103
|
const rounds = parseFeedbackMd(raw);
|
|
26082
26104
|
return rounds.at(-1)?.index ?? 1;
|
|
26083
26105
|
} catch {
|
|
@@ -26086,7 +26108,7 @@ var deriveActiveRoundIndex = async (feedbackFile) => {
|
|
|
26086
26108
|
};
|
|
26087
26109
|
var writeRoundBody = async (path2, body) => {
|
|
26088
26110
|
try {
|
|
26089
|
-
const content = await
|
|
26111
|
+
const content = await fs27.readFile(String(path2), "utf8");
|
|
26090
26112
|
const lastMarker = content.lastIndexOf(MARKER_COMMENT);
|
|
26091
26113
|
if (lastMarker === -1) {
|
|
26092
26114
|
return Result.error(
|
|
@@ -26126,7 +26148,7 @@ var allocateRoundPaths = (reviewRoot, roundIndex) => {
|
|
|
26126
26148
|
};
|
|
26127
26149
|
var ensureRoundDir = async (dir) => {
|
|
26128
26150
|
try {
|
|
26129
|
-
await
|
|
26151
|
+
await fs27.mkdir(String(dir), { recursive: true });
|
|
26130
26152
|
return Result.ok(void 0);
|
|
26131
26153
|
} catch (cause) {
|
|
26132
26154
|
return Result.error(
|
|
@@ -26161,7 +26183,7 @@ var reviewRoundLeaf = (deps, opts) => leaf(LEAF_NAME3, {
|
|
|
26161
26183
|
if (!wrote.ok) return Result.error(wrote.error);
|
|
26162
26184
|
return Result.ok(void 0);
|
|
26163
26185
|
},
|
|
26164
|
-
readFeedbackFile: () =>
|
|
26186
|
+
readFeedbackFile: () => fs27.readFile(String(input.feedbackFile), "utf8"),
|
|
26165
26187
|
readProgressSnippet: () => readProgressSnippet(input.progressFile),
|
|
26166
26188
|
buildPrompt: async (params) => {
|
|
26167
26189
|
const outputContractSection = renderContractSectionFor(reviewRoundOutputContract, paths.value.outputDir);
|
|
@@ -26189,7 +26211,7 @@ var reviewRoundLeaf = (deps, opts) => leaf(LEAF_NAME3, {
|
|
|
26189
26211
|
const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt));
|
|
26190
26212
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
26191
26213
|
const chainSessionId = currentSessionId();
|
|
26192
|
-
const
|
|
26214
|
+
const spawn2 = await deps.provider.generate({
|
|
26193
26215
|
prompt,
|
|
26194
26216
|
cwd: paths.value.outputDir,
|
|
26195
26217
|
additionalRoots: opts.additionalRoots,
|
|
@@ -26202,7 +26224,7 @@ var reviewRoundLeaf = (deps, opts) => leaf(LEAF_NAME3, {
|
|
|
26202
26224
|
// the provider's SIGTERM ladder instead of letting it run to completion.
|
|
26203
26225
|
...signal !== void 0 ? { abortSignal: signal } : {}
|
|
26204
26226
|
});
|
|
26205
|
-
if (!
|
|
26227
|
+
if (!spawn2.ok) return Result.error(spawn2.error);
|
|
26206
26228
|
const validated = await validateSignalsFile(paths.value.outputDir, reviewRoundOutputContract);
|
|
26207
26229
|
if (!validated.ok) return Result.error(validated.error);
|
|
26208
26230
|
for (const sig of validated.value) deps.publishSignal(sig);
|
|
@@ -26432,7 +26454,7 @@ var stampPass = (collected, accepted, retired, stampTime, path2) => {
|
|
|
26432
26454
|
var isLive = (record) => record.retiredAt === void 0 || record.retiredAt === null;
|
|
26433
26455
|
|
|
26434
26456
|
// src/application/flows/_shared/memory/distill-propose.ts
|
|
26435
|
-
import { promises as
|
|
26457
|
+
import { promises as fs28 } from "fs";
|
|
26436
26458
|
import { join as join58 } from "path";
|
|
26437
26459
|
|
|
26438
26460
|
// src/integration/ai/prompts/distill-learnings/definition.ts
|
|
@@ -26544,7 +26566,7 @@ var distillProposeUseCase = async (deps, tool, input, signal) => {
|
|
|
26544
26566
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
26545
26567
|
const toolDir = join58(String(deps.distillRoot), tool);
|
|
26546
26568
|
try {
|
|
26547
|
-
await
|
|
26569
|
+
await fs28.mkdir(toolDir, { recursive: true });
|
|
26548
26570
|
} catch (cause) {
|
|
26549
26571
|
return Result.error(
|
|
26550
26572
|
new StorageError({
|
|
@@ -26598,11 +26620,11 @@ var renderProjectTooling = (repository) => {
|
|
|
26598
26620
|
const lines = [];
|
|
26599
26621
|
if (repository.setupScript !== void 0) lines.push(`- Setup: ${repository.setupScript}`);
|
|
26600
26622
|
if (repository.verifyScript !== void 0) lines.push(`- Verify: ${repository.verifyScript}`);
|
|
26601
|
-
return lines.length > 0 ? lines.join("\n") :
|
|
26623
|
+
return renderProjectToolingSection(lines.length > 0 ? lines.join("\n") : void 0);
|
|
26602
26624
|
};
|
|
26603
26625
|
var safeReadText = async (path2) => {
|
|
26604
26626
|
try {
|
|
26605
|
-
return await
|
|
26627
|
+
return await fs28.readFile(path2, "utf8");
|
|
26606
26628
|
} catch {
|
|
26607
26629
|
return void 0;
|
|
26608
26630
|
}
|
|
@@ -26665,7 +26687,7 @@ var distillConfirmLeaf = (deps, tool) => leaf(`distill-confirm-${tool}`, {
|
|
|
26665
26687
|
});
|
|
26666
26688
|
|
|
26667
26689
|
// src/application/flows/_shared/memory/distill-write.ts
|
|
26668
|
-
import { promises as
|
|
26690
|
+
import { promises as fs29 } from "fs";
|
|
26669
26691
|
var distillWriteUseCase = async (deps, tool, input) => {
|
|
26670
26692
|
const log = deps.logger.named(`memory.distill-write-${tool}`);
|
|
26671
26693
|
if (!input.accepted || input.proposedContent === void 0 || input.targetPath === void 0) {
|
|
@@ -26694,14 +26716,14 @@ var distillWriteUseCase = async (deps, tool, input) => {
|
|
|
26694
26716
|
var makeBackupPath = (targetPath, now) => `${String(targetPath)}.bak.${String(now).replace(/:/g, "-")}`;
|
|
26695
26717
|
var fileExists = async (path2) => {
|
|
26696
26718
|
try {
|
|
26697
|
-
return (await
|
|
26719
|
+
return (await fs29.stat(path2)).isFile();
|
|
26698
26720
|
} catch {
|
|
26699
26721
|
return false;
|
|
26700
26722
|
}
|
|
26701
26723
|
};
|
|
26702
26724
|
var safeReadText2 = async (path2) => {
|
|
26703
26725
|
try {
|
|
26704
|
-
return await
|
|
26726
|
+
return await fs29.readFile(path2, "utf8");
|
|
26705
26727
|
} catch {
|
|
26706
26728
|
return void 0;
|
|
26707
26729
|
}
|
|
@@ -27858,7 +27880,7 @@ var probeReadinessLeaf = (deps, tool) => leaf(`probe-${tool}`, {
|
|
|
27858
27880
|
});
|
|
27859
27881
|
|
|
27860
27882
|
// src/application/flows/readiness/leaves/propose.ts
|
|
27861
|
-
import { promises as
|
|
27883
|
+
import { promises as fs30 } from "fs";
|
|
27862
27884
|
|
|
27863
27885
|
// src/integration/ai/prompts/readiness/definition.ts
|
|
27864
27886
|
var CLAUDE_CODE = "claude-code";
|
|
@@ -28127,7 +28149,7 @@ var proposeReadinessUseCase = async (deps, tool, input, abortSignal) => {
|
|
|
28127
28149
|
let existingBody;
|
|
28128
28150
|
if (existingPath !== void 0) {
|
|
28129
28151
|
try {
|
|
28130
|
-
existingBody = await
|
|
28152
|
+
existingBody = await fs30.readFile(existingPath, "utf8");
|
|
28131
28153
|
} catch {
|
|
28132
28154
|
existingBody = void 0;
|
|
28133
28155
|
}
|
|
@@ -28259,7 +28281,7 @@ var proposeReadinessLeaf = (deps, tool) => leaf(`propose-${tool}`, {
|
|
|
28259
28281
|
});
|
|
28260
28282
|
|
|
28261
28283
|
// src/application/flows/readiness/leaves/write.ts
|
|
28262
|
-
import { promises as
|
|
28284
|
+
import { promises as fs31 } from "fs";
|
|
28263
28285
|
var writeReadinessUseCase = async (deps, tool, input) => {
|
|
28264
28286
|
const log = deps.logger.named(`readiness.write-${tool}`);
|
|
28265
28287
|
if (!input.accepted || input.proposal === void 0) {
|
|
@@ -28293,7 +28315,7 @@ var makeBackupPath2 = (targetPath, now) => {
|
|
|
28293
28315
|
};
|
|
28294
28316
|
var fileExists2 = async (path2) => {
|
|
28295
28317
|
try {
|
|
28296
|
-
const stat = await
|
|
28318
|
+
const stat = await fs31.stat(path2);
|
|
28297
28319
|
return stat.isFile();
|
|
28298
28320
|
} catch {
|
|
28299
28321
|
return false;
|
|
@@ -28301,7 +28323,7 @@ var fileExists2 = async (path2) => {
|
|
|
28301
28323
|
};
|
|
28302
28324
|
var safeReadText3 = async (path2) => {
|
|
28303
28325
|
try {
|
|
28304
|
-
return await
|
|
28326
|
+
return await fs31.readFile(path2, "utf8");
|
|
28305
28327
|
} catch {
|
|
28306
28328
|
return void 0;
|
|
28307
28329
|
}
|
|
@@ -28326,11 +28348,11 @@ var writeReadinessLeaf = (deps, tool) => leaf(`write-${tool}`, {
|
|
|
28326
28348
|
});
|
|
28327
28349
|
|
|
28328
28350
|
// src/application/flows/_shared/allocate-run-dir.ts
|
|
28329
|
-
import { promises as
|
|
28351
|
+
import { promises as fs33 } from "fs";
|
|
28330
28352
|
import { join as join63 } from "path";
|
|
28331
28353
|
|
|
28332
28354
|
// src/integration/ai/runs/_engine/run-artifacts.ts
|
|
28333
|
-
import { promises as
|
|
28355
|
+
import { promises as fs32 } from "fs";
|
|
28334
28356
|
import { join as join62 } from "path";
|
|
28335
28357
|
var BODY_PREVIEW_LIMIT = 800;
|
|
28336
28358
|
var buildRunDirName = () => {
|
|
@@ -28341,7 +28363,7 @@ var buildRunDirName = () => {
|
|
|
28341
28363
|
var readRunBodyPreview = async (runDir, options) => {
|
|
28342
28364
|
let raw;
|
|
28343
28365
|
try {
|
|
28344
|
-
raw = await
|
|
28366
|
+
raw = await fs32.readFile(join62(String(runDir), "body.txt"), "utf8");
|
|
28345
28367
|
} catch (cause) {
|
|
28346
28368
|
if (isErrnoException(cause) && cause.code === "ENOENT") return void 0;
|
|
28347
28369
|
const code = isErrnoException(cause) ? cause.code : "unknown";
|
|
@@ -28361,7 +28383,7 @@ var allocateRunDirLeaf = (opts) => leaf(opts.name, {
|
|
|
28361
28383
|
useCase: {
|
|
28362
28384
|
execute: async (input) => {
|
|
28363
28385
|
try {
|
|
28364
|
-
await
|
|
28386
|
+
await fs33.mkdir(input.path, { recursive: true });
|
|
28365
28387
|
} catch (cause) {
|
|
28366
28388
|
return Result.error(
|
|
28367
28389
|
new StorageError({
|
|
@@ -28749,16 +28771,16 @@ var proposeUseCase = async (deps, input, abortSignal) => {
|
|
|
28749
28771
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
28750
28772
|
const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt.value));
|
|
28751
28773
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
28752
|
-
const
|
|
28774
|
+
const spawn2 = await deps.provider.generate(
|
|
28753
28775
|
readOnlySignalsSession(buildSessionOpts(deps, input, paths.value, prompt.value, abortSignal))
|
|
28754
28776
|
);
|
|
28755
|
-
if (!
|
|
28777
|
+
if (!spawn2.ok) {
|
|
28756
28778
|
log.error(`provider failed for repo ${input.repository.name}`, {
|
|
28757
28779
|
repositoryId: String(input.repository.id),
|
|
28758
|
-
error:
|
|
28780
|
+
error: spawn2.error.message,
|
|
28759
28781
|
runDir: String(input.runDir)
|
|
28760
28782
|
});
|
|
28761
|
-
return Result.error(
|
|
28783
|
+
return Result.error(spawn2.error);
|
|
28762
28784
|
}
|
|
28763
28785
|
const validated = await validateSignalsFile(input.runDir, detectSkillsOutputContract);
|
|
28764
28786
|
if (!validated.ok) {
|
|
@@ -29194,16 +29216,16 @@ var proposeUseCase2 = async (deps, input, abortSignal) => {
|
|
|
29194
29216
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
29195
29217
|
const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt.value));
|
|
29196
29218
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
29197
|
-
const
|
|
29219
|
+
const spawn2 = await deps.provider.generate(
|
|
29198
29220
|
readOnlySignalsSession(buildSessionOpts2(deps, input, paths.value, prompt.value, abortSignal))
|
|
29199
29221
|
);
|
|
29200
|
-
if (!
|
|
29222
|
+
if (!spawn2.ok) {
|
|
29201
29223
|
log.error(`provider failed for repo ${input.repository.name}`, {
|
|
29202
29224
|
repositoryId: String(input.repository.id),
|
|
29203
|
-
error:
|
|
29225
|
+
error: spawn2.error.message,
|
|
29204
29226
|
runDir: String(input.runDir)
|
|
29205
29227
|
});
|
|
29206
|
-
return Result.error(
|
|
29228
|
+
return Result.error(spawn2.error);
|
|
29207
29229
|
}
|
|
29208
29230
|
const validated = await validateSignalsFile(input.runDir, detectScriptsOutputContract);
|
|
29209
29231
|
if (!validated.ok) {
|
|
@@ -31025,8 +31047,8 @@ var defaultRowFor = (flowId, settings) => {
|
|
|
31025
31047
|
return primaryFlowRow(settings.ai, aiFlow);
|
|
31026
31048
|
};
|
|
31027
31049
|
var labelKeepDefault = (value) => `Keep default (${value ?? "unset"})`;
|
|
31028
|
-
var formatRow = (row, globalEffort) => {
|
|
31029
|
-
const resolved = resolveEffortForRow(row, globalEffort);
|
|
31050
|
+
var formatRow = (row, globalEffort, flow) => {
|
|
31051
|
+
const resolved = resolveEffortForRow(row, globalEffort, flow);
|
|
31030
31052
|
return `${row.provider} / ${row.model} / ${resolved ?? "auto"}`;
|
|
31031
31053
|
};
|
|
31032
31054
|
var pickProviderStep = async (interactive, header, defaultRow) => {
|
|
@@ -31060,9 +31082,9 @@ var computeEffortDefaultLabel = (defaultRow, globalEffort, providerChanged, mode
|
|
|
31060
31082
|
}
|
|
31061
31083
|
return labelKeepDefault(resolvedRowEffort ?? "auto");
|
|
31062
31084
|
};
|
|
31063
|
-
var pickEffortStep = async (interactive, header, defaultRow, globalEffort, effectiveProvider, providerChanged, modelChanged) => {
|
|
31085
|
+
var pickEffortStep = async (interactive, header, defaultRow, globalEffort, effectiveProvider, providerChanged, modelChanged, flow) => {
|
|
31064
31086
|
const effortCatalog = PROVIDER_EFFORT_LEVELS[effectiveProvider];
|
|
31065
|
-
const resolvedRowEffort = resolveEffortForRow(defaultRow, globalEffort);
|
|
31087
|
+
const resolvedRowEffort = resolveEffortForRow(defaultRow, globalEffort, flow);
|
|
31066
31088
|
const effortDefaultLabel = computeEffortDefaultLabel(
|
|
31067
31089
|
defaultRow,
|
|
31068
31090
|
globalEffort,
|
|
@@ -31087,7 +31109,7 @@ var assembleRowOverride = (defaultRow, providerChanged, providerValue, modelValu
|
|
|
31087
31109
|
if (providerChanged && override.model === void 0) override.model = modelValue;
|
|
31088
31110
|
return override;
|
|
31089
31111
|
};
|
|
31090
|
-
var customizeRow = async (interactive, header, defaultRow, globalEffort, availableModelsFor) => {
|
|
31112
|
+
var customizeRow = async (interactive, header, defaultRow, globalEffort, availableModelsFor, flow) => {
|
|
31091
31113
|
const providerStep = await pickProviderStep(interactive, header, defaultRow);
|
|
31092
31114
|
if (providerStep === void 0) return void 0;
|
|
31093
31115
|
const { providerValue, providerChanged, effectiveProvider } = providerStep;
|
|
@@ -31108,14 +31130,15 @@ var customizeRow = async (interactive, header, defaultRow, globalEffort, availab
|
|
|
31108
31130
|
globalEffort,
|
|
31109
31131
|
effectiveProvider,
|
|
31110
31132
|
providerChanged,
|
|
31111
|
-
modelChanged
|
|
31133
|
+
modelChanged,
|
|
31134
|
+
flow
|
|
31112
31135
|
);
|
|
31113
31136
|
if (effortValue === void 0) return void 0;
|
|
31114
31137
|
return assembleRowOverride(defaultRow, providerChanged, providerValue, modelValue, effortValue);
|
|
31115
31138
|
};
|
|
31116
|
-
var buildPickerHeader = (flowId, flowTitle, settings) => flowId === "implement" ? `${flowTitle} \u2014 current defaults:
|
|
31117
|
-
generator: ${formatRow(settings.ai.implement.generator, settings.ai.effort)}
|
|
31118
|
-
evaluator: ${formatRow(settings.ai.implement.evaluator, settings.ai.effort)}` : `${flowTitle} \u2014 current default: ${formatRow(defaultRowFor(flowId, settings), settings.ai.effort)}`;
|
|
31139
|
+
var buildPickerHeader = (flowId, flowTitle, settings, aiFlow) => flowId === "implement" ? `${flowTitle} \u2014 current defaults:
|
|
31140
|
+
generator: ${formatRow(settings.ai.implement.generator, settings.ai.effort, aiFlow)}
|
|
31141
|
+
evaluator: ${formatRow(settings.ai.implement.evaluator, settings.ai.effort, aiFlow)}` : `${flowTitle} \u2014 current default: ${formatRow(defaultRowFor(flowId, settings), settings.ai.effort, aiFlow)}`;
|
|
31119
31142
|
var originLabel = (origin) => {
|
|
31120
31143
|
switch (origin) {
|
|
31121
31144
|
case "bundled-default":
|
|
@@ -31173,7 +31196,7 @@ var runSkillsStep = async (interactive, flowTitle, skillCandidates) => {
|
|
|
31173
31196
|
skills: { disabled, saveAsDefault: saveAns.value === "remember" }
|
|
31174
31197
|
};
|
|
31175
31198
|
};
|
|
31176
|
-
var runImplementCustomize = async (interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput) => {
|
|
31199
|
+
var runImplementCustomize = async (interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput, aiFlow) => {
|
|
31177
31200
|
const roles = ["generator", "evaluator"];
|
|
31178
31201
|
const collected = {};
|
|
31179
31202
|
for (const role of roles) {
|
|
@@ -31181,7 +31204,7 @@ var runImplementCustomize = async (interactive, header, flowTitle, settings, ava
|
|
|
31181
31204
|
const roleHeader = `${header}
|
|
31182
31205
|
|
|
31183
31206
|
Role: ${role}`;
|
|
31184
|
-
const result = await customizeRow(interactive, roleHeader, row, settings.ai.effort, availableModelsFor);
|
|
31207
|
+
const result = await customizeRow(interactive, roleHeader, row, settings.ai.effort, availableModelsFor, aiFlow);
|
|
31185
31208
|
if (result === void 0) return { kind: "cancel" };
|
|
31186
31209
|
if (Object.keys(result).length > 0) collected[role] = result;
|
|
31187
31210
|
}
|
|
@@ -31205,10 +31228,10 @@ Role: ${role}`;
|
|
|
31205
31228
|
...skills !== void 0 ? { skills } : {}
|
|
31206
31229
|
};
|
|
31207
31230
|
};
|
|
31208
|
-
var runSingleRowCustomize = async (interactive, header, flowId, flowTitle, settings, availableModelsFor, skillsStepInput) => {
|
|
31231
|
+
var runSingleRowCustomize = async (interactive, header, flowId, flowTitle, settings, availableModelsFor, skillsStepInput, aiFlow) => {
|
|
31209
31232
|
const defaultRow = defaultRowFor(flowId, settings);
|
|
31210
31233
|
if (defaultRow === void 0) return { kind: "defaults" };
|
|
31211
|
-
const override = await customizeRow(interactive, header, defaultRow, settings.ai.effort, availableModelsFor);
|
|
31234
|
+
const override = await customizeRow(interactive, header, defaultRow, settings.ai.effort, availableModelsFor, aiFlow);
|
|
31212
31235
|
if (override === void 0) return { kind: "cancel" };
|
|
31213
31236
|
const candidates = await effectiveSkillCandidates(
|
|
31214
31237
|
skillsStepInput.candidates,
|
|
@@ -31234,7 +31257,7 @@ var runCustomizePicker = async ({
|
|
|
31234
31257
|
}) => {
|
|
31235
31258
|
const aiFlow = aiFlowIdForPicker(flowId);
|
|
31236
31259
|
if (aiFlow === void 0) return { kind: "defaults" };
|
|
31237
|
-
const header = buildPickerHeader(flowId, flowTitle, settings);
|
|
31260
|
+
const header = buildPickerHeader(flowId, flowTitle, settings, aiFlow);
|
|
31238
31261
|
const action = await interactive.askChoice(
|
|
31239
31262
|
`${header}
|
|
31240
31263
|
|
|
@@ -31252,9 +31275,18 @@ What would you like to do?`,
|
|
|
31252
31275
|
...rebuildSkillCandidates !== void 0 ? { rebuild: rebuildSkillCandidates } : {}
|
|
31253
31276
|
};
|
|
31254
31277
|
if (flowId === "implement") {
|
|
31255
|
-
return runImplementCustomize(interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput);
|
|
31278
|
+
return runImplementCustomize(interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput, aiFlow);
|
|
31256
31279
|
}
|
|
31257
|
-
return runSingleRowCustomize(
|
|
31280
|
+
return runSingleRowCustomize(
|
|
31281
|
+
interactive,
|
|
31282
|
+
header,
|
|
31283
|
+
flowId,
|
|
31284
|
+
flowTitle,
|
|
31285
|
+
settings,
|
|
31286
|
+
availableModelsFor,
|
|
31287
|
+
skillsStepInput,
|
|
31288
|
+
aiFlow
|
|
31289
|
+
);
|
|
31258
31290
|
};
|
|
31259
31291
|
|
|
31260
31292
|
// src/application/flows/settings-set/flow.ts
|
|
@@ -31765,10 +31797,10 @@ var FeedbackLine = ({ text }) => {
|
|
|
31765
31797
|
if (text === void 0) return null;
|
|
31766
31798
|
if (typeof text === "string") {
|
|
31767
31799
|
const { color: color2, body } = resolveStructured(text);
|
|
31768
|
-
return /* @__PURE__ */ jsx45(Box34, { paddingX: spacing.indent, marginTop:
|
|
31800
|
+
return /* @__PURE__ */ jsx45(Box34, { paddingX: spacing.indent, marginTop: spacing.section, children: /* @__PURE__ */ jsx45(Text35, { color: color2, children: body }) });
|
|
31769
31801
|
}
|
|
31770
31802
|
const { glyph, color } = toneConfig(text.tone);
|
|
31771
|
-
return /* @__PURE__ */ jsx45(Box34, { paddingX: spacing.indent, marginTop:
|
|
31803
|
+
return /* @__PURE__ */ jsx45(Box34, { paddingX: spacing.indent, marginTop: spacing.section, children: /* @__PURE__ */ jsxs34(Text35, { color, children: [
|
|
31772
31804
|
glyph.length > 0 ? `${glyph} ` : "",
|
|
31773
31805
|
text.text
|
|
31774
31806
|
] }) });
|
|
@@ -34374,7 +34406,7 @@ var HeaderCardImpl = ({
|
|
|
34374
34406
|
String(tasksTotal)
|
|
34375
34407
|
] })
|
|
34376
34408
|
] }),
|
|
34377
|
-
isRunning && /* @__PURE__ */ jsx62(Box50, { marginLeft:
|
|
34409
|
+
isRunning && /* @__PURE__ */ jsx62(Box50, { marginLeft: spacing.indent, children: /* @__PURE__ */ jsx62(Spinner, { active: isRunning, color: inkColors.info, label: "live" }) })
|
|
34378
34410
|
] }),
|
|
34379
34411
|
/* @__PURE__ */ jsx62(
|
|
34380
34412
|
ModelLines,
|
|
@@ -35144,7 +35176,7 @@ var TASK_STATUS_COLOR = {
|
|
|
35144
35176
|
aborted: inkColors.warning,
|
|
35145
35177
|
skipped: inkColors.muted
|
|
35146
35178
|
};
|
|
35147
|
-
var SidebarDivider = ({ width }) => /* @__PURE__ */ jsx69(Box56, { paddingX:
|
|
35179
|
+
var SidebarDivider = ({ width }) => /* @__PURE__ */ jsx69(Box56, { paddingX: spacing.gutter, marginTop: spacing.gutter, children: /* @__PURE__ */ jsx69(Text56, { color: inkColors.rule, children: glyphs.sectionRule.repeat(Math.max(0, width - 2)) }) });
|
|
35148
35180
|
var truncateName = (name, maxChars) => name.length > maxChars ? `${name.slice(0, Math.max(0, maxChars - 1))}${glyphs.clipEllipsis}` : name;
|
|
35149
35181
|
var TaskNavList = ({
|
|
35150
35182
|
tasks,
|
|
@@ -39388,10 +39420,10 @@ var projectInput = (ctx) => {
|
|
|
39388
39420
|
};
|
|
39389
39421
|
var runAuthoringSpawn = async (deps, session, unitRoot, log) => {
|
|
39390
39422
|
try {
|
|
39391
|
-
const
|
|
39392
|
-
if (!
|
|
39393
|
-
if (
|
|
39394
|
-
log.warn(`create-pr: AI authoring failed, falling back to template (${
|
|
39423
|
+
const spawn2 = await deps.provider.generate(session);
|
|
39424
|
+
if (!spawn2.ok) {
|
|
39425
|
+
if (spawn2.error.code === ErrorCode.Aborted) return Result.error(spawn2.error);
|
|
39426
|
+
log.warn(`create-pr: AI authoring failed, falling back to template (${spawn2.error.message})`);
|
|
39395
39427
|
return Result.ok({});
|
|
39396
39428
|
}
|
|
39397
39429
|
const validated = await validateSignalsFile(unitRoot, generatePrContentOutputContract);
|
|
@@ -39861,7 +39893,7 @@ import { basename as basename5, join as join69 } from "path";
|
|
|
39861
39893
|
|
|
39862
39894
|
// src/application/ui/tui/prompts/path-picker-prompt.tsx
|
|
39863
39895
|
import { useEffect as useEffect40, useState as useState45 } from "react";
|
|
39864
|
-
import { promises as
|
|
39896
|
+
import { promises as fs34 } from "fs";
|
|
39865
39897
|
import { dirname as dirname21, join as join68 } from "path";
|
|
39866
39898
|
import { homedir as homedir2 } from "os";
|
|
39867
39899
|
import { Box as Box76, Text as Text72, useInput as useInput28 } from "ink";
|
|
@@ -39880,7 +39912,7 @@ var useDirectoryEntries = (cwd, showHidden) => {
|
|
|
39880
39912
|
let cancelled = false;
|
|
39881
39913
|
const load = async () => {
|
|
39882
39914
|
try {
|
|
39883
|
-
const items = await
|
|
39915
|
+
const items = await fs34.readdir(cwd, { withFileTypes: true });
|
|
39884
39916
|
if (cancelled) return;
|
|
39885
39917
|
const filtered = items.filter((d) => showHidden || !d.name.startsWith(".")).filter((d) => d.isDirectory()).map((d) => ({ name: d.name, isDirectory: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
39886
39918
|
setEntries(filtered);
|
|
@@ -39962,7 +39994,7 @@ var submitTypedPath = async (raw, { setError, setTyping, onSubmit }) => {
|
|
|
39962
39994
|
return;
|
|
39963
39995
|
}
|
|
39964
39996
|
try {
|
|
39965
|
-
const stat = await
|
|
39997
|
+
const stat = await fs34.stat(expanded);
|
|
39966
39998
|
if (!stat.isDirectory()) {
|
|
39967
39999
|
setError(`${expanded} is not a directory`);
|
|
39968
40000
|
setTyping(false);
|
|
@@ -40037,8 +40069,8 @@ var PathPickerPrompt = ({
|
|
|
40037
40069
|
] }) }),
|
|
40038
40070
|
/* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, children: /* @__PURE__ */ jsx94(Text72, { dimColor: true, children: cwd }) }),
|
|
40039
40071
|
error !== void 0 && /* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, children: /* @__PURE__ */ jsx94(Text72, { color: inkColors.error, children: error }) }),
|
|
40040
|
-
/* @__PURE__ */ jsx94(Box76, { flexDirection: "column", marginTop:
|
|
40041
|
-
typing ? /* @__PURE__ */ jsxs72(Box76, { flexDirection: "column", marginTop:
|
|
40072
|
+
/* @__PURE__ */ jsx94(Box76, { flexDirection: "column", marginTop: spacing.section, children: /* @__PURE__ */ jsx94(PathPickerRows, { rows, start, end, cursor }) }),
|
|
40073
|
+
typing ? /* @__PURE__ */ jsxs72(Box76, { flexDirection: "column", marginTop: spacing.section, paddingX: spacing.indent, children: [
|
|
40042
40074
|
/* @__PURE__ */ jsx94(Text72, { dimColor: true, children: "Type an absolute path (~/ allowed). Enter validates; esc returns to the picker." }),
|
|
40043
40075
|
/* @__PURE__ */ jsx94(
|
|
40044
40076
|
TextPrompt,
|
|
@@ -40049,7 +40081,7 @@ var PathPickerPrompt = ({
|
|
|
40049
40081
|
onCancel: () => setTyping(false)
|
|
40050
40082
|
}
|
|
40051
40083
|
)
|
|
40052
|
-
] }) : /* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, marginTop:
|
|
40084
|
+
] }) : /* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, marginTop: spacing.section, children: /* @__PURE__ */ jsxs72(Text72, { dimColor: true, children: [
|
|
40053
40085
|
"\u21B5 open/select \xB7 \u232B up \xB7 esc cancel \xB7 ~ home \xB7 t type \xB7 . ",
|
|
40054
40086
|
showHidden ? "hide" : "show",
|
|
40055
40087
|
" hidden"
|
|
@@ -40967,7 +40999,7 @@ var PickProjectView = () => {
|
|
|
40967
40999
|
};
|
|
40968
41000
|
|
|
40969
41001
|
// src/application/ui/tui/views/pick-sprint-view.tsx
|
|
40970
|
-
import {
|
|
41002
|
+
import { useMemo as useMemo29, useState as useState50 } from "react";
|
|
40971
41003
|
import { Box as Box85, Text as Text80, useInput as useInput31 } from "ink";
|
|
40972
41004
|
|
|
40973
41005
|
// src/application/ui/tui/views/pick-sprint-internals/types.ts
|
|
@@ -41032,99 +41064,75 @@ var flatten = (groups, includeCreate) => {
|
|
|
41032
41064
|
}
|
|
41033
41065
|
return rows;
|
|
41034
41066
|
};
|
|
41035
|
-
var
|
|
41036
|
-
|
|
41037
|
-
|
|
41038
|
-
|
|
41039
|
-
|
|
41040
|
-
|
|
41041
|
-
|
|
41042
|
-
};
|
|
41043
|
-
var nextCursorableIndex = (rows, from, direction) => {
|
|
41044
|
-
const candidates = cursorableRowIndices(rows);
|
|
41045
|
-
if (candidates.length === 0) return from;
|
|
41046
|
-
if (direction === 1) {
|
|
41047
|
-
const next = candidates.find((i) => i > from);
|
|
41048
|
-
return next ?? from;
|
|
41049
|
-
}
|
|
41050
|
-
let prev = from;
|
|
41051
|
-
for (const i of candidates) {
|
|
41052
|
-
if (i < from) prev = i;
|
|
41053
|
-
else break;
|
|
41054
|
-
}
|
|
41055
|
-
return prev === from && candidates.includes(from) ? from : prev;
|
|
41056
|
-
};
|
|
41057
|
-
var cursorableNear = (rows, target, direction) => {
|
|
41058
|
-
const candidates = cursorableRowIndices(rows);
|
|
41059
|
-
if (candidates.length === 0) return target;
|
|
41060
|
-
if (candidates.includes(target)) return target;
|
|
41061
|
-
const below = candidates.find((i) => i > target);
|
|
41062
|
-
let above;
|
|
41063
|
-
for (const i of candidates) {
|
|
41064
|
-
if (i < target) above = i;
|
|
41065
|
-
else break;
|
|
41067
|
+
var CREATE_ROW_ID = "__create__";
|
|
41068
|
+
var cursorableRows = (rows) => rows.filter((r) => r.kind !== "header");
|
|
41069
|
+
var cursorableRowId = (row) => row.kind === "create" ? CREATE_ROW_ID : row.sprint.id;
|
|
41070
|
+
var preferredCursorId = (rows, preferredSprintId) => {
|
|
41071
|
+
if (preferredSprintId !== void 0) {
|
|
41072
|
+
const match = rows.find((r) => r.kind === "sprint" && r.sprint.id === preferredSprintId);
|
|
41073
|
+
if (match !== void 0) return match.sprint.id;
|
|
41066
41074
|
}
|
|
41067
|
-
const
|
|
41068
|
-
|
|
41069
|
-
|
|
41075
|
+
const firstSprint = rows.find((r) => r.kind === "sprint");
|
|
41076
|
+
if (firstSprint !== void 0) return firstSprint.sprint.id;
|
|
41077
|
+
const firstCreate = rows.find((r) => r.kind === "create");
|
|
41078
|
+
return firstCreate !== void 0 ? CREATE_ROW_ID : "";
|
|
41070
41079
|
};
|
|
41071
41080
|
|
|
41072
41081
|
// src/application/ui/tui/views/pick-sprint-internals/window.ts
|
|
41073
41082
|
var VERTICAL_CHROME_ROWS = 12;
|
|
41074
41083
|
var MIN_VISIBLE_ROWS = 8;
|
|
41075
|
-
var computeWindow = (totalRows, cursor, visible) => {
|
|
41076
|
-
if (totalRows <= visible) return { start: 0, end: totalRows, hiddenAbove: 0, hiddenBelow: 0 };
|
|
41077
|
-
const half = Math.floor(visible / 2);
|
|
41078
|
-
let start = Math.max(0, cursor - half);
|
|
41079
|
-
let end = start + visible;
|
|
41080
|
-
if (end > totalRows) {
|
|
41081
|
-
end = totalRows;
|
|
41082
|
-
start = Math.max(0, end - visible);
|
|
41083
|
-
}
|
|
41084
|
-
return { start, end, hiddenAbove: start, hiddenBelow: totalRows - end };
|
|
41085
|
-
};
|
|
41086
41084
|
|
|
41087
41085
|
// src/application/ui/tui/views/pick-sprint-internals/row-views.tsx
|
|
41088
41086
|
import { useMemo as useMemo28 } from "react";
|
|
41089
41087
|
import { Box as Box84, Text as Text79 } from "ink";
|
|
41090
41088
|
import { jsx as jsx102, jsxs as jsxs80 } from "react/jsx-runtime";
|
|
41091
|
-
var
|
|
41089
|
+
var PickerRowList = ({
|
|
41092
41090
|
rows,
|
|
41093
|
-
cursor,
|
|
41094
41091
|
visibleRows,
|
|
41095
|
-
|
|
41092
|
+
active,
|
|
41093
|
+
initialCursorId,
|
|
41094
|
+
currentSprintId,
|
|
41095
|
+
onSubmit
|
|
41096
41096
|
}) => {
|
|
41097
|
-
const
|
|
41098
|
-
const
|
|
41097
|
+
const items = useMemo28(() => cursorableRows(rows), [rows]);
|
|
41098
|
+
const { focusedItem } = useListWindow({
|
|
41099
|
+
items,
|
|
41100
|
+
getId: cursorableRowId,
|
|
41101
|
+
visibleRows,
|
|
41102
|
+
active,
|
|
41103
|
+
initialCursorId,
|
|
41104
|
+
onSubmit
|
|
41105
|
+
});
|
|
41106
|
+
const focusedId = focusedItem !== void 0 ? cursorableRowId(focusedItem) : void 0;
|
|
41107
|
+
const focusedRowIndex = useMemo28(() => {
|
|
41108
|
+
if (focusedId === void 0) return 0;
|
|
41109
|
+
const idx = rows.findIndex((row) => row.kind !== "header" && cursorableRowId(row) === focusedId);
|
|
41110
|
+
return idx < 0 ? 0 : idx;
|
|
41111
|
+
}, [rows, focusedId]);
|
|
41112
|
+
const renderWindow = useMemo28(
|
|
41113
|
+
() => computeListWindow(rows.length, focusedRowIndex, visibleRows),
|
|
41114
|
+
[rows.length, focusedRowIndex, visibleRows]
|
|
41115
|
+
);
|
|
41116
|
+
const renderRows = useMemo28(
|
|
41117
|
+
() => rows.slice(renderWindow.start, renderWindow.end),
|
|
41118
|
+
[rows, renderWindow.start, renderWindow.end]
|
|
41119
|
+
);
|
|
41099
41120
|
return /* @__PURE__ */ jsxs80(Box84, { flexDirection: "column", children: [
|
|
41100
|
-
|
|
41101
|
-
|
|
41102
|
-
|
|
41103
|
-
"
|
|
41104
|
-
] }) }),
|
|
41105
|
-
slice.map((row, i) => {
|
|
41106
|
-
const absoluteIndex = i + window.start;
|
|
41107
|
-
if (row.kind === "header") {
|
|
41108
|
-
return /* @__PURE__ */ jsx102(HeaderRowView, { row }, `h-${row.groupKey}-${String(absoluteIndex)}`);
|
|
41109
|
-
}
|
|
41110
|
-
if (row.kind === "create") {
|
|
41111
|
-
return /* @__PURE__ */ jsx102(CreateRowView, { focused: absoluteIndex === cursor }, `create-${String(absoluteIndex)}`);
|
|
41112
|
-
}
|
|
41121
|
+
/* @__PURE__ */ jsx102(OverflowRow, { direction: "above", count: renderWindow.start }),
|
|
41122
|
+
renderRows.map((row) => {
|
|
41123
|
+
if (row.kind === "header") return /* @__PURE__ */ jsx102(HeaderRowView, { row }, `h-${row.groupKey}`);
|
|
41124
|
+
if (row.kind === "create") return /* @__PURE__ */ jsx102(CreateRowView, { focused: focusedId === cursorableRowId(row) }, "create");
|
|
41113
41125
|
return /* @__PURE__ */ jsx102(
|
|
41114
41126
|
SprintRowView,
|
|
41115
41127
|
{
|
|
41116
41128
|
sprint: row.sprint,
|
|
41117
|
-
focused:
|
|
41129
|
+
focused: focusedId === row.sprint.id,
|
|
41118
41130
|
isCurrent: currentSprintId === row.sprint.id
|
|
41119
41131
|
},
|
|
41120
41132
|
row.sprint.id
|
|
41121
41133
|
);
|
|
41122
41134
|
}),
|
|
41123
|
-
|
|
41124
|
-
"\u25BC ",
|
|
41125
|
-
String(window.hiddenBelow),
|
|
41126
|
-
" more below"
|
|
41127
|
-
] }) })
|
|
41135
|
+
/* @__PURE__ */ jsx102(OverflowRow, { direction: "below", count: rows.length - renderWindow.end })
|
|
41128
41136
|
] });
|
|
41129
41137
|
};
|
|
41130
41138
|
var CreateRowView = ({ focused }) => /* @__PURE__ */ jsxs80(Box84, { flexDirection: "column", paddingX: spacing.indent, children: [
|
|
@@ -41186,15 +41194,6 @@ var SprintRowView = ({
|
|
|
41186
41194
|
|
|
41187
41195
|
// src/application/ui/tui/views/pick-sprint-view.tsx
|
|
41188
41196
|
import { Fragment as Fragment12, jsx as jsx103, jsxs as jsxs81 } from "react/jsx-runtime";
|
|
41189
|
-
var preferredCursorIndex = (rows, preferredSprintId) => {
|
|
41190
|
-
if (preferredSprintId !== void 0) {
|
|
41191
|
-
const i = rows.findIndex((r) => r.kind === "sprint" && r.sprint.id === preferredSprintId);
|
|
41192
|
-
if (i !== -1) return i;
|
|
41193
|
-
}
|
|
41194
|
-
const firstSprint = rows.findIndex((r) => r.kind === "sprint");
|
|
41195
|
-
if (firstSprint !== -1) return firstSprint;
|
|
41196
|
-
return cursorableRowIndices(rows)[0] ?? 0;
|
|
41197
|
-
};
|
|
41198
41197
|
var usePickerRows = (deps, selection) => {
|
|
41199
41198
|
const [scopeAll, setScopeAll] = useState50(true);
|
|
41200
41199
|
const [hideDone, setHideDone] = useState50(false);
|
|
@@ -41229,28 +41228,7 @@ var usePickerRows = (deps, selection) => {
|
|
|
41229
41228
|
if (!hideDone || sprintCount > 0) return false;
|
|
41230
41229
|
return flatten(buildGroups(data, selection.projectId, scopeAll), false).some((r) => r.kind === "sprint");
|
|
41231
41230
|
}, [hideDone, sprintCount, data, selection.projectId, scopeAll]);
|
|
41232
|
-
const
|
|
41233
|
-
const [cursor, setCursor] = useState50(initialIdx);
|
|
41234
|
-
useEffect45(() => {
|
|
41235
|
-
setCursor((c) => {
|
|
41236
|
-
if (rows.length === 0) return 0;
|
|
41237
|
-
if (c >= rows.length) return Math.max(0, rows.length - 1);
|
|
41238
|
-
const focused = rows[c];
|
|
41239
|
-
if (focused?.kind !== "sprint" && focused?.kind !== "create") {
|
|
41240
|
-
return cursorableRowIndices(rows)[0] ?? c;
|
|
41241
|
-
}
|
|
41242
|
-
return c;
|
|
41243
|
-
});
|
|
41244
|
-
}, [rows]);
|
|
41245
|
-
useEffect45(() => {
|
|
41246
|
-
setCursor(initialIdx);
|
|
41247
|
-
}, [initialIdx]);
|
|
41248
|
-
const toggleScope = () => {
|
|
41249
|
-
const next = !scopeAll;
|
|
41250
|
-
setScopeAll(next);
|
|
41251
|
-
const nextRows = flatten(buildGroups(visibleData, selection.projectId, next), includeCreate);
|
|
41252
|
-
setCursor(preferredCursorIndex(nextRows, selection.sprintId));
|
|
41253
|
-
};
|
|
41231
|
+
const toggleScope = () => setScopeAll((v) => !v);
|
|
41254
41232
|
return {
|
|
41255
41233
|
state,
|
|
41256
41234
|
reload,
|
|
@@ -41261,24 +41239,9 @@ var usePickerRows = (deps, selection) => {
|
|
|
41261
41239
|
scopeAll,
|
|
41262
41240
|
hideDone,
|
|
41263
41241
|
setHideDone,
|
|
41264
|
-
toggleScope
|
|
41265
|
-
cursor,
|
|
41266
|
-
setCursor
|
|
41242
|
+
toggleScope
|
|
41267
41243
|
};
|
|
41268
41244
|
};
|
|
41269
|
-
var isNavigationInput = (input, key) => key.upArrow || key.downArrow || key.pageUp || key.pageDown || key.home || key.end || input === "j" || input === "k";
|
|
41270
|
-
var resolveArrowMove = (rows, cursor, visibleRows, input, key) => {
|
|
41271
|
-
if (key.upArrow || input === "k") return nextCursorableIndex(rows, cursor, -1);
|
|
41272
|
-
if (key.downArrow || input === "j") return nextCursorableIndex(rows, cursor, 1);
|
|
41273
|
-
if (key.pageUp) return cursorableNear(rows, Math.max(0, cursor - visibleRows), -1);
|
|
41274
|
-
if (key.pageDown) return cursorableNear(rows, Math.min(rows.length - 1, cursor + visibleRows), 1);
|
|
41275
|
-
if (key.home) return cursorableRowIndices(rows)[0] ?? 0;
|
|
41276
|
-
if (key.end) {
|
|
41277
|
-
const candidates = cursorableRowIndices(rows);
|
|
41278
|
-
return candidates[candidates.length - 1] ?? cursor;
|
|
41279
|
-
}
|
|
41280
|
-
return void 0;
|
|
41281
|
-
};
|
|
41282
41245
|
var PickerBody = ({
|
|
41283
41246
|
helpOpen,
|
|
41284
41247
|
state,
|
|
@@ -41287,11 +41250,14 @@ var PickerBody = ({
|
|
|
41287
41250
|
scopeAll,
|
|
41288
41251
|
hideDone,
|
|
41289
41252
|
rows,
|
|
41290
|
-
cursor,
|
|
41291
41253
|
visibleRows,
|
|
41254
|
+
listActive,
|
|
41255
|
+
initialCursorId,
|
|
41256
|
+
listKey,
|
|
41292
41257
|
currentSprintId,
|
|
41293
41258
|
currentSprintLabel,
|
|
41294
|
-
feedback: feedback2
|
|
41259
|
+
feedback: feedback2,
|
|
41260
|
+
onRowSubmit
|
|
41295
41261
|
}) => helpOpen ? /* @__PURE__ */ jsx103(HelpOverlay, {}) : state.kind === "loading" || state.kind === "idle" ? /* @__PURE__ */ jsx103(LoadingRow, { label: "Loading sprints\u2026" }) : state.kind === "error" ? /* @__PURE__ */ jsx103(LoadErrorRow, { message: "Failed to load sprints.", color: inkColors.error }) : sprintCount === 0 ? /* @__PURE__ */ jsx103(Box85, { flexDirection: "column", paddingX: spacing.indent, children: hiddenByDoneFilter ? /* @__PURE__ */ jsxs81(Fragment12, { children: [
|
|
41296
41262
|
/* @__PURE__ */ jsx103(Text80, { children: "All sprints here are done (hidden)." }),
|
|
41297
41263
|
/* @__PURE__ */ jsx103(Text80, { dimColor: true, children: "Press f to show them, or + to create a new one." })
|
|
@@ -41316,7 +41282,18 @@ var PickerBody = ({
|
|
|
41316
41282
|
/* @__PURE__ */ jsx103(Text80, { color: inkColors.primary, bold: true, children: currentSprintLabel })
|
|
41317
41283
|
] }) : /* @__PURE__ */ jsx103(Text80, { children: "press \u21B5 to confirm" })
|
|
41318
41284
|
] }) }),
|
|
41319
|
-
/* @__PURE__ */ jsx103(
|
|
41285
|
+
/* @__PURE__ */ jsx103(
|
|
41286
|
+
PickerRowList,
|
|
41287
|
+
{
|
|
41288
|
+
rows,
|
|
41289
|
+
visibleRows,
|
|
41290
|
+
active: listActive,
|
|
41291
|
+
initialCursorId,
|
|
41292
|
+
currentSprintId,
|
|
41293
|
+
onSubmit: onRowSubmit
|
|
41294
|
+
},
|
|
41295
|
+
listKey
|
|
41296
|
+
),
|
|
41320
41297
|
/* @__PURE__ */ jsx103(Box85, { marginTop: spacing.section, paddingX: spacing.indent, children: /* @__PURE__ */ jsxs81(Text80, { dimColor: true, children: [
|
|
41321
41298
|
glyphs.bullet,
|
|
41322
41299
|
" \u21B5 use the highlighted sprint ",
|
|
@@ -41330,67 +41307,15 @@ var PickerBody = ({
|
|
|
41330
41307
|
glyphs.bullet,
|
|
41331
41308
|
" + create a new one"
|
|
41332
41309
|
] }) }),
|
|
41333
|
-
feedback2 !== void 0 && /* @__PURE__ */ jsx103(Box85, { paddingX: spacing.indent, marginTop:
|
|
41310
|
+
feedback2 !== void 0 && /* @__PURE__ */ jsx103(Box85, { paddingX: spacing.indent, marginTop: spacing.section, children: /* @__PURE__ */ jsx103(Text80, { color: inkColors.error, children: feedback2 }) })
|
|
41334
41311
|
] });
|
|
41335
|
-
var usePickerInput = ({
|
|
41336
|
-
modalOpen,
|
|
41337
|
-
rows,
|
|
41338
|
-
cursor,
|
|
41339
|
-
visibleRows,
|
|
41340
|
-
setCursor,
|
|
41341
|
-
pick,
|
|
41342
|
-
launchCreateSprint: launchCreateSprint2,
|
|
41343
|
-
toggleScope,
|
|
41344
|
-
setHideDone,
|
|
41345
|
-
reload
|
|
41346
|
-
}) => {
|
|
41347
|
-
useInput31((input, key) => {
|
|
41348
|
-
if (modalOpen) return;
|
|
41349
|
-
if (isNavigationInput(input, key)) {
|
|
41350
|
-
setCursor((c) => resolveArrowMove(rows, c, visibleRows, input, key) ?? c);
|
|
41351
|
-
return;
|
|
41352
|
-
}
|
|
41353
|
-
if (key.return) {
|
|
41354
|
-
const row = rows[cursor];
|
|
41355
|
-
if (row?.kind === "sprint") pick(row.sprint);
|
|
41356
|
-
else if (row?.kind === "create") void launchCreateSprint2();
|
|
41357
|
-
return;
|
|
41358
|
-
}
|
|
41359
|
-
if (input === "t") {
|
|
41360
|
-
toggleScope();
|
|
41361
|
-
return;
|
|
41362
|
-
}
|
|
41363
|
-
if (input === "f") {
|
|
41364
|
-
setHideDone((v) => !v);
|
|
41365
|
-
return;
|
|
41366
|
-
}
|
|
41367
|
-
if (input === "+" || input === "c") {
|
|
41368
|
-
void launchCreateSprint2();
|
|
41369
|
-
return;
|
|
41370
|
-
}
|
|
41371
|
-
if (input === "r") reload();
|
|
41372
|
-
});
|
|
41373
|
-
};
|
|
41374
41312
|
var PickSprintView = () => {
|
|
41375
41313
|
const deps = useDeps();
|
|
41376
41314
|
const router = useRouter();
|
|
41377
41315
|
const selection = useSelection();
|
|
41378
41316
|
const ui = useUiState();
|
|
41379
41317
|
const [feedback2, setFeedback] = useState50(void 0);
|
|
41380
|
-
const {
|
|
41381
|
-
state,
|
|
41382
|
-
reload,
|
|
41383
|
-
data,
|
|
41384
|
-
rows,
|
|
41385
|
-
sprintCount,
|
|
41386
|
-
hiddenByDoneFilter,
|
|
41387
|
-
scopeAll,
|
|
41388
|
-
hideDone,
|
|
41389
|
-
setHideDone,
|
|
41390
|
-
toggleScope,
|
|
41391
|
-
cursor,
|
|
41392
|
-
setCursor
|
|
41393
|
-
} = usePickerRows(deps, selection);
|
|
41318
|
+
const { state, reload, data, rows, sprintCount, hiddenByDoneFilter, scopeAll, hideDone, setHideDone, toggleScope } = usePickerRows(deps, selection);
|
|
41394
41319
|
useViewHints([
|
|
41395
41320
|
{ keys: "\u2191/\u2193/j/k", label: "move" },
|
|
41396
41321
|
{ keys: "\u21B5", label: "use sprint" },
|
|
@@ -41417,18 +41342,28 @@ var PickSprintView = () => {
|
|
|
41417
41342
|
onError: setFeedback,
|
|
41418
41343
|
noProjectMessage: `${glyphs.cross} select a project first`
|
|
41419
41344
|
});
|
|
41420
|
-
|
|
41421
|
-
|
|
41422
|
-
|
|
41423
|
-
|
|
41424
|
-
|
|
41425
|
-
|
|
41426
|
-
|
|
41427
|
-
|
|
41428
|
-
|
|
41429
|
-
|
|
41430
|
-
|
|
41345
|
+
const onRowSubmit = (row) => {
|
|
41346
|
+
if (row.kind === "sprint") pick(row.sprint);
|
|
41347
|
+
else void launchCreateSprint2();
|
|
41348
|
+
};
|
|
41349
|
+
useInput31((input) => {
|
|
41350
|
+
if (ui.modalOpen) return;
|
|
41351
|
+
if (input === "t") {
|
|
41352
|
+
toggleScope();
|
|
41353
|
+
return;
|
|
41354
|
+
}
|
|
41355
|
+
if (input === "f") {
|
|
41356
|
+
setHideDone((v) => !v);
|
|
41357
|
+
return;
|
|
41358
|
+
}
|
|
41359
|
+
if (input === "+" || input === "c") {
|
|
41360
|
+
void launchCreateSprint2();
|
|
41361
|
+
return;
|
|
41362
|
+
}
|
|
41363
|
+
if (input === "r") reload();
|
|
41431
41364
|
});
|
|
41365
|
+
const initialCursorId = useMemo29(() => preferredCursorId(rows, selection.sprintId), [rows, selection.sprintId]);
|
|
41366
|
+
const listKey = `${String(scopeAll)}-${selection.sprintId ?? "unseeded"}`;
|
|
41432
41367
|
return /* @__PURE__ */ jsx103(ViewShell, { title: "Pick a sprint", subtitle: "Switch sprint (and project) in one step", suppressScrollArrows: true, children: /* @__PURE__ */ jsx103(
|
|
41433
41368
|
PickerBody,
|
|
41434
41369
|
{
|
|
@@ -41439,11 +41374,14 @@ var PickSprintView = () => {
|
|
|
41439
41374
|
scopeAll,
|
|
41440
41375
|
hideDone,
|
|
41441
41376
|
rows,
|
|
41442
|
-
cursor,
|
|
41443
41377
|
visibleRows,
|
|
41378
|
+
listActive: !ui.modalOpen,
|
|
41379
|
+
initialCursorId,
|
|
41380
|
+
listKey,
|
|
41444
41381
|
currentSprintId: selection.sprintId,
|
|
41445
41382
|
currentSprintLabel: selection.sprintLabel,
|
|
41446
|
-
feedback: feedback2
|
|
41383
|
+
feedback: feedback2,
|
|
41384
|
+
onRowSubmit
|
|
41447
41385
|
}
|
|
41448
41386
|
) });
|
|
41449
41387
|
};
|
|
@@ -41498,11 +41436,10 @@ var renderView = (entry) => {
|
|
|
41498
41436
|
};
|
|
41499
41437
|
|
|
41500
41438
|
// src/application/ui/tui/runtime/use-global-keys.ts
|
|
41501
|
-
import { useEffect as
|
|
41439
|
+
import { useEffect as useEffect45, useMemo as useMemo30, useRef as useRef18 } from "react";
|
|
41502
41440
|
import { useApp, useInput as useInput32 } from "ink";
|
|
41503
41441
|
|
|
41504
41442
|
// src/integration/io/clipboard.ts
|
|
41505
|
-
import { spawn as nodeSpawn2 } from "child_process";
|
|
41506
41443
|
var resolveHelpers = ({ platform, env }) => {
|
|
41507
41444
|
if (platform === "darwin") return [{ cmd: "pbcopy", args: [] }];
|
|
41508
41445
|
if (platform === "win32") return [{ cmd: "clip.exe", args: [] }];
|
|
@@ -41521,10 +41458,10 @@ var resolveHelpers = ({ platform, env }) => {
|
|
|
41521
41458
|
return [];
|
|
41522
41459
|
};
|
|
41523
41460
|
var SPAWN_FAILED = "spawn-failed";
|
|
41524
|
-
var runHelper = (
|
|
41461
|
+
var runHelper = (spawn2, helper, text) => new Promise((resolve) => {
|
|
41525
41462
|
let child;
|
|
41526
41463
|
try {
|
|
41527
|
-
child =
|
|
41464
|
+
child = spawn2(helper.cmd, helper.args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
41528
41465
|
} catch (cause) {
|
|
41529
41466
|
resolve(
|
|
41530
41467
|
Result.error({
|
|
@@ -41568,7 +41505,7 @@ var runHelper = (spawn3, helper, text) => new Promise((resolve) => {
|
|
|
41568
41505
|
}
|
|
41569
41506
|
});
|
|
41570
41507
|
var createCopyToClipboard = (opts = {}) => {
|
|
41571
|
-
const
|
|
41508
|
+
const spawn2 = opts.spawn ?? crossPlatformSpawn;
|
|
41572
41509
|
const platform = opts.platform ?? process.platform;
|
|
41573
41510
|
const env = opts.env ?? process.env;
|
|
41574
41511
|
const helpers = resolveHelpers({ platform, env });
|
|
@@ -41581,7 +41518,7 @@ var createCopyToClipboard = (opts = {}) => {
|
|
|
41581
41518
|
}
|
|
41582
41519
|
let lastError;
|
|
41583
41520
|
for (const helper of helpers) {
|
|
41584
|
-
const result = await runHelper(
|
|
41521
|
+
const result = await runHelper(spawn2, helper, text);
|
|
41585
41522
|
if (result.ok) return result;
|
|
41586
41523
|
lastError = result.error;
|
|
41587
41524
|
if (result.error.code !== "no-helper") return result;
|
|
@@ -41610,7 +41547,7 @@ var useGlobalKeys = (opts = {}) => {
|
|
|
41610
41547
|
[opts.copyToClipboard]
|
|
41611
41548
|
);
|
|
41612
41549
|
const clearTimerRef = useRef18(void 0);
|
|
41613
|
-
|
|
41550
|
+
useEffect45(
|
|
41614
41551
|
() => () => {
|
|
41615
41552
|
if (clearTimerRef.current !== void 0) clearTimeout(clearTimerRef.current);
|
|
41616
41553
|
},
|
|
@@ -41779,7 +41716,7 @@ var scheduleClear = (eventBus, ref3) => {
|
|
|
41779
41716
|
};
|
|
41780
41717
|
|
|
41781
41718
|
// src/application/ui/tui/components/memory-pressure-banner.tsx
|
|
41782
|
-
import { useEffect as
|
|
41719
|
+
import { useEffect as useEffect46, useState as useState51 } from "react";
|
|
41783
41720
|
import { Box as Box86, Text as Text81 } from "ink";
|
|
41784
41721
|
import { jsxs as jsxs82 } from "react/jsx-runtime";
|
|
41785
41722
|
var formatMb = (bytes) => `${(bytes / 1048576).toFixed(0)} MB`;
|
|
@@ -41787,7 +41724,7 @@ var formatPercent = (ratio) => `${Math.round(ratio * 100)}%`;
|
|
|
41787
41724
|
var MemoryPressureBanner = () => {
|
|
41788
41725
|
const deps = useDeps();
|
|
41789
41726
|
const [latest, setLatest] = useState51(void 0);
|
|
41790
|
-
|
|
41727
|
+
useEffect46(() => {
|
|
41791
41728
|
return deps.eventBus.subscribe((event) => {
|
|
41792
41729
|
if (event.type === "memory-pressure") setLatest(event);
|
|
41793
41730
|
});
|
|
@@ -41810,13 +41747,13 @@ var MemoryPressureBanner = () => {
|
|
|
41810
41747
|
};
|
|
41811
41748
|
|
|
41812
41749
|
// src/application/ui/tui/components/chain-log-degraded-banner.tsx
|
|
41813
|
-
import { useEffect as
|
|
41750
|
+
import { useEffect as useEffect47, useState as useState52 } from "react";
|
|
41814
41751
|
import { Box as Box87, Text as Text82 } from "ink";
|
|
41815
41752
|
import { jsx as jsx105, jsxs as jsxs83 } from "react/jsx-runtime";
|
|
41816
41753
|
var ChainLogDegradedBanner = () => {
|
|
41817
41754
|
const deps = useDeps();
|
|
41818
41755
|
const [degraded, setDegraded] = useState52(false);
|
|
41819
|
-
|
|
41756
|
+
useEffect47(() => {
|
|
41820
41757
|
return deps.eventBus.subscribe((event) => {
|
|
41821
41758
|
if (event.type === "chain-log-degraded") setDegraded(true);
|
|
41822
41759
|
});
|
|
@@ -41829,9 +41766,9 @@ var ChainLogDegradedBanner = () => {
|
|
|
41829
41766
|
};
|
|
41830
41767
|
|
|
41831
41768
|
// src/application/ui/tui/components/progress-overlay.tsx
|
|
41832
|
-
import { promises as
|
|
41769
|
+
import { promises as fs35 } from "fs";
|
|
41833
41770
|
import { join as join71 } from "path";
|
|
41834
|
-
import { useEffect as
|
|
41771
|
+
import { useEffect as useEffect48, useState as useState53 } from "react";
|
|
41835
41772
|
import { Box as Box88, Text as Text83, useInput as useInput33 } from "ink";
|
|
41836
41773
|
import { Fragment as Fragment13, jsx as jsx106, jsxs as jsxs84 } from "react/jsx-runtime";
|
|
41837
41774
|
var CHROME_ROWS4 = 10;
|
|
@@ -41842,7 +41779,7 @@ var formatAgo = (modifiedAtMs, now) => {
|
|
|
41842
41779
|
};
|
|
41843
41780
|
var useProgressFile = (sprintId, dataRoot) => {
|
|
41844
41781
|
const [state, setState] = useState53({ kind: "loading" });
|
|
41845
|
-
|
|
41782
|
+
useEffect48(() => {
|
|
41846
41783
|
let cancelled = false;
|
|
41847
41784
|
if (sprintId === void 0) {
|
|
41848
41785
|
setState({ kind: "missing" });
|
|
@@ -41857,7 +41794,7 @@ var useProgressFile = (sprintId, dataRoot) => {
|
|
|
41857
41794
|
return;
|
|
41858
41795
|
}
|
|
41859
41796
|
const progressPath = join71(dir, "progress.md");
|
|
41860
|
-
const [stat, content] = await Promise.all([
|
|
41797
|
+
const [stat, content] = await Promise.all([fs35.stat(progressPath), fs35.readFile(progressPath, "utf8")]);
|
|
41861
41798
|
if (cancelled) return;
|
|
41862
41799
|
const modifiedAtMs = stat.mtimeMs;
|
|
41863
41800
|
if (content.trim().length === 0) {
|
|
@@ -41887,7 +41824,7 @@ var useProgressFile = (sprintId, dataRoot) => {
|
|
|
41887
41824
|
var useProgressScroll = (state, bodyRows) => {
|
|
41888
41825
|
const [offset, setOffset] = useState53(0);
|
|
41889
41826
|
const lineCount = state.kind === "ok" ? state.lines.length : 0;
|
|
41890
|
-
|
|
41827
|
+
useEffect48(() => {
|
|
41891
41828
|
setOffset(0);
|
|
41892
41829
|
}, [lineCount]);
|
|
41893
41830
|
const maxOffset = Math.max(0, lineCount - bodyRows);
|
|
@@ -42084,7 +42021,7 @@ import { useState as useState55 } from "react";
|
|
|
42084
42021
|
import { useApp as useApp2 } from "ink";
|
|
42085
42022
|
|
|
42086
42023
|
// src/application/ui/tui/migration/migration-gate.tsx
|
|
42087
|
-
import { useEffect as
|
|
42024
|
+
import { useEffect as useEffect49, useRef as useRef19, useState as useState54 } from "react";
|
|
42088
42025
|
import { Box as Box90, Text as Text84, useInput as useInput34 } from "ink";
|
|
42089
42026
|
|
|
42090
42027
|
// src/application/ui/tui/migration/learnings-backfill-adapter.ts
|
|
@@ -42132,7 +42069,7 @@ var MigrationGate = (props) => {
|
|
|
42132
42069
|
const { engine, dataRoot, stateRoot, appVersion, now, writeFile: writeFile3, onResolve, onQuit } = props;
|
|
42133
42070
|
const [state, setState] = useState54({ kind: "scanning" });
|
|
42134
42071
|
const scannedRef = useRef19(false);
|
|
42135
|
-
|
|
42072
|
+
useEffect49(() => {
|
|
42136
42073
|
if (scannedRef.current) return;
|
|
42137
42074
|
scannedRef.current = true;
|
|
42138
42075
|
let cancelled = false;
|
|
@@ -42378,7 +42315,7 @@ var MigrationRoute = ({ gate, app, onResolved }) => {
|
|
|
42378
42315
|
};
|
|
42379
42316
|
|
|
42380
42317
|
// src/integration/persistence/data-migration/version-marker.ts
|
|
42381
|
-
import { promises as
|
|
42318
|
+
import { promises as fs36 } from "fs";
|
|
42382
42319
|
import { join as join72 } from "path";
|
|
42383
42320
|
import { z as z50 } from "zod";
|
|
42384
42321
|
var CURRENT_DATA_VERSION = 2;
|
|
@@ -42391,7 +42328,7 @@ var versionMarkerPath = (dataRoot) => join72(String(dataRoot), DATA_VERSION_FILE
|
|
|
42391
42328
|
var readDataVersion = async (dataRoot) => {
|
|
42392
42329
|
let bytes;
|
|
42393
42330
|
try {
|
|
42394
|
-
bytes = await
|
|
42331
|
+
bytes = await fs36.readFile(versionMarkerPath(dataRoot), "utf8");
|
|
42395
42332
|
} catch {
|
|
42396
42333
|
return { dataVersion: 1, lastWrittenByAppVersion: "" };
|
|
42397
42334
|
}
|
|
@@ -42413,8 +42350,8 @@ var writeDataVersion = async (dataRoot, marker) => {
|
|
|
42413
42350
|
const body = `${JSON.stringify(marker, null, 2)}
|
|
42414
42351
|
`;
|
|
42415
42352
|
try {
|
|
42416
|
-
await
|
|
42417
|
-
await
|
|
42353
|
+
await fs36.mkdir(String(dataRoot), { recursive: true });
|
|
42354
|
+
await fs36.writeFile(path2, body, "utf8");
|
|
42418
42355
|
return Result.ok(void 0);
|
|
42419
42356
|
} catch (cause) {
|
|
42420
42357
|
return Result.error(new StorageError({ subCode: "io", message: `marker write failed: ${path2}`, path: path2, cause }));
|
|
@@ -42426,7 +42363,7 @@ var needsMigration = async (dataRoot) => {
|
|
|
42426
42363
|
};
|
|
42427
42364
|
|
|
42428
42365
|
// src/integration/persistence/data-migration/dry-run.ts
|
|
42429
|
-
import { promises as
|
|
42366
|
+
import { promises as fs37 } from "fs";
|
|
42430
42367
|
import { join as join73 } from "path";
|
|
42431
42368
|
var PROJECTS_DIR = "projects";
|
|
42432
42369
|
var SPRINTS_DIR = "sprints";
|
|
@@ -42445,7 +42382,7 @@ var dryRun = async (dataRoot) => {
|
|
|
42445
42382
|
var checkBackupTargetWritable = async (dataRoot, acc) => {
|
|
42446
42383
|
const parent = join73(String(dataRoot), "..");
|
|
42447
42384
|
try {
|
|
42448
|
-
await
|
|
42385
|
+
await fs37.access(parent, fs37.constants.W_OK);
|
|
42449
42386
|
} catch {
|
|
42450
42387
|
acc.problems.push({
|
|
42451
42388
|
name: parent,
|
|
@@ -42563,7 +42500,7 @@ var planRename = async (parent, fromName, toName, id, slug, kind, acc) => {
|
|
|
42563
42500
|
};
|
|
42564
42501
|
var pathPresent = async (path2) => {
|
|
42565
42502
|
try {
|
|
42566
|
-
await
|
|
42503
|
+
await fs37.stat(path2);
|
|
42567
42504
|
return true;
|
|
42568
42505
|
} catch {
|
|
42569
42506
|
return false;
|
|
@@ -42571,7 +42508,7 @@ var pathPresent = async (path2) => {
|
|
|
42571
42508
|
};
|
|
42572
42509
|
var isDirectory = async (path2) => {
|
|
42573
42510
|
try {
|
|
42574
|
-
return (await
|
|
42511
|
+
return (await fs37.stat(path2)).isDirectory();
|
|
42575
42512
|
} catch {
|
|
42576
42513
|
return false;
|
|
42577
42514
|
}
|
|
@@ -42594,7 +42531,7 @@ var readProjectSlugForId = async (dataRoot, projectId) => {
|
|
|
42594
42531
|
var readSlugField = async (filePath) => {
|
|
42595
42532
|
let bytes;
|
|
42596
42533
|
try {
|
|
42597
|
-
bytes = await
|
|
42534
|
+
bytes = await fs37.readFile(filePath, "utf8");
|
|
42598
42535
|
} catch {
|
|
42599
42536
|
return void 0;
|
|
42600
42537
|
}
|
|
@@ -42611,11 +42548,11 @@ var readSlugField = async (filePath) => {
|
|
|
42611
42548
|
};
|
|
42612
42549
|
|
|
42613
42550
|
// src/integration/persistence/data-migration/apply.ts
|
|
42614
|
-
import { promises as
|
|
42551
|
+
import { promises as fs40 } from "fs";
|
|
42615
42552
|
import { join as join76 } from "path";
|
|
42616
42553
|
|
|
42617
42554
|
// src/integration/persistence/data-migration/backup.ts
|
|
42618
|
-
import { promises as
|
|
42555
|
+
import { promises as fs38 } from "fs";
|
|
42619
42556
|
import { basename as basename7, dirname as dirname22, join as join74 } from "path";
|
|
42620
42557
|
var BACKUP_DIR_PREFIX = "data.backup-";
|
|
42621
42558
|
var backupDirName = (fromVersion, timestamp) => `${BACKUP_DIR_PREFIX}v${String(fromVersion)}-${sanitizeTimestamp(timestamp)}`;
|
|
@@ -42623,7 +42560,7 @@ var backupDataDir = async (dataRoot, fromVersion, timestamp) => {
|
|
|
42623
42560
|
const src = String(dataRoot);
|
|
42624
42561
|
const dest = join74(dirname22(src), backupDirName(fromVersion, timestamp));
|
|
42625
42562
|
try {
|
|
42626
|
-
await
|
|
42563
|
+
await fs38.cp(src, dest, {
|
|
42627
42564
|
recursive: true,
|
|
42628
42565
|
// Follow symlinks so the backup captures their real CONTENT, not a dangling node. The default
|
|
42629
42566
|
// (`dereference: false`) copies a symlink as a link, which would leave the backup incomplete for
|
|
@@ -42645,7 +42582,7 @@ var backupDataDir = async (dataRoot, fromVersion, timestamp) => {
|
|
|
42645
42582
|
);
|
|
42646
42583
|
}
|
|
42647
42584
|
try {
|
|
42648
|
-
const stat = await
|
|
42585
|
+
const stat = await fs38.stat(dest);
|
|
42649
42586
|
if (!stat.isDirectory()) {
|
|
42650
42587
|
return Result.error(
|
|
42651
42588
|
new StorageError({ subCode: "io", message: `backup verify failed: ${dest} is not a directory`, path: dest })
|
|
@@ -42667,8 +42604,8 @@ var backupDataDir = async (dataRoot, fromVersion, timestamp) => {
|
|
|
42667
42604
|
};
|
|
42668
42605
|
var verifyEntryCount = async (src, dest) => {
|
|
42669
42606
|
try {
|
|
42670
|
-
const srcEntries = (await
|
|
42671
|
-
const destEntries = await
|
|
42607
|
+
const srcEntries = (await fs38.readdir(src)).filter((name) => !name.startsWith(BACKUP_DIR_PREFIX));
|
|
42608
|
+
const destEntries = await fs38.readdir(dest);
|
|
42672
42609
|
if (destEntries.length < srcEntries.length) {
|
|
42673
42610
|
return Result.error(
|
|
42674
42611
|
new StorageError({
|
|
@@ -42688,10 +42625,10 @@ var verifyEntryCount = async (src, dest) => {
|
|
|
42688
42625
|
var sanitizeTimestamp = (timestamp) => timestamp.replace(/:/g, "-");
|
|
42689
42626
|
|
|
42690
42627
|
// src/integration/persistence/data-migration/lock-guard.ts
|
|
42691
|
-
import { promises as
|
|
42628
|
+
import { promises as fs39 } from "fs";
|
|
42692
42629
|
import { join as join75 } from "path";
|
|
42693
42630
|
var LOCKS_SUBDIR2 = "locks";
|
|
42694
|
-
var HELD_WITHIN_MS =
|
|
42631
|
+
var HELD_WITHIN_MS = DEFAULT_STALE_AFTER_MS;
|
|
42695
42632
|
var anyLockHeld = async (stateRoot) => {
|
|
42696
42633
|
const locksDir = join75(String(stateRoot), LOCKS_SUBDIR2);
|
|
42697
42634
|
const entries = await listDir(locksDir);
|
|
@@ -42701,7 +42638,7 @@ var anyLockHeld = async (stateRoot) => {
|
|
|
42701
42638
|
if (!name.endsWith(".lock")) continue;
|
|
42702
42639
|
let mtimeMs;
|
|
42703
42640
|
try {
|
|
42704
|
-
({ mtimeMs } = await
|
|
42641
|
+
({ mtimeMs } = await fs39.lstat(join75(locksDir, name)));
|
|
42705
42642
|
} catch {
|
|
42706
42643
|
continue;
|
|
42707
42644
|
}
|
|
@@ -42794,7 +42731,7 @@ var mergeOne = async (merge, ctx) => {
|
|
|
42794
42731
|
};
|
|
42795
42732
|
var readTextOrEmpty = async (path2) => {
|
|
42796
42733
|
try {
|
|
42797
|
-
return await
|
|
42734
|
+
return await fs40.readFile(path2, "utf8");
|
|
42798
42735
|
} catch {
|
|
42799
42736
|
return "";
|
|
42800
42737
|
}
|
|
@@ -42812,7 +42749,7 @@ var backfillLearningsMd = async (dataRoot, ctx) => {
|
|
|
42812
42749
|
if (await pathPresent2(mdPath)) continue;
|
|
42813
42750
|
let body;
|
|
42814
42751
|
try {
|
|
42815
|
-
body = await
|
|
42752
|
+
body = await fs40.readFile(ledgerPath, "utf8");
|
|
42816
42753
|
} catch {
|
|
42817
42754
|
continue;
|
|
42818
42755
|
}
|
|
@@ -42825,7 +42762,7 @@ var backfillLearningsMd = async (dataRoot, ctx) => {
|
|
|
42825
42762
|
};
|
|
42826
42763
|
var ledgerExceedsCeiling = async (ledgerPath) => {
|
|
42827
42764
|
try {
|
|
42828
|
-
const { size } = await
|
|
42765
|
+
const { size } = await fs40.stat(ledgerPath);
|
|
42829
42766
|
return size > MIRROR_BACKFILL_CEILING_BYTES;
|
|
42830
42767
|
} catch {
|
|
42831
42768
|
return false;
|
|
@@ -42833,7 +42770,7 @@ var ledgerExceedsCeiling = async (ledgerPath) => {
|
|
|
42833
42770
|
};
|
|
42834
42771
|
var pathPresent2 = async (path2) => {
|
|
42835
42772
|
try {
|
|
42836
|
-
await
|
|
42773
|
+
await fs40.stat(path2);
|
|
42837
42774
|
return true;
|
|
42838
42775
|
} catch {
|
|
42839
42776
|
return false;
|
|
@@ -42879,7 +42816,7 @@ var resolveInitialState = ({
|
|
|
42879
42816
|
};
|
|
42880
42817
|
|
|
42881
42818
|
// src/integration/persistence/selection/last-selection-store.ts
|
|
42882
|
-
import { promises as
|
|
42819
|
+
import { promises as fs41 } from "fs";
|
|
42883
42820
|
import { join as join77 } from "path";
|
|
42884
42821
|
var FILE_NAME = "last-selection.json";
|
|
42885
42822
|
var createLastSelectionStore = (stateRoot) => {
|
|
@@ -42887,7 +42824,7 @@ var createLastSelectionStore = (stateRoot) => {
|
|
|
42887
42824
|
return {
|
|
42888
42825
|
async read() {
|
|
42889
42826
|
try {
|
|
42890
|
-
const raw = await
|
|
42827
|
+
const raw = await fs41.readFile(path2, "utf8");
|
|
42891
42828
|
const parsed = JSON.parse(raw);
|
|
42892
42829
|
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
42893
42830
|
const rec = parsed;
|
|
@@ -42905,10 +42842,10 @@ var createLastSelectionStore = (stateRoot) => {
|
|
|
42905
42842
|
async write(value) {
|
|
42906
42843
|
try {
|
|
42907
42844
|
if (value === void 0) {
|
|
42908
|
-
await
|
|
42845
|
+
await fs41.rm(path2, { force: true });
|
|
42909
42846
|
return;
|
|
42910
42847
|
}
|
|
42911
|
-
await
|
|
42848
|
+
await fs41.writeFile(path2, JSON.stringify(value, null, 2), "utf8");
|
|
42912
42849
|
} catch {
|
|
42913
42850
|
}
|
|
42914
42851
|
}
|
|
@@ -43007,13 +42944,13 @@ var startHeapWatchdog = (deps) => {
|
|
|
43007
42944
|
};
|
|
43008
42945
|
|
|
43009
42946
|
// src/integration/observability/heap-snapshot.ts
|
|
43010
|
-
import * as
|
|
42947
|
+
import * as fs42 from "fs";
|
|
43011
42948
|
import * as path from "path";
|
|
43012
42949
|
import * as v82 from "v8";
|
|
43013
42950
|
var snapshotFilename = (timestamp) => `heap-${timestamp.replace(/[:.]/g, "-")}.heapsnapshot`;
|
|
43014
42951
|
var writeHeapSnapshotToDir = (dir, clock) => {
|
|
43015
42952
|
try {
|
|
43016
|
-
|
|
42953
|
+
fs42.mkdirSync(dir, { recursive: true });
|
|
43017
42954
|
const timestamp = clock?.() ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
43018
42955
|
const fullPath = path.join(dir, snapshotFilename(timestamp));
|
|
43019
42956
|
v82.writeHeapSnapshot(fullPath);
|
|
@@ -44534,11 +44471,11 @@ var formatTaskLine = (t) => {
|
|
|
44534
44471
|
};
|
|
44535
44472
|
|
|
44536
44473
|
// src/application/ui/cli/commands/runs.ts
|
|
44537
|
-
import { promises as
|
|
44474
|
+
import { promises as fs44 } from "fs";
|
|
44538
44475
|
import { createInterface as createInterface2 } from "readline";
|
|
44539
44476
|
|
|
44540
44477
|
// src/integration/ai/runs/_engine/run-enumeration.ts
|
|
44541
|
-
import { promises as
|
|
44478
|
+
import { promises as fs43 } from "fs";
|
|
44542
44479
|
import { join as join80 } from "path";
|
|
44543
44480
|
var parseRunTimestamp = (runDirName) => {
|
|
44544
44481
|
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z-/.exec(runDirName);
|
|
@@ -44639,7 +44576,7 @@ var listRuns = async (runsRoot) => {
|
|
|
44639
44576
|
const root = String(runsRoot);
|
|
44640
44577
|
let flowDirs;
|
|
44641
44578
|
try {
|
|
44642
|
-
flowDirs = await
|
|
44579
|
+
flowDirs = await fs43.readdir(root, { withFileTypes: true });
|
|
44643
44580
|
} catch (cause) {
|
|
44644
44581
|
if (isErrnoException2(cause) && cause.code === "ENOENT") return Result.ok([]);
|
|
44645
44582
|
return Result.error(
|
|
@@ -44656,7 +44593,7 @@ var listRuns = async (runsRoot) => {
|
|
|
44656
44593
|
const flowPath = join80(root, flowDir.name);
|
|
44657
44594
|
let runDirs;
|
|
44658
44595
|
try {
|
|
44659
|
-
runDirs = await
|
|
44596
|
+
runDirs = await fs43.readdir(flowPath, { withFileTypes: true });
|
|
44660
44597
|
} catch (cause) {
|
|
44661
44598
|
if (isErrnoException2(cause) && cause.code === "ENOENT") continue;
|
|
44662
44599
|
return Result.error(
|
|
@@ -44701,7 +44638,7 @@ var computeDirSize = async (dir) => {
|
|
|
44701
44638
|
let total = 0;
|
|
44702
44639
|
let entries;
|
|
44703
44640
|
try {
|
|
44704
|
-
entries = await
|
|
44641
|
+
entries = await fs43.readdir(dir, { withFileTypes: true });
|
|
44705
44642
|
} catch {
|
|
44706
44643
|
return 0;
|
|
44707
44644
|
}
|
|
@@ -44712,7 +44649,7 @@ var computeDirSize = async (dir) => {
|
|
|
44712
44649
|
continue;
|
|
44713
44650
|
}
|
|
44714
44651
|
try {
|
|
44715
|
-
const stat = await
|
|
44652
|
+
const stat = await fs43.lstat(entryPath);
|
|
44716
44653
|
if (stat.isFile()) total += stat.size;
|
|
44717
44654
|
} catch {
|
|
44718
44655
|
}
|
|
@@ -45033,7 +44970,7 @@ var performPrune = async (candidates) => {
|
|
|
45033
44970
|
let freedCount = 0;
|
|
45034
44971
|
for (const candidate of candidates) {
|
|
45035
44972
|
try {
|
|
45036
|
-
await
|
|
44973
|
+
await fs44.rm(String(candidate.path), { recursive: true, force: false });
|
|
45037
44974
|
freedBytes += candidate.sizeBytes;
|
|
45038
44975
|
freedCount += 1;
|
|
45039
44976
|
} catch (cause) {
|