@sleds/cli 0.1.37 → 0.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sleds.mjs +185 -11
- package/package.json +1 -1
package/dist/sleds.mjs
CHANGED
|
@@ -7952,7 +7952,8 @@ function parseRun(v) {
|
|
|
7952
7952
|
...typeof v.pr.conflict === "string" ? { conflict: v.pr.conflict } : {},
|
|
7953
7953
|
...typeof v.pr.resolving === "string" ? { resolving: v.pr.resolving } : {},
|
|
7954
7954
|
...conflicted ? { conflicted } : {},
|
|
7955
|
-
...typeof v.pr.reviewed === "string" ? { reviewed: v.pr.reviewed } : {}
|
|
7955
|
+
...typeof v.pr.reviewed === "string" ? { reviewed: v.pr.reviewed } : {},
|
|
7956
|
+
...typeof v.pr.look === "string" ? { look: v.pr.look } : {}
|
|
7956
7957
|
}
|
|
7957
7958
|
} : {}
|
|
7958
7959
|
};
|
|
@@ -8023,6 +8024,7 @@ function parseLock(text) {
|
|
|
8023
8024
|
runner: typeof v.runner === "string" ? v.runner : "",
|
|
8024
8025
|
atOnce: n(v.atOnce, 1),
|
|
8025
8026
|
interval: n(v.interval, 0),
|
|
8027
|
+
...typeof v.cliVersion === "string" && v.cliVersion ? { cliVersion: v.cliVersion } : {},
|
|
8026
8028
|
...typeof v.checkedAt === "number" ? { checkedAt: v.checkedAt } : {},
|
|
8027
8029
|
...typeof v.nextCheckAt === "number" ? { nextCheckAt: v.nextCheckAt } : {},
|
|
8028
8030
|
...machine ? { machine } : {},
|
|
@@ -10345,7 +10347,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10345
10347
|
name: flags.name,
|
|
10346
10348
|
os: host.os ?? process.platform,
|
|
10347
10349
|
runner: flags.runner,
|
|
10348
|
-
version: host.version ?? "0.1.
|
|
10350
|
+
version: host.version ?? "0.1.38" ?? "dev",
|
|
10349
10351
|
running: live.length,
|
|
10350
10352
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
10351
10353
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -10657,6 +10659,15 @@ function prForBranch(exec, repoPath, branch) {
|
|
|
10657
10659
|
return { error: e instanceof Error ? e.message : String(e) };
|
|
10658
10660
|
}
|
|
10659
10661
|
}
|
|
10662
|
+
var URL_RE = /https:\/\/[^\s)>\]"']+/;
|
|
10663
|
+
var ARTIFACT_RE = /https:\/\/claude\.ai\/[^\s)>\]"']*artifact\/[^\s)>\]"']+/;
|
|
10664
|
+
function mockOf(body) {
|
|
10665
|
+
const header = /<!--\s*sleds:header\s*-->([\s\S]*?)<!--\s*\/sleds:header\s*-->/.exec(body)?.[1];
|
|
10666
|
+
const line = header?.split(`
|
|
10667
|
+
`).find((l) => /\*\*Mocks\*\*\s*:/i.test(l));
|
|
10668
|
+
const fromHeader = line ? URL_RE.exec(line)?.[0] : undefined;
|
|
10669
|
+
return fromHeader ?? ARTIFACT_RE.exec(body)?.[0];
|
|
10670
|
+
}
|
|
10660
10671
|
var FAILED = new Set([
|
|
10661
10672
|
"FAILURE",
|
|
10662
10673
|
"ERROR",
|
|
@@ -10702,6 +10713,7 @@ function parsePrView(stdout) {
|
|
|
10702
10713
|
pending++;
|
|
10703
10714
|
}
|
|
10704
10715
|
const mergeCommit = typeof o.mergeCommit === "object" && o.mergeCommit !== null ? o.mergeCommit.oid : undefined;
|
|
10716
|
+
const mock = typeof o.body === "string" ? mockOf(o.body) : undefined;
|
|
10705
10717
|
return {
|
|
10706
10718
|
state: o.state,
|
|
10707
10719
|
author: loginOf(o.author),
|
|
@@ -10712,6 +10724,8 @@ function parsePrView(stdout) {
|
|
|
10712
10724
|
reviewDecision: typeof o.reviewDecision === "string" ? o.reviewDecision : "",
|
|
10713
10725
|
feedback: 0,
|
|
10714
10726
|
authorFeedback: 0,
|
|
10727
|
+
draft: o.isDraft === true,
|
|
10728
|
+
...mock ? { mock } : {},
|
|
10715
10729
|
...typeof o.headRefOid === "string" && o.headRefOid ? { head: o.headRefOid } : {},
|
|
10716
10730
|
...typeof mergeCommit === "string" && mergeCommit ? { mergeCommit } : {}
|
|
10717
10731
|
};
|
|
@@ -10766,7 +10780,7 @@ function prView(exec, repoPath, url) {
|
|
|
10766
10780
|
"view",
|
|
10767
10781
|
url,
|
|
10768
10782
|
"--json",
|
|
10769
|
-
"state,author,statusCheckRollup,reviewDecision,comments,reviews,headRefOid,mergeable,baseRefName,mergeCommit"
|
|
10783
|
+
"state,author,statusCheckRollup,reviewDecision,comments,reviews,headRefOid,mergeable,baseRefName,mergeCommit,isDraft,body"
|
|
10770
10784
|
], { cwd: repoPath });
|
|
10771
10785
|
const view = r.status === 0 ? parsePrView(r.stdout) : null;
|
|
10772
10786
|
if (!view || view.state !== "OPEN")
|
|
@@ -11232,6 +11246,10 @@ init_machine();
|
|
|
11232
11246
|
|
|
11233
11247
|
// src/engine/sledsSource.ts
|
|
11234
11248
|
init_src();
|
|
11249
|
+
function looksOf(s) {
|
|
11250
|
+
const l = s;
|
|
11251
|
+
return l && typeof l.openLook === "function" && typeof l.clearLook === "function" ? s : null;
|
|
11252
|
+
}
|
|
11235
11253
|
var FAIL_LOG_MAX = 2000;
|
|
11236
11254
|
var UNREACHABLE2 = "SLEDS unreachable";
|
|
11237
11255
|
function keysIn(states, state) {
|
|
@@ -11373,20 +11391,34 @@ var PROFILE_TEXT = [
|
|
|
11373
11391
|
"reposDir",
|
|
11374
11392
|
"engine"
|
|
11375
11393
|
];
|
|
11394
|
+
var SHELL_TEXT = ["agent", "launcher"];
|
|
11376
11395
|
function profileOf(r) {
|
|
11377
11396
|
if (!r || r.ok === false || !isObject8(r.profile))
|
|
11378
11397
|
return null;
|
|
11379
11398
|
const p = r.profile;
|
|
11399
|
+
const member = isObject8(r.member) ? r.member : {};
|
|
11380
11400
|
const out = {
|
|
11381
11401
|
pickup: p.pickup === "auto" ? "auto" : "approve",
|
|
11382
11402
|
...HOLD_DEFAULTS2,
|
|
11383
11403
|
holdOff: p.holdOff === true
|
|
11384
11404
|
};
|
|
11385
11405
|
for (const k of PROFILE_TEXT) {
|
|
11406
|
+
if (SHELL_TEXT.includes(k))
|
|
11407
|
+
continue;
|
|
11386
11408
|
const v = str6(p[k])?.trim();
|
|
11387
11409
|
if (v)
|
|
11388
11410
|
out[k] = v;
|
|
11389
11411
|
}
|
|
11412
|
+
const notYours = [];
|
|
11413
|
+
for (const k of SHELL_TEXT) {
|
|
11414
|
+
const mine = str6(member[k])?.trim();
|
|
11415
|
+
if (mine)
|
|
11416
|
+
out[k] = mine;
|
|
11417
|
+
else if (str6(p[k])?.trim())
|
|
11418
|
+
notYours.push(k);
|
|
11419
|
+
}
|
|
11420
|
+
if (notYours.length)
|
|
11421
|
+
out.notYours = notYours;
|
|
11390
11422
|
const atOnce = num5(p.atOnce);
|
|
11391
11423
|
if (atOnce !== undefined && atOnce >= 1)
|
|
11392
11424
|
out.atOnce = Math.round(atOnce);
|
|
@@ -11746,6 +11778,36 @@ function sledsSource(client, o) {
|
|
|
11746
11778
|
throw new Error(serverMessage(r));
|
|
11747
11779
|
return eventOpenOf(r.json);
|
|
11748
11780
|
},
|
|
11781
|
+
async openLook(runId, look, engine) {
|
|
11782
|
+
const r = await client.postFull("/proxy/run", {
|
|
11783
|
+
runId,
|
|
11784
|
+
engine,
|
|
11785
|
+
event: {
|
|
11786
|
+
kind: "look",
|
|
11787
|
+
md: look.md,
|
|
11788
|
+
about: { prs: [look.pr], ...look.mock ? { mock: look.mock } : {} }
|
|
11789
|
+
}
|
|
11790
|
+
});
|
|
11791
|
+
if (!r)
|
|
11792
|
+
throw new Error(`${UNREACHABLE2} — the look not raised`);
|
|
11793
|
+
if (r.status >= 300 || !r.json || r.json.ok === false)
|
|
11794
|
+
throw new Error(serverMessage(r));
|
|
11795
|
+
const decisionId = str6(r.json.decisionId);
|
|
11796
|
+
const state = str6(r.json.state);
|
|
11797
|
+
return {
|
|
11798
|
+
opened: r.json.opened === true,
|
|
11799
|
+
...decisionId ? { decisionId } : {},
|
|
11800
|
+
...state ? { state } : {}
|
|
11801
|
+
};
|
|
11802
|
+
},
|
|
11803
|
+
async clearLook(runId, clear, engine) {
|
|
11804
|
+
const r = await client.postFull("/proxy/run", { runId, engine, clearLook: clear });
|
|
11805
|
+
if (!r)
|
|
11806
|
+
throw new Error(`${UNREACHABLE2} — the look not cleared`);
|
|
11807
|
+
if (r.status >= 300 || !r.json || r.json.ok === false)
|
|
11808
|
+
throw new Error(serverMessage(r));
|
|
11809
|
+
return r.json.cleared === true;
|
|
11810
|
+
},
|
|
11749
11811
|
async decisionOf(item, decisionId) {
|
|
11750
11812
|
const route = await routeOf(item);
|
|
11751
11813
|
const decisions = Array.isArray(route?.decisions) ? route.decisions.filter(isObject8) : [];
|
|
@@ -12207,9 +12269,13 @@ async function tick(deps) {
|
|
|
12207
12269
|
deps.providerHold = d.providerHold;
|
|
12208
12270
|
deps.atOnce = d.atOnce;
|
|
12209
12271
|
}
|
|
12272
|
+
removeEndedMcpConfigs(deps, made);
|
|
12210
12273
|
}
|
|
12274
|
+
return report;
|
|
12275
|
+
}
|
|
12276
|
+
function removeEndedMcpConfigs(deps, made) {
|
|
12211
12277
|
if (!deps.removeMcpConfig || made.size === 0)
|
|
12212
|
-
return
|
|
12278
|
+
return;
|
|
12213
12279
|
const state = deps.readState();
|
|
12214
12280
|
const live = liveMcpConfigs(state);
|
|
12215
12281
|
for (const p of made) {
|
|
@@ -12227,7 +12293,6 @@ async function tick(deps) {
|
|
|
12227
12293
|
r.mcpConfig = undefined;
|
|
12228
12294
|
deps.writeState(state);
|
|
12229
12295
|
}
|
|
12230
|
-
return report;
|
|
12231
12296
|
}
|
|
12232
12297
|
async function tickOnce(d) {
|
|
12233
12298
|
d.tickProfile = profileOnce(d.readProfile);
|
|
@@ -13513,11 +13578,47 @@ var SELF_NUDGES = 2;
|
|
|
13513
13578
|
function reviewNudge(url, count) {
|
|
13514
13579
|
return `New review feedback on your PR (${count} ${count === 1 ? "comment" : "comments"} from people so far): ${url}. Read all of it (gh pr view ${url} --comments, and the inline threads), answer each comment on its own thread with the fix or a reasoned reply, push, and leave the threads for the reviewer to resolve. End every comment and reply you post on the PR with the line ${ENGINE_MARK} so the engine knows it is yours and not the reviewer's.`;
|
|
13515
13580
|
}
|
|
13581
|
+
function lookQuestion(url, mock) {
|
|
13582
|
+
return `A draft PR waits for your look: ${url}.${mock ? ` The mock: ${mock}.` : " Its body names no mock; the PR is the place to look."} Looks good marks it ready for review.`;
|
|
13583
|
+
}
|
|
13584
|
+
async function judgeLook(d, run, view) {
|
|
13585
|
+
const looks = looksOf(d.source);
|
|
13586
|
+
const pr = run.pr;
|
|
13587
|
+
if (!looks || !run.runId)
|
|
13588
|
+
return;
|
|
13589
|
+
const draft = view.state === "OPEN" && view.draft;
|
|
13590
|
+
if (draft && !pr.look) {
|
|
13591
|
+
try {
|
|
13592
|
+
const r = await looks.openLook(run.runId, {
|
|
13593
|
+
md: lookQuestion(pr.url, view.mock),
|
|
13594
|
+
pr: pr.url,
|
|
13595
|
+
...view.mock ? { mock: view.mock } : {}
|
|
13596
|
+
}, d.engine);
|
|
13597
|
+
if (!r.decisionId)
|
|
13598
|
+
return;
|
|
13599
|
+
pr.look = r.decisionId;
|
|
13600
|
+
if (r.opened)
|
|
13601
|
+
d.log(`${ref(run)}: ${pr.url} is a draft — a look waits for a person`);
|
|
13602
|
+
} catch (e) {
|
|
13603
|
+
d.log(`${ref(run)}: could not raise the look on ${pr.url}: ${message(e)}`);
|
|
13604
|
+
}
|
|
13605
|
+
} else if (!draft && pr.look) {
|
|
13606
|
+
const why = view.state === "MERGED" ? "the PR merged" : view.state === "CLOSED" ? "the PR closed without a merge" : "the PR left draft";
|
|
13607
|
+
try {
|
|
13608
|
+
if (await looks.clearLook(run.runId, { pr: pr.url, why }, d.engine))
|
|
13609
|
+
d.log(`${ref(run)}: the look on ${pr.url} is cleared — ${why}`);
|
|
13610
|
+
pr.look = undefined;
|
|
13611
|
+
} catch (e) {
|
|
13612
|
+
d.log(`${ref(run)}: could not clear the look on ${pr.url}: ${message(e)}`);
|
|
13613
|
+
}
|
|
13614
|
+
}
|
|
13615
|
+
}
|
|
13516
13616
|
async function follow(d, state, run, claim, runner, finished, attention, changes, decided) {
|
|
13517
13617
|
const pr = run.pr;
|
|
13518
13618
|
const view = d.prView(d.exec, run.repoPath, pr.url);
|
|
13519
13619
|
if (!view)
|
|
13520
13620
|
return true;
|
|
13621
|
+
await judgeLook(d, run, view);
|
|
13521
13622
|
if (view.state === "MERGED") {
|
|
13522
13623
|
const landed = await ripple(d, state, run, claim, runner, pr.url, finished, decided, view.head, view.mergeCommit);
|
|
13523
13624
|
return !landed;
|
|
@@ -15429,6 +15530,8 @@ function flagsFromProfile(flags, given, profile, home) {
|
|
|
15429
15530
|
const out = { ...flags };
|
|
15430
15531
|
const used = [];
|
|
15431
15532
|
const skipped = [];
|
|
15533
|
+
for (const k of profile.notYours ?? [])
|
|
15534
|
+
skipped.push(`${k} (the workspace default — the engine runs only one you set yourself)`);
|
|
15432
15535
|
if (profile.atOnce !== undefined && !has("at-once")) {
|
|
15433
15536
|
out.atOnce = profile.atOnce;
|
|
15434
15537
|
used.push(`at-once ${profile.atOnce}`);
|
|
@@ -15819,7 +15922,8 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
15819
15922
|
startedAt: (o.now ?? Date.now)(),
|
|
15820
15923
|
runner: deps.runner.name,
|
|
15821
15924
|
atOnce: flags.atOnce,
|
|
15822
|
-
interval: flags.interval
|
|
15925
|
+
interval: flags.interval,
|
|
15926
|
+
cliVersion: "0.1.38"
|
|
15823
15927
|
};
|
|
15824
15928
|
const beat = (checkedAt) => {
|
|
15825
15929
|
const m = deps.machine;
|
|
@@ -17421,6 +17525,8 @@ function flagsFromProfile2(flags, given, profile, home) {
|
|
|
17421
17525
|
const out = { ...flags };
|
|
17422
17526
|
const used = [];
|
|
17423
17527
|
const skipped = [];
|
|
17528
|
+
for (const k of profile.notYours ?? [])
|
|
17529
|
+
skipped.push(`${k} (the workspace default — the engine runs only one you set yourself)`);
|
|
17424
17530
|
if (profile.atOnce !== undefined && !has("at-once")) {
|
|
17425
17531
|
out.atOnce = profile.atOnce;
|
|
17426
17532
|
used.push(`at-once ${profile.atOnce}`);
|
|
@@ -17849,7 +17955,8 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
17849
17955
|
startedAt: (o.now ?? Date.now)(),
|
|
17850
17956
|
runner: deps.runner.name,
|
|
17851
17957
|
atOnce: flags.atOnce,
|
|
17852
|
-
interval: flags.interval
|
|
17958
|
+
interval: flags.interval,
|
|
17959
|
+
cliVersion: "0.1.38"
|
|
17853
17960
|
};
|
|
17854
17961
|
const beat = (checkedAt) => {
|
|
17855
17962
|
const m = deps.machine;
|
|
@@ -18827,6 +18934,7 @@ function engineStatus(i) {
|
|
|
18827
18934
|
...opt("runner", lock.runner || undefined),
|
|
18828
18935
|
atOnce: lock.atOnce,
|
|
18829
18936
|
interval: lock.interval,
|
|
18937
|
+
cliVersion: lock.cliVersion ?? "",
|
|
18830
18938
|
...opt("checkedAt", iso2(lock.checkedAt)),
|
|
18831
18939
|
...opt("nextCheckAt", alive ? iso2(lock.nextCheckAt) : undefined),
|
|
18832
18940
|
...opt("machine", lock.machine ? { ...lock.machine, at: new Date(lock.machine.at).toISOString() } : undefined),
|
|
@@ -20764,8 +20872,9 @@ var mcp = async (o) => {
|
|
|
20764
20872
|
(o.warn ?? ((line) => process.stderr.write(`${line}
|
|
20765
20873
|
`)))("sleds mcp: on this run's token (SLEDS_RUN_TOKEN), not the seat's");
|
|
20766
20874
|
let buffer = "";
|
|
20875
|
+
const decoder = new TextDecoder;
|
|
20767
20876
|
for await (const chunk of stdin) {
|
|
20768
|
-
buffer += typeof chunk === "string" ? chunk :
|
|
20877
|
+
buffer += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
|
|
20769
20878
|
let nl = buffer.indexOf(`
|
|
20770
20879
|
`);
|
|
20771
20880
|
while (nl !== -1) {
|
|
@@ -20778,6 +20887,7 @@ var mcp = async (o) => {
|
|
|
20778
20887
|
`);
|
|
20779
20888
|
}
|
|
20780
20889
|
}
|
|
20890
|
+
buffer += decoder.decode();
|
|
20781
20891
|
if (buffer.trim().length > 0) {
|
|
20782
20892
|
const out = await handleLine2(buffer, { home: o.home, env });
|
|
20783
20893
|
if (out !== null)
|
|
@@ -21381,13 +21491,13 @@ init_src();
|
|
|
21381
21491
|
import { readFileSync as readFileSync24 } from "node:fs";
|
|
21382
21492
|
import { hostname as hostname12 } from "node:os";
|
|
21383
21493
|
var ARM_USAGE = "usage: sleds route arm <key> [--off] [--json] [--name <engine>] [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--mode <permission-mode>] [--model <id>] [--effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--repos-dir <dir>]";
|
|
21384
|
-
var USAGE = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route decisions [<key>] [--flagged] | sleds route decision <key> <id> --seen | --answer --stdin [--route accept|keep|redraft] | --keep-route | --apply [--answer --stdin] | --overrule --stdin | sleds route run <key> [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--interval <s>] [--lease <min>] [--idle <min>] [--repo <path>] [--repos-dir <dir>] [--mode <permission-mode>] [--model <id>] [--branch '<template>'] [--pr-title '<template>'] [--once]";
|
|
21494
|
+
var USAGE = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route decisions [<key>] [--flagged] | sleds route decision <key> <id> --seen | --answer --stdin [--route accept|keep|redraft] | --keep-route | --apply [--answer --stdin] | --overrule --stdin | --ready | sleds route run <key> [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--interval <s>] [--lease <min>] [--idle <min>] [--repo <path>] [--repos-dir <dir>] [--mode <permission-mode>] [--model <id>] [--branch '<template>'] [--pr-title '<template>'] [--once]";
|
|
21385
21495
|
var ANSWER_USAGE = "usage: sleds route answer <key>/<node> <answer…> | sleds route answer <key>/<node> --stdin [--json]";
|
|
21386
21496
|
var PROPOSE_USAGE = "usage: sleds route propose <key> (--file <nodes.json> | --stdin) [--name <engine>] [--json]";
|
|
21387
21497
|
var APPROVE_USAGE = "usage: sleds route approve <key> [--file <nodes.json>] [--json]";
|
|
21388
21498
|
var REDRAFT_USAGE = "usage: sleds route redraft <key> [--json]";
|
|
21389
21499
|
var SINGLE_USAGE = "usage: sleds route single <key> [--json]";
|
|
21390
|
-
var DECISION_USAGE = "usage: sleds route decision <key> <id> --seen | sleds route decision <key> <id> (--answer | --overrule) (<answer…> | --stdin) | sleds route decision <key> <id> [--answer (<answer…> | --stdin)] --route accept|keep|redraft | sleds route decision <key> <id> --keep-route | sleds route decision <key> <id> --apply [--answer (<answer…> | --stdin)] [--json]";
|
|
21500
|
+
var DECISION_USAGE = "usage: sleds route decision <key> <id> --seen | sleds route decision <key> <id> (--answer | --overrule) (<answer…> | --stdin) | sleds route decision <key> <id> [--answer (<answer…> | --stdin)] --route accept|keep|redraft | sleds route decision <key> <id> --keep-route | sleds route decision <key> <id> --apply [--answer (<answer…> | --stdin)] | sleds route decision <key> <id> --ready [--json]";
|
|
21391
21501
|
var ROUTE_VERDICTS = ["accept", "keep", "redraft"];
|
|
21392
21502
|
function nodesFromJson(text) {
|
|
21393
21503
|
let parsed;
|
|
@@ -21716,6 +21826,8 @@ function appliedLine(key, applied) {
|
|
|
21716
21826
|
return a.changed === false ? `${key}: ${what} was already set — answered` : `${key}: set ${what} — answered`;
|
|
21717
21827
|
}
|
|
21718
21828
|
async function decisionCmd(o, argv) {
|
|
21829
|
+
if (argv.includes("--ready"))
|
|
21830
|
+
return readyCmd(o, argv);
|
|
21719
21831
|
const log = o.log ?? console.log;
|
|
21720
21832
|
const error = o.error ?? console.error;
|
|
21721
21833
|
const json = argv.includes("--json");
|
|
@@ -21769,6 +21881,68 @@ async function decisionCmd(o, argv) {
|
|
|
21769
21881
|
}
|
|
21770
21882
|
return said(o, key, json, r, () => log(seen ? `${key}: marked seen` : overrule ? `${key}: overruled — your answer stands in the log` : apply ? appliedLine(key, r.json?.applied) : answeredLine(key, r.json?.route ?? route, r.json?.held)));
|
|
21771
21883
|
}
|
|
21884
|
+
var LOOK_READY_ANSWER = "Looks good — marked ready for review.";
|
|
21885
|
+
function lookToReady(route, id) {
|
|
21886
|
+
const rows = Array.isArray(route?.decisions) ? route.decisions : [];
|
|
21887
|
+
const d = rows.find((r) => r.id === id);
|
|
21888
|
+
if (!d)
|
|
21889
|
+
return { error: `no decision ${id} on this item` };
|
|
21890
|
+
if (d.kind !== "look")
|
|
21891
|
+
return { error: `decision ${id} is not a look — --ready marks a look` };
|
|
21892
|
+
if (d.state !== "escalated")
|
|
21893
|
+
return { error: `decision ${id} is ${String(d.state)} — not a look you owe` };
|
|
21894
|
+
const about = d.about && typeof d.about === "object" ? d.about : {};
|
|
21895
|
+
const pr = Array.isArray(about.prs) ? about.prs.find((p) => typeof p === "string" && p) : undefined;
|
|
21896
|
+
return typeof pr === "string" ? { pr } : { error: `decision ${id} names no PR` };
|
|
21897
|
+
}
|
|
21898
|
+
async function readyCmd(o, argv) {
|
|
21899
|
+
const log = o.log ?? console.log;
|
|
21900
|
+
const error = o.error ?? console.error;
|
|
21901
|
+
const json = argv.includes("--json");
|
|
21902
|
+
const words = argv.filter((a) => a !== "--ready" && a !== "--json");
|
|
21903
|
+
const [key, id] = words;
|
|
21904
|
+
if (!key || !id || words.length !== 2 || words.some((w) => w.startsWith("--"))) {
|
|
21905
|
+
error(DECISION_USAGE);
|
|
21906
|
+
return 2;
|
|
21907
|
+
}
|
|
21908
|
+
const client = engineClient(o);
|
|
21909
|
+
if ("error" in client) {
|
|
21910
|
+
error(client.error);
|
|
21911
|
+
return 1;
|
|
21912
|
+
}
|
|
21913
|
+
const reader = routeReader(o);
|
|
21914
|
+
if ("error" in reader) {
|
|
21915
|
+
error(reader.error);
|
|
21916
|
+
return 1;
|
|
21917
|
+
}
|
|
21918
|
+
const route = await reader.read(key).catch((e) => {
|
|
21919
|
+
error(e instanceof Error ? e.message : String(e));
|
|
21920
|
+
return;
|
|
21921
|
+
});
|
|
21922
|
+
if (route === undefined)
|
|
21923
|
+
return 1;
|
|
21924
|
+
const look = lookToReady(route, id);
|
|
21925
|
+
if ("error" in look) {
|
|
21926
|
+
error(`${key}: ${look.error}`);
|
|
21927
|
+
return 1;
|
|
21928
|
+
}
|
|
21929
|
+
const gh = (o.exec ?? defaultExec)("gh", ["pr", "ready", look.pr]);
|
|
21930
|
+
if (gh.status !== 0) {
|
|
21931
|
+
error(gh.stderr.trim() || `gh pr ready ${look.pr} exited ${gh.status}`);
|
|
21932
|
+
error(`${key}: the look is not answered`);
|
|
21933
|
+
return 1;
|
|
21934
|
+
}
|
|
21935
|
+
const r = await client.postFull("/proxy/route/decision", {
|
|
21936
|
+
project: key,
|
|
21937
|
+
id,
|
|
21938
|
+
answer: LOOK_READY_ANSWER
|
|
21939
|
+
});
|
|
21940
|
+
if (!r) {
|
|
21941
|
+
error(`${key}: ${look.pr} is ready for review, but SLEDS is unreachable — the look is not answered`);
|
|
21942
|
+
return 1;
|
|
21943
|
+
}
|
|
21944
|
+
return said(o, key, json, r, () => log(`${key}: ${look.pr} marked ready for review — the look is answered`));
|
|
21945
|
+
}
|
|
21772
21946
|
var DECISIONS_USAGE = "usage: sleds route decisions [<key>] [--flagged] [--json]";
|
|
21773
21947
|
function decisionsLines(raw) {
|
|
21774
21948
|
const j = clean(raw);
|
|
@@ -24215,7 +24389,7 @@ ${renderHelpText(argv[1])}`);
|
|
|
24215
24389
|
case "version":
|
|
24216
24390
|
case "--version":
|
|
24217
24391
|
case "-v":
|
|
24218
|
-
console.log("sleds 0.1.
|
|
24392
|
+
console.log("sleds 0.1.38");
|
|
24219
24393
|
return 0;
|
|
24220
24394
|
default:
|
|
24221
24395
|
console.error(`unknown command: ${cmd}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleds/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "SLEDS CLI — the cost and limits of AI agent work, decided at the wall. Point Claude Code at a SLEDS proxy; estimate before you run; hold, switch model, or run overnight when you hit a limit.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|