@sleds/cli 0.1.40 → 0.1.42
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/README.md +1 -1
- package/dist/sleds.mjs +146 -54
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ The flags `sleds route run` and `sleds engine` share:
|
|
|
87
87
|
--repo <path> | --repos-dir <dir> where the node's repo is checked out
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
`sleds engine` also takes `--name <n>` (default the hostname; it signs as `engine:<n>`), `--interval <s>`, `--lease <min>`, `--idle <min>` (default 20: a session showing nothing new and no PR is parked for a person, its last message the question, and the answer is typed back into it) and `--once` for a single tick. `--jira` reads `JIRA_BASE_URL`, `JIRA_EMAIL` and `JIRA_TOKEN
|
|
90
|
+
`sleds engine` also takes `--name <n>` (default the hostname; it signs as `engine:<n>`), `--interval <s>`, `--lease <min>`, `--idle <min>` (default 20: a session showing nothing new and no PR is parked for a person, its last message the question, and the answer is typed back into it) and `--once` for a single tick. `--jira` reads `JIRA_BASE_URL`, `JIRA_EMAIL` and `JIRA_TOKEN`, and still needs a seat: before every write to Jira (a status move, a comment) it asks SLEDS, and writes nothing when the org runs Jira read only, the switch is off, the issue's project is not watched, SLEDS cannot be reached, or `JIRA_WRITES=off` is set in its environment (spec 39 rule 4). It will not start when SLEDS says no writes for the epic's project. `sleds help` has the full list.
|
|
91
91
|
|
|
92
92
|
`sleds engine status [--name <n>] [--json]` says what that engine holds now: the nodes it runs (with where to watch each), the PRs it follows and their checks, the next check, and whether it is alive. It reads the engine's state file and the lock the engine keeps beside it (`<name>.lock`: its pid, written each tick, removed on a clean stop), not SLEDS, so it answers offline. `--json` is the contract a front end reads; both it and the state file carry a version, `v` (spec 28 §4.2). For a `sleds route run` engine, pass `--name <n>-<KEY>`.
|
|
93
93
|
|
package/dist/sleds.mjs
CHANGED
|
@@ -345,11 +345,11 @@ var init_guides = __esm(() => {
|
|
|
345
345
|
{
|
|
346
346
|
do: "Open `Work` and read the tabs",
|
|
347
347
|
where: "Work",
|
|
348
|
-
note: "
|
|
348
|
+
note: "Three tabs, each with its count: Ready · On sleds · Landed this week; what waits for product's yes is on Intake (a line above the tabs says how many). The search at the top narrows the rows as you type; the chips beside the tabs narrow by source, worth, type and `filed by` (you first). Your own work, and what is in front of you, is on Sleds."
|
|
349
349
|
},
|
|
350
350
|
{
|
|
351
351
|
do: "Read a pool item: the band, the worth, the chip, the readiness",
|
|
352
|
-
note: "`suggested` (dashed, with a star) names the sled the rules (and the model) suggest, with why on hover; `waiting for product` needs a yes; the five boxes say what the item has (why · done · worth · repo · route) and `N gaps` names what is missing before anyone could start; an amber note says a related or duplicate item exists. `Take it` puts it on your sled; the ⋯ menu can put it on the suggested sled."
|
|
352
|
+
note: "`suggested` (dashed, with a star) names the sled the rules (and the model) suggest, with why on hover; `waiting for product` needs a yes (on Intake, where a row opens a modal to decide it); the five boxes say what the item has (why · done · worth · repo · route) and `N gaps` names what is missing before anyone could start; an amber note says a related or duplicate item exists. `Take it` puts it on your sled; the ⋯ menu can put it on the suggested sled."
|
|
353
353
|
},
|
|
354
354
|
{
|
|
355
355
|
do: "Read a sled card: the seats, the jobs, the band spent",
|
|
@@ -679,16 +679,16 @@ var init_guides = __esm(() => {
|
|
|
679
679
|
note: "Emails, comma-separated. Blank means any owner or admin of the workspace."
|
|
680
680
|
},
|
|
681
681
|
{
|
|
682
|
-
do: "Approvers decide
|
|
683
|
-
where: "Overview › `Waiting on product
|
|
684
|
-
note: "`Approve` opens the gate; `Defer` keeps it closed and carries the note back to the filer, who answers in the brief and it comes back. In chat the same is `approve {project, decision, note?}`."
|
|
682
|
+
do: "Approvers decide on Intake",
|
|
683
|
+
where: "Intake (the rail), or Overview › `Waiting on product`",
|
|
684
|
+
note: "A row opens the ask in a modal over the ledger: `Approve` opens the gate; `Defer` keeps it closed and carries the note back to the filer, who answers in the brief and it comes back; `Ask the filer` sends the one question. A, D and Q do the same from the keyboard and move to the next ask. In chat the same is `approve {project, decision, note?}`."
|
|
685
685
|
},
|
|
686
686
|
{
|
|
687
687
|
do: "Plan after the yes",
|
|
688
688
|
note: "An approved epic can be planned in chat (`plan`): its milestones are sized, filed under it, and inherit the yes — they do not queue again. The epic's cap is the sum."
|
|
689
689
|
}
|
|
690
690
|
],
|
|
691
|
-
check: "File an ask from chat (`intake`): its item shows waiting for product
|
|
691
|
+
check: "File an ask from chat (`intake`): its item shows waiting for product on Intake, the approvers see it under Waiting on product, and `run` on it is refused until someone approves."
|
|
692
692
|
}
|
|
693
693
|
];
|
|
694
694
|
});
|
|
@@ -1346,6 +1346,9 @@ var init_related = __esm(() => {
|
|
|
1346
1346
|
"via"
|
|
1347
1347
|
]);
|
|
1348
1348
|
});
|
|
1349
|
+
|
|
1350
|
+
// ../../packages/shared/src/intake/rank.ts
|
|
1351
|
+
var init_rank = () => {};
|
|
1349
1352
|
// ../../packages/shared/src/profile/profile.ts
|
|
1350
1353
|
var LIGHT;
|
|
1351
1354
|
var init_profile = __esm(() => {
|
|
@@ -5543,6 +5546,34 @@ var init_fieldMap = __esm(() => {
|
|
|
5543
5546
|
var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
|
|
5544
5547
|
var init_whatsNew = __esm(() => {
|
|
5545
5548
|
WHATS_NEW = [
|
|
5549
|
+
{
|
|
5550
|
+
date: "2026-10-20",
|
|
5551
|
+
title: "Intake: the gate gets its own page",
|
|
5552
|
+
lede: "What waits for product's yes was the first tab on Work. It is a page now, Intake, between Work and Sleds on the rail: the same rows, with the policy an approver decides under on one line, the counts over them, and a modal that reads one ask fast and decides it without leaving the list. Work keeps what is ready and who is on it, and opens on Ready.",
|
|
5553
|
+
sections: [
|
|
5554
|
+
{
|
|
5555
|
+
title: "One ask at a time, over the ledger",
|
|
5556
|
+
where: "Intake › a row",
|
|
5557
|
+
body: "A row on Intake opens a centred modal, not the item's page: what SLEDS says and why, the six steps from filed to landed with this one's place on them, the brief, and — folded, each with its answer on the fold — the ticket, the split and who takes it. Approve, Defer with a note and Ask the filer are at the foot, with Open the full item for anything deeper. ‹ › and the arrow keys walk the ledger in its order; A, D and Q decide and move on; Decide in order starts at the top. Esc goes back to the ledger as it was."
|
|
5558
|
+
},
|
|
5559
|
+
{
|
|
5560
|
+
title: "The policy on the page",
|
|
5561
|
+
where: "Intake, above the rows",
|
|
5562
|
+
body: "Above the rows, one dashed line says whose yes is needed and for which asks, who the approvers are, and what a yes writes to Jira — read from Policy, with Change in Policy beside it. Under it, four counts: open, need a worth, waiting on a filer, and became work this week (which opens the Record)."
|
|
5563
|
+
},
|
|
5564
|
+
{
|
|
5565
|
+
title: "Where things went on Work",
|
|
5566
|
+
where: "Work",
|
|
5567
|
+
body: "Work's tabs are Ready, On sleds and Landed. A line above them says how many wait in Intake and links there; the Board layout says the same in place of its Waiting column. The rail's count under Work is what is ready now; Intake's is what waits. Old links — Approvals, a waiting lane, a search hit on a waiting item — land on Intake."
|
|
5568
|
+
}
|
|
5569
|
+
],
|
|
5570
|
+
also: [
|
|
5571
|
+
{
|
|
5572
|
+
label: "Next",
|
|
5573
|
+
text: "Worth from the modal and from Jira's Business Value, the return of each ask against its time, SLEDS's own suggested verdict, and auto-approve in shadow mode first — spec 41's later slices."
|
|
5574
|
+
}
|
|
5575
|
+
]
|
|
5576
|
+
},
|
|
5546
5577
|
{
|
|
5547
5578
|
date: "2026-10-10",
|
|
5548
5579
|
title: "Docs beside the work, and webhooks for your own tools",
|
|
@@ -7523,6 +7554,7 @@ var init_src = __esm(() => {
|
|
|
7523
7554
|
init_verbs();
|
|
7524
7555
|
init_brief();
|
|
7525
7556
|
init_related();
|
|
7557
|
+
init_rank();
|
|
7526
7558
|
init_markdown2();
|
|
7527
7559
|
init_plan3();
|
|
7528
7560
|
init_recommend();
|
|
@@ -8222,6 +8254,7 @@ init_guides();
|
|
|
8222
8254
|
init_verbs();
|
|
8223
8255
|
init_brief();
|
|
8224
8256
|
init_related();
|
|
8257
|
+
init_rank();
|
|
8225
8258
|
init_markdown2();
|
|
8226
8259
|
init_plan3();
|
|
8227
8260
|
init_recommend();
|
|
@@ -10267,28 +10300,17 @@ function cmuxRunner(exec, deps = {}) {
|
|
|
10267
10300
|
}
|
|
10268
10301
|
let id = parseCmuxWorkspace(r.stdout);
|
|
10269
10302
|
const ref = id ? null : parseCmuxRef(r.stdout);
|
|
10270
|
-
let closed = false;
|
|
10271
10303
|
if (ref) {
|
|
10272
|
-
let
|
|
10273
|
-
for (let i = 0;i < CMUX_LIST_TRIES; i++) {
|
|
10304
|
+
for (let i = 0;i < CMUX_LIST_TRIES && !id; i++) {
|
|
10274
10305
|
if (i > 0)
|
|
10275
10306
|
sleep(CMUX_LIST_WAIT_MS);
|
|
10276
10307
|
const l = cmux(["--id-format", "both", "list-workspaces"]);
|
|
10277
|
-
if (l.status
|
|
10278
|
-
|
|
10279
|
-
listed = l.stdout;
|
|
10280
|
-
if (namedLines(l.stdout, o.name).length > 0)
|
|
10281
|
-
break;
|
|
10282
|
-
}
|
|
10283
|
-
if (listed !== null) {
|
|
10284
|
-
id = cmuxIdForRef(listed, ref, o.name);
|
|
10285
|
-
const stray = id ? null : cmuxIdForName(listed, o.name);
|
|
10286
|
-
if (stray)
|
|
10287
|
-
closed = closeWorkspace(stray);
|
|
10308
|
+
if (l.status === 0)
|
|
10309
|
+
id = cmuxIdForRef(l.stdout, ref, o.name);
|
|
10288
10310
|
}
|
|
10289
10311
|
}
|
|
10290
10312
|
if (!id) {
|
|
10291
|
-
throw new Error(ref ? `cmux workspace create ${o.name} gave ${ref} but no workspace id for it; ${
|
|
10313
|
+
throw new Error(ref ? `cmux workspace create ${o.name} gave ${ref} but no workspace id for it; ${UNWATCHED}` : `cmux workspace create ${o.name} returned no workspace (${r.stdout.trim() || "nothing"}); ${UNWATCHED}`);
|
|
10292
10314
|
}
|
|
10293
10315
|
if (group && listed && !joined) {
|
|
10294
10316
|
const c = cmux([
|
|
@@ -10422,7 +10444,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
|
|
|
10422
10444
|
name: flags.name,
|
|
10423
10445
|
os: host.os ?? process.platform,
|
|
10424
10446
|
runner: flags.runner,
|
|
10425
|
-
version: host.version ?? "0.1.
|
|
10447
|
+
version: host.version ?? "0.1.42" ?? "dev",
|
|
10426
10448
|
running: live.length,
|
|
10427
10449
|
items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
|
|
10428
10450
|
...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
|
|
@@ -11025,6 +11047,29 @@ function adfComment(text) {
|
|
|
11025
11047
|
}
|
|
11026
11048
|
};
|
|
11027
11049
|
}
|
|
11050
|
+
function localWritesOff(env) {
|
|
11051
|
+
return env.JIRA_WRITES?.trim().toLowerCase() === "off";
|
|
11052
|
+
}
|
|
11053
|
+
function sledsJiraGate(client, env = process.env) {
|
|
11054
|
+
return {
|
|
11055
|
+
async ask(w) {
|
|
11056
|
+
if (localWritesOff(env))
|
|
11057
|
+
return "JIRA_WRITES=off is set here";
|
|
11058
|
+
const r = await client.postFull("/proxy/jira/write", { ...w }).catch(() => null);
|
|
11059
|
+
if (!r)
|
|
11060
|
+
return "SLEDS could not be reached to say whether writes to Jira are on";
|
|
11061
|
+
if (r.status !== 200 || !r.json)
|
|
11062
|
+
return `SLEDS would not say whether writes to Jira are on (HTTP ${r.status})`;
|
|
11063
|
+
const refusal = r.json.refusal;
|
|
11064
|
+
if (refusal === null)
|
|
11065
|
+
return null;
|
|
11066
|
+
return str6(r.json.why) ?? (typeof refusal === "string" ? refusal : "SLEDS refused the write");
|
|
11067
|
+
},
|
|
11068
|
+
async told(w, outcome) {
|
|
11069
|
+
await client.postFull("/proxy/jira/write", { ...w, outcome }).catch(() => null);
|
|
11070
|
+
}
|
|
11071
|
+
};
|
|
11072
|
+
}
|
|
11028
11073
|
function jiraSource(o) {
|
|
11029
11074
|
const fetchFn = o.fetchFn ?? fetch;
|
|
11030
11075
|
const base = o.baseUrl.replace(/\/+$/, "");
|
|
@@ -11065,23 +11110,35 @@ function jiraSource(o) {
|
|
|
11065
11110
|
byNode.set(n.node, n);
|
|
11066
11111
|
return [epicItem(epic, nodes)];
|
|
11067
11112
|
};
|
|
11113
|
+
const write = async (issue, sw, what, body) => {
|
|
11114
|
+
const w = {
|
|
11115
|
+
issueKey: issue,
|
|
11116
|
+
switch: sw,
|
|
11117
|
+
method: "POST",
|
|
11118
|
+
endpoint: `/issue/{key}/${what}`
|
|
11119
|
+
};
|
|
11120
|
+
const refused = await o.gate.ask(w).catch((e) => `SLEDS could not be reached (${e instanceof Error ? e.message : String(e)})`);
|
|
11121
|
+
if (refused)
|
|
11122
|
+
return { ok: false, status: 0, json: null, refused };
|
|
11123
|
+
const r = await call(`/rest/api/3/issue/${issue}/${what}`, { method: "POST", body });
|
|
11124
|
+
await o.gate.told(w, r.ok ? "sent" : `failed:${r.status}`).catch(() => {
|
|
11125
|
+
return;
|
|
11126
|
+
});
|
|
11127
|
+
return r;
|
|
11128
|
+
};
|
|
11068
11129
|
const transitionTo = async (issue, category) => {
|
|
11069
11130
|
const t = await call(`/rest/api/3/issue/${issue}/transitions`);
|
|
11070
11131
|
const list = Array.isArray(t.json?.transitions) ? t.json.transitions.filter(isObject8) : [];
|
|
11071
11132
|
const hit = list.find((x) => isObject8(x.to) && categoryOf({ status: x.to }) === category && x.id !== undefined);
|
|
11072
11133
|
if (!hit)
|
|
11073
|
-
return false;
|
|
11074
|
-
const r = await
|
|
11075
|
-
|
|
11076
|
-
body: { transition: { id: String(hit.id) } }
|
|
11077
|
-
});
|
|
11078
|
-
return r.ok;
|
|
11134
|
+
return { ok: false };
|
|
11135
|
+
const r = await write(issue, "status", "transitions", { transition: { id: String(hit.id) } });
|
|
11136
|
+
return { ok: r.ok, ...r.refused ? { refused: r.refused } : {} };
|
|
11079
11137
|
};
|
|
11080
11138
|
const comment = async (issue, text) => {
|
|
11081
|
-
const r = await
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
});
|
|
11139
|
+
const r = await write(issue, "comments", "comment", adfComment(text));
|
|
11140
|
+
if (r.refused)
|
|
11141
|
+
throw new Error(`Jira: not written to ${issue} — ${r.refused}`);
|
|
11085
11142
|
if (!r.ok)
|
|
11086
11143
|
throw new Error(`Jira: could not comment on ${issue} — ${errorOf(r)}`);
|
|
11087
11144
|
};
|
|
@@ -11107,8 +11164,16 @@ function jiraSource(o) {
|
|
|
11107
11164
|
const pack = `# ${n.issue} · ${n.summary}
|
|
11108
11165
|
|
|
11109
11166
|
${body}`.trimEnd();
|
|
11110
|
-
const moved = await transitionTo(n.issue, "indeterminate").catch(() =>
|
|
11111
|
-
|
|
11167
|
+
const moved = await transitionTo(n.issue, "indeterminate").catch(() => ({
|
|
11168
|
+
ok: false,
|
|
11169
|
+
refused: undefined
|
|
11170
|
+
}));
|
|
11171
|
+
if (moved.refused)
|
|
11172
|
+
return {
|
|
11173
|
+
ok: false,
|
|
11174
|
+
error: `not taken: not written to Jira (${n.issue}) — ${moved.refused}`
|
|
11175
|
+
};
|
|
11176
|
+
if (!moved.ok)
|
|
11112
11177
|
return {
|
|
11113
11178
|
ok: false,
|
|
11114
11179
|
error: `Jira would not move ${n.issue} to In Progress (no such transition, or no permission) — the engine needs it to mark the node taken`
|
|
@@ -11135,8 +11200,8 @@ ${body}`.trimEnd();
|
|
|
11135
11200
|
if (outcome.kind === "pr")
|
|
11136
11201
|
return comment(issue, `PR opened by ${by}: ${outcome.ref}`);
|
|
11137
11202
|
if (outcome.kind === "merged") {
|
|
11138
|
-
const done = await transitionTo(issue, "done").catch(() => false);
|
|
11139
|
-
return comment(issue, done ? `PR merged by ${by}'s engine: ${outcome.ref}` : `PR merged: ${outcome.ref}. Jira would not move this to Done (no such transition, or no permission) — move it so the nodes it blocks can start.`);
|
|
11203
|
+
const done = await transitionTo(issue, "done").catch(() => ({ ok: false }));
|
|
11204
|
+
return comment(issue, done.ok ? `PR merged by ${by}'s engine: ${outcome.ref}` : `PR merged: ${outcome.ref}. Jira would not move this to Done (no such transition, or no permission) — move it so the nodes it blocks can start.`);
|
|
11140
11205
|
}
|
|
11141
11206
|
if (outcome.kind === "park")
|
|
11142
11207
|
return comment(issue, outcome.question);
|
|
@@ -11148,20 +11213,21 @@ ${outcome.log.slice(-JIRA_LOG_MAX)}`);
|
|
|
11148
11213
|
}
|
|
11149
11214
|
};
|
|
11150
11215
|
}
|
|
11151
|
-
function
|
|
11152
|
-
const
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11216
|
+
function jiraEnvError(env) {
|
|
11217
|
+
const missing = ["JIRA_BASE_URL", "JIRA_EMAIL", "JIRA_TOKEN"].filter((k) => !env[k]?.trim());
|
|
11218
|
+
return missing.length > 0 ? `Jira source needs ${missing.join(", ")} (e.g. JIRA_BASE_URL=https://qwick.atlassian.net, an API token in JIRA_TOKEN)` : null;
|
|
11219
|
+
}
|
|
11220
|
+
function jiraFromEnv(epic, gate, env = process.env) {
|
|
11221
|
+
const error = jiraEnvError(env);
|
|
11222
|
+
if (error)
|
|
11223
|
+
return { error };
|
|
11224
|
+
return jiraSource({
|
|
11225
|
+
epic,
|
|
11226
|
+
baseUrl: String(env.JIRA_BASE_URL).trim(),
|
|
11227
|
+
email: String(env.JIRA_EMAIL).trim(),
|
|
11228
|
+
token: String(env.JIRA_TOKEN).trim(),
|
|
11229
|
+
gate
|
|
11230
|
+
});
|
|
11165
11231
|
}
|
|
11166
11232
|
|
|
11167
11233
|
// src/engine/loop.ts
|
|
@@ -16270,7 +16336,7 @@ async function driveEngine(o, flags, deps, until = {}) {
|
|
|
16270
16336
|
runner: deps.runner.name,
|
|
16271
16337
|
atOnce: flags.atOnce,
|
|
16272
16338
|
interval: flags.interval,
|
|
16273
|
-
cliVersion: "0.1.
|
|
16339
|
+
cliVersion: "0.1.42"
|
|
16274
16340
|
};
|
|
16275
16341
|
const beat = (checkedAt) => {
|
|
16276
16342
|
const m = deps.machine;
|
|
@@ -18087,6 +18153,15 @@ function orgClient2(o, flag) {
|
|
|
18087
18153
|
const org = engineOrg2(o, flag);
|
|
18088
18154
|
return engineClient2(o, org.seated ? org.slug ?? undefined : flag);
|
|
18089
18155
|
}
|
|
18156
|
+
async function jiraWritesAtStart(gate, epic) {
|
|
18157
|
+
const no = await gate.ask({
|
|
18158
|
+
issueKey: epic.trim().toUpperCase(),
|
|
18159
|
+
switch: "status",
|
|
18160
|
+
method: "POST",
|
|
18161
|
+
endpoint: "/issue/{key}/transitions"
|
|
18162
|
+
});
|
|
18163
|
+
return no ? `--jira ${epic}: not starting — SLEDS says no writes to Jira: ${no}. A take moves the issue to In Progress, so the engine cannot take a node without one.` : null;
|
|
18164
|
+
}
|
|
18090
18165
|
function realPidAlive2(pid) {
|
|
18091
18166
|
try {
|
|
18092
18167
|
process.kill(pid, 0);
|
|
@@ -18323,7 +18398,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
|
|
|
18323
18398
|
runner: deps.runner.name,
|
|
18324
18399
|
atOnce: flags.atOnce,
|
|
18325
18400
|
interval: flags.interval,
|
|
18326
|
-
cliVersion: "0.1.
|
|
18401
|
+
cliVersion: "0.1.42"
|
|
18327
18402
|
};
|
|
18328
18403
|
const beat = (checkedAt) => {
|
|
18329
18404
|
const m = deps.machine;
|
|
@@ -18574,7 +18649,24 @@ var engineCmd = async (o) => {
|
|
|
18574
18649
|
if (o.source)
|
|
18575
18650
|
source = o.source;
|
|
18576
18651
|
else if (flags.jira) {
|
|
18577
|
-
const
|
|
18652
|
+
const env = o.env ?? process.env;
|
|
18653
|
+
const missing = jiraEnvError(env);
|
|
18654
|
+
if (missing) {
|
|
18655
|
+
error(missing);
|
|
18656
|
+
return 1;
|
|
18657
|
+
}
|
|
18658
|
+
const c = o.client ?? orgClient2(o, given);
|
|
18659
|
+
if ("error" in c) {
|
|
18660
|
+
error(`--jira asks SLEDS before every write to Jira (spec 39), so it needs a seat: ${c.error}`);
|
|
18661
|
+
return 1;
|
|
18662
|
+
}
|
|
18663
|
+
const gate = sledsJiraGate(c, env);
|
|
18664
|
+
const no = await jiraWritesAtStart(gate, flags.jira);
|
|
18665
|
+
if (no) {
|
|
18666
|
+
error(no);
|
|
18667
|
+
return 1;
|
|
18668
|
+
}
|
|
18669
|
+
const j = jiraFromEnv(flags.jira, gate, env);
|
|
18578
18670
|
if ("error" in j) {
|
|
18579
18671
|
error(j.error);
|
|
18580
18672
|
return 1;
|
|
@@ -24783,7 +24875,7 @@ ${renderHelpText(argv[1])}`);
|
|
|
24783
24875
|
case "version":
|
|
24784
24876
|
case "--version":
|
|
24785
24877
|
case "-v":
|
|
24786
|
-
console.log("sleds 0.1.
|
|
24878
|
+
console.log("sleds 0.1.42");
|
|
24787
24879
|
return 0;
|
|
24788
24880
|
default:
|
|
24789
24881
|
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.42",
|
|
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",
|