ralphctl 0.17.0 → 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("");
|
|
@@ -15319,7 +15314,7 @@ var validateSignalsFile = async (outputDir, contract) => {
|
|
|
15319
15314
|
}
|
|
15320
15315
|
const wrapper = current;
|
|
15321
15316
|
const inner = wrapper.signals;
|
|
15322
|
-
const defaulted = defaultMissingTimestamps(inner);
|
|
15317
|
+
const defaulted = renameNarrativeBodyToText(defaultMissingTimestamps(inner));
|
|
15323
15318
|
const parsed = contract.signalsSchema.safeParse(defaulted);
|
|
15324
15319
|
if (!parsed.success) {
|
|
15325
15320
|
return Result.error(
|
|
@@ -15337,6 +15332,18 @@ var describeJsonError = (cause) => {
|
|
|
15337
15332
|
if (cause instanceof Error) return cause.message;
|
|
15338
15333
|
return String(cause);
|
|
15339
15334
|
};
|
|
15335
|
+
var NARRATIVE_SIGNAL_KINDS = /* @__PURE__ */ new Set(["learning", "note", "decision"]);
|
|
15336
|
+
var renameNarrativeBodyToText = (inner) => {
|
|
15337
|
+
if (!Array.isArray(inner)) return inner;
|
|
15338
|
+
return inner.map((sig) => {
|
|
15339
|
+
if (typeof sig !== "object" || sig === null) return sig;
|
|
15340
|
+
const s = sig;
|
|
15341
|
+
if (typeof s.type !== "string" || !NARRATIVE_SIGNAL_KINDS.has(s.type)) return sig;
|
|
15342
|
+
if (typeof s.text === "string" || typeof s.body !== "string") return sig;
|
|
15343
|
+
const { body, ...rest } = s;
|
|
15344
|
+
return { ...rest, text: body };
|
|
15345
|
+
});
|
|
15346
|
+
};
|
|
15340
15347
|
var defaultMissingTimestamps = (inner) => {
|
|
15341
15348
|
if (!Array.isArray(inner)) return inner;
|
|
15342
15349
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -15427,6 +15434,16 @@ var refineExampleSignals = [
|
|
|
15427
15434
|
type: "decision",
|
|
15428
15435
|
text: "Scoped export to CSV only for v1; XLSX deferred to a follow-up ticket.",
|
|
15429
15436
|
timestamp: EXAMPLE_TS
|
|
15437
|
+
},
|
|
15438
|
+
{
|
|
15439
|
+
type: "learning",
|
|
15440
|
+
text: "The export path already streams; the ticket must not assume an in-memory buffer.",
|
|
15441
|
+
timestamp: EXAMPLE_TS
|
|
15442
|
+
},
|
|
15443
|
+
{
|
|
15444
|
+
type: "note",
|
|
15445
|
+
text: "The user ruled column ordering out of scope \u2014 captured as a deferred item rather than an AC.",
|
|
15446
|
+
timestamp: EXAMPLE_TS
|
|
15430
15447
|
}
|
|
15431
15448
|
];
|
|
15432
15449
|
var refineOutputContract = {
|
|
@@ -15654,11 +15671,29 @@ var assertTemplateKeysFilled = (rendered, template, partialBodies, values, where
|
|
|
15654
15671
|
};
|
|
15655
15672
|
|
|
15656
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
|
+
};
|
|
15657
15680
|
var buildPrompt = async (loader, def, input) => {
|
|
15658
15681
|
const template = await loader.load(def.templateName);
|
|
15659
15682
|
if (!template.ok) return Result.error(template.error);
|
|
15660
15683
|
const values = {};
|
|
15661
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
|
+
}
|
|
15662
15697
|
if (def.partials !== void 0) {
|
|
15663
15698
|
for (const [placeholder, name] of Object.entries(def.partials)) {
|
|
15664
15699
|
const partial = await loader.load(name);
|
|
@@ -16840,6 +16875,21 @@ var planExampleSignals = [
|
|
|
16840
16875
|
type: "task-plan",
|
|
16841
16876
|
tasksJson: '[{"name":"Wire export endpoint","ticketRef":"<ticket-uuid>","projectPath":"/abs/repo","steps":["..."],"verificationCriteria":["..."]}]',
|
|
16842
16877
|
timestamp: EXAMPLE_TS2
|
|
16878
|
+
},
|
|
16879
|
+
{
|
|
16880
|
+
type: "decision",
|
|
16881
|
+
text: "Split the export work into endpoint + serializer tasks so the shared file is owned by exactly one task.",
|
|
16882
|
+
timestamp: EXAMPLE_TS2
|
|
16883
|
+
},
|
|
16884
|
+
{
|
|
16885
|
+
type: "learning",
|
|
16886
|
+
text: "The repo runs its integration suite from a separate config; the root test command does not cover it.",
|
|
16887
|
+
timestamp: EXAMPLE_TS2
|
|
16888
|
+
},
|
|
16889
|
+
{
|
|
16890
|
+
type: "note",
|
|
16891
|
+
text: "Two approved tickets touch the same route file; sequenced them via blockedBy rather than splitting the file.",
|
|
16892
|
+
timestamp: EXAMPLE_TS2
|
|
16843
16893
|
}
|
|
16844
16894
|
];
|
|
16845
16895
|
var planOutputContract = {
|
|
@@ -18509,7 +18559,6 @@ var branchPreflightLeaf = (deps, opts, name = "branch-preflight") => leaf(name,
|
|
|
18509
18559
|
});
|
|
18510
18560
|
|
|
18511
18561
|
// src/application/flows/implement/leaves/build-task-workspace.ts
|
|
18512
|
-
import { promises as fs21 } from "fs";
|
|
18513
18562
|
import { join as join44 } from "path";
|
|
18514
18563
|
|
|
18515
18564
|
// src/business/task/compose-criteria-history.ts
|
|
@@ -18770,6 +18819,16 @@ var generatorExampleSignals = [
|
|
|
18770
18819
|
appliesTo: "config / io layer",
|
|
18771
18820
|
timestamp: EXAMPLE_TS3
|
|
18772
18821
|
},
|
|
18822
|
+
{
|
|
18823
|
+
type: "decision",
|
|
18824
|
+
text: "Centralised the parse in one helper rather than patching both call sites, to avoid future drift.",
|
|
18825
|
+
timestamp: EXAMPLE_TS3
|
|
18826
|
+
},
|
|
18827
|
+
{
|
|
18828
|
+
type: "note",
|
|
18829
|
+
text: "Left the unrelated typo at src/baz.ts:8 alone \u2014 out of scope for this task.",
|
|
18830
|
+
timestamp: EXAMPLE_TS3
|
|
18831
|
+
},
|
|
18773
18832
|
{ type: "task-verified", output: "$ <project test command>\n... 42 passed", timestamp: EXAMPLE_TS3 },
|
|
18774
18833
|
{
|
|
18775
18834
|
type: COMMIT_MESSAGE_KIND,
|
|
@@ -18802,22 +18861,6 @@ ${body}
|
|
|
18802
18861
|
};
|
|
18803
18862
|
|
|
18804
18863
|
// src/application/flows/implement/leaves/build-task-workspace.ts
|
|
18805
|
-
var writeOrError = async (path2, content) => {
|
|
18806
|
-
try {
|
|
18807
|
-
await fs21.mkdir(path2.slice(0, path2.lastIndexOf("/")), { recursive: true });
|
|
18808
|
-
await fs21.writeFile(path2, content, "utf8");
|
|
18809
|
-
return Result.ok(void 0);
|
|
18810
|
-
} catch (cause) {
|
|
18811
|
-
return Result.error(
|
|
18812
|
-
new StorageError({
|
|
18813
|
-
subCode: "io",
|
|
18814
|
-
message: `failed to write task workspace file: ${path2}`,
|
|
18815
|
-
path: path2,
|
|
18816
|
-
cause
|
|
18817
|
-
})
|
|
18818
|
-
);
|
|
18819
|
-
}
|
|
18820
|
-
};
|
|
18821
18864
|
var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-${String(taskId)}`, {
|
|
18822
18865
|
useCase: {
|
|
18823
18866
|
execute: async (input) => {
|
|
@@ -18826,6 +18869,10 @@ var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-
|
|
|
18826
18869
|
const previewOutputDir = AbsolutePath.parse(join44(workspaceRoot, "rounds", "1", "generator"));
|
|
18827
18870
|
if (!previewOutputDir.ok) return Result.error(previewOutputDir.error);
|
|
18828
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);
|
|
18829
18876
|
const prompt = await buildImplementPrompt(deps.templateLoader, {
|
|
18830
18877
|
task: input.task,
|
|
18831
18878
|
projectPath: String(opts.cwd),
|
|
@@ -18836,9 +18883,9 @@ var buildTaskWorkspaceLeaf = (deps, opts, taskId) => leaf(`build-task-workspace-
|
|
|
18836
18883
|
...opts.verifyScript !== void 0 ? { verifyScript: opts.verifyScript } : {}
|
|
18837
18884
|
});
|
|
18838
18885
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
18839
|
-
const wrotePrompt = await
|
|
18886
|
+
const wrotePrompt = await deps.writeFile(parsedPromptPath.value, String(prompt.value));
|
|
18840
18887
|
if (!wrotePrompt.ok) return Result.error(wrotePrompt.error);
|
|
18841
|
-
const wroteContract = await
|
|
18888
|
+
const wroteContract = await deps.writeFile(parsedContractPath.value, renderContractMd(input.task));
|
|
18842
18889
|
if (!wroteContract.ok) return Result.error(wroteContract.error);
|
|
18843
18890
|
log.debug("task workspace built", { taskId: input.task.id, workspaceRoot });
|
|
18844
18891
|
const parsedRoot = AbsolutePath.parse(workspaceRoot);
|
|
@@ -19259,6 +19306,7 @@ var markTaskDone = (task, now) => {
|
|
|
19259
19306
|
...guard2.value.escalatedFromModel !== void 0 ? { escalatedFromModel: guard2.value.escalatedFromModel } : {},
|
|
19260
19307
|
...guard2.value.escalatedToModel !== void 0 ? { escalatedToModel: guard2.value.escalatedToModel } : {},
|
|
19261
19308
|
...guard2.value.escalatedToEffort !== void 0 ? { escalatedToEffort: guard2.value.escalatedToEffort } : {},
|
|
19309
|
+
...guard2.value.escalatedToEvaluatorEffort !== void 0 ? { escalatedToEvaluatorEffort: guard2.value.escalatedToEvaluatorEffort } : {},
|
|
19262
19310
|
status: "done",
|
|
19263
19311
|
attempts,
|
|
19264
19312
|
finalAttemptN: verified.n
|
|
@@ -19303,6 +19351,11 @@ var recordTaskEffortEscalation = (task, toEffort) => {
|
|
|
19303
19351
|
if (!to.ok) return Result.error(to.error);
|
|
19304
19352
|
return Result.ok({ ...task, escalatedToEffort: to.value });
|
|
19305
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
|
+
};
|
|
19306
19359
|
|
|
19307
19360
|
// src/business/task/escalation-policy.ts
|
|
19308
19361
|
var BANNER_SHOW_EVENT = "banner-show";
|
|
@@ -19326,11 +19379,14 @@ var decideEscalation = (props) => {
|
|
|
19326
19379
|
if (nudgedAtTop) return { kind: "topped-out", model: props.generatorModel };
|
|
19327
19380
|
const effortTarget = nextEffortRung(props.generatorProvider, props.generatorModel, props.generatorEffort);
|
|
19328
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 } } : {};
|
|
19329
19384
|
return {
|
|
19330
19385
|
kind: "escalate-effort",
|
|
19331
19386
|
model: props.generatorModel,
|
|
19332
19387
|
from: props.generatorEffort ?? "default",
|
|
19333
|
-
to: effortTarget
|
|
19388
|
+
to: effortTarget,
|
|
19389
|
+
...evaluatorCarry
|
|
19334
19390
|
};
|
|
19335
19391
|
}
|
|
19336
19392
|
return { kind: "nudge", currentModel: props.generatorModel };
|
|
@@ -19402,11 +19458,12 @@ var applyEscalation = (props) => {
|
|
|
19402
19458
|
return Result.ok({ task: stamped.value });
|
|
19403
19459
|
}
|
|
19404
19460
|
case "escalate-effort": {
|
|
19461
|
+
const evaluatorClause = decision.evaluator !== void 0 ? `; evaluator effort: ${decision.evaluator.from} \u2192 ${decision.evaluator.to}` : "";
|
|
19405
19462
|
eventBus.publish({
|
|
19406
19463
|
type: BANNER_SHOW_EVENT,
|
|
19407
19464
|
id: bannerId,
|
|
19408
19465
|
tier: "info",
|
|
19409
|
-
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}`,
|
|
19410
19467
|
cause,
|
|
19411
19468
|
at: now
|
|
19412
19469
|
});
|
|
@@ -19415,7 +19472,8 @@ var applyEscalation = (props) => {
|
|
|
19415
19472
|
model: decision.model,
|
|
19416
19473
|
from: decision.from,
|
|
19417
19474
|
to: decision.to,
|
|
19418
|
-
reason: trigger
|
|
19475
|
+
reason: trigger,
|
|
19476
|
+
...decision.evaluator !== void 0 ? { evaluatorFrom: decision.evaluator.from, evaluatorTo: decision.evaluator.to } : {}
|
|
19419
19477
|
});
|
|
19420
19478
|
return Result.ok({ task });
|
|
19421
19479
|
}
|
|
@@ -19516,7 +19574,12 @@ var resolveEscalatableRemedy = (exit, props, cfg) => {
|
|
|
19516
19574
|
// provider (or the row carries no effort dimension the caller could resolve) the policy never
|
|
19517
19575
|
// returns `escalate-effort` and behaves exactly as before.
|
|
19518
19576
|
generatorProvider: props.generatorProvider,
|
|
19519
|
-
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
|
|
19520
19583
|
});
|
|
19521
19584
|
const applied = applyEscalation({
|
|
19522
19585
|
task: props.task,
|
|
@@ -19536,6 +19599,11 @@ var resolveEscalatableRemedy = (exit, props, cfg) => {
|
|
|
19536
19599
|
const stamped = recordTaskEffortEscalation(task, decision.to);
|
|
19537
19600
|
if (!stamped.ok) return Result.error(stamped.error);
|
|
19538
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
|
+
}
|
|
19539
19607
|
}
|
|
19540
19608
|
const shouldFailAttempt = decision.kind === "escalate" || decision.kind === "escalate-effort" || decision.kind === "nudge";
|
|
19541
19609
|
return Result.ok({
|
|
@@ -19632,7 +19700,10 @@ var finalizeGenEvalLeaf = (deps, taskId) => leaf(`finalize-gen-eval-${String(tas
|
|
|
19632
19700
|
clock: deps.clock,
|
|
19633
19701
|
generatorModel: input.generatorModel,
|
|
19634
19702
|
...input.generatorProvider !== void 0 ? { generatorProvider: input.generatorProvider } : {},
|
|
19635
|
-
...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 } : {}
|
|
19636
19707
|
})
|
|
19637
19708
|
},
|
|
19638
19709
|
input: (ctx) => {
|
|
@@ -19654,6 +19725,7 @@ var finalizeGenEvalLeaf = (deps, taskId) => leaf(`finalize-gen-eval-${String(tas
|
|
|
19654
19725
|
}
|
|
19655
19726
|
const generatorModel = ctx.currentTask.escalatedToModel ?? deps.configuredGeneratorModel;
|
|
19656
19727
|
const generatorEffort = ctx.currentTask.escalatedToEffort ?? deps.configuredGeneratorEffort;
|
|
19728
|
+
const evaluatorEffort = ctx.currentTask.escalatedToEvaluatorEffort ?? deps.configuredEvaluatorEffort;
|
|
19657
19729
|
return {
|
|
19658
19730
|
task: ctx.currentTask,
|
|
19659
19731
|
sprintId: ctx.sprintId,
|
|
@@ -19661,7 +19733,10 @@ var finalizeGenEvalLeaf = (deps, taskId) => leaf(`finalize-gen-eval-${String(tas
|
|
|
19661
19733
|
turnsUsed: ctx.genEvalTurn ?? 0,
|
|
19662
19734
|
generatorModel,
|
|
19663
19735
|
...deps.configuredGeneratorProvider !== void 0 ? { generatorProvider: deps.configuredGeneratorProvider } : {},
|
|
19664
|
-
...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 } : {}
|
|
19665
19740
|
};
|
|
19666
19741
|
},
|
|
19667
19742
|
output: (ctx, out) => {
|
|
@@ -19799,7 +19874,7 @@ var computePlateauVerdict = (priorTurns, current, options) => {
|
|
|
19799
19874
|
|
|
19800
19875
|
// src/application/flows/implement/leaves/evaluator.ts
|
|
19801
19876
|
import { dirname as dirname17, join as join46 } from "path";
|
|
19802
|
-
import { promises as
|
|
19877
|
+
import { promises as fs22 } from "fs";
|
|
19803
19878
|
|
|
19804
19879
|
// src/business/task/turn-error-policy.ts
|
|
19805
19880
|
var isRecoverableTurnError = (err) => err.code !== ErrorCode.Aborted && err.code !== ErrorCode.RateLimit;
|
|
@@ -20137,7 +20212,7 @@ var buildEvaluateContinuationPrompt = async (deps, input) => buildPrompt(deps, e
|
|
|
20137
20212
|
|
|
20138
20213
|
// src/integration/ai/contract/_engine/corrective-retry.ts
|
|
20139
20214
|
import { ZodError } from "zod";
|
|
20140
|
-
var isCorrectableContractError = (err) => err
|
|
20215
|
+
var isCorrectableContractError = (err) => err instanceof InvalidStateError || err instanceof ParseError;
|
|
20141
20216
|
var MAX_ENUMERATED_ISSUES = 10;
|
|
20142
20217
|
var correctiveBody = (err, signalsPath, selfContainedContext) => {
|
|
20143
20218
|
const lines = [];
|
|
@@ -20208,6 +20283,14 @@ var validateSignalsFileWithCorrectiveRetry = async (deps, contract) => {
|
|
|
20208
20283
|
return Result.ok({ signals: revalidated.value, nudgeCount: attempt });
|
|
20209
20284
|
}
|
|
20210
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
|
+
}
|
|
20211
20294
|
}
|
|
20212
20295
|
log.warn("corrective retries exhausted \u2014 self-blocking", {
|
|
20213
20296
|
outputDir: String(deps.outputDir),
|
|
@@ -20454,6 +20537,16 @@ var evaluatorExampleSignals = [
|
|
|
20454
20537
|
],
|
|
20455
20538
|
critique: "Correctness: add edge-case handling for empty input at src/foo.ts:23.",
|
|
20456
20539
|
timestamp: EXAMPLE_TS4
|
|
20540
|
+
},
|
|
20541
|
+
{
|
|
20542
|
+
type: "learning",
|
|
20543
|
+
text: "The suite needs a built fixture; a cold checkout fails the C1 command for reasons unrelated to the task.",
|
|
20544
|
+
timestamp: EXAMPLE_TS4
|
|
20545
|
+
},
|
|
20546
|
+
{
|
|
20547
|
+
type: "note",
|
|
20548
|
+
text: "C2 was checked by reading the call site \u2014 the criterion is manual, so no command was run.",
|
|
20549
|
+
timestamp: EXAMPLE_TS4
|
|
20457
20550
|
}
|
|
20458
20551
|
];
|
|
20459
20552
|
var evaluatorOutputContract = {
|
|
@@ -20469,7 +20562,7 @@ var evaluatorOutputContract = {
|
|
|
20469
20562
|
};
|
|
20470
20563
|
|
|
20471
20564
|
// src/application/flows/implement/leaves/round-artifacts.ts
|
|
20472
|
-
import { promises as
|
|
20565
|
+
import { promises as fs21 } from "fs";
|
|
20473
20566
|
import { join as join45 } from "path";
|
|
20474
20567
|
var nextRoundNum = async (workspaceRoot) => {
|
|
20475
20568
|
const entries = await listDir(join45(String(workspaceRoot), "rounds"));
|
|
@@ -20488,7 +20581,7 @@ var readRoundSessionId = async (workspaceRoot, round, role) => {
|
|
|
20488
20581
|
const path2 = join45(String(workspaceRoot), "rounds", String(round), role, "session-id.txt");
|
|
20489
20582
|
let content;
|
|
20490
20583
|
try {
|
|
20491
|
-
content = await
|
|
20584
|
+
content = await fs21.readFile(path2, "utf8");
|
|
20492
20585
|
} catch {
|
|
20493
20586
|
return void 0;
|
|
20494
20587
|
}
|
|
@@ -20560,7 +20653,7 @@ var positiveCountCarry = (field, delta, prior) => delta > 0 ? { [field]: (prior
|
|
|
20560
20653
|
// src/application/flows/implement/leaves/evaluator.ts
|
|
20561
20654
|
var readCappedProgress = async (path2, currentTaskId, model) => {
|
|
20562
20655
|
try {
|
|
20563
|
-
return capProgressBody(await
|
|
20656
|
+
return capProgressBody(await fs22.readFile(path2, "utf8"), {
|
|
20564
20657
|
currentTaskId,
|
|
20565
20658
|
recentBudgetTokens: progressCapBudgetForModel(model)
|
|
20566
20659
|
});
|
|
@@ -20619,7 +20712,7 @@ var makeEvaluatorReinvoke = (deps, args) => async (corrective, attempt) => {
|
|
|
20619
20712
|
args.signalsFile,
|
|
20620
20713
|
"evaluator",
|
|
20621
20714
|
resume,
|
|
20622
|
-
|
|
20715
|
+
args.effectiveEffort,
|
|
20623
20716
|
args.signal,
|
|
20624
20717
|
bodyFile.ok ? bodyFile.value : void 0
|
|
20625
20718
|
)
|
|
@@ -20647,7 +20740,8 @@ var makeEvaluatorCallEvaluate = (deps, args) => async (task) => {
|
|
|
20647
20740
|
const initialBodyFile = AbsolutePath.parse(
|
|
20648
20741
|
roundBodyPath(args.input.workspaceRoot, args.input.roundNum, "evaluator")
|
|
20649
20742
|
);
|
|
20650
|
-
const
|
|
20743
|
+
const effectiveEffort = task.escalatedToEvaluatorEffort ?? deps.effort;
|
|
20744
|
+
const spawn2 = await deps.provider.generate(
|
|
20651
20745
|
implementSession(
|
|
20652
20746
|
args.input.workspaceRoot,
|
|
20653
20747
|
deps.cwd,
|
|
@@ -20657,12 +20751,12 @@ var makeEvaluatorCallEvaluate = (deps, args) => async (task) => {
|
|
|
20657
20751
|
args.signalsFile,
|
|
20658
20752
|
"evaluator",
|
|
20659
20753
|
args.input.priorEvaluatorSessionId,
|
|
20660
|
-
|
|
20754
|
+
effectiveEffort,
|
|
20661
20755
|
args.signal,
|
|
20662
20756
|
initialBodyFile.ok ? initialBodyFile.value : void 0
|
|
20663
20757
|
)
|
|
20664
20758
|
);
|
|
20665
|
-
if (!
|
|
20759
|
+
if (!spawn2.ok) return Result.error(spawn2.error);
|
|
20666
20760
|
const validated = await validateSignalsFileWithCorrectiveRetry(
|
|
20667
20761
|
{
|
|
20668
20762
|
outputDir: args.outputDir,
|
|
@@ -20684,6 +20778,7 @@ var makeEvaluatorCallEvaluate = (deps, args) => async (task) => {
|
|
|
20684
20778
|
workspaceRoot: args.input.workspaceRoot,
|
|
20685
20779
|
roundNum: args.input.roundNum,
|
|
20686
20780
|
signalsFile: args.signalsFile,
|
|
20781
|
+
effectiveEffort,
|
|
20687
20782
|
priorEvaluatorSessionId: args.input.priorEvaluatorSessionId,
|
|
20688
20783
|
signal: args.signal
|
|
20689
20784
|
})
|
|
@@ -20803,7 +20898,7 @@ var evaluatorLeaf = (deps, taskId) => leaf(`evaluator-${String(taskId)}`, {
|
|
|
20803
20898
|
|
|
20804
20899
|
// src/application/flows/implement/leaves/generator.ts
|
|
20805
20900
|
import { dirname as dirname18, join as join47 } from "path";
|
|
20806
|
-
import { promises as
|
|
20901
|
+
import { promises as fs24 } from "fs";
|
|
20807
20902
|
|
|
20808
20903
|
// src/business/task/run-generator-turn.ts
|
|
20809
20904
|
var findTaskBlocked = (signals) => signals.find((s) => s.type === "task-blocked")?.reason;
|
|
@@ -20970,7 +21065,8 @@ var composeDimensionTrajectory = (input) => {
|
|
|
20970
21065
|
const priorFailed = failedDimensions(prior.evaluation);
|
|
20971
21066
|
const fixed = sortedLimited([...priorFailed].filter((d) => !latestFailed.has(d)));
|
|
20972
21067
|
const newlyFailing = sortedLimited([...latestFailed].filter((d) => !priorFailed.has(d)));
|
|
20973
|
-
const
|
|
21068
|
+
const stillFailingAll = [...latestFailed].filter((d) => priorFailed.has(d));
|
|
21069
|
+
const stillFailing = sortedLimited(stillFailingAll);
|
|
20974
21070
|
const lines = [];
|
|
20975
21071
|
for (const d of fixed) {
|
|
20976
21072
|
lines.push(`- ${d}: fixed since last round \u2014 keep it passing.`);
|
|
@@ -20984,7 +21080,7 @@ var composeDimensionTrajectory = (input) => {
|
|
|
20984
21080
|
}
|
|
20985
21081
|
if (lines.length === 0) return "";
|
|
20986
21082
|
const threshold = Math.max(2, Math.trunc(input.plateauThreshold));
|
|
20987
|
-
const longestStall =
|
|
21083
|
+
const longestStall = stillFailingAll.reduce((max, d) => Math.max(max, consecutiveFailing(history, d)), 0);
|
|
20988
21084
|
const pressure = longestStall >= threshold - 1 ? [
|
|
20989
21085
|
"",
|
|
20990
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.`
|
|
@@ -21103,7 +21199,7 @@ var formatRetryFeedback = (task, logTail) => {
|
|
|
21103
21199
|
};
|
|
21104
21200
|
|
|
21105
21201
|
// src/integration/io/read-log-tail.ts
|
|
21106
|
-
import { promises as
|
|
21202
|
+
import { promises as fs23 } from "fs";
|
|
21107
21203
|
var DEFAULT_LOG_TAIL_BYTES = 4096;
|
|
21108
21204
|
var createFsLogTailReader = () => {
|
|
21109
21205
|
return async (path2, maxBytes = DEFAULT_LOG_TAIL_BYTES) => {
|
|
@@ -21111,7 +21207,7 @@ var createFsLogTailReader = () => {
|
|
|
21111
21207
|
if (cap === 0) return "";
|
|
21112
21208
|
let handle;
|
|
21113
21209
|
try {
|
|
21114
|
-
handle = await
|
|
21210
|
+
handle = await fs23.open(String(path2), "r");
|
|
21115
21211
|
const stat = await handle.stat();
|
|
21116
21212
|
const size = stat.size;
|
|
21117
21213
|
if (size <= 0) return "";
|
|
@@ -21142,7 +21238,7 @@ var countTurnActionKinds = (out) => {
|
|
|
21142
21238
|
};
|
|
21143
21239
|
var readCappedProgress2 = async (path2, currentTaskId, model) => {
|
|
21144
21240
|
try {
|
|
21145
|
-
return capProgressBody(await
|
|
21241
|
+
return capProgressBody(await fs24.readFile(path2, "utf8"), {
|
|
21146
21242
|
currentTaskId,
|
|
21147
21243
|
recentBudgetTokens: progressCapBudgetForModel(model)
|
|
21148
21244
|
});
|
|
@@ -21307,7 +21403,7 @@ var makeGeneratorCallImplement = (deps, taskId, args) => async (task) => {
|
|
|
21307
21403
|
const effectiveModel = task.escalatedToModel ?? deps.model;
|
|
21308
21404
|
const effectiveEffort = task.escalatedToEffort ?? deps.effort;
|
|
21309
21405
|
const initialBodyFile = AbsolutePath.parse(roundBodyPath(args.input.workspaceRoot, args.roundNum, "generator"));
|
|
21310
|
-
const
|
|
21406
|
+
const spawn2 = await deps.provider.generate(
|
|
21311
21407
|
implementSession(
|
|
21312
21408
|
args.input.workspaceRoot,
|
|
21313
21409
|
deps.cwd,
|
|
@@ -21322,7 +21418,7 @@ var makeGeneratorCallImplement = (deps, taskId, args) => async (task) => {
|
|
|
21322
21418
|
initialBodyFile.ok ? initialBodyFile.value : void 0
|
|
21323
21419
|
)
|
|
21324
21420
|
);
|
|
21325
|
-
if (!
|
|
21421
|
+
if (!spawn2.ok) return Result.error(spawn2.error);
|
|
21326
21422
|
const validated = await validateSignalsFileWithCorrectiveRetry(
|
|
21327
21423
|
{
|
|
21328
21424
|
outputDir: args.outputDir,
|
|
@@ -22668,13 +22764,13 @@ var quarantineRetryDiffLeaf = (deps, opts, taskId) => leaf(`quarantine-retry-dif
|
|
|
22668
22764
|
var isRedVerifyRetry = (ctx) => ctx.lastShouldFailAttempt === true && ctx.lastBlockReason !== void 0;
|
|
22669
22765
|
|
|
22670
22766
|
// src/application/flows/implement/leaves/progress-journal.ts
|
|
22671
|
-
import { promises as
|
|
22767
|
+
import { promises as fs25 } from "fs";
|
|
22672
22768
|
|
|
22673
22769
|
// src/business/sprint/render-sprint-state-header.ts
|
|
22674
|
-
var
|
|
22770
|
+
var EM_DASH3 = "\u2014";
|
|
22675
22771
|
var cell = (text) => neutralizeProseHeadings(sanitizeInline(text));
|
|
22676
22772
|
var isStale = (task) => task.status === "in_progress" && task.attemptCount > 0;
|
|
22677
|
-
var orEmDash = (value) => value !== null && value.length > 0 ? value :
|
|
22773
|
+
var orEmDash = (value) => value !== null && value.length > 0 ? value : EM_DASH3;
|
|
22678
22774
|
var renderIdentity = (input) => [
|
|
22679
22775
|
`# Sprint: ${cell(input.sprintName)}`,
|
|
22680
22776
|
"",
|
|
@@ -22694,7 +22790,7 @@ var renderBlockers = (tasks) => {
|
|
|
22694
22790
|
const blockers = tasks.filter((t) => t.status === "blocked");
|
|
22695
22791
|
if (blockers.length === 0) return [];
|
|
22696
22792
|
const bullets = blockers.map((t) => {
|
|
22697
|
-
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;
|
|
22698
22794
|
return `- ${cell(t.name)}: ${reason}`;
|
|
22699
22795
|
});
|
|
22700
22796
|
return ["## Blockers", "", ...bullets, ""];
|
|
@@ -22707,7 +22803,7 @@ var renderStale = (tasks) => {
|
|
|
22707
22803
|
);
|
|
22708
22804
|
return ["## Stale tasks", "", ...bullets, ""];
|
|
22709
22805
|
};
|
|
22710
|
-
var passesCell = (task) => task.criteriaTotal === 0 ?
|
|
22806
|
+
var passesCell = (task) => task.criteriaTotal === 0 ? EM_DASH3 : `${String(task.criteriaPassed)}/${String(task.criteriaTotal)}`;
|
|
22711
22807
|
var renderTaskTable = (tasks) => {
|
|
22712
22808
|
if (tasks.length === 0) return ["## Tasks", "", "_No tasks planned yet._", ""];
|
|
22713
22809
|
const rows = tasks.map((t) => `| ${cell(t.name)} | ${t.status} | ${passesCell(t)} |`);
|
|
@@ -22845,7 +22941,7 @@ var buildAttemptSection = (input, totalRounds, clock) => {
|
|
|
22845
22941
|
};
|
|
22846
22942
|
var readExisting = async (path2) => {
|
|
22847
22943
|
try {
|
|
22848
|
-
return await
|
|
22944
|
+
return await fs25.readFile(String(path2), "utf8");
|
|
22849
22945
|
} catch {
|
|
22850
22946
|
return "";
|
|
22851
22947
|
}
|
|
@@ -23085,16 +23181,16 @@ ${output.slice(output.length - tail)}`;
|
|
|
23085
23181
|
};
|
|
23086
23182
|
|
|
23087
23183
|
// src/business/task/render-round-outcome.ts
|
|
23088
|
-
var
|
|
23184
|
+
var EM_DASH4 = "\u2014";
|
|
23089
23185
|
var renderRoundOutcome = (input) => {
|
|
23090
23186
|
const lines = [];
|
|
23091
23187
|
lines.push(`# Round ${String(input.roundN)} \xB7 attempt ${String(input.attemptN)}`);
|
|
23092
23188
|
lines.push("");
|
|
23093
|
-
lines.push(`- generator session: ${input.generatorSessionId ??
|
|
23094
|
-
lines.push(`- evaluator session: ${input.evaluatorSessionId ??
|
|
23095
|
-
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)}`);
|
|
23096
23192
|
lines.push(`- verdict: ${input.verdict}`);
|
|
23097
|
-
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}`);
|
|
23098
23194
|
const dimensionsBlock = renderDimensions(input.evaluation, input.attempt.evaluation);
|
|
23099
23195
|
if (dimensionsBlock !== void 0) {
|
|
23100
23196
|
lines.push("");
|
|
@@ -23166,25 +23262,6 @@ var formatFailedDimensions = (evaluation) => {
|
|
|
23166
23262
|
};
|
|
23167
23263
|
var SHA_DISPLAY_LENGTH2 = 7;
|
|
23168
23264
|
var shortSha = (sha) => sha.slice(0, SHA_DISPLAY_LENGTH2);
|
|
23169
|
-
var MS_PER_SECOND2 = 1e3;
|
|
23170
|
-
var MS_PER_MINUTE2 = 60 * MS_PER_SECOND2;
|
|
23171
|
-
var MS_PER_HOUR2 = 60 * MS_PER_MINUTE2;
|
|
23172
|
-
var formatDuration2 = (ms) => {
|
|
23173
|
-
if (ms === void 0) return EM_DASH3;
|
|
23174
|
-
if (ms < 0) return `${String(ms)}ms`;
|
|
23175
|
-
if (ms < MS_PER_SECOND2) return `${String(ms)}ms`;
|
|
23176
|
-
if (ms < MS_PER_MINUTE2) {
|
|
23177
|
-
return `${String(Math.floor(ms / MS_PER_SECOND2))}s`;
|
|
23178
|
-
}
|
|
23179
|
-
if (ms < MS_PER_HOUR2) {
|
|
23180
|
-
const minutes2 = Math.floor(ms / MS_PER_MINUTE2);
|
|
23181
|
-
const seconds = Math.floor(ms % MS_PER_MINUTE2 / MS_PER_SECOND2);
|
|
23182
|
-
return seconds > 0 ? `${String(minutes2)}m ${String(seconds)}s` : `${String(minutes2)}m`;
|
|
23183
|
-
}
|
|
23184
|
-
const hours = Math.floor(ms / MS_PER_HOUR2);
|
|
23185
|
-
const minutes = Math.floor(ms % MS_PER_HOUR2 / MS_PER_MINUTE2);
|
|
23186
|
-
return minutes > 0 ? `${String(hours)}h ${String(minutes)}m` : `${String(hours)}h`;
|
|
23187
|
-
};
|
|
23188
23265
|
|
|
23189
23266
|
// src/application/flows/implement/leaves/settle-attempt.ts
|
|
23190
23267
|
var settleAttemptLeaf = (deps, opts, taskId) => {
|
|
@@ -23594,7 +23671,7 @@ var createPerTaskSubchain = (deps, opts, task, repo, readConfig) => {
|
|
|
23594
23671
|
)
|
|
23595
23672
|
] : [],
|
|
23596
23673
|
buildTaskWorkspaceLeaf(
|
|
23597
|
-
{ templateLoader: deps.templateLoader, logger: deps.logger },
|
|
23674
|
+
{ templateLoader: deps.templateLoader, logger: deps.logger, writeFile: deps.writeFile },
|
|
23598
23675
|
{
|
|
23599
23676
|
sprintDir: opts.sprintDir,
|
|
23600
23677
|
cwd: repo.path,
|
|
@@ -23702,7 +23779,14 @@ var createPerTaskSubchain = (deps, opts, task, repo, readConfig) => {
|
|
|
23702
23779
|
// before spending the nudge. `providerId` is the resolved provider enum string;
|
|
23703
23780
|
// `nextEffortRung` skips gracefully for any non-effort provider or model.
|
|
23704
23781
|
configuredGeneratorProvider: opts.generator.providerId,
|
|
23705
|
-
...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 } : {}
|
|
23706
23790
|
},
|
|
23707
23791
|
taskId
|
|
23708
23792
|
),
|
|
@@ -24835,6 +24919,16 @@ var createPublishSignal = (eventBus, source, taskId) => (signal) => {
|
|
|
24835
24919
|
eventBus.publish({ type: "ai-signal", signal, source, ...taskId !== void 0 ? { taskId } : {} });
|
|
24836
24920
|
};
|
|
24837
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
|
+
|
|
24838
24932
|
// src/application/flows/implement/merge-wave.ts
|
|
24839
24933
|
var branchSettled = (outcome) => outcome.status === "completed" || outcome.error !== void 0;
|
|
24840
24934
|
var mergeImplementWave = (base, outcomes) => {
|
|
@@ -24845,13 +24939,7 @@ var mergeImplementWave = (base, outcomes) => {
|
|
|
24845
24939
|
}
|
|
24846
24940
|
const tasks = base.tasks?.map((t) => byId.get(t.id) ?? t);
|
|
24847
24941
|
return {
|
|
24848
|
-
|
|
24849
|
-
sprintId: base.sprintId,
|
|
24850
|
-
...base.sprint !== void 0 ? { sprint: base.sprint } : {},
|
|
24851
|
-
...base.execution !== void 0 ? { execution: base.execution } : {},
|
|
24852
|
-
...base.progressFile !== void 0 ? { progressFile: base.progressFile } : {},
|
|
24853
|
-
...base.setupVerifiedRepoIdsThisRun !== void 0 ? { setupVerifiedRepoIdsThisRun: base.setupVerifiedRepoIdsThisRun } : {},
|
|
24854
|
-
...base.priorLearnings !== void 0 ? { priorLearnings: base.priorLearnings } : {},
|
|
24942
|
+
...projectSprintScopedFields(base),
|
|
24855
24943
|
// task list → overlay
|
|
24856
24944
|
...tasks !== void 0 ? { tasks } : {}
|
|
24857
24945
|
// per-task + signal-accum classes intentionally omitted → undefined in the merged ctx.
|
|
@@ -24859,17 +24947,8 @@ var mergeImplementWave = (base, outcomes) => {
|
|
|
24859
24947
|
};
|
|
24860
24948
|
var forkCtx = (base, repo, worktreePath) => {
|
|
24861
24949
|
const ctx = {
|
|
24862
|
-
|
|
24863
|
-
|
|
24864
|
-
...base.sprint !== void 0 ? { sprint: base.sprint } : {},
|
|
24865
|
-
...base.execution !== void 0 ? { execution: base.execution } : {},
|
|
24866
|
-
...base.progressFile !== void 0 ? { progressFile: base.progressFile } : {},
|
|
24867
|
-
...base.tasks !== void 0 ? { tasks: base.tasks } : {},
|
|
24868
|
-
// run-scoped (like `execution`): the set of repos this launch's setup verified survives the
|
|
24869
|
-
// fork so a parallel branch's first pre-task-verify can also take the fresh-setup skip.
|
|
24870
|
-
...base.setupVerifiedRepoIdsThisRun !== void 0 ? { setupVerifiedRepoIdsThisRun: base.setupVerifiedRepoIdsThisRun } : {},
|
|
24871
|
-
// run-scoped cross-sprint memory: every branch's generator reads the same loaded ledger.
|
|
24872
|
-
...base.priorLearnings !== void 0 ? { priorLearnings: base.priorLearnings } : {}
|
|
24950
|
+
...projectSprintScopedFields(base),
|
|
24951
|
+
...base.tasks !== void 0 ? { tasks: base.tasks } : {}
|
|
24873
24952
|
// per-task + signal-accum classes cleared (omitted → undefined); `priorPostVerifyOutcome`
|
|
24874
24953
|
// dropped (accepted cost). `expectedBranch` is intentionally NOT set here — see the docstring:
|
|
24875
24954
|
// the branch element omits `branch-preflight`, so leaving it `undefined` is correct.
|
|
@@ -25719,7 +25798,7 @@ var launchImplement = async (ctx) => {
|
|
|
25719
25798
|
import { join as join60 } from "path";
|
|
25720
25799
|
|
|
25721
25800
|
// src/application/flows/review/leaves/ensure-feedback-file.ts
|
|
25722
|
-
import { promises as
|
|
25801
|
+
import { promises as fs26 } from "fs";
|
|
25723
25802
|
|
|
25724
25803
|
// src/business/feedback/md-parser.ts
|
|
25725
25804
|
var ROUND_HEADING_RE = /^##\s+Round\s+(\d+)\s*$/;
|
|
@@ -25794,12 +25873,12 @@ var ensureFeedbackFileLeaf = (feedbackFile) => leaf("ensure-feedback-file", {
|
|
|
25794
25873
|
useCase: {
|
|
25795
25874
|
execute: async (path2) => {
|
|
25796
25875
|
try {
|
|
25797
|
-
await
|
|
25876
|
+
await fs26.access(String(path2));
|
|
25798
25877
|
return Result.ok(void 0);
|
|
25799
25878
|
} catch {
|
|
25800
25879
|
}
|
|
25801
25880
|
try {
|
|
25802
|
-
await
|
|
25881
|
+
await fs26.writeFile(String(path2), TEMPLATE, { flag: "wx" });
|
|
25803
25882
|
return Result.ok(void 0);
|
|
25804
25883
|
} catch (cause) {
|
|
25805
25884
|
if (typeof cause === "object" && cause !== null && cause.code === "EEXIST") {
|
|
@@ -25821,7 +25900,7 @@ var ensureFeedbackFileLeaf = (feedbackFile) => leaf("ensure-feedback-file", {
|
|
|
25821
25900
|
});
|
|
25822
25901
|
|
|
25823
25902
|
// src/application/flows/review/leaves/review-round.ts
|
|
25824
|
-
import { promises as
|
|
25903
|
+
import { promises as fs27 } from "fs";
|
|
25825
25904
|
import { join as join56 } from "path";
|
|
25826
25905
|
|
|
25827
25906
|
// src/business/feedback/apply-feedback.ts
|
|
@@ -26004,7 +26083,7 @@ var reviewRoundOutputContract = {
|
|
|
26004
26083
|
var readProgressSnippet = async (path2) => {
|
|
26005
26084
|
if (path2 === void 0) return "_(no progress file)_";
|
|
26006
26085
|
try {
|
|
26007
|
-
const content = await
|
|
26086
|
+
const content = await fs27.readFile(String(path2), "utf8");
|
|
26008
26087
|
return content.length > 4e3 ? `${content.slice(0, 4e3)}
|
|
26009
26088
|
[truncated]` : content;
|
|
26010
26089
|
} catch {
|
|
@@ -26020,7 +26099,7 @@ ${renderEmptyRound(nextIndex)}${ROUND_SEPARATOR}
|
|
|
26020
26099
|
};
|
|
26021
26100
|
var deriveActiveRoundIndex = async (feedbackFile) => {
|
|
26022
26101
|
try {
|
|
26023
|
-
const raw = await
|
|
26102
|
+
const raw = await fs27.readFile(String(feedbackFile), "utf8");
|
|
26024
26103
|
const rounds = parseFeedbackMd(raw);
|
|
26025
26104
|
return rounds.at(-1)?.index ?? 1;
|
|
26026
26105
|
} catch {
|
|
@@ -26029,7 +26108,7 @@ var deriveActiveRoundIndex = async (feedbackFile) => {
|
|
|
26029
26108
|
};
|
|
26030
26109
|
var writeRoundBody = async (path2, body) => {
|
|
26031
26110
|
try {
|
|
26032
|
-
const content = await
|
|
26111
|
+
const content = await fs27.readFile(String(path2), "utf8");
|
|
26033
26112
|
const lastMarker = content.lastIndexOf(MARKER_COMMENT);
|
|
26034
26113
|
if (lastMarker === -1) {
|
|
26035
26114
|
return Result.error(
|
|
@@ -26069,7 +26148,7 @@ var allocateRoundPaths = (reviewRoot, roundIndex) => {
|
|
|
26069
26148
|
};
|
|
26070
26149
|
var ensureRoundDir = async (dir) => {
|
|
26071
26150
|
try {
|
|
26072
|
-
await
|
|
26151
|
+
await fs27.mkdir(String(dir), { recursive: true });
|
|
26073
26152
|
return Result.ok(void 0);
|
|
26074
26153
|
} catch (cause) {
|
|
26075
26154
|
return Result.error(
|
|
@@ -26104,7 +26183,7 @@ var reviewRoundLeaf = (deps, opts) => leaf(LEAF_NAME3, {
|
|
|
26104
26183
|
if (!wrote.ok) return Result.error(wrote.error);
|
|
26105
26184
|
return Result.ok(void 0);
|
|
26106
26185
|
},
|
|
26107
|
-
readFeedbackFile: () =>
|
|
26186
|
+
readFeedbackFile: () => fs27.readFile(String(input.feedbackFile), "utf8"),
|
|
26108
26187
|
readProgressSnippet: () => readProgressSnippet(input.progressFile),
|
|
26109
26188
|
buildPrompt: async (params) => {
|
|
26110
26189
|
const outputContractSection = renderContractSectionFor(reviewRoundOutputContract, paths.value.outputDir);
|
|
@@ -26132,7 +26211,7 @@ var reviewRoundLeaf = (deps, opts) => leaf(LEAF_NAME3, {
|
|
|
26132
26211
|
const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt));
|
|
26133
26212
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
26134
26213
|
const chainSessionId = currentSessionId();
|
|
26135
|
-
const
|
|
26214
|
+
const spawn2 = await deps.provider.generate({
|
|
26136
26215
|
prompt,
|
|
26137
26216
|
cwd: paths.value.outputDir,
|
|
26138
26217
|
additionalRoots: opts.additionalRoots,
|
|
@@ -26145,7 +26224,7 @@ var reviewRoundLeaf = (deps, opts) => leaf(LEAF_NAME3, {
|
|
|
26145
26224
|
// the provider's SIGTERM ladder instead of letting it run to completion.
|
|
26146
26225
|
...signal !== void 0 ? { abortSignal: signal } : {}
|
|
26147
26226
|
});
|
|
26148
|
-
if (!
|
|
26227
|
+
if (!spawn2.ok) return Result.error(spawn2.error);
|
|
26149
26228
|
const validated = await validateSignalsFile(paths.value.outputDir, reviewRoundOutputContract);
|
|
26150
26229
|
if (!validated.ok) return Result.error(validated.error);
|
|
26151
26230
|
for (const sig of validated.value) deps.publishSignal(sig);
|
|
@@ -26375,7 +26454,7 @@ var stampPass = (collected, accepted, retired, stampTime, path2) => {
|
|
|
26375
26454
|
var isLive = (record) => record.retiredAt === void 0 || record.retiredAt === null;
|
|
26376
26455
|
|
|
26377
26456
|
// src/application/flows/_shared/memory/distill-propose.ts
|
|
26378
|
-
import { promises as
|
|
26457
|
+
import { promises as fs28 } from "fs";
|
|
26379
26458
|
import { join as join58 } from "path";
|
|
26380
26459
|
|
|
26381
26460
|
// src/integration/ai/prompts/distill-learnings/definition.ts
|
|
@@ -26487,7 +26566,7 @@ var distillProposeUseCase = async (deps, tool, input, signal) => {
|
|
|
26487
26566
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
26488
26567
|
const toolDir = join58(String(deps.distillRoot), tool);
|
|
26489
26568
|
try {
|
|
26490
|
-
await
|
|
26569
|
+
await fs28.mkdir(toolDir, { recursive: true });
|
|
26491
26570
|
} catch (cause) {
|
|
26492
26571
|
return Result.error(
|
|
26493
26572
|
new StorageError({
|
|
@@ -26541,11 +26620,11 @@ var renderProjectTooling = (repository) => {
|
|
|
26541
26620
|
const lines = [];
|
|
26542
26621
|
if (repository.setupScript !== void 0) lines.push(`- Setup: ${repository.setupScript}`);
|
|
26543
26622
|
if (repository.verifyScript !== void 0) lines.push(`- Verify: ${repository.verifyScript}`);
|
|
26544
|
-
return lines.length > 0 ? lines.join("\n") :
|
|
26623
|
+
return renderProjectToolingSection(lines.length > 0 ? lines.join("\n") : void 0);
|
|
26545
26624
|
};
|
|
26546
26625
|
var safeReadText = async (path2) => {
|
|
26547
26626
|
try {
|
|
26548
|
-
return await
|
|
26627
|
+
return await fs28.readFile(path2, "utf8");
|
|
26549
26628
|
} catch {
|
|
26550
26629
|
return void 0;
|
|
26551
26630
|
}
|
|
@@ -26608,7 +26687,7 @@ var distillConfirmLeaf = (deps, tool) => leaf(`distill-confirm-${tool}`, {
|
|
|
26608
26687
|
});
|
|
26609
26688
|
|
|
26610
26689
|
// src/application/flows/_shared/memory/distill-write.ts
|
|
26611
|
-
import { promises as
|
|
26690
|
+
import { promises as fs29 } from "fs";
|
|
26612
26691
|
var distillWriteUseCase = async (deps, tool, input) => {
|
|
26613
26692
|
const log = deps.logger.named(`memory.distill-write-${tool}`);
|
|
26614
26693
|
if (!input.accepted || input.proposedContent === void 0 || input.targetPath === void 0) {
|
|
@@ -26637,14 +26716,14 @@ var distillWriteUseCase = async (deps, tool, input) => {
|
|
|
26637
26716
|
var makeBackupPath = (targetPath, now) => `${String(targetPath)}.bak.${String(now).replace(/:/g, "-")}`;
|
|
26638
26717
|
var fileExists = async (path2) => {
|
|
26639
26718
|
try {
|
|
26640
|
-
return (await
|
|
26719
|
+
return (await fs29.stat(path2)).isFile();
|
|
26641
26720
|
} catch {
|
|
26642
26721
|
return false;
|
|
26643
26722
|
}
|
|
26644
26723
|
};
|
|
26645
26724
|
var safeReadText2 = async (path2) => {
|
|
26646
26725
|
try {
|
|
26647
|
-
return await
|
|
26726
|
+
return await fs29.readFile(path2, "utf8");
|
|
26648
26727
|
} catch {
|
|
26649
26728
|
return void 0;
|
|
26650
26729
|
}
|
|
@@ -27801,7 +27880,7 @@ var probeReadinessLeaf = (deps, tool) => leaf(`probe-${tool}`, {
|
|
|
27801
27880
|
});
|
|
27802
27881
|
|
|
27803
27882
|
// src/application/flows/readiness/leaves/propose.ts
|
|
27804
|
-
import { promises as
|
|
27883
|
+
import { promises as fs30 } from "fs";
|
|
27805
27884
|
|
|
27806
27885
|
// src/integration/ai/prompts/readiness/definition.ts
|
|
27807
27886
|
var CLAUDE_CODE = "claude-code";
|
|
@@ -28020,7 +28099,17 @@ var readinessExampleSignals = [
|
|
|
28020
28099
|
content: "# Verify\n\nRun `<verify command>` (typecheck + lint + test).",
|
|
28021
28100
|
timestamp: EXAMPLE_TS6
|
|
28022
28101
|
},
|
|
28023
|
-
{ type: "skill-suggestions", names: ["typescript-strict", "code-style-conventions"], timestamp: EXAMPLE_TS6 }
|
|
28102
|
+
{ type: "skill-suggestions", names: ["typescript-strict", "code-style-conventions"], timestamp: EXAMPLE_TS6 },
|
|
28103
|
+
{
|
|
28104
|
+
type: "learning",
|
|
28105
|
+
text: "The verify command lives in a workspace package script, not the repo root manifest.",
|
|
28106
|
+
timestamp: EXAMPLE_TS6
|
|
28107
|
+
},
|
|
28108
|
+
{
|
|
28109
|
+
type: "note",
|
|
28110
|
+
text: "No lint config found, so the proposed verify skill covers typecheck + test only.",
|
|
28111
|
+
timestamp: EXAMPLE_TS6
|
|
28112
|
+
}
|
|
28024
28113
|
];
|
|
28025
28114
|
var readinessOutputContract = {
|
|
28026
28115
|
schemaVersion: 1,
|
|
@@ -28060,7 +28149,7 @@ var proposeReadinessUseCase = async (deps, tool, input, abortSignal) => {
|
|
|
28060
28149
|
let existingBody;
|
|
28061
28150
|
if (existingPath !== void 0) {
|
|
28062
28151
|
try {
|
|
28063
|
-
existingBody = await
|
|
28152
|
+
existingBody = await fs30.readFile(existingPath, "utf8");
|
|
28064
28153
|
} catch {
|
|
28065
28154
|
existingBody = void 0;
|
|
28066
28155
|
}
|
|
@@ -28192,7 +28281,7 @@ var proposeReadinessLeaf = (deps, tool) => leaf(`propose-${tool}`, {
|
|
|
28192
28281
|
});
|
|
28193
28282
|
|
|
28194
28283
|
// src/application/flows/readiness/leaves/write.ts
|
|
28195
|
-
import { promises as
|
|
28284
|
+
import { promises as fs31 } from "fs";
|
|
28196
28285
|
var writeReadinessUseCase = async (deps, tool, input) => {
|
|
28197
28286
|
const log = deps.logger.named(`readiness.write-${tool}`);
|
|
28198
28287
|
if (!input.accepted || input.proposal === void 0) {
|
|
@@ -28226,7 +28315,7 @@ var makeBackupPath2 = (targetPath, now) => {
|
|
|
28226
28315
|
};
|
|
28227
28316
|
var fileExists2 = async (path2) => {
|
|
28228
28317
|
try {
|
|
28229
|
-
const stat = await
|
|
28318
|
+
const stat = await fs31.stat(path2);
|
|
28230
28319
|
return stat.isFile();
|
|
28231
28320
|
} catch {
|
|
28232
28321
|
return false;
|
|
@@ -28234,7 +28323,7 @@ var fileExists2 = async (path2) => {
|
|
|
28234
28323
|
};
|
|
28235
28324
|
var safeReadText3 = async (path2) => {
|
|
28236
28325
|
try {
|
|
28237
|
-
return await
|
|
28326
|
+
return await fs31.readFile(path2, "utf8");
|
|
28238
28327
|
} catch {
|
|
28239
28328
|
return void 0;
|
|
28240
28329
|
}
|
|
@@ -28259,11 +28348,11 @@ var writeReadinessLeaf = (deps, tool) => leaf(`write-${tool}`, {
|
|
|
28259
28348
|
});
|
|
28260
28349
|
|
|
28261
28350
|
// src/application/flows/_shared/allocate-run-dir.ts
|
|
28262
|
-
import { promises as
|
|
28351
|
+
import { promises as fs33 } from "fs";
|
|
28263
28352
|
import { join as join63 } from "path";
|
|
28264
28353
|
|
|
28265
28354
|
// src/integration/ai/runs/_engine/run-artifacts.ts
|
|
28266
|
-
import { promises as
|
|
28355
|
+
import { promises as fs32 } from "fs";
|
|
28267
28356
|
import { join as join62 } from "path";
|
|
28268
28357
|
var BODY_PREVIEW_LIMIT = 800;
|
|
28269
28358
|
var buildRunDirName = () => {
|
|
@@ -28274,7 +28363,7 @@ var buildRunDirName = () => {
|
|
|
28274
28363
|
var readRunBodyPreview = async (runDir, options) => {
|
|
28275
28364
|
let raw;
|
|
28276
28365
|
try {
|
|
28277
|
-
raw = await
|
|
28366
|
+
raw = await fs32.readFile(join62(String(runDir), "body.txt"), "utf8");
|
|
28278
28367
|
} catch (cause) {
|
|
28279
28368
|
if (isErrnoException(cause) && cause.code === "ENOENT") return void 0;
|
|
28280
28369
|
const code = isErrnoException(cause) ? cause.code : "unknown";
|
|
@@ -28294,7 +28383,7 @@ var allocateRunDirLeaf = (opts) => leaf(opts.name, {
|
|
|
28294
28383
|
useCase: {
|
|
28295
28384
|
execute: async (input) => {
|
|
28296
28385
|
try {
|
|
28297
|
-
await
|
|
28386
|
+
await fs33.mkdir(input.path, { recursive: true });
|
|
28298
28387
|
} catch (cause) {
|
|
28299
28388
|
return Result.error(
|
|
28300
28389
|
new StorageError({
|
|
@@ -28682,16 +28771,16 @@ var proposeUseCase = async (deps, input, abortSignal) => {
|
|
|
28682
28771
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
28683
28772
|
const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt.value));
|
|
28684
28773
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
28685
|
-
const
|
|
28774
|
+
const spawn2 = await deps.provider.generate(
|
|
28686
28775
|
readOnlySignalsSession(buildSessionOpts(deps, input, paths.value, prompt.value, abortSignal))
|
|
28687
28776
|
);
|
|
28688
|
-
if (!
|
|
28777
|
+
if (!spawn2.ok) {
|
|
28689
28778
|
log.error(`provider failed for repo ${input.repository.name}`, {
|
|
28690
28779
|
repositoryId: String(input.repository.id),
|
|
28691
|
-
error:
|
|
28780
|
+
error: spawn2.error.message,
|
|
28692
28781
|
runDir: String(input.runDir)
|
|
28693
28782
|
});
|
|
28694
|
-
return Result.error(
|
|
28783
|
+
return Result.error(spawn2.error);
|
|
28695
28784
|
}
|
|
28696
28785
|
const validated = await validateSignalsFile(input.runDir, detectSkillsOutputContract);
|
|
28697
28786
|
if (!validated.ok) {
|
|
@@ -29127,16 +29216,16 @@ var proposeUseCase2 = async (deps, input, abortSignal) => {
|
|
|
29127
29216
|
if (!prompt.ok) return Result.error(prompt.error);
|
|
29128
29217
|
const promptWrote = await writeTextAtomic(String(paths.value.promptFile), String(prompt.value));
|
|
29129
29218
|
if (!promptWrote.ok) return Result.error(promptWrote.error);
|
|
29130
|
-
const
|
|
29219
|
+
const spawn2 = await deps.provider.generate(
|
|
29131
29220
|
readOnlySignalsSession(buildSessionOpts2(deps, input, paths.value, prompt.value, abortSignal))
|
|
29132
29221
|
);
|
|
29133
|
-
if (!
|
|
29222
|
+
if (!spawn2.ok) {
|
|
29134
29223
|
log.error(`provider failed for repo ${input.repository.name}`, {
|
|
29135
29224
|
repositoryId: String(input.repository.id),
|
|
29136
|
-
error:
|
|
29225
|
+
error: spawn2.error.message,
|
|
29137
29226
|
runDir: String(input.runDir)
|
|
29138
29227
|
});
|
|
29139
|
-
return Result.error(
|
|
29228
|
+
return Result.error(spawn2.error);
|
|
29140
29229
|
}
|
|
29141
29230
|
const validated = await validateSignalsFile(input.runDir, detectScriptsOutputContract);
|
|
29142
29231
|
if (!validated.ok) {
|
|
@@ -29632,6 +29721,21 @@ var ideateExampleSignals = [
|
|
|
29632
29721
|
type: "ideated-tickets",
|
|
29633
29722
|
outputJson: '{"requirements":"# Export CSV\\n\\n## Problem\\n\u2026","tasks":[{"name":"Wire endpoint","projectPath":"/abs/repo","steps":["\u2026"],"verificationCriteria":["\u2026"]}]}',
|
|
29634
29723
|
timestamp: EXAMPLE_TS9
|
|
29724
|
+
},
|
|
29725
|
+
{
|
|
29726
|
+
type: "decision",
|
|
29727
|
+
text: "Scoped export to CSV only for v1; XLSX deferred to a follow-up ticket.",
|
|
29728
|
+
timestamp: EXAMPLE_TS9
|
|
29729
|
+
},
|
|
29730
|
+
{
|
|
29731
|
+
type: "learning",
|
|
29732
|
+
text: "The repo runs its integration suite from a separate config; the root test command does not cover it.",
|
|
29733
|
+
timestamp: EXAMPLE_TS9
|
|
29734
|
+
},
|
|
29735
|
+
{
|
|
29736
|
+
type: "note",
|
|
29737
|
+
text: "The user ruled out changing the existing download endpoint, so the new route is additive.",
|
|
29738
|
+
timestamp: EXAMPLE_TS9
|
|
29635
29739
|
}
|
|
29636
29740
|
];
|
|
29637
29741
|
var ideateOutputContract = {
|
|
@@ -30943,8 +31047,8 @@ var defaultRowFor = (flowId, settings) => {
|
|
|
30943
31047
|
return primaryFlowRow(settings.ai, aiFlow);
|
|
30944
31048
|
};
|
|
30945
31049
|
var labelKeepDefault = (value) => `Keep default (${value ?? "unset"})`;
|
|
30946
|
-
var formatRow = (row, globalEffort) => {
|
|
30947
|
-
const resolved = resolveEffortForRow(row, globalEffort);
|
|
31050
|
+
var formatRow = (row, globalEffort, flow) => {
|
|
31051
|
+
const resolved = resolveEffortForRow(row, globalEffort, flow);
|
|
30948
31052
|
return `${row.provider} / ${row.model} / ${resolved ?? "auto"}`;
|
|
30949
31053
|
};
|
|
30950
31054
|
var pickProviderStep = async (interactive, header, defaultRow) => {
|
|
@@ -30978,9 +31082,9 @@ var computeEffortDefaultLabel = (defaultRow, globalEffort, providerChanged, mode
|
|
|
30978
31082
|
}
|
|
30979
31083
|
return labelKeepDefault(resolvedRowEffort ?? "auto");
|
|
30980
31084
|
};
|
|
30981
|
-
var pickEffortStep = async (interactive, header, defaultRow, globalEffort, effectiveProvider, providerChanged, modelChanged) => {
|
|
31085
|
+
var pickEffortStep = async (interactive, header, defaultRow, globalEffort, effectiveProvider, providerChanged, modelChanged, flow) => {
|
|
30982
31086
|
const effortCatalog = PROVIDER_EFFORT_LEVELS[effectiveProvider];
|
|
30983
|
-
const resolvedRowEffort = resolveEffortForRow(defaultRow, globalEffort);
|
|
31087
|
+
const resolvedRowEffort = resolveEffortForRow(defaultRow, globalEffort, flow);
|
|
30984
31088
|
const effortDefaultLabel = computeEffortDefaultLabel(
|
|
30985
31089
|
defaultRow,
|
|
30986
31090
|
globalEffort,
|
|
@@ -31005,7 +31109,7 @@ var assembleRowOverride = (defaultRow, providerChanged, providerValue, modelValu
|
|
|
31005
31109
|
if (providerChanged && override.model === void 0) override.model = modelValue;
|
|
31006
31110
|
return override;
|
|
31007
31111
|
};
|
|
31008
|
-
var customizeRow = async (interactive, header, defaultRow, globalEffort, availableModelsFor) => {
|
|
31112
|
+
var customizeRow = async (interactive, header, defaultRow, globalEffort, availableModelsFor, flow) => {
|
|
31009
31113
|
const providerStep = await pickProviderStep(interactive, header, defaultRow);
|
|
31010
31114
|
if (providerStep === void 0) return void 0;
|
|
31011
31115
|
const { providerValue, providerChanged, effectiveProvider } = providerStep;
|
|
@@ -31026,14 +31130,15 @@ var customizeRow = async (interactive, header, defaultRow, globalEffort, availab
|
|
|
31026
31130
|
globalEffort,
|
|
31027
31131
|
effectiveProvider,
|
|
31028
31132
|
providerChanged,
|
|
31029
|
-
modelChanged
|
|
31133
|
+
modelChanged,
|
|
31134
|
+
flow
|
|
31030
31135
|
);
|
|
31031
31136
|
if (effortValue === void 0) return void 0;
|
|
31032
31137
|
return assembleRowOverride(defaultRow, providerChanged, providerValue, modelValue, effortValue);
|
|
31033
31138
|
};
|
|
31034
|
-
var buildPickerHeader = (flowId, flowTitle, settings) => flowId === "implement" ? `${flowTitle} \u2014 current defaults:
|
|
31035
|
-
generator: ${formatRow(settings.ai.implement.generator, settings.ai.effort)}
|
|
31036
|
-
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)}`;
|
|
31037
31142
|
var originLabel = (origin) => {
|
|
31038
31143
|
switch (origin) {
|
|
31039
31144
|
case "bundled-default":
|
|
@@ -31091,7 +31196,7 @@ var runSkillsStep = async (interactive, flowTitle, skillCandidates) => {
|
|
|
31091
31196
|
skills: { disabled, saveAsDefault: saveAns.value === "remember" }
|
|
31092
31197
|
};
|
|
31093
31198
|
};
|
|
31094
|
-
var runImplementCustomize = async (interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput) => {
|
|
31199
|
+
var runImplementCustomize = async (interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput, aiFlow) => {
|
|
31095
31200
|
const roles = ["generator", "evaluator"];
|
|
31096
31201
|
const collected = {};
|
|
31097
31202
|
for (const role of roles) {
|
|
@@ -31099,7 +31204,7 @@ var runImplementCustomize = async (interactive, header, flowTitle, settings, ava
|
|
|
31099
31204
|
const roleHeader = `${header}
|
|
31100
31205
|
|
|
31101
31206
|
Role: ${role}`;
|
|
31102
|
-
const result = await customizeRow(interactive, roleHeader, row, settings.ai.effort, availableModelsFor);
|
|
31207
|
+
const result = await customizeRow(interactive, roleHeader, row, settings.ai.effort, availableModelsFor, aiFlow);
|
|
31103
31208
|
if (result === void 0) return { kind: "cancel" };
|
|
31104
31209
|
if (Object.keys(result).length > 0) collected[role] = result;
|
|
31105
31210
|
}
|
|
@@ -31123,10 +31228,10 @@ Role: ${role}`;
|
|
|
31123
31228
|
...skills !== void 0 ? { skills } : {}
|
|
31124
31229
|
};
|
|
31125
31230
|
};
|
|
31126
|
-
var runSingleRowCustomize = async (interactive, header, flowId, flowTitle, settings, availableModelsFor, skillsStepInput) => {
|
|
31231
|
+
var runSingleRowCustomize = async (interactive, header, flowId, flowTitle, settings, availableModelsFor, skillsStepInput, aiFlow) => {
|
|
31127
31232
|
const defaultRow = defaultRowFor(flowId, settings);
|
|
31128
31233
|
if (defaultRow === void 0) return { kind: "defaults" };
|
|
31129
|
-
const override = await customizeRow(interactive, header, defaultRow, settings.ai.effort, availableModelsFor);
|
|
31234
|
+
const override = await customizeRow(interactive, header, defaultRow, settings.ai.effort, availableModelsFor, aiFlow);
|
|
31130
31235
|
if (override === void 0) return { kind: "cancel" };
|
|
31131
31236
|
const candidates = await effectiveSkillCandidates(
|
|
31132
31237
|
skillsStepInput.candidates,
|
|
@@ -31152,7 +31257,7 @@ var runCustomizePicker = async ({
|
|
|
31152
31257
|
}) => {
|
|
31153
31258
|
const aiFlow = aiFlowIdForPicker(flowId);
|
|
31154
31259
|
if (aiFlow === void 0) return { kind: "defaults" };
|
|
31155
|
-
const header = buildPickerHeader(flowId, flowTitle, settings);
|
|
31260
|
+
const header = buildPickerHeader(flowId, flowTitle, settings, aiFlow);
|
|
31156
31261
|
const action = await interactive.askChoice(
|
|
31157
31262
|
`${header}
|
|
31158
31263
|
|
|
@@ -31170,9 +31275,18 @@ What would you like to do?`,
|
|
|
31170
31275
|
...rebuildSkillCandidates !== void 0 ? { rebuild: rebuildSkillCandidates } : {}
|
|
31171
31276
|
};
|
|
31172
31277
|
if (flowId === "implement") {
|
|
31173
|
-
return runImplementCustomize(interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput);
|
|
31278
|
+
return runImplementCustomize(interactive, header, flowTitle, settings, availableModelsFor, skillsStepInput, aiFlow);
|
|
31174
31279
|
}
|
|
31175
|
-
return runSingleRowCustomize(
|
|
31280
|
+
return runSingleRowCustomize(
|
|
31281
|
+
interactive,
|
|
31282
|
+
header,
|
|
31283
|
+
flowId,
|
|
31284
|
+
flowTitle,
|
|
31285
|
+
settings,
|
|
31286
|
+
availableModelsFor,
|
|
31287
|
+
skillsStepInput,
|
|
31288
|
+
aiFlow
|
|
31289
|
+
);
|
|
31176
31290
|
};
|
|
31177
31291
|
|
|
31178
31292
|
// src/application/flows/settings-set/flow.ts
|
|
@@ -31683,10 +31797,10 @@ var FeedbackLine = ({ text }) => {
|
|
|
31683
31797
|
if (text === void 0) return null;
|
|
31684
31798
|
if (typeof text === "string") {
|
|
31685
31799
|
const { color: color2, body } = resolveStructured(text);
|
|
31686
|
-
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 }) });
|
|
31687
31801
|
}
|
|
31688
31802
|
const { glyph, color } = toneConfig(text.tone);
|
|
31689
|
-
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: [
|
|
31690
31804
|
glyph.length > 0 ? `${glyph} ` : "",
|
|
31691
31805
|
text.text
|
|
31692
31806
|
] }) });
|
|
@@ -34292,7 +34406,7 @@ var HeaderCardImpl = ({
|
|
|
34292
34406
|
String(tasksTotal)
|
|
34293
34407
|
] })
|
|
34294
34408
|
] }),
|
|
34295
|
-
isRunning && /* @__PURE__ */ jsx62(Box50, { marginLeft:
|
|
34409
|
+
isRunning && /* @__PURE__ */ jsx62(Box50, { marginLeft: spacing.indent, children: /* @__PURE__ */ jsx62(Spinner, { active: isRunning, color: inkColors.info, label: "live" }) })
|
|
34296
34410
|
] }),
|
|
34297
34411
|
/* @__PURE__ */ jsx62(
|
|
34298
34412
|
ModelLines,
|
|
@@ -35062,7 +35176,7 @@ var TASK_STATUS_COLOR = {
|
|
|
35062
35176
|
aborted: inkColors.warning,
|
|
35063
35177
|
skipped: inkColors.muted
|
|
35064
35178
|
};
|
|
35065
|
-
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)) }) });
|
|
35066
35180
|
var truncateName = (name, maxChars) => name.length > maxChars ? `${name.slice(0, Math.max(0, maxChars - 1))}${glyphs.clipEllipsis}` : name;
|
|
35067
35181
|
var TaskNavList = ({
|
|
35068
35182
|
tasks,
|
|
@@ -39306,10 +39420,10 @@ var projectInput = (ctx) => {
|
|
|
39306
39420
|
};
|
|
39307
39421
|
var runAuthoringSpawn = async (deps, session, unitRoot, log) => {
|
|
39308
39422
|
try {
|
|
39309
|
-
const
|
|
39310
|
-
if (!
|
|
39311
|
-
if (
|
|
39312
|
-
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})`);
|
|
39313
39427
|
return Result.ok({});
|
|
39314
39428
|
}
|
|
39315
39429
|
const validated = await validateSignalsFile(unitRoot, generatePrContentOutputContract);
|
|
@@ -39779,7 +39893,7 @@ import { basename as basename5, join as join69 } from "path";
|
|
|
39779
39893
|
|
|
39780
39894
|
// src/application/ui/tui/prompts/path-picker-prompt.tsx
|
|
39781
39895
|
import { useEffect as useEffect40, useState as useState45 } from "react";
|
|
39782
|
-
import { promises as
|
|
39896
|
+
import { promises as fs34 } from "fs";
|
|
39783
39897
|
import { dirname as dirname21, join as join68 } from "path";
|
|
39784
39898
|
import { homedir as homedir2 } from "os";
|
|
39785
39899
|
import { Box as Box76, Text as Text72, useInput as useInput28 } from "ink";
|
|
@@ -39798,7 +39912,7 @@ var useDirectoryEntries = (cwd, showHidden) => {
|
|
|
39798
39912
|
let cancelled = false;
|
|
39799
39913
|
const load = async () => {
|
|
39800
39914
|
try {
|
|
39801
|
-
const items = await
|
|
39915
|
+
const items = await fs34.readdir(cwd, { withFileTypes: true });
|
|
39802
39916
|
if (cancelled) return;
|
|
39803
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));
|
|
39804
39918
|
setEntries(filtered);
|
|
@@ -39880,7 +39994,7 @@ var submitTypedPath = async (raw, { setError, setTyping, onSubmit }) => {
|
|
|
39880
39994
|
return;
|
|
39881
39995
|
}
|
|
39882
39996
|
try {
|
|
39883
|
-
const stat = await
|
|
39997
|
+
const stat = await fs34.stat(expanded);
|
|
39884
39998
|
if (!stat.isDirectory()) {
|
|
39885
39999
|
setError(`${expanded} is not a directory`);
|
|
39886
40000
|
setTyping(false);
|
|
@@ -39955,8 +40069,8 @@ var PathPickerPrompt = ({
|
|
|
39955
40069
|
] }) }),
|
|
39956
40070
|
/* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, children: /* @__PURE__ */ jsx94(Text72, { dimColor: true, children: cwd }) }),
|
|
39957
40071
|
error !== void 0 && /* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, children: /* @__PURE__ */ jsx94(Text72, { color: inkColors.error, children: error }) }),
|
|
39958
|
-
/* @__PURE__ */ jsx94(Box76, { flexDirection: "column", marginTop:
|
|
39959
|
-
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: [
|
|
39960
40074
|
/* @__PURE__ */ jsx94(Text72, { dimColor: true, children: "Type an absolute path (~/ allowed). Enter validates; esc returns to the picker." }),
|
|
39961
40075
|
/* @__PURE__ */ jsx94(
|
|
39962
40076
|
TextPrompt,
|
|
@@ -39967,7 +40081,7 @@ var PathPickerPrompt = ({
|
|
|
39967
40081
|
onCancel: () => setTyping(false)
|
|
39968
40082
|
}
|
|
39969
40083
|
)
|
|
39970
|
-
] }) : /* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, marginTop:
|
|
40084
|
+
] }) : /* @__PURE__ */ jsx94(Box76, { paddingX: spacing.indent, marginTop: spacing.section, children: /* @__PURE__ */ jsxs72(Text72, { dimColor: true, children: [
|
|
39971
40085
|
"\u21B5 open/select \xB7 \u232B up \xB7 esc cancel \xB7 ~ home \xB7 t type \xB7 . ",
|
|
39972
40086
|
showHidden ? "hide" : "show",
|
|
39973
40087
|
" hidden"
|
|
@@ -40885,7 +40999,7 @@ var PickProjectView = () => {
|
|
|
40885
40999
|
};
|
|
40886
41000
|
|
|
40887
41001
|
// src/application/ui/tui/views/pick-sprint-view.tsx
|
|
40888
|
-
import {
|
|
41002
|
+
import { useMemo as useMemo29, useState as useState50 } from "react";
|
|
40889
41003
|
import { Box as Box85, Text as Text80, useInput as useInput31 } from "ink";
|
|
40890
41004
|
|
|
40891
41005
|
// src/application/ui/tui/views/pick-sprint-internals/types.ts
|
|
@@ -40950,99 +41064,75 @@ var flatten = (groups, includeCreate) => {
|
|
|
40950
41064
|
}
|
|
40951
41065
|
return rows;
|
|
40952
41066
|
};
|
|
40953
|
-
var
|
|
40954
|
-
|
|
40955
|
-
|
|
40956
|
-
|
|
40957
|
-
|
|
40958
|
-
|
|
40959
|
-
|
|
40960
|
-
};
|
|
40961
|
-
var nextCursorableIndex = (rows, from, direction) => {
|
|
40962
|
-
const candidates = cursorableRowIndices(rows);
|
|
40963
|
-
if (candidates.length === 0) return from;
|
|
40964
|
-
if (direction === 1) {
|
|
40965
|
-
const next = candidates.find((i) => i > from);
|
|
40966
|
-
return next ?? from;
|
|
40967
|
-
}
|
|
40968
|
-
let prev = from;
|
|
40969
|
-
for (const i of candidates) {
|
|
40970
|
-
if (i < from) prev = i;
|
|
40971
|
-
else break;
|
|
40972
|
-
}
|
|
40973
|
-
return prev === from && candidates.includes(from) ? from : prev;
|
|
40974
|
-
};
|
|
40975
|
-
var cursorableNear = (rows, target, direction) => {
|
|
40976
|
-
const candidates = cursorableRowIndices(rows);
|
|
40977
|
-
if (candidates.length === 0) return target;
|
|
40978
|
-
if (candidates.includes(target)) return target;
|
|
40979
|
-
const below = candidates.find((i) => i > target);
|
|
40980
|
-
let above;
|
|
40981
|
-
for (const i of candidates) {
|
|
40982
|
-
if (i < target) above = i;
|
|
40983
|
-
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;
|
|
40984
41074
|
}
|
|
40985
|
-
const
|
|
40986
|
-
|
|
40987
|
-
|
|
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 : "";
|
|
40988
41079
|
};
|
|
40989
41080
|
|
|
40990
41081
|
// src/application/ui/tui/views/pick-sprint-internals/window.ts
|
|
40991
41082
|
var VERTICAL_CHROME_ROWS = 12;
|
|
40992
41083
|
var MIN_VISIBLE_ROWS = 8;
|
|
40993
|
-
var computeWindow = (totalRows, cursor, visible) => {
|
|
40994
|
-
if (totalRows <= visible) return { start: 0, end: totalRows, hiddenAbove: 0, hiddenBelow: 0 };
|
|
40995
|
-
const half = Math.floor(visible / 2);
|
|
40996
|
-
let start = Math.max(0, cursor - half);
|
|
40997
|
-
let end = start + visible;
|
|
40998
|
-
if (end > totalRows) {
|
|
40999
|
-
end = totalRows;
|
|
41000
|
-
start = Math.max(0, end - visible);
|
|
41001
|
-
}
|
|
41002
|
-
return { start, end, hiddenAbove: start, hiddenBelow: totalRows - end };
|
|
41003
|
-
};
|
|
41004
41084
|
|
|
41005
41085
|
// src/application/ui/tui/views/pick-sprint-internals/row-views.tsx
|
|
41006
41086
|
import { useMemo as useMemo28 } from "react";
|
|
41007
41087
|
import { Box as Box84, Text as Text79 } from "ink";
|
|
41008
41088
|
import { jsx as jsx102, jsxs as jsxs80 } from "react/jsx-runtime";
|
|
41009
|
-
var
|
|
41089
|
+
var PickerRowList = ({
|
|
41010
41090
|
rows,
|
|
41011
|
-
cursor,
|
|
41012
41091
|
visibleRows,
|
|
41013
|
-
|
|
41092
|
+
active,
|
|
41093
|
+
initialCursorId,
|
|
41094
|
+
currentSprintId,
|
|
41095
|
+
onSubmit
|
|
41014
41096
|
}) => {
|
|
41015
|
-
const
|
|
41016
|
-
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
|
+
);
|
|
41017
41120
|
return /* @__PURE__ */ jsxs80(Box84, { flexDirection: "column", children: [
|
|
41018
|
-
|
|
41019
|
-
|
|
41020
|
-
|
|
41021
|
-
"
|
|
41022
|
-
] }) }),
|
|
41023
|
-
slice.map((row, i) => {
|
|
41024
|
-
const absoluteIndex = i + window.start;
|
|
41025
|
-
if (row.kind === "header") {
|
|
41026
|
-
return /* @__PURE__ */ jsx102(HeaderRowView, { row }, `h-${row.groupKey}-${String(absoluteIndex)}`);
|
|
41027
|
-
}
|
|
41028
|
-
if (row.kind === "create") {
|
|
41029
|
-
return /* @__PURE__ */ jsx102(CreateRowView, { focused: absoluteIndex === cursor }, `create-${String(absoluteIndex)}`);
|
|
41030
|
-
}
|
|
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");
|
|
41031
41125
|
return /* @__PURE__ */ jsx102(
|
|
41032
41126
|
SprintRowView,
|
|
41033
41127
|
{
|
|
41034
41128
|
sprint: row.sprint,
|
|
41035
|
-
focused:
|
|
41129
|
+
focused: focusedId === row.sprint.id,
|
|
41036
41130
|
isCurrent: currentSprintId === row.sprint.id
|
|
41037
41131
|
},
|
|
41038
41132
|
row.sprint.id
|
|
41039
41133
|
);
|
|
41040
41134
|
}),
|
|
41041
|
-
|
|
41042
|
-
"\u25BC ",
|
|
41043
|
-
String(window.hiddenBelow),
|
|
41044
|
-
" more below"
|
|
41045
|
-
] }) })
|
|
41135
|
+
/* @__PURE__ */ jsx102(OverflowRow, { direction: "below", count: rows.length - renderWindow.end })
|
|
41046
41136
|
] });
|
|
41047
41137
|
};
|
|
41048
41138
|
var CreateRowView = ({ focused }) => /* @__PURE__ */ jsxs80(Box84, { flexDirection: "column", paddingX: spacing.indent, children: [
|
|
@@ -41104,15 +41194,6 @@ var SprintRowView = ({
|
|
|
41104
41194
|
|
|
41105
41195
|
// src/application/ui/tui/views/pick-sprint-view.tsx
|
|
41106
41196
|
import { Fragment as Fragment12, jsx as jsx103, jsxs as jsxs81 } from "react/jsx-runtime";
|
|
41107
|
-
var preferredCursorIndex = (rows, preferredSprintId) => {
|
|
41108
|
-
if (preferredSprintId !== void 0) {
|
|
41109
|
-
const i = rows.findIndex((r) => r.kind === "sprint" && r.sprint.id === preferredSprintId);
|
|
41110
|
-
if (i !== -1) return i;
|
|
41111
|
-
}
|
|
41112
|
-
const firstSprint = rows.findIndex((r) => r.kind === "sprint");
|
|
41113
|
-
if (firstSprint !== -1) return firstSprint;
|
|
41114
|
-
return cursorableRowIndices(rows)[0] ?? 0;
|
|
41115
|
-
};
|
|
41116
41197
|
var usePickerRows = (deps, selection) => {
|
|
41117
41198
|
const [scopeAll, setScopeAll] = useState50(true);
|
|
41118
41199
|
const [hideDone, setHideDone] = useState50(false);
|
|
@@ -41147,28 +41228,7 @@ var usePickerRows = (deps, selection) => {
|
|
|
41147
41228
|
if (!hideDone || sprintCount > 0) return false;
|
|
41148
41229
|
return flatten(buildGroups(data, selection.projectId, scopeAll), false).some((r) => r.kind === "sprint");
|
|
41149
41230
|
}, [hideDone, sprintCount, data, selection.projectId, scopeAll]);
|
|
41150
|
-
const
|
|
41151
|
-
const [cursor, setCursor] = useState50(initialIdx);
|
|
41152
|
-
useEffect45(() => {
|
|
41153
|
-
setCursor((c) => {
|
|
41154
|
-
if (rows.length === 0) return 0;
|
|
41155
|
-
if (c >= rows.length) return Math.max(0, rows.length - 1);
|
|
41156
|
-
const focused = rows[c];
|
|
41157
|
-
if (focused?.kind !== "sprint" && focused?.kind !== "create") {
|
|
41158
|
-
return cursorableRowIndices(rows)[0] ?? c;
|
|
41159
|
-
}
|
|
41160
|
-
return c;
|
|
41161
|
-
});
|
|
41162
|
-
}, [rows]);
|
|
41163
|
-
useEffect45(() => {
|
|
41164
|
-
setCursor(initialIdx);
|
|
41165
|
-
}, [initialIdx]);
|
|
41166
|
-
const toggleScope = () => {
|
|
41167
|
-
const next = !scopeAll;
|
|
41168
|
-
setScopeAll(next);
|
|
41169
|
-
const nextRows = flatten(buildGroups(visibleData, selection.projectId, next), includeCreate);
|
|
41170
|
-
setCursor(preferredCursorIndex(nextRows, selection.sprintId));
|
|
41171
|
-
};
|
|
41231
|
+
const toggleScope = () => setScopeAll((v) => !v);
|
|
41172
41232
|
return {
|
|
41173
41233
|
state,
|
|
41174
41234
|
reload,
|
|
@@ -41179,24 +41239,9 @@ var usePickerRows = (deps, selection) => {
|
|
|
41179
41239
|
scopeAll,
|
|
41180
41240
|
hideDone,
|
|
41181
41241
|
setHideDone,
|
|
41182
|
-
toggleScope
|
|
41183
|
-
cursor,
|
|
41184
|
-
setCursor
|
|
41242
|
+
toggleScope
|
|
41185
41243
|
};
|
|
41186
41244
|
};
|
|
41187
|
-
var isNavigationInput = (input, key) => key.upArrow || key.downArrow || key.pageUp || key.pageDown || key.home || key.end || input === "j" || input === "k";
|
|
41188
|
-
var resolveArrowMove = (rows, cursor, visibleRows, input, key) => {
|
|
41189
|
-
if (key.upArrow || input === "k") return nextCursorableIndex(rows, cursor, -1);
|
|
41190
|
-
if (key.downArrow || input === "j") return nextCursorableIndex(rows, cursor, 1);
|
|
41191
|
-
if (key.pageUp) return cursorableNear(rows, Math.max(0, cursor - visibleRows), -1);
|
|
41192
|
-
if (key.pageDown) return cursorableNear(rows, Math.min(rows.length - 1, cursor + visibleRows), 1);
|
|
41193
|
-
if (key.home) return cursorableRowIndices(rows)[0] ?? 0;
|
|
41194
|
-
if (key.end) {
|
|
41195
|
-
const candidates = cursorableRowIndices(rows);
|
|
41196
|
-
return candidates[candidates.length - 1] ?? cursor;
|
|
41197
|
-
}
|
|
41198
|
-
return void 0;
|
|
41199
|
-
};
|
|
41200
41245
|
var PickerBody = ({
|
|
41201
41246
|
helpOpen,
|
|
41202
41247
|
state,
|
|
@@ -41205,11 +41250,14 @@ var PickerBody = ({
|
|
|
41205
41250
|
scopeAll,
|
|
41206
41251
|
hideDone,
|
|
41207
41252
|
rows,
|
|
41208
|
-
cursor,
|
|
41209
41253
|
visibleRows,
|
|
41254
|
+
listActive,
|
|
41255
|
+
initialCursorId,
|
|
41256
|
+
listKey,
|
|
41210
41257
|
currentSprintId,
|
|
41211
41258
|
currentSprintLabel,
|
|
41212
|
-
feedback: feedback2
|
|
41259
|
+
feedback: feedback2,
|
|
41260
|
+
onRowSubmit
|
|
41213
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: [
|
|
41214
41262
|
/* @__PURE__ */ jsx103(Text80, { children: "All sprints here are done (hidden)." }),
|
|
41215
41263
|
/* @__PURE__ */ jsx103(Text80, { dimColor: true, children: "Press f to show them, or + to create a new one." })
|
|
@@ -41234,7 +41282,18 @@ var PickerBody = ({
|
|
|
41234
41282
|
/* @__PURE__ */ jsx103(Text80, { color: inkColors.primary, bold: true, children: currentSprintLabel })
|
|
41235
41283
|
] }) : /* @__PURE__ */ jsx103(Text80, { children: "press \u21B5 to confirm" })
|
|
41236
41284
|
] }) }),
|
|
41237
|
-
/* @__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
|
+
),
|
|
41238
41297
|
/* @__PURE__ */ jsx103(Box85, { marginTop: spacing.section, paddingX: spacing.indent, children: /* @__PURE__ */ jsxs81(Text80, { dimColor: true, children: [
|
|
41239
41298
|
glyphs.bullet,
|
|
41240
41299
|
" \u21B5 use the highlighted sprint ",
|
|
@@ -41248,67 +41307,15 @@ var PickerBody = ({
|
|
|
41248
41307
|
glyphs.bullet,
|
|
41249
41308
|
" + create a new one"
|
|
41250
41309
|
] }) }),
|
|
41251
|
-
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 }) })
|
|
41252
41311
|
] });
|
|
41253
|
-
var usePickerInput = ({
|
|
41254
|
-
modalOpen,
|
|
41255
|
-
rows,
|
|
41256
|
-
cursor,
|
|
41257
|
-
visibleRows,
|
|
41258
|
-
setCursor,
|
|
41259
|
-
pick,
|
|
41260
|
-
launchCreateSprint: launchCreateSprint2,
|
|
41261
|
-
toggleScope,
|
|
41262
|
-
setHideDone,
|
|
41263
|
-
reload
|
|
41264
|
-
}) => {
|
|
41265
|
-
useInput31((input, key) => {
|
|
41266
|
-
if (modalOpen) return;
|
|
41267
|
-
if (isNavigationInput(input, key)) {
|
|
41268
|
-
setCursor((c) => resolveArrowMove(rows, c, visibleRows, input, key) ?? c);
|
|
41269
|
-
return;
|
|
41270
|
-
}
|
|
41271
|
-
if (key.return) {
|
|
41272
|
-
const row = rows[cursor];
|
|
41273
|
-
if (row?.kind === "sprint") pick(row.sprint);
|
|
41274
|
-
else if (row?.kind === "create") void launchCreateSprint2();
|
|
41275
|
-
return;
|
|
41276
|
-
}
|
|
41277
|
-
if (input === "t") {
|
|
41278
|
-
toggleScope();
|
|
41279
|
-
return;
|
|
41280
|
-
}
|
|
41281
|
-
if (input === "f") {
|
|
41282
|
-
setHideDone((v) => !v);
|
|
41283
|
-
return;
|
|
41284
|
-
}
|
|
41285
|
-
if (input === "+" || input === "c") {
|
|
41286
|
-
void launchCreateSprint2();
|
|
41287
|
-
return;
|
|
41288
|
-
}
|
|
41289
|
-
if (input === "r") reload();
|
|
41290
|
-
});
|
|
41291
|
-
};
|
|
41292
41312
|
var PickSprintView = () => {
|
|
41293
41313
|
const deps = useDeps();
|
|
41294
41314
|
const router = useRouter();
|
|
41295
41315
|
const selection = useSelection();
|
|
41296
41316
|
const ui = useUiState();
|
|
41297
41317
|
const [feedback2, setFeedback] = useState50(void 0);
|
|
41298
|
-
const {
|
|
41299
|
-
state,
|
|
41300
|
-
reload,
|
|
41301
|
-
data,
|
|
41302
|
-
rows,
|
|
41303
|
-
sprintCount,
|
|
41304
|
-
hiddenByDoneFilter,
|
|
41305
|
-
scopeAll,
|
|
41306
|
-
hideDone,
|
|
41307
|
-
setHideDone,
|
|
41308
|
-
toggleScope,
|
|
41309
|
-
cursor,
|
|
41310
|
-
setCursor
|
|
41311
|
-
} = usePickerRows(deps, selection);
|
|
41318
|
+
const { state, reload, data, rows, sprintCount, hiddenByDoneFilter, scopeAll, hideDone, setHideDone, toggleScope } = usePickerRows(deps, selection);
|
|
41312
41319
|
useViewHints([
|
|
41313
41320
|
{ keys: "\u2191/\u2193/j/k", label: "move" },
|
|
41314
41321
|
{ keys: "\u21B5", label: "use sprint" },
|
|
@@ -41335,18 +41342,28 @@ var PickSprintView = () => {
|
|
|
41335
41342
|
onError: setFeedback,
|
|
41336
41343
|
noProjectMessage: `${glyphs.cross} select a project first`
|
|
41337
41344
|
});
|
|
41338
|
-
|
|
41339
|
-
|
|
41340
|
-
|
|
41341
|
-
|
|
41342
|
-
|
|
41343
|
-
|
|
41344
|
-
|
|
41345
|
-
|
|
41346
|
-
|
|
41347
|
-
|
|
41348
|
-
|
|
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();
|
|
41349
41364
|
});
|
|
41365
|
+
const initialCursorId = useMemo29(() => preferredCursorId(rows, selection.sprintId), [rows, selection.sprintId]);
|
|
41366
|
+
const listKey = `${String(scopeAll)}-${selection.sprintId ?? "unseeded"}`;
|
|
41350
41367
|
return /* @__PURE__ */ jsx103(ViewShell, { title: "Pick a sprint", subtitle: "Switch sprint (and project) in one step", suppressScrollArrows: true, children: /* @__PURE__ */ jsx103(
|
|
41351
41368
|
PickerBody,
|
|
41352
41369
|
{
|
|
@@ -41357,11 +41374,14 @@ var PickSprintView = () => {
|
|
|
41357
41374
|
scopeAll,
|
|
41358
41375
|
hideDone,
|
|
41359
41376
|
rows,
|
|
41360
|
-
cursor,
|
|
41361
41377
|
visibleRows,
|
|
41378
|
+
listActive: !ui.modalOpen,
|
|
41379
|
+
initialCursorId,
|
|
41380
|
+
listKey,
|
|
41362
41381
|
currentSprintId: selection.sprintId,
|
|
41363
41382
|
currentSprintLabel: selection.sprintLabel,
|
|
41364
|
-
feedback: feedback2
|
|
41383
|
+
feedback: feedback2,
|
|
41384
|
+
onRowSubmit
|
|
41365
41385
|
}
|
|
41366
41386
|
) });
|
|
41367
41387
|
};
|
|
@@ -41416,11 +41436,10 @@ var renderView = (entry) => {
|
|
|
41416
41436
|
};
|
|
41417
41437
|
|
|
41418
41438
|
// src/application/ui/tui/runtime/use-global-keys.ts
|
|
41419
|
-
import { useEffect as
|
|
41439
|
+
import { useEffect as useEffect45, useMemo as useMemo30, useRef as useRef18 } from "react";
|
|
41420
41440
|
import { useApp, useInput as useInput32 } from "ink";
|
|
41421
41441
|
|
|
41422
41442
|
// src/integration/io/clipboard.ts
|
|
41423
|
-
import { spawn as nodeSpawn2 } from "child_process";
|
|
41424
41443
|
var resolveHelpers = ({ platform, env }) => {
|
|
41425
41444
|
if (platform === "darwin") return [{ cmd: "pbcopy", args: [] }];
|
|
41426
41445
|
if (platform === "win32") return [{ cmd: "clip.exe", args: [] }];
|
|
@@ -41439,10 +41458,10 @@ var resolveHelpers = ({ platform, env }) => {
|
|
|
41439
41458
|
return [];
|
|
41440
41459
|
};
|
|
41441
41460
|
var SPAWN_FAILED = "spawn-failed";
|
|
41442
|
-
var runHelper = (
|
|
41461
|
+
var runHelper = (spawn2, helper, text) => new Promise((resolve) => {
|
|
41443
41462
|
let child;
|
|
41444
41463
|
try {
|
|
41445
|
-
child =
|
|
41464
|
+
child = spawn2(helper.cmd, helper.args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
41446
41465
|
} catch (cause) {
|
|
41447
41466
|
resolve(
|
|
41448
41467
|
Result.error({
|
|
@@ -41486,7 +41505,7 @@ var runHelper = (spawn3, helper, text) => new Promise((resolve) => {
|
|
|
41486
41505
|
}
|
|
41487
41506
|
});
|
|
41488
41507
|
var createCopyToClipboard = (opts = {}) => {
|
|
41489
|
-
const
|
|
41508
|
+
const spawn2 = opts.spawn ?? crossPlatformSpawn;
|
|
41490
41509
|
const platform = opts.platform ?? process.platform;
|
|
41491
41510
|
const env = opts.env ?? process.env;
|
|
41492
41511
|
const helpers = resolveHelpers({ platform, env });
|
|
@@ -41499,7 +41518,7 @@ var createCopyToClipboard = (opts = {}) => {
|
|
|
41499
41518
|
}
|
|
41500
41519
|
let lastError;
|
|
41501
41520
|
for (const helper of helpers) {
|
|
41502
|
-
const result = await runHelper(
|
|
41521
|
+
const result = await runHelper(spawn2, helper, text);
|
|
41503
41522
|
if (result.ok) return result;
|
|
41504
41523
|
lastError = result.error;
|
|
41505
41524
|
if (result.error.code !== "no-helper") return result;
|
|
@@ -41528,7 +41547,7 @@ var useGlobalKeys = (opts = {}) => {
|
|
|
41528
41547
|
[opts.copyToClipboard]
|
|
41529
41548
|
);
|
|
41530
41549
|
const clearTimerRef = useRef18(void 0);
|
|
41531
|
-
|
|
41550
|
+
useEffect45(
|
|
41532
41551
|
() => () => {
|
|
41533
41552
|
if (clearTimerRef.current !== void 0) clearTimeout(clearTimerRef.current);
|
|
41534
41553
|
},
|
|
@@ -41697,7 +41716,7 @@ var scheduleClear = (eventBus, ref3) => {
|
|
|
41697
41716
|
};
|
|
41698
41717
|
|
|
41699
41718
|
// src/application/ui/tui/components/memory-pressure-banner.tsx
|
|
41700
|
-
import { useEffect as
|
|
41719
|
+
import { useEffect as useEffect46, useState as useState51 } from "react";
|
|
41701
41720
|
import { Box as Box86, Text as Text81 } from "ink";
|
|
41702
41721
|
import { jsxs as jsxs82 } from "react/jsx-runtime";
|
|
41703
41722
|
var formatMb = (bytes) => `${(bytes / 1048576).toFixed(0)} MB`;
|
|
@@ -41705,7 +41724,7 @@ var formatPercent = (ratio) => `${Math.round(ratio * 100)}%`;
|
|
|
41705
41724
|
var MemoryPressureBanner = () => {
|
|
41706
41725
|
const deps = useDeps();
|
|
41707
41726
|
const [latest, setLatest] = useState51(void 0);
|
|
41708
|
-
|
|
41727
|
+
useEffect46(() => {
|
|
41709
41728
|
return deps.eventBus.subscribe((event) => {
|
|
41710
41729
|
if (event.type === "memory-pressure") setLatest(event);
|
|
41711
41730
|
});
|
|
@@ -41728,13 +41747,13 @@ var MemoryPressureBanner = () => {
|
|
|
41728
41747
|
};
|
|
41729
41748
|
|
|
41730
41749
|
// src/application/ui/tui/components/chain-log-degraded-banner.tsx
|
|
41731
|
-
import { useEffect as
|
|
41750
|
+
import { useEffect as useEffect47, useState as useState52 } from "react";
|
|
41732
41751
|
import { Box as Box87, Text as Text82 } from "ink";
|
|
41733
41752
|
import { jsx as jsx105, jsxs as jsxs83 } from "react/jsx-runtime";
|
|
41734
41753
|
var ChainLogDegradedBanner = () => {
|
|
41735
41754
|
const deps = useDeps();
|
|
41736
41755
|
const [degraded, setDegraded] = useState52(false);
|
|
41737
|
-
|
|
41756
|
+
useEffect47(() => {
|
|
41738
41757
|
return deps.eventBus.subscribe((event) => {
|
|
41739
41758
|
if (event.type === "chain-log-degraded") setDegraded(true);
|
|
41740
41759
|
});
|
|
@@ -41747,9 +41766,9 @@ var ChainLogDegradedBanner = () => {
|
|
|
41747
41766
|
};
|
|
41748
41767
|
|
|
41749
41768
|
// src/application/ui/tui/components/progress-overlay.tsx
|
|
41750
|
-
import { promises as
|
|
41769
|
+
import { promises as fs35 } from "fs";
|
|
41751
41770
|
import { join as join71 } from "path";
|
|
41752
|
-
import { useEffect as
|
|
41771
|
+
import { useEffect as useEffect48, useState as useState53 } from "react";
|
|
41753
41772
|
import { Box as Box88, Text as Text83, useInput as useInput33 } from "ink";
|
|
41754
41773
|
import { Fragment as Fragment13, jsx as jsx106, jsxs as jsxs84 } from "react/jsx-runtime";
|
|
41755
41774
|
var CHROME_ROWS4 = 10;
|
|
@@ -41760,7 +41779,7 @@ var formatAgo = (modifiedAtMs, now) => {
|
|
|
41760
41779
|
};
|
|
41761
41780
|
var useProgressFile = (sprintId, dataRoot) => {
|
|
41762
41781
|
const [state, setState] = useState53({ kind: "loading" });
|
|
41763
|
-
|
|
41782
|
+
useEffect48(() => {
|
|
41764
41783
|
let cancelled = false;
|
|
41765
41784
|
if (sprintId === void 0) {
|
|
41766
41785
|
setState({ kind: "missing" });
|
|
@@ -41775,7 +41794,7 @@ var useProgressFile = (sprintId, dataRoot) => {
|
|
|
41775
41794
|
return;
|
|
41776
41795
|
}
|
|
41777
41796
|
const progressPath = join71(dir, "progress.md");
|
|
41778
|
-
const [stat, content] = await Promise.all([
|
|
41797
|
+
const [stat, content] = await Promise.all([fs35.stat(progressPath), fs35.readFile(progressPath, "utf8")]);
|
|
41779
41798
|
if (cancelled) return;
|
|
41780
41799
|
const modifiedAtMs = stat.mtimeMs;
|
|
41781
41800
|
if (content.trim().length === 0) {
|
|
@@ -41805,7 +41824,7 @@ var useProgressFile = (sprintId, dataRoot) => {
|
|
|
41805
41824
|
var useProgressScroll = (state, bodyRows) => {
|
|
41806
41825
|
const [offset, setOffset] = useState53(0);
|
|
41807
41826
|
const lineCount = state.kind === "ok" ? state.lines.length : 0;
|
|
41808
|
-
|
|
41827
|
+
useEffect48(() => {
|
|
41809
41828
|
setOffset(0);
|
|
41810
41829
|
}, [lineCount]);
|
|
41811
41830
|
const maxOffset = Math.max(0, lineCount - bodyRows);
|
|
@@ -42002,7 +42021,7 @@ import { useState as useState55 } from "react";
|
|
|
42002
42021
|
import { useApp as useApp2 } from "ink";
|
|
42003
42022
|
|
|
42004
42023
|
// src/application/ui/tui/migration/migration-gate.tsx
|
|
42005
|
-
import { useEffect as
|
|
42024
|
+
import { useEffect as useEffect49, useRef as useRef19, useState as useState54 } from "react";
|
|
42006
42025
|
import { Box as Box90, Text as Text84, useInput as useInput34 } from "ink";
|
|
42007
42026
|
|
|
42008
42027
|
// src/application/ui/tui/migration/learnings-backfill-adapter.ts
|
|
@@ -42050,7 +42069,7 @@ var MigrationGate = (props) => {
|
|
|
42050
42069
|
const { engine, dataRoot, stateRoot, appVersion, now, writeFile: writeFile3, onResolve, onQuit } = props;
|
|
42051
42070
|
const [state, setState] = useState54({ kind: "scanning" });
|
|
42052
42071
|
const scannedRef = useRef19(false);
|
|
42053
|
-
|
|
42072
|
+
useEffect49(() => {
|
|
42054
42073
|
if (scannedRef.current) return;
|
|
42055
42074
|
scannedRef.current = true;
|
|
42056
42075
|
let cancelled = false;
|
|
@@ -42296,7 +42315,7 @@ var MigrationRoute = ({ gate, app, onResolved }) => {
|
|
|
42296
42315
|
};
|
|
42297
42316
|
|
|
42298
42317
|
// src/integration/persistence/data-migration/version-marker.ts
|
|
42299
|
-
import { promises as
|
|
42318
|
+
import { promises as fs36 } from "fs";
|
|
42300
42319
|
import { join as join72 } from "path";
|
|
42301
42320
|
import { z as z50 } from "zod";
|
|
42302
42321
|
var CURRENT_DATA_VERSION = 2;
|
|
@@ -42309,7 +42328,7 @@ var versionMarkerPath = (dataRoot) => join72(String(dataRoot), DATA_VERSION_FILE
|
|
|
42309
42328
|
var readDataVersion = async (dataRoot) => {
|
|
42310
42329
|
let bytes;
|
|
42311
42330
|
try {
|
|
42312
|
-
bytes = await
|
|
42331
|
+
bytes = await fs36.readFile(versionMarkerPath(dataRoot), "utf8");
|
|
42313
42332
|
} catch {
|
|
42314
42333
|
return { dataVersion: 1, lastWrittenByAppVersion: "" };
|
|
42315
42334
|
}
|
|
@@ -42331,8 +42350,8 @@ var writeDataVersion = async (dataRoot, marker) => {
|
|
|
42331
42350
|
const body = `${JSON.stringify(marker, null, 2)}
|
|
42332
42351
|
`;
|
|
42333
42352
|
try {
|
|
42334
|
-
await
|
|
42335
|
-
await
|
|
42353
|
+
await fs36.mkdir(String(dataRoot), { recursive: true });
|
|
42354
|
+
await fs36.writeFile(path2, body, "utf8");
|
|
42336
42355
|
return Result.ok(void 0);
|
|
42337
42356
|
} catch (cause) {
|
|
42338
42357
|
return Result.error(new StorageError({ subCode: "io", message: `marker write failed: ${path2}`, path: path2, cause }));
|
|
@@ -42344,7 +42363,7 @@ var needsMigration = async (dataRoot) => {
|
|
|
42344
42363
|
};
|
|
42345
42364
|
|
|
42346
42365
|
// src/integration/persistence/data-migration/dry-run.ts
|
|
42347
|
-
import { promises as
|
|
42366
|
+
import { promises as fs37 } from "fs";
|
|
42348
42367
|
import { join as join73 } from "path";
|
|
42349
42368
|
var PROJECTS_DIR = "projects";
|
|
42350
42369
|
var SPRINTS_DIR = "sprints";
|
|
@@ -42363,7 +42382,7 @@ var dryRun = async (dataRoot) => {
|
|
|
42363
42382
|
var checkBackupTargetWritable = async (dataRoot, acc) => {
|
|
42364
42383
|
const parent = join73(String(dataRoot), "..");
|
|
42365
42384
|
try {
|
|
42366
|
-
await
|
|
42385
|
+
await fs37.access(parent, fs37.constants.W_OK);
|
|
42367
42386
|
} catch {
|
|
42368
42387
|
acc.problems.push({
|
|
42369
42388
|
name: parent,
|
|
@@ -42481,7 +42500,7 @@ var planRename = async (parent, fromName, toName, id, slug, kind, acc) => {
|
|
|
42481
42500
|
};
|
|
42482
42501
|
var pathPresent = async (path2) => {
|
|
42483
42502
|
try {
|
|
42484
|
-
await
|
|
42503
|
+
await fs37.stat(path2);
|
|
42485
42504
|
return true;
|
|
42486
42505
|
} catch {
|
|
42487
42506
|
return false;
|
|
@@ -42489,7 +42508,7 @@ var pathPresent = async (path2) => {
|
|
|
42489
42508
|
};
|
|
42490
42509
|
var isDirectory = async (path2) => {
|
|
42491
42510
|
try {
|
|
42492
|
-
return (await
|
|
42511
|
+
return (await fs37.stat(path2)).isDirectory();
|
|
42493
42512
|
} catch {
|
|
42494
42513
|
return false;
|
|
42495
42514
|
}
|
|
@@ -42512,7 +42531,7 @@ var readProjectSlugForId = async (dataRoot, projectId) => {
|
|
|
42512
42531
|
var readSlugField = async (filePath) => {
|
|
42513
42532
|
let bytes;
|
|
42514
42533
|
try {
|
|
42515
|
-
bytes = await
|
|
42534
|
+
bytes = await fs37.readFile(filePath, "utf8");
|
|
42516
42535
|
} catch {
|
|
42517
42536
|
return void 0;
|
|
42518
42537
|
}
|
|
@@ -42529,11 +42548,11 @@ var readSlugField = async (filePath) => {
|
|
|
42529
42548
|
};
|
|
42530
42549
|
|
|
42531
42550
|
// src/integration/persistence/data-migration/apply.ts
|
|
42532
|
-
import { promises as
|
|
42551
|
+
import { promises as fs40 } from "fs";
|
|
42533
42552
|
import { join as join76 } from "path";
|
|
42534
42553
|
|
|
42535
42554
|
// src/integration/persistence/data-migration/backup.ts
|
|
42536
|
-
import { promises as
|
|
42555
|
+
import { promises as fs38 } from "fs";
|
|
42537
42556
|
import { basename as basename7, dirname as dirname22, join as join74 } from "path";
|
|
42538
42557
|
var BACKUP_DIR_PREFIX = "data.backup-";
|
|
42539
42558
|
var backupDirName = (fromVersion, timestamp) => `${BACKUP_DIR_PREFIX}v${String(fromVersion)}-${sanitizeTimestamp(timestamp)}`;
|
|
@@ -42541,7 +42560,7 @@ var backupDataDir = async (dataRoot, fromVersion, timestamp) => {
|
|
|
42541
42560
|
const src = String(dataRoot);
|
|
42542
42561
|
const dest = join74(dirname22(src), backupDirName(fromVersion, timestamp));
|
|
42543
42562
|
try {
|
|
42544
|
-
await
|
|
42563
|
+
await fs38.cp(src, dest, {
|
|
42545
42564
|
recursive: true,
|
|
42546
42565
|
// Follow symlinks so the backup captures their real CONTENT, not a dangling node. The default
|
|
42547
42566
|
// (`dereference: false`) copies a symlink as a link, which would leave the backup incomplete for
|
|
@@ -42563,7 +42582,7 @@ var backupDataDir = async (dataRoot, fromVersion, timestamp) => {
|
|
|
42563
42582
|
);
|
|
42564
42583
|
}
|
|
42565
42584
|
try {
|
|
42566
|
-
const stat = await
|
|
42585
|
+
const stat = await fs38.stat(dest);
|
|
42567
42586
|
if (!stat.isDirectory()) {
|
|
42568
42587
|
return Result.error(
|
|
42569
42588
|
new StorageError({ subCode: "io", message: `backup verify failed: ${dest} is not a directory`, path: dest })
|
|
@@ -42585,8 +42604,8 @@ var backupDataDir = async (dataRoot, fromVersion, timestamp) => {
|
|
|
42585
42604
|
};
|
|
42586
42605
|
var verifyEntryCount = async (src, dest) => {
|
|
42587
42606
|
try {
|
|
42588
|
-
const srcEntries = (await
|
|
42589
|
-
const destEntries = await
|
|
42607
|
+
const srcEntries = (await fs38.readdir(src)).filter((name) => !name.startsWith(BACKUP_DIR_PREFIX));
|
|
42608
|
+
const destEntries = await fs38.readdir(dest);
|
|
42590
42609
|
if (destEntries.length < srcEntries.length) {
|
|
42591
42610
|
return Result.error(
|
|
42592
42611
|
new StorageError({
|
|
@@ -42606,10 +42625,10 @@ var verifyEntryCount = async (src, dest) => {
|
|
|
42606
42625
|
var sanitizeTimestamp = (timestamp) => timestamp.replace(/:/g, "-");
|
|
42607
42626
|
|
|
42608
42627
|
// src/integration/persistence/data-migration/lock-guard.ts
|
|
42609
|
-
import { promises as
|
|
42628
|
+
import { promises as fs39 } from "fs";
|
|
42610
42629
|
import { join as join75 } from "path";
|
|
42611
42630
|
var LOCKS_SUBDIR2 = "locks";
|
|
42612
|
-
var HELD_WITHIN_MS =
|
|
42631
|
+
var HELD_WITHIN_MS = DEFAULT_STALE_AFTER_MS;
|
|
42613
42632
|
var anyLockHeld = async (stateRoot) => {
|
|
42614
42633
|
const locksDir = join75(String(stateRoot), LOCKS_SUBDIR2);
|
|
42615
42634
|
const entries = await listDir(locksDir);
|
|
@@ -42619,7 +42638,7 @@ var anyLockHeld = async (stateRoot) => {
|
|
|
42619
42638
|
if (!name.endsWith(".lock")) continue;
|
|
42620
42639
|
let mtimeMs;
|
|
42621
42640
|
try {
|
|
42622
|
-
({ mtimeMs } = await
|
|
42641
|
+
({ mtimeMs } = await fs39.lstat(join75(locksDir, name)));
|
|
42623
42642
|
} catch {
|
|
42624
42643
|
continue;
|
|
42625
42644
|
}
|
|
@@ -42712,7 +42731,7 @@ var mergeOne = async (merge, ctx) => {
|
|
|
42712
42731
|
};
|
|
42713
42732
|
var readTextOrEmpty = async (path2) => {
|
|
42714
42733
|
try {
|
|
42715
|
-
return await
|
|
42734
|
+
return await fs40.readFile(path2, "utf8");
|
|
42716
42735
|
} catch {
|
|
42717
42736
|
return "";
|
|
42718
42737
|
}
|
|
@@ -42730,7 +42749,7 @@ var backfillLearningsMd = async (dataRoot, ctx) => {
|
|
|
42730
42749
|
if (await pathPresent2(mdPath)) continue;
|
|
42731
42750
|
let body;
|
|
42732
42751
|
try {
|
|
42733
|
-
body = await
|
|
42752
|
+
body = await fs40.readFile(ledgerPath, "utf8");
|
|
42734
42753
|
} catch {
|
|
42735
42754
|
continue;
|
|
42736
42755
|
}
|
|
@@ -42743,7 +42762,7 @@ var backfillLearningsMd = async (dataRoot, ctx) => {
|
|
|
42743
42762
|
};
|
|
42744
42763
|
var ledgerExceedsCeiling = async (ledgerPath) => {
|
|
42745
42764
|
try {
|
|
42746
|
-
const { size } = await
|
|
42765
|
+
const { size } = await fs40.stat(ledgerPath);
|
|
42747
42766
|
return size > MIRROR_BACKFILL_CEILING_BYTES;
|
|
42748
42767
|
} catch {
|
|
42749
42768
|
return false;
|
|
@@ -42751,7 +42770,7 @@ var ledgerExceedsCeiling = async (ledgerPath) => {
|
|
|
42751
42770
|
};
|
|
42752
42771
|
var pathPresent2 = async (path2) => {
|
|
42753
42772
|
try {
|
|
42754
|
-
await
|
|
42773
|
+
await fs40.stat(path2);
|
|
42755
42774
|
return true;
|
|
42756
42775
|
} catch {
|
|
42757
42776
|
return false;
|
|
@@ -42797,7 +42816,7 @@ var resolveInitialState = ({
|
|
|
42797
42816
|
};
|
|
42798
42817
|
|
|
42799
42818
|
// src/integration/persistence/selection/last-selection-store.ts
|
|
42800
|
-
import { promises as
|
|
42819
|
+
import { promises as fs41 } from "fs";
|
|
42801
42820
|
import { join as join77 } from "path";
|
|
42802
42821
|
var FILE_NAME = "last-selection.json";
|
|
42803
42822
|
var createLastSelectionStore = (stateRoot) => {
|
|
@@ -42805,7 +42824,7 @@ var createLastSelectionStore = (stateRoot) => {
|
|
|
42805
42824
|
return {
|
|
42806
42825
|
async read() {
|
|
42807
42826
|
try {
|
|
42808
|
-
const raw = await
|
|
42827
|
+
const raw = await fs41.readFile(path2, "utf8");
|
|
42809
42828
|
const parsed = JSON.parse(raw);
|
|
42810
42829
|
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
42811
42830
|
const rec = parsed;
|
|
@@ -42823,10 +42842,10 @@ var createLastSelectionStore = (stateRoot) => {
|
|
|
42823
42842
|
async write(value) {
|
|
42824
42843
|
try {
|
|
42825
42844
|
if (value === void 0) {
|
|
42826
|
-
await
|
|
42845
|
+
await fs41.rm(path2, { force: true });
|
|
42827
42846
|
return;
|
|
42828
42847
|
}
|
|
42829
|
-
await
|
|
42848
|
+
await fs41.writeFile(path2, JSON.stringify(value, null, 2), "utf8");
|
|
42830
42849
|
} catch {
|
|
42831
42850
|
}
|
|
42832
42851
|
}
|
|
@@ -42925,13 +42944,13 @@ var startHeapWatchdog = (deps) => {
|
|
|
42925
42944
|
};
|
|
42926
42945
|
|
|
42927
42946
|
// src/integration/observability/heap-snapshot.ts
|
|
42928
|
-
import * as
|
|
42947
|
+
import * as fs42 from "fs";
|
|
42929
42948
|
import * as path from "path";
|
|
42930
42949
|
import * as v82 from "v8";
|
|
42931
42950
|
var snapshotFilename = (timestamp) => `heap-${timestamp.replace(/[:.]/g, "-")}.heapsnapshot`;
|
|
42932
42951
|
var writeHeapSnapshotToDir = (dir, clock) => {
|
|
42933
42952
|
try {
|
|
42934
|
-
|
|
42953
|
+
fs42.mkdirSync(dir, { recursive: true });
|
|
42935
42954
|
const timestamp = clock?.() ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
42936
42955
|
const fullPath = path.join(dir, snapshotFilename(timestamp));
|
|
42937
42956
|
v82.writeHeapSnapshot(fullPath);
|
|
@@ -44452,11 +44471,11 @@ var formatTaskLine = (t) => {
|
|
|
44452
44471
|
};
|
|
44453
44472
|
|
|
44454
44473
|
// src/application/ui/cli/commands/runs.ts
|
|
44455
|
-
import { promises as
|
|
44474
|
+
import { promises as fs44 } from "fs";
|
|
44456
44475
|
import { createInterface as createInterface2 } from "readline";
|
|
44457
44476
|
|
|
44458
44477
|
// src/integration/ai/runs/_engine/run-enumeration.ts
|
|
44459
|
-
import { promises as
|
|
44478
|
+
import { promises as fs43 } from "fs";
|
|
44460
44479
|
import { join as join80 } from "path";
|
|
44461
44480
|
var parseRunTimestamp = (runDirName) => {
|
|
44462
44481
|
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z-/.exec(runDirName);
|
|
@@ -44557,7 +44576,7 @@ var listRuns = async (runsRoot) => {
|
|
|
44557
44576
|
const root = String(runsRoot);
|
|
44558
44577
|
let flowDirs;
|
|
44559
44578
|
try {
|
|
44560
|
-
flowDirs = await
|
|
44579
|
+
flowDirs = await fs43.readdir(root, { withFileTypes: true });
|
|
44561
44580
|
} catch (cause) {
|
|
44562
44581
|
if (isErrnoException2(cause) && cause.code === "ENOENT") return Result.ok([]);
|
|
44563
44582
|
return Result.error(
|
|
@@ -44574,7 +44593,7 @@ var listRuns = async (runsRoot) => {
|
|
|
44574
44593
|
const flowPath = join80(root, flowDir.name);
|
|
44575
44594
|
let runDirs;
|
|
44576
44595
|
try {
|
|
44577
|
-
runDirs = await
|
|
44596
|
+
runDirs = await fs43.readdir(flowPath, { withFileTypes: true });
|
|
44578
44597
|
} catch (cause) {
|
|
44579
44598
|
if (isErrnoException2(cause) && cause.code === "ENOENT") continue;
|
|
44580
44599
|
return Result.error(
|
|
@@ -44619,7 +44638,7 @@ var computeDirSize = async (dir) => {
|
|
|
44619
44638
|
let total = 0;
|
|
44620
44639
|
let entries;
|
|
44621
44640
|
try {
|
|
44622
|
-
entries = await
|
|
44641
|
+
entries = await fs43.readdir(dir, { withFileTypes: true });
|
|
44623
44642
|
} catch {
|
|
44624
44643
|
return 0;
|
|
44625
44644
|
}
|
|
@@ -44630,7 +44649,7 @@ var computeDirSize = async (dir) => {
|
|
|
44630
44649
|
continue;
|
|
44631
44650
|
}
|
|
44632
44651
|
try {
|
|
44633
|
-
const stat = await
|
|
44652
|
+
const stat = await fs43.lstat(entryPath);
|
|
44634
44653
|
if (stat.isFile()) total += stat.size;
|
|
44635
44654
|
} catch {
|
|
44636
44655
|
}
|
|
@@ -44951,7 +44970,7 @@ var performPrune = async (candidates) => {
|
|
|
44951
44970
|
let freedCount = 0;
|
|
44952
44971
|
for (const candidate of candidates) {
|
|
44953
44972
|
try {
|
|
44954
|
-
await
|
|
44973
|
+
await fs44.rm(String(candidate.path), { recursive: true, force: false });
|
|
44955
44974
|
freedBytes += candidate.sizeBytes;
|
|
44956
44975
|
freedCount += 1;
|
|
44957
44976
|
} catch (cause) {
|