@sleds/cli 0.1.41 → 0.1.43

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.
Files changed (2) hide show
  1. package/dist/sleds.mjs +402 -107
  2. package/package.json +1 -1
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: "Four tabs, each with its count: Waiting for product · Ready · On sleds · Landed this week. 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."
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 from the board or the Overview",
683
- where: "Overview › `Waiting on product`, or the item on Work",
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 in the pool on Work, the approvers see it under Waiting on product, and `run` on it is refused until someone approves."
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
  });
@@ -1083,6 +1083,83 @@ var init_verbs = __esm(() => {
1083
1083
  ];
1084
1084
  });
1085
1085
 
1086
+ // ../../packages/shared/src/intake/forms.ts
1087
+ function norm(label) {
1088
+ return label.replace(/[*_`~]/g, "").replace(/\(s\)/gi, "s").replace(/[:?]+\s*$/, "").replace(/\s+/g, " ").trim().toLowerCase();
1089
+ }
1090
+ function readForm(text) {
1091
+ const lines = text.split(/\r?\n/);
1092
+ for (const def of FORMS) {
1093
+ const found = [];
1094
+ let cur = null;
1095
+ let known = 0;
1096
+ for (const raw of lines) {
1097
+ const line = raw.trim();
1098
+ if (!line)
1099
+ continue;
1100
+ const inline = /^\**([^:*]{2,60}?)\**\s*:\s*(.+)$/.exec(line);
1101
+ const whole = norm(line);
1102
+ const slotWhole = def.labels[whole];
1103
+ const slotInline = inline ? def.labels[norm(inline[1] ?? "")] : undefined;
1104
+ if (slotWhole) {
1105
+ cur = { label: line.replace(/[*_]/g, "").replace(/:\s*$/, ""), slot: slotWhole, value: [] };
1106
+ found.push(cur);
1107
+ known++;
1108
+ } else if (slotInline && inline) {
1109
+ cur = { label: (inline[1] ?? "").trim(), slot: slotInline, value: [inline[2] ?? ""] };
1110
+ found.push(cur);
1111
+ known++;
1112
+ } else if (/^\*[^*]{2,60}\*:?$/.test(line) && cur) {
1113
+ cur = { label: line.replace(/[*:]/g, "").trim(), slot: null, value: [] };
1114
+ found.push(cur);
1115
+ } else if (cur)
1116
+ cur.value.push(line);
1117
+ }
1118
+ if (known < def.min)
1119
+ continue;
1120
+ const val = (f) => f.value.join(" ").trim();
1121
+ const of = (slot) => found.filter((f) => f.slot === slot && val(f)).map((f) => ({ label: f.label, v: val(f) }));
1122
+ const ask = of("ask")[0]?.v;
1123
+ const who = of("for").map((x) => x.v);
1124
+ const extra = found.filter((f) => f.slot === null && val(f)).map((f) => `${f.label}: ${val(f)}`);
1125
+ const why = [who.length > 0 ? `For ${who.join(", ")}.` : "", ask ?? "", ...extra].filter(Boolean).join(" ");
1126
+ const touches = of("touches").map((x) => `${x.label} ${x.v}`).join(" · ");
1127
+ const n = of("demand")[0]?.v;
1128
+ const demand = n ? /^\d+$/.test(n) ? `${n} clients affected` : n : undefined;
1129
+ const rev = of("revenue")[0]?.v;
1130
+ return {
1131
+ form: def.name,
1132
+ ...ask ? { ask } : {},
1133
+ ...why ? { why } : {},
1134
+ ...touches ? { touches } : {},
1135
+ ...demand ? { demand } : {},
1136
+ ...rev && YES.test(rev) ? { worth: "high · impacting revenue" } : {}
1137
+ };
1138
+ }
1139
+ return null;
1140
+ }
1141
+ var FORMS, YES;
1142
+ var init_forms = __esm(() => {
1143
+ FORMS = [
1144
+ {
1145
+ name: "Create Tech Support Ticket",
1146
+ labels: {
1147
+ "users experiencing the issue": "for",
1148
+ "user experiencing the issue": "for",
1149
+ sids: "touches",
1150
+ sid: "touches",
1151
+ "location ids": "touches",
1152
+ "location id": "touches",
1153
+ "issue description": "ask",
1154
+ "number of clients affected": "demand",
1155
+ "is the issue currently impacting revenue": "revenue"
1156
+ },
1157
+ min: 3
1158
+ }
1159
+ ];
1160
+ YES = /^(yes|y|yep|true|definitely|it is)\b/i;
1161
+ });
1162
+
1086
1163
  // ../../packages/shared/src/intake/brief.ts
1087
1164
  function labelOf(line) {
1088
1165
  const h = /^#{1,6}\s+(.+?)\s*#*\s*$/.exec(line);
@@ -1155,13 +1232,16 @@ function worthFromAnswer(answer) {
1155
1232
  function draftBrief(input) {
1156
1233
  const text = (input.text ?? "").trim();
1157
1234
  const a = input.answers ?? {};
1158
- const { preamble, byKey } = sections(text);
1159
- const askText = a.ask?.trim() || askFrom(text, preamble);
1235
+ const form = readForm(text);
1236
+ const parsed = sections(text);
1237
+ const preamble = parsed.preamble;
1238
+ const byKey = form ? { ...parsed.byKey, why: form.why, touches: form.touches, demand: form.demand } : parsed.byKey;
1239
+ const askText = a.ask?.trim() || (form?.ask ? askFrom(form.ask, form.ask) : askFrom(text, preamble));
1160
1240
  if (!askText)
1161
1241
  return null;
1162
1242
  const ask = a.ask?.trim() ? { text: clip(a.ask, MAX_ASK), from: "filer" } : { text: clip(askText, MAX_ASK), from: "artifact" };
1163
1243
  const pick = (k) => field(a[k], "filer") ?? field(byKey[k], "artifact");
1164
- const worth = worthFromAnswer(a.worth) ?? parseWorth(text) ?? undefined;
1244
+ const worth = worthFromAnswer(a.worth) ?? parseWorth(text) ?? worthFromAnswer(form?.worth) ?? undefined;
1165
1245
  const sources = [...new Set((input.links ?? []).map((l) => l.trim()).filter(Boolean))].slice(0, MAX_SOURCES);
1166
1246
  return {
1167
1247
  ask,
@@ -1288,6 +1368,7 @@ var MAX_FIELD = 280, MAX_ASK = 120, MAX_SOURCES = 12, QUESTIONS, REQUIRED_TO_FIL
1288
1368
  }, fromWord = (f) => f.from === "filer" ? "from you" : f.from === "product" ? "from the product map" : "from what you sent";
1289
1369
  var init_brief = __esm(() => {
1290
1370
  init_worth();
1371
+ init_forms();
1291
1372
  QUESTIONS = {
1292
1373
  why: "Who is it for, and why now?",
1293
1374
  done: "What does done look like, in your words?",
@@ -1346,6 +1427,9 @@ var init_related = __esm(() => {
1346
1427
  "via"
1347
1428
  ]);
1348
1429
  });
1430
+
1431
+ // ../../packages/shared/src/intake/rank.ts
1432
+ var init_rank = () => {};
1349
1433
  // ../../packages/shared/src/profile/profile.ts
1350
1434
  var LIGHT;
1351
1435
  var init_profile = __esm(() => {
@@ -1766,7 +1850,7 @@ function buildRoute(drafts, opts = {}) {
1766
1850
  problems.push({ level: "error", text: "a route needs at least one node" });
1767
1851
  if (drafts.length > MAX_NODES)
1768
1852
  problems.push({ level: "error", text: `a route holds at most ${MAX_NODES} nodes` });
1769
- const given = drafts.map((d) => d.key ? norm(d.key) : "");
1853
+ const given = drafts.map((d) => d.key ? norm2(d.key) : "");
1770
1854
  const fresh = mintKeys(given.filter((k) => !k).length, given.filter((k) => k));
1771
1855
  let f = 0;
1772
1856
  const keys = given.map((k) => k || fresh[f++]);
@@ -1781,7 +1865,7 @@ function buildRoute(drafts, opts = {}) {
1781
1865
  const title = stripControl(d.title.replace(/\s+/g, " ")).trim().slice(0, 160);
1782
1866
  if (!title)
1783
1867
  problems.push({ level: "error", node: key, text: `node ${key} has no title` });
1784
- const blockedBy = [...new Set((d.blockedBy ?? []).map(norm).filter(Boolean))];
1868
+ const blockedBy = [...new Set((d.blockedBy ?? []).map(norm2).filter(Boolean))];
1785
1869
  for (const b of blockedBy) {
1786
1870
  if (b === key)
1787
1871
  problems.push({ level: "error", node: key, text: `node ${key} blocks itself` });
@@ -1958,7 +2042,7 @@ ${state}`)}, fillcolor="${fill}", color="${border}"${style}];`);
1958
2042
  `)}
1959
2043
  `;
1960
2044
  }
1961
- var ROUTE_APPROACH_MAX = 2000, PLAN_APPROACH_MAX = 1200, PLAN_LIST_MAX = 8, PLAN_LINE_MAX = 240, CONTROL, MAX_NODES = 24, DEPTH_WARN = 4, norm = (s) => s.trim().toUpperCase(), AREA_ROOTS, NOT_AREAS, sameRepo = (a, b) => a.length === 0 || b.length === 0 || a.some((r) => b.includes(r)), DOT_COLORS, ROUTE_DRAFT_RULE = "Draft the route: PR-sized nodes, file-disjoint where they run in parallel, each with a title, the files or areas it touches, the repo, and the nodes it waits on (by key: A, B, C…). Prefer wide waves over chains deeper than four. Make the tough calls once, so each node's session follows them: an `approach` paragraph for the whole route, and on each node a `plan` — its approach, what it `provides` that later nodes rely on (names, shapes, files), what it `expects` from the nodes it waits on, and the `doneChecks` that say it landed. Show the graph and the plans as text and ask for a yes; file nothing before it. Then `route {q, approach, nodes: [{key, title, touches, repos, blockedBy, plan: {approach, provides, expects, doneChecks}}], confirm: true}`.";
2045
+ var ROUTE_APPROACH_MAX = 2000, PLAN_APPROACH_MAX = 1200, PLAN_LIST_MAX = 8, PLAN_LINE_MAX = 240, CONTROL, MAX_NODES = 24, DEPTH_WARN = 4, norm2 = (s) => s.trim().toUpperCase(), AREA_ROOTS, NOT_AREAS, sameRepo = (a, b) => a.length === 0 || b.length === 0 || a.some((r) => b.includes(r)), DOT_COLORS, ROUTE_DRAFT_RULE = "Draft the route: PR-sized nodes, file-disjoint where they run in parallel, each with a title, the files or areas it touches, the repo, and the nodes it waits on (by key: A, B, C…). Prefer wide waves over chains deeper than four. Make the tough calls once, so each node's session follows them: an `approach` paragraph for the whole route, and on each node a `plan` — its approach, what it `provides` that later nodes rely on (names, shapes, files), what it `expects` from the nodes it waits on, and the `doneChecks` that say it landed. Show the graph and the plans as text and ask for a yes; file nothing before it. Then `route {q, approach, nodes: [{key, title, touches, repos, blockedBy, plan: {approach, provides, expects, doneChecks}}], confirm: true}`.";
1962
2046
  var init_route = __esm(() => {
1963
2047
  CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
1964
2048
  AREA_ROOTS = new Set(["apps", "packages", "services", "libs", "crates", "modules"]);
@@ -5543,6 +5627,72 @@ var init_fieldMap = __esm(() => {
5543
5627
  var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
5544
5628
  var init_whatsNew = __esm(() => {
5545
5629
  WHATS_NEW = [
5630
+ {
5631
+ date: "2026-10-21",
5632
+ title: "The Slack thread is the item's discussion",
5633
+ lede: "An item filed from a Slack thread keeps that thread. What people say there is on the item, what is said on the item goes back there, every move is one line in the thread, and everyone who has spoken hears what happens next.",
5634
+ sections: [
5635
+ {
5636
+ title: "Replies are comments; moves are lines",
5637
+ where: "Slack, after Filed as",
5638
+ body: "After Filed as, a reply in the thread is a comment on the item, as that person, marked from Slack. Approve, defer, take, land and archive each post one line in the thread with a link to the item. The item's Details has Slack, which opens the thread."
5639
+ },
5640
+ {
5641
+ title: "Comments go out",
5642
+ where: "The item › Discussion",
5643
+ body: "A comment written on the item is posted in its thread as the person who wrote it, and to Jira when the comments switch is on. The line under the composer says where it will go. SLEDS's own strips stay on SLEDS."
5644
+ },
5645
+ {
5646
+ title: "Watchers",
5647
+ where: "The item › Discussion, and @SLEDS in the thread",
5648
+ body: "Whoever filed it, holds it, or has spoken on it hears each move — once, never their own. Watch on the Discussion adds you; Unwatch stops it. In a thread, @SLEDS watch QW-123 ties an item to that thread, @SLEDS status answers with the item's status, and @SLEDS ask: … puts a question to the filer."
5649
+ },
5650
+ {
5651
+ title: "Forms are read as fields",
5652
+ where: "Slack, filing from a workflow post",
5653
+ body: "A support ticket form posted by a Slack workflow is read field by field: the issue description is the ask, who is affected and why go to the why, the IDs to what it touches, the count of clients to demand, and a yes on revenue to a high worth. The draft then asks only what the form did not say."
5654
+ },
5655
+ {
5656
+ title: "Which workspace, picked",
5657
+ where: "Slack, filing",
5658
+ body: "A channel tied to a workspace files there. With several workspaces and none tied to the channel, SLEDS asks which, as a numbered pick: reply with the number. The Jira key is never typed — the Filed as reply shows it."
5659
+ }
5660
+ ],
5661
+ also: [
5662
+ {
5663
+ label: "Fixed",
5664
+ text: "An ask filed from Slack now creates its Jira ticket in the workspace it was filed on, and the reply names the key or says why none was made."
5665
+ }
5666
+ ]
5667
+ },
5668
+ {
5669
+ date: "2026-10-20",
5670
+ title: "Intake: the gate gets its own page",
5671
+ 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.",
5672
+ sections: [
5673
+ {
5674
+ title: "One ask at a time, over the ledger",
5675
+ where: "Intake › a row",
5676
+ 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."
5677
+ },
5678
+ {
5679
+ title: "The policy on the page",
5680
+ where: "Intake, above the rows",
5681
+ 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)."
5682
+ },
5683
+ {
5684
+ title: "Where things went on Work",
5685
+ where: "Work",
5686
+ 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."
5687
+ }
5688
+ ],
5689
+ also: [
5690
+ {
5691
+ label: "Next",
5692
+ 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."
5693
+ }
5694
+ ]
5695
+ },
5546
5696
  {
5547
5697
  date: "2026-10-10",
5548
5698
  title: "Docs beside the work, and webhooks for your own tools",
@@ -6544,7 +6694,7 @@ function nodePrompt(o) {
6544
6694
  `- Work on branch \`${o.branch}\` from the latest \`origin/${base}\` (create it if this checkout is not on it). One branch, one PR.`,
6545
6695
  `- Open the PR against \`${base}\` with \`gh pr create${visual ? " --draft" : ""}\`${o.prTitle ? ` and title it exactly \`${o.prTitle}\` (the repo's CI checks it)` : ""}. Its body carries \`SLEDS-Node: ${claim.node}\` on the line under \`SLEDS-Source:\` when the pack names a source.`,
6546
6696
  ...claim.pack.includes(PR_HEADER_OPEN) ? [
6547
- `- The PR body starts with the pack's PR header block (\`${PR_HEADER_OPEN}\` … \`${PR_HEADER_CLOSE}\`, in its part 5), above everything else and as given; when you edit the body later, keep it on top.`
6697
+ `- The PR body starts with the pack's PR header block (\`${PR_HEADER_OPEN}\` … \`${PR_HEADER_CLOSE}\`, in its part 5), above everything else. The pack wrote it before your work existed: when you make a mock (a screenshot or an artifact) or write a doc, put its link on that part's line in place of \`None\` — not only lower in the body (SL-1746). When you edit the body later, keep it on top.`
6548
6698
  ] : [],
6549
6699
  "- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
6550
6700
  "- Stay inside this node's scope. Work another node needs goes in the PR description, not in this branch.",
@@ -7522,7 +7672,9 @@ var init_src = __esm(() => {
7522
7672
  init_guides();
7523
7673
  init_verbs();
7524
7674
  init_brief();
7675
+ init_forms();
7525
7676
  init_related();
7677
+ init_rank();
7526
7678
  init_markdown2();
7527
7679
  init_plan3();
7528
7680
  init_recommend();
@@ -8221,7 +8373,9 @@ init_cowork();
8221
8373
  init_guides();
8222
8374
  init_verbs();
8223
8375
  init_brief();
8376
+ init_forms();
8224
8377
  init_related();
8378
+ init_rank();
8225
8379
  init_markdown2();
8226
8380
  init_plan3();
8227
8381
  init_recommend();
@@ -9199,12 +9353,13 @@ var scanTranscriptForCredits = (path, prior) => {
9199
9353
  return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
9200
9354
  }
9201
9355
  };
9202
- var seatWindowBody = (w) => ({
9356
+ var seatWindowBody = (w, account) => ({
9203
9357
  capturedAt: w.capturedAt,
9204
9358
  source: "claude",
9205
9359
  ...w.fiveHour ? { fiveHour: w.fiveHour } : {},
9206
9360
  ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
9207
- ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
9361
+ ...w.spendLimit ? { spendLimit: w.spendLimit } : {},
9362
+ ...account ? { account } : {}
9208
9363
  });
9209
9364
  var postJson = async (o, path, body) => {
9210
9365
  const fetchFn = o.fetchFn ?? fetch;
@@ -9220,7 +9375,7 @@ var postJson = async (o, path, body) => {
9220
9375
  return false;
9221
9376
  }
9222
9377
  };
9223
- var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
9378
+ var postSeatWindow = (o, w, account) => postJson(o, "/proxy/seat", seatWindowBody(w, account));
9224
9379
  var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", { source: "claude", ...input });
9225
9380
  var SEAT_WALL_LINE = "Seat limit hit. sleds on --until-reset to continue on the platform key, or sleds defer to resume here at the reset.";
9226
9381
  var SEAT_WALL_LINE_NO_KEY = "Seat limit hit. sleds defer resumes this session on this Mac at the reset; /sled 5 to hand off — this org has no platform key to switch to.";
@@ -9414,7 +9569,7 @@ import {
9414
9569
  existsSync as existsSync16,
9415
9570
  lstatSync,
9416
9571
  mkdtempSync,
9417
- readFileSync as readFileSync16,
9572
+ readFileSync as readFileSync17,
9418
9573
  readlinkSync,
9419
9574
  rmSync as rmSync8,
9420
9575
  writeFileSync as writeFileSync17
@@ -9817,7 +9972,7 @@ function resolveRepoPath(o) {
9817
9972
  const want = o.repos.length ? o.repos.join(", ") : "this node";
9818
9973
  const dir = names[0] ? ` (the folder that holds ${names[0]})` : "";
9819
9974
  return {
9820
- error: `No repo for ${want}: this engine runs in ${o.cwd}, which is not a git checkout. Set Repos dir in Settings${dir}, or start the engine with --repo.`
9975
+ error: `No repo for ${want}: this engine runs in ${o.cwd}, which is not a git checkout. Set Repos folders in Settings › Running work${dir}, or start the engine with --repo.`
9821
9976
  };
9822
9977
  }
9823
9978
  for (const name of names) {
@@ -10267,28 +10422,17 @@ function cmuxRunner(exec, deps = {}) {
10267
10422
  }
10268
10423
  let id = parseCmuxWorkspace(r.stdout);
10269
10424
  const ref = id ? null : parseCmuxRef(r.stdout);
10270
- let closed = false;
10271
10425
  if (ref) {
10272
- let listed = null;
10273
- for (let i = 0;i < CMUX_LIST_TRIES; i++) {
10426
+ for (let i = 0;i < CMUX_LIST_TRIES && !id; i++) {
10274
10427
  if (i > 0)
10275
10428
  sleep(CMUX_LIST_WAIT_MS);
10276
10429
  const l = cmux(["--id-format", "both", "list-workspaces"]);
10277
- if (l.status !== 0)
10278
- continue;
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);
10430
+ if (l.status === 0)
10431
+ id = cmuxIdForRef(l.stdout, ref, o.name);
10288
10432
  }
10289
10433
  }
10290
10434
  if (!id) {
10291
- throw new Error(ref ? `cmux workspace create ${o.name} gave ${ref} but no workspace id for it; ${closed ? "that workspace was closed" : UNWATCHED}` : `cmux workspace create ${o.name} returned no workspace (${r.stdout.trim() || "nothing"}); ${UNWATCHED}`);
10435
+ 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
10436
  }
10293
10437
  if (group && listed && !joined) {
10294
10438
  const c = cmux([
@@ -10353,7 +10497,7 @@ function cmuxRunner(exec, deps = {}) {
10353
10497
  throw new Error(`cmux send-key ${h.id} failed: ${failed(r)}`);
10354
10498
  },
10355
10499
  stop(h) {
10356
- cmux(["close-workspace", "--workspace", h.id]);
10500
+ closeWorkspace(h.id);
10357
10501
  if (!h.group)
10358
10502
  return;
10359
10503
  const l = cmux(LIST_GROUPS);
@@ -10422,7 +10566,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
10422
10566
  name: flags.name,
10423
10567
  os: host.os ?? process.platform,
10424
10568
  runner: flags.runner,
10425
- version: host.version ?? "0.1.41" ?? "dev",
10569
+ version: host.version ?? "0.1.43" ?? "dev",
10426
10570
  running: live.length,
10427
10571
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
10428
10572
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -10441,10 +10585,13 @@ function beatBody(state, flags, host = {}, cap = {}) {
10441
10585
  }
10442
10586
  async function postBeat(client, body) {
10443
10587
  try {
10444
- const r = await client.post("/proxy/engine/beat", { ...body });
10445
- return r?.ok === true;
10588
+ const r = await client.postFull("/proxy/engine/beat", { ...body });
10589
+ if (r?.status === 200 && r.json?.ok === true)
10590
+ return { ok: true };
10591
+ const error = r?.json?.error;
10592
+ return r?.status === 409 && typeof error === "string" ? { ok: false, refused: error } : { ok: false };
10446
10593
  } catch {
10447
- return false;
10594
+ return { ok: false };
10448
10595
  }
10449
10596
  }
10450
10597
  function beater(client, now, everyMs = BEAT_EVERY_MS) {
@@ -11232,6 +11379,11 @@ var notificationType = (text) => {
11232
11379
 
11233
11380
  // src/commands/permissionEvent.ts
11234
11381
  var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11382
+ var NOT_A_TOOL = new Set(["askuserquestion", "exitplanmode", "enterplanmode"]);
11383
+ var isToolPermission = (message) => {
11384
+ const tool = /permission to use\s+(.+?)\.?\s*$/i.exec(message)?.[1];
11385
+ return !tool || !NOT_A_TOOL.has(tool.replace(/[\s_-]/g, "").toLowerCase());
11386
+ };
11235
11387
  var readPermissionMark = (file) => {
11236
11388
  try {
11237
11389
  const raw = JSON.parse(readFileSync10(file, "utf8"));
@@ -12114,7 +12266,36 @@ var DIALOG_ASKS = /^Do you want to .+\?$/;
12114
12266
  var DIALOG_OPTION = /^(❯\s*)?(\d)\.\s+(.+)$/;
12115
12267
  var DIALOG_FOOTER = /^(?:esc to |tab to |enter to |ctrl\+)/i;
12116
12268
  var unbox = (line) => line.trimEnd().replace(/^\s*[│┃║]\s?/, "").replace(/\s*[│┃║]$/, "");
12269
+ var PICKER_CHIP = /^[☐☒✔✓□■]\s+\S/;
12270
+ var PICKER_OPTION = /^(?:Type something\.?|Chat about this|Submit answers?)$/i;
12271
+ var PICKER_FOOTER = /\benter to select\b|\bto navigate\b/i;
12272
+ var PICKER_ASKS = /^(?:Would you like to proceed\?|Ready to code\?)$/;
12273
+ var PICKER_REACH = 40;
12274
+ function choicePicker(screen) {
12275
+ const lines = screen.split(`
12276
+ `).map((l) => unbox(l).trim());
12277
+ let c = -1;
12278
+ for (let i = lines.length - 1;i >= 0; i--)
12279
+ if (/^❯\s*\d\.\s/.test(lines[i])) {
12280
+ c = i;
12281
+ break;
12282
+ }
12283
+ if (c < 0)
12284
+ return false;
12285
+ let from = c;
12286
+ while (from > 0 && c - from < PICKER_REACH && !/^⏺/.test(lines[from - 1]))
12287
+ from--;
12288
+ for (let i = from;i < lines.length; i++) {
12289
+ const t = lines[i];
12290
+ const choice = DIALOG_OPTION.exec(t)?.[3]?.trim() ?? "";
12291
+ if (PICKER_CHIP.test(t) || PICKER_ASKS.test(t) || PICKER_OPTION.test(choice) || i > c && PICKER_FOOTER.test(t))
12292
+ return true;
12293
+ }
12294
+ return false;
12295
+ }
12117
12296
  function permissionDialog(screen) {
12297
+ if (choicePicker(screen))
12298
+ return null;
12118
12299
  const raw = screen.split(`
12119
12300
  `);
12120
12301
  const lines = raw.map(unbox);
@@ -13129,7 +13310,11 @@ async function settlePermission(d, runner, run, shown) {
13129
13310
  run.permission = undefined;
13130
13311
  return false;
13131
13312
  }
13132
- const mark = markFile ? readPermissionMark(markFile) : null;
13313
+ let mark = markFile ? readPermissionMark(markFile) : null;
13314
+ if (mark && markFile && (!isToolPermission(mark.message) || runner.readsSession && choicePicker(shown))) {
13315
+ clearPermissionMark(markFile);
13316
+ mark = null;
13317
+ }
13133
13318
  if (!dialog && !mark)
13134
13319
  return false;
13135
13320
  if (!dialog && runner.readsSession && !run.seenTicks)
@@ -15293,7 +15478,7 @@ var statusLine2 = (session, events, now) => {
15293
15478
  return `managed session: idle — its turn ended${money}`;
15294
15479
  }
15295
15480
  };
15296
- var YES = /^\s*(?:y|yes|allow|ok|okay|go|go ahead|approve[d]?)\b/i;
15481
+ var YES2 = /^\s*(?:y|yes|allow|ok|okay|go|go ahead|approve[d]?)\b/i;
15297
15482
  function managedRunner(exec, deps = {}) {
15298
15483
  const env = deps.env ?? process.env;
15299
15484
  const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
@@ -15418,7 +15603,7 @@ ${renderTranscript(eventMessages(all))}`;
15418
15603
  const idle = lastIdle(all);
15419
15604
  const asks = str9(obj2(idle?.stop_reason)?.type) === "requires_action" ? pendingAsks(all, idle) : [];
15420
15605
  if (asks.length) {
15421
- const allow = YES.test(text);
15606
+ const allow = YES2.test(text);
15422
15607
  api("POST", `/v1/sessions/${h.id}/events`, {
15423
15608
  events: asks.map((e) => ({
15424
15609
  type: "user.tool_confirmation",
@@ -16314,7 +16499,7 @@ async function driveEngine(o, flags, deps, until = {}) {
16314
16499
  runner: deps.runner.name,
16315
16500
  atOnce: flags.atOnce,
16316
16501
  interval: flags.interval,
16317
- cliVersion: "0.1.41"
16502
+ cliVersion: "0.1.43"
16318
16503
  };
16319
16504
  const beat = (checkedAt) => {
16320
16505
  const m = deps.machine;
@@ -16625,7 +16810,6 @@ async function enginePauseCmd(o) {
16625
16810
 
16626
16811
  // src/commands/engineInstall.ts
16627
16812
  init_config();
16628
- init_state();
16629
16813
  import {
16630
16814
  existsSync as existsSync14,
16631
16815
  mkdirSync as mkdirSync14,
@@ -16637,6 +16821,7 @@ import {
16637
16821
  } from "node:fs";
16638
16822
  import { hostname as hostname3 } from "node:os";
16639
16823
  import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join18 } from "node:path";
16824
+ init_state();
16640
16825
 
16641
16826
  // src/schedule.ts
16642
16827
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -17124,6 +17309,42 @@ var musherAgentState = (home, env = process.env) => {
17124
17309
  }
17125
17310
  };
17126
17311
 
17312
+ // src/claudeAccount.ts
17313
+ import { readFileSync as readFileSync16 } from "node:fs";
17314
+ var PLANS = {
17315
+ claude_max: "Max",
17316
+ claude_pro: "Pro",
17317
+ claude_team: "Team",
17318
+ claude_enterprise: "Enterprise"
17319
+ };
17320
+ var str11 = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : undefined;
17321
+ var parseClaudeAccount = (raw) => {
17322
+ if (typeof raw !== "object" || raw === null)
17323
+ return null;
17324
+ const acct = raw.oauthAccount;
17325
+ if (typeof acct !== "object" || acct === null)
17326
+ return null;
17327
+ const a = acct;
17328
+ const email = str11(a.emailAddress);
17329
+ if (!email)
17330
+ return null;
17331
+ const out = { email };
17332
+ const type = str11(a.organizationType);
17333
+ if (type && PLANS[type])
17334
+ out.plan = PLANS[type];
17335
+ const orgName = str11(a.organizationName);
17336
+ if (orgName)
17337
+ out.orgName = orgName;
17338
+ return out;
17339
+ };
17340
+ var readClaudeAccount = (home, env = process.env) => {
17341
+ try {
17342
+ return parseClaudeAccount(JSON.parse(readFileSync16(claudeUserConfigPath2(home, env), "utf8")));
17343
+ } catch {
17344
+ return null;
17345
+ }
17346
+ };
17347
+
17127
17348
  // src/commands/status.ts
17128
17349
  init_config();
17129
17350
  init_settings();
@@ -17608,12 +17829,12 @@ function engineChecks(o, env) {
17608
17829
  level: "warn",
17609
17830
  name: "repos dir",
17610
17831
  detail: `${dirs.join(" → ")} — ${missing.join(" and ")} ${missing.length === 1 ? "does" : "do"} not exist`,
17611
- fix: "create the folder or drop it from Repos dir in Settings › Engine"
17832
+ fix: "create the folder or drop it from Repos folders in Settings › Running work"
17612
17833
  } : dirs.length ? { level: "ok", name: "repos dir", detail: dirs.join(" → ") } : repo ? { level: "ok", name: "repos dir", detail: repo } : {
17613
17834
  level: "warn",
17614
17835
  name: "repos dir",
17615
- detail: `${e.label} names no --repos-dir — it works only if Settings › Engine sets Repos dir`,
17616
- fix: "set Repos dir in Settings › Engine, or sleds engine install --repos-dir ~/code"
17836
+ detail: `${e.label} names no --repos-dir — it works only if your engine profile sets Repos folders`,
17837
+ fix: "set Repos folders in Settings › Running work, or sleds engine install --repos-dir ~/code"
17617
17838
  });
17618
17839
  }
17619
17840
  return checks;
@@ -17623,7 +17844,7 @@ function boxChecks(o, env, which, need) {
17623
17844
  const file = boxEnvPath(o.home);
17624
17845
  let named = false;
17625
17846
  try {
17626
- named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync16(file, "utf8"));
17847
+ named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync17(file, "utf8"));
17627
17848
  } catch {
17628
17849
  named = false;
17629
17850
  }
@@ -17729,7 +17950,7 @@ import {
17729
17950
  appendFileSync as appendFileSync3,
17730
17951
  chmodSync as chmodSync5,
17731
17952
  mkdirSync as mkdirSync16,
17732
- readFileSync as readFileSync17,
17953
+ readFileSync as readFileSync18,
17733
17954
  existsSync as realExists2,
17734
17955
  rmSync as rmSync9,
17735
17956
  writeFileSync as writeFileSync18
@@ -18151,7 +18372,7 @@ function realPidAlive2(pid) {
18151
18372
  function memberMcpConfigs2(o, cwd) {
18152
18373
  const read = o.readJson ?? ((p) => {
18153
18374
  try {
18154
- return JSON.parse(readFileSync17(p, "utf8"));
18375
+ return JSON.parse(readFileSync18(p, "utf8"));
18155
18376
  } catch {
18156
18377
  return null;
18157
18378
  }
@@ -18376,7 +18597,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
18376
18597
  runner: deps.runner.name,
18377
18598
  atOnce: flags.atOnce,
18378
18599
  interval: flags.interval,
18379
- cliVersion: "0.1.41"
18600
+ cliVersion: "0.1.43"
18380
18601
  };
18381
18602
  const beat = (checkedAt) => {
18382
18603
  const m = deps.machine;
@@ -18683,6 +18904,13 @@ var engineCmd = async (o) => {
18683
18904
  if (client) {
18684
18905
  const send = beater(client, o.now ?? Date.now);
18685
18906
  const who = { name: flags.name, runner: runner.name };
18907
+ let refused;
18908
+ const sayRefused = (r) => {
18909
+ if (!r || r.ok || !r.refused || r.refused === refused)
18910
+ return;
18911
+ refused = r.refused;
18912
+ error(`${r.refused}. This engine is not shown as up until it beats under a name of its own.`);
18913
+ };
18686
18914
  afterTick = () => {
18687
18915
  try {
18688
18916
  const m = deps.machine;
@@ -18690,7 +18918,7 @@ var engineCmd = async (o) => {
18690
18918
  atOnce: deps.atOnce,
18691
18919
  ...m?.last ? { machine: m.last } : {},
18692
18920
  ...m?.hold && !m.off ? { hold: m.hold } : {}
18693
- }));
18921
+ }))?.then(sayRefused, () => {});
18694
18922
  } catch {}
18695
18923
  };
18696
18924
  }
@@ -18818,11 +19046,10 @@ async function enginePauseCmd2(o) {
18818
19046
 
18819
19047
  // src/commands/engineInstall.ts
18820
19048
  init_config();
18821
- init_state();
18822
19049
  import {
18823
19050
  existsSync as existsSync17,
18824
19051
  mkdirSync as mkdirSync17,
18825
- readFileSync as readFileSync18,
19052
+ readFileSync as readFileSync19,
18826
19053
  readdirSync as readdirSync4,
18827
19054
  realpathSync as realpathSync2,
18828
19055
  rmSync as rmSync10,
@@ -18830,6 +19057,7 @@ import {
18830
19057
  } from "node:fs";
18831
19058
  import { hostname as hostname5 } from "node:os";
18832
19059
  import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join21 } from "node:path";
19060
+ init_state();
18833
19061
  var ENGINE_LABEL2 = "ai.sleds.engine";
18834
19062
  var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
18835
19063
  ${ENGINE_USAGE}`;
@@ -18948,7 +19176,7 @@ function readEngines2(dir, suffix, manager, parse) {
18948
19176
  const path = join21(dir, name);
18949
19177
  let parsed;
18950
19178
  try {
18951
- parsed = parse(readFileSync18(path, "utf8"), name);
19179
+ parsed = parse(readFileSync19(path, "utf8"), name);
18952
19180
  } catch {
18953
19181
  parsed = null;
18954
19182
  }
@@ -18973,6 +19201,50 @@ function installedEngines2(home) {
18973
19201
  ];
18974
19202
  }
18975
19203
  var unsupported2 = (platform) => `sleds engine install needs launchd (macOS) or systemd (Linux). On ${platform}, keep \`sleds engine\` running under your own service manager (a scheduled task, tmux).`;
19204
+ var REPOS_DIR_NEEDED = "Set the folder your repos are cloned in first: `sleds engine profile set reposDir=~/code` (Settings › Running work › Repos folders), or `sleds engine install --repos-dir ~/code`. An installed engine starts outside any checkout, so without one every node it takes parks on No repo.";
19205
+ async function ensureReposDir(o) {
19206
+ const log = o.log ?? console.log;
19207
+ const error = o.error ?? console.error;
19208
+ const parsed = parseEngineArgs(o.argv, o.host ?? hostname5());
19209
+ if ("error" in parsed)
19210
+ return true;
19211
+ if (parsed.given.includes("repos-dir") || parsed.given.includes("repo"))
19212
+ return true;
19213
+ const client = engineClient({ home: o.home, ...o.env ? { env: o.env } : {}, ...o.client ? { client: o.client } : {} }, parsed.flags.org);
19214
+ if ("error" in client)
19215
+ return true;
19216
+ const profile = await readProfile(client);
19217
+ if (!profile) {
19218
+ log("note: your engine profile could not be read — if it names no repos folder, the engine says so when it starts");
19219
+ return true;
19220
+ }
19221
+ if (profile.reposDir)
19222
+ return true;
19223
+ if (!o.ask) {
19224
+ error(REPOS_DIR_NEEDED);
19225
+ return false;
19226
+ }
19227
+ const answer = (await o.ask("Where are your repos cloned? Folders searched in order, split on : (~/code): ")).trim();
19228
+ const dirs = splitReposDirs(answer);
19229
+ if (dirs.length === 0) {
19230
+ error(REPOS_DIR_NEEDED);
19231
+ return false;
19232
+ }
19233
+ const exists = o.exists ?? existsSync17;
19234
+ const missing = dirs.filter((d) => !exists(/^~(\/|$)/.test(d) ? join21(o.home, d.slice(1)) : d));
19235
+ if (missing.length) {
19236
+ error(`Not a folder on this machine: ${missing.join(", ")}. ${REPOS_DIR_NEEDED}`);
19237
+ return false;
19238
+ }
19239
+ const r = await client.postFull("/proxy/engine/profile", { profile: { reposDir: dirs.join(":") } }).catch(() => null);
19240
+ if (!r || r.status >= 300 || r.json?.ok === false) {
19241
+ const why = typeof r?.json?.error === "string" ? r.json.error : "SLEDS unreachable";
19242
+ error(`The repos folder was not saved (${why}). ${REPOS_DIR_NEEDED}`);
19243
+ return false;
19244
+ }
19245
+ log(`repos folders saved to your engine profile: ${dirs.join(" → ")}`);
19246
+ return true;
19247
+ }
18976
19248
  function engineInstall2(o) {
18977
19249
  const log = o.log ?? console.log;
18978
19250
  const error = o.error ?? console.error;
@@ -19147,13 +19419,13 @@ function ago(at, now) {
19147
19419
 
19148
19420
  // src/commands/engineList.ts
19149
19421
  var ENGINE_LIST_USAGE = "usage: sleds engine list [--json]";
19150
- var str11 = (x) => typeof x === "string" ? x : "";
19422
+ var str12 = (x) => typeof x === "string" ? x : "";
19151
19423
  var numOrNull = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
19152
19424
  function machineOf(x) {
19153
19425
  if (!x || typeof x !== "object")
19154
19426
  return null;
19155
19427
  const m = x;
19156
- const out = { pressure: str11(m.pressure) || "unknown" };
19428
+ const out = { pressure: str12(m.pressure) || "unknown" };
19157
19429
  for (const k of ["cpuPct", "load1", "cores", "loadPerCore"]) {
19158
19430
  const n = numOrNull(m[k]);
19159
19431
  if (n !== null)
@@ -19169,8 +19441,8 @@ function workOf(x) {
19169
19441
  if (!w || typeof w !== "object")
19170
19442
  return [];
19171
19443
  const o = w;
19172
- const item = str11(o.item);
19173
- return item ? [{ item, node: str11(o.node), title: typeof o.title === "string" ? o.title : null }] : [];
19444
+ const item = str12(o.item);
19445
+ return item ? [{ item, node: str12(o.node), title: typeof o.title === "string" ? o.title : null }] : [];
19174
19446
  });
19175
19447
  }
19176
19448
  function engineRows(raw) {
@@ -19179,16 +19451,16 @@ function engineRows(raw) {
19179
19451
  if (!r || typeof r !== "object")
19180
19452
  return [];
19181
19453
  const e = r;
19182
- const name = str11(e.name);
19454
+ const name = str12(e.name);
19183
19455
  if (!name)
19184
19456
  return [];
19185
19457
  return [
19186
19458
  {
19187
19459
  name,
19188
- email: str11(e.email),
19189
- os: str11(e.os),
19190
- runner: str11(e.runner),
19191
- version: str11(e.version),
19460
+ email: str12(e.email),
19461
+ os: str12(e.os),
19462
+ runner: str12(e.runner),
19463
+ version: str12(e.version),
19192
19464
  running: typeof e.running === "number" ? e.running : 0,
19193
19465
  items: Array.isArray(e.items) ? e.items.filter((i) => typeof i === "string") : [],
19194
19466
  seatKind: typeof e.seatKind === "string" ? e.seatKind : null,
@@ -19241,7 +19513,7 @@ var engineListCmd = async (o) => {
19241
19513
  return 1;
19242
19514
  }
19243
19515
  if (r.status !== 200 || !r.json || r.json.ok === false) {
19244
- error(str11(r.json?.error) || `HTTP ${r.status}`);
19516
+ error(str12(r.json?.error) || `HTTP ${r.status}`);
19245
19517
  return 1;
19246
19518
  }
19247
19519
  const rows = engineRows(r.json);
@@ -19258,7 +19530,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
19258
19530
  import { hostname as hostname6 } from "node:os";
19259
19531
 
19260
19532
  // src/commands/engineStatus.ts
19261
- import { existsSync as existsSync18, readFileSync as readFileSync19 } from "node:fs";
19533
+ import { existsSync as existsSync18, readFileSync as readFileSync20 } from "node:fs";
19262
19534
  init_state();
19263
19535
 
19264
19536
  // src/engine/status.ts
@@ -19548,7 +19820,7 @@ var engineLogCmd = (o) => {
19548
19820
 
19549
19821
  // src/commands/engineProfile.ts
19550
19822
  init_src();
19551
- import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
19823
+ import { existsSync as existsSync19, readFileSync as readFileSync21 } from "node:fs";
19552
19824
  import { hostname as hostname7 } from "node:os";
19553
19825
  var PROFILE_USAGE = "usage: sleds engine profile [--json] | sleds engine profile set <field>=<value>… [--workspace] [--json] | sleds engine profile set --stdin [--workspace] [--json] | sleds engine profile --strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp [--workspace] [--json] | sleds engine profile --sandbox [--sandbox-allow <a,b>] | --no-sandbox [--workspace] [--json] | sleds engine profile set --hold on|off [--workspace] [--json] | sleds engine profile set cmuxPassword=<pw> | --cmux-password <pw> [--json]";
19554
19826
  var PROFILE_FIELDS = [
@@ -19813,7 +20085,7 @@ async function engineProfileCmd(o) {
19813
20085
  const rest = words.slice(1);
19814
20086
  let stdin = null;
19815
20087
  if (fromStdin) {
19816
- const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync20(0, "utf8")))());
20088
+ const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync21(0, "utf8")))());
19817
20089
  if ("error" in taken || taken.password !== undefined && password !== undefined) {
19818
20090
  error("error" in taken ? taken.error : "the cmux password is given once");
19819
20091
  error(PROFILE_USAGE);
@@ -19898,7 +20170,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
19898
20170
 
19899
20171
  // src/commands/engineStatus.ts
19900
20172
  init_state();
19901
- import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
20173
+ import { existsSync as existsSync20, readFileSync as readFileSync22 } from "node:fs";
19902
20174
  import { hostname as hostname8 } from "node:os";
19903
20175
  var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
19904
20176
  function parseStatusArgs(argv, host) {
@@ -19954,7 +20226,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
19954
20226
  }
19955
20227
  function safeRead(path) {
19956
20228
  try {
19957
- return readFileSync21(path, "utf8");
20229
+ return readFileSync22(path, "utf8");
19958
20230
  } catch {
19959
20231
  return "";
19960
20232
  }
@@ -20075,28 +20347,28 @@ init_meter();
20075
20347
 
20076
20348
  // src/owed.ts
20077
20349
  init_config();
20078
- import { existsSync as existsSync21, mkdirSync as mkdirSync18, readFileSync as readFileSync22, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
20350
+ import { existsSync as existsSync21, mkdirSync as mkdirSync18, readFileSync as readFileSync23, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
20079
20351
  import { dirname as dirname14, join as join22 } from "node:path";
20080
20352
  var owedPath = (home) => join22(configDir(home), "owed.json");
20081
20353
  var sessionPath = (home) => join22(configDir(home), "session.json");
20082
20354
  var OWED_TTL_MS = 3 * 24 * 60 * 60000;
20083
- var str12 = (v) => typeof v === "string" ? v : undefined;
20355
+ var str13 = (v) => typeof v === "string" ? v : undefined;
20084
20356
  function sourceKey(s) {
20085
20357
  const trim = (x) => x.trim().slice(0, 120);
20086
- const kind = str12(s.kind) ?? "manual";
20087
- const project = trim(str12(s.project) ?? "");
20088
- const doc = str12(s.doc);
20089
- const item = str12(s.item);
20358
+ const kind = str13(s.kind) ?? "manual";
20359
+ const project = trim(str13(s.project) ?? "");
20360
+ const doc = str13(s.doc);
20361
+ const item = str13(s.item);
20090
20362
  return `${kind}:${project}${doc ? `/${trim(doc)}` : ""}${item ? `#${trim(item)}` : ""}`;
20091
20363
  }
20092
20364
  function sourceText2(s) {
20093
- return [`${str12(s.kind) ?? "?"} ${str12(s.project) ?? "?"}`, str12(s.doc), str12(s.item)].filter(Boolean).join(" › ");
20365
+ return [`${str13(s.kind) ?? "?"} ${str13(s.project) ?? "?"}`, str13(s.doc), str13(s.item)].filter(Boolean).join(" › ");
20094
20366
  }
20095
20367
  var readJson = (p) => {
20096
20368
  if (!existsSync21(p))
20097
20369
  return null;
20098
20370
  try {
20099
- return JSON.parse(readFileSync22(p, "utf8"));
20371
+ return JSON.parse(readFileSync23(p, "utf8"));
20100
20372
  } catch {
20101
20373
  return null;
20102
20374
  }
@@ -21123,7 +21395,7 @@ var login = async (o) => {
21123
21395
  // src/commands/logout.ts
21124
21396
  init_config();
21125
21397
  init_state();
21126
- import { existsSync as existsSync22, readFileSync as readFileSync23, rmSync as rmSync11 } from "node:fs";
21398
+ import { existsSync as existsSync22, readFileSync as readFileSync24, rmSync as rmSync11 } from "node:fs";
21127
21399
  import { hostname as hostname10 } from "node:os";
21128
21400
  init_settings();
21129
21401
  var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
@@ -21280,7 +21552,7 @@ async function logout(o) {
21280
21552
  };
21281
21553
  writeConfig(o.home, next);
21282
21554
  const tp = tokenPath(o.home, env);
21283
- if (existsSync22(tp) && readFileSync23(tp, "utf8").trim() === seat.token)
21555
+ if (existsSync22(tp) && readFileSync24(tp, "utf8").trim() === seat.token)
21284
21556
  rmSync11(tp, { force: true });
21285
21557
  const active = next.activeOrg ?? next.org?.slug ?? null;
21286
21558
  const result = { ok: true, email, org: slug, paused, active };
@@ -21358,7 +21630,7 @@ var meter = async (o) => {
21358
21630
  init_config();
21359
21631
  init_meter();
21360
21632
  import { spawnSync as spawnSync7 } from "node:child_process";
21361
- import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync24, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
21633
+ import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync25, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
21362
21634
  import { hostname as hostname11 } from "node:os";
21363
21635
  import { dirname as dirname15 } from "node:path";
21364
21636
  var defaultWhich3 = (cmd) => {
@@ -21835,7 +22107,7 @@ var packCmd = async (o) => {
21835
22107
  };
21836
22108
 
21837
22109
  // src/commands/permissionEvent.ts
21838
- import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as readFileSync25, renameSync as renameSync8, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "node:fs";
22110
+ import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as readFileSync26, renameSync as renameSync8, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "node:fs";
21839
22111
  import { dirname as dirname16 } from "node:path";
21840
22112
  var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
21841
22113
  var messageOf = (text) => {
@@ -21846,12 +22118,20 @@ var messageOf = (text) => {
21846
22118
  return "";
21847
22119
  }
21848
22120
  };
22121
+ var NOT_A_TOOL2 = new Set(["askuserquestion", "exitplanmode", "enterplanmode"]);
22122
+ var isToolPermission2 = (message) => {
22123
+ const tool = /permission to use\s+(.+?)\.?\s*$/i.exec(message)?.[1];
22124
+ return !tool || !NOT_A_TOOL2.has(tool.replace(/[\s_-]/g, "").toLowerCase());
22125
+ };
21849
22126
  var permissionEvent = async (o) => {
21850
22127
  try {
21851
22128
  const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
21852
22129
  if (!o.mark || notificationType(input) !== "permission_prompt")
21853
22130
  return 0;
21854
- const mark = { message: messageOf(input), at: (o.now ?? Date.now)() };
22131
+ const message = messageOf(input);
22132
+ if (!isToolPermission2(message))
22133
+ return 0;
22134
+ const mark = { message, at: (o.now ?? Date.now)() };
21855
22135
  mkdirSync21(dirname16(o.mark), { recursive: true, mode: 448 });
21856
22136
  const tmp = `${o.mark}.${process.pid}.tmp`;
21857
22137
  writeFileSync24(tmp, `${JSON.stringify(mark)}
@@ -21946,7 +22226,7 @@ var promptEvent = async (o) => {
21946
22226
 
21947
22227
  // src/commands/route.ts
21948
22228
  init_src();
21949
- import { readFileSync as readFileSync26 } from "node:fs";
22229
+ import { readFileSync as readFileSync27 } from "node:fs";
21950
22230
  import { hostname as hostname12 } from "node:os";
21951
22231
  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>]";
21952
22232
  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]";
@@ -22119,7 +22399,7 @@ async function answerCmd(o, argv) {
22119
22399
  const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
22120
22400
  const ref = words[0] ?? "";
22121
22401
  const slash = ref.lastIndexOf("/");
22122
- const answer = (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : words.slice(1).join(" ")).trim();
22402
+ const answer = (fromStdin ? (o.stdin ?? (() => readFileSync27(0, "utf8")))() : words.slice(1).join(" ")).trim();
22123
22403
  if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
22124
22404
  error(ANSWER_USAGE);
22125
22405
  return 2;
@@ -22316,7 +22596,7 @@ async function decisionCmd(o, argv) {
22316
22596
  error(DECISION_USAGE);
22317
22597
  return 2;
22318
22598
  }
22319
- const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : rest.join(" ")).trim();
22599
+ const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync27(0, "utf8")))() : rest.join(" ")).trim();
22320
22600
  if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
22321
22601
  error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
22322
22602
  return 2;
@@ -22457,7 +22737,7 @@ function approvedLine(key, j) {
22457
22737
  function readNodes(o, a) {
22458
22738
  let text;
22459
22739
  try {
22460
- text = a.file ? (o.readFile ?? ((p) => readFileSync26(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync26(0, "utf8")))();
22740
+ text = a.file ? (o.readFile ?? ((p) => readFileSync27(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync27(0, "utf8")))();
22461
22741
  } catch (e) {
22462
22742
  return { error: e instanceof Error ? e.message : String(e) };
22463
22743
  }
@@ -22768,7 +23048,7 @@ var seatEvent = async (o) => {
22768
23048
 
22769
23049
  // src/commands/seatSync.ts
22770
23050
  import { execFileSync as execFileSync4 } from "node:child_process";
22771
- import { existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync27, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
23051
+ import { existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync28, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
22772
23052
  import { platform as platform2 } from "node:os";
22773
23053
  import { dirname as dirname17, join as join25 } from "node:path";
22774
23054
 
@@ -23118,7 +23398,7 @@ function findCookieDb(home, source) {
23118
23398
  return db;
23119
23399
  }
23120
23400
  const localStatePath = join25(root, "Local State");
23121
- const localState = existsSync25(localStatePath) ? readFileSync27(localStatePath, "utf8") : null;
23401
+ const localState = existsSync25(localStatePath) ? readFileSync28(localStatePath, "utf8") : null;
23122
23402
  const wanted = source.profile ?? "Default";
23123
23403
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
23124
23404
  if (ambiguous)
@@ -23298,7 +23578,7 @@ var seatSync = async (o) => {
23298
23578
  };
23299
23579
  function readSeatToken(home, source) {
23300
23580
  try {
23301
- return readFileSync27(seatTokenPath(home, source), "utf8").trim() || undefined;
23581
+ return readFileSync28(seatTokenPath(home, source), "utf8").trim() || undefined;
23302
23582
  } catch {
23303
23583
  return;
23304
23584
  }
@@ -23381,7 +23661,7 @@ var sessionEvent = async (o) => {
23381
23661
 
23382
23662
  // src/commands/specEvent.ts
23383
23663
  init_meter();
23384
- import { readFileSync as readFileSync28, statSync as statSync2 } from "node:fs";
23664
+ import { readFileSync as readFileSync29, statSync as statSync2 } from "node:fs";
23385
23665
  import { basename as basename6, isAbsolute as isAbsolute4, relative } from "node:path";
23386
23666
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
23387
23667
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -23437,7 +23717,7 @@ function readFileOrNull(path) {
23437
23717
  try {
23438
23718
  if (statSync2(path).size > 2000000)
23439
23719
  return null;
23440
- return readFileSync28(path, "utf8");
23720
+ return readFileSync29(path, "utf8");
23441
23721
  } catch {
23442
23722
  return null;
23443
23723
  }
@@ -23496,9 +23776,9 @@ var specEvent = async (o) => {
23496
23776
  };
23497
23777
 
23498
23778
  // src/commands/status.ts
23779
+ import { existsSync as existsSync26 } from "node:fs";
23499
23780
  init_config();
23500
23781
  init_settings();
23501
- import { existsSync as existsSync26 } from "node:fs";
23502
23782
  var HEALTH_TIMEOUT_MS2 = 4000;
23503
23783
  var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_MS2) => {
23504
23784
  try {
@@ -23528,6 +23808,13 @@ var fetchProxyStatus = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_M
23528
23808
  }
23529
23809
  };
23530
23810
  var row2 = (label, value) => `${label.padEnd(12)}${value}`;
23811
+ var claudeLine = (c, seatEmail) => {
23812
+ if (!c)
23813
+ return "no Claude login found (signed out, or on an API key)";
23814
+ const org = c.orgName && (c.plan === "Team" || c.plan === "Enterprise") ? ` (${c.orgName})` : "";
23815
+ const other = seatEmail && seatEmail.toLowerCase() !== c.email.toLowerCase() ? " — not the seat's account" : "";
23816
+ return `${c.email}${c.plan ? ` · ${c.plan}` : ""}${org}${other}`;
23817
+ };
23531
23818
  var formatStatus = (s) => {
23532
23819
  const lines = [];
23533
23820
  const stale = s.mode === "on" && !s.pointed;
@@ -23560,6 +23847,8 @@ var formatStatus = (s) => {
23560
23847
  lines.push(row2("Seat:", `${s.email}${s.agentKind ? ` · ${s.agentKind}` : ""}`));
23561
23848
  else if (s.agentKind)
23562
23849
  lines.push(row2("Agent:", s.agentKind));
23850
+ if (s.claude !== undefined)
23851
+ lines.push(row2("Claude:", claudeLine(s.claude, s.email)));
23563
23852
  if (s.org)
23564
23853
  lines.push(row2("Org:", `${s.org.name}${s.org.slug ? ` (${s.org.slug})` : ""} · ${s.org.plan} · ${s.org.upstream === "none" ? "no platform key (walls: hold or hand off)" : s.org.upstream === "own" ? "platform key" : "shared key"}${s.orgFrom === "env" ? " · from SLEDS_ORG" : ""}`));
23565
23854
  if (s.otherOrgs)
@@ -23605,6 +23894,8 @@ var status3 = async (o) => {
23605
23894
  info.agentKind = cfg.agentKind;
23606
23895
  if (cfg.wiring)
23607
23896
  info.wiring = cfg.wiring;
23897
+ if (cfg.agentKind !== "codex")
23898
+ info.claude = readClaudeAccount(o.home, o.env ?? process.env);
23608
23899
  if (org)
23609
23900
  info.org = {
23610
23901
  name: org.name,
@@ -23726,7 +24017,7 @@ var rememberSeat = (o, key, window, session, now, input) => {
23726
24017
  const posts = [];
23727
24018
  if (src && shouldPostSeat(prior, window, now)) {
23728
24019
  state = markPosted(state, window, now);
23729
- posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window));
24020
+ posts.push(postSeatWindow({ ...src, fetchFn: o.fetchFn, timeoutMs: o.timeoutMs ?? SEAT_POST_TIMEOUT_MS }, window, readClaudeAccount(o.home, o.env ?? process.env)));
23730
24021
  }
23731
24022
  const events = seatWallEvents(prior, window);
23732
24023
  const credits = creditsEvent(o, prior, input, now);
@@ -23766,7 +24057,7 @@ var announce = (o, events) => {
23766
24057
 
23767
24058
  // src/commands/stopEvent.ts
23768
24059
  init_meter();
23769
- import { existsSync as existsSync27, readFileSync as readFileSync29 } from "node:fs";
24060
+ import { existsSync as existsSync27, readFileSync as readFileSync30 } from "node:fs";
23770
24061
 
23771
24062
  // src/transcript.ts
23772
24063
  var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -23862,7 +24153,7 @@ async function postTranscript(o, payload, now) {
23862
24153
  const src = meterSource(o.home);
23863
24154
  if (!src)
23864
24155
  return false;
23865
- const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync29(p, "utf8") : null);
24156
+ const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync30(p, "utf8") : null);
23866
24157
  let text;
23867
24158
  try {
23868
24159
  text = read(path);
@@ -24083,7 +24374,7 @@ var verify = async (o) => {
24083
24374
 
24084
24375
  // src/commands/webhooks.ts
24085
24376
  var USAGE2 = "usage: sleds webhooks [--json] | sleds webhooks deliveries <id or name> [--json]";
24086
- var str13 = (x) => typeof x === "string" ? x : "";
24377
+ var str14 = (x) => typeof x === "string" ? x : "";
24087
24378
  function ago3(at, now) {
24088
24379
  const m = Math.max(0, Math.floor((now - at) / 60000));
24089
24380
  if (m < 1)
@@ -24099,7 +24390,7 @@ function webhookLines(raw, now) {
24099
24390
  if (!rows.length)
24100
24391
  return ["no webhooks — add one in the app: Settings › Integrations › Webhooks"];
24101
24392
  return rows.map((h) => {
24102
- let host = str13(h.url);
24393
+ let host = str14(h.url);
24103
24394
  try {
24104
24395
  host = new URL(host.replace(/\{[a-z.]+\}/g, "x")).host;
24105
24396
  } catch {}
@@ -24107,21 +24398,21 @@ function webhookLines(raw, now) {
24107
24398
  const last = h.lastDelivery;
24108
24399
  const lastText = last?.at ? `last ${last.status ?? "—"} ${last.state ?? ""} ${ago3(last.at, now)}`.replace(/\s+/g, " ") : "nothing sent yet";
24109
24400
  const off = h.enabled ? "on " : "off";
24110
- const why = !h.enabled && h.disabledWhy ? ` (${str13(h.disabledWhy)})` : "";
24111
- return `${off} ${str13(h.name)} ${host} ${events.includes("*") ? "all events" : events.join(",")} ${lastText}${why} [${str13(h.id)}]`;
24401
+ const why = !h.enabled && h.disabledWhy ? ` (${str14(h.disabledWhy)})` : "";
24402
+ return `${off} ${str14(h.name)} ${host} ${events.includes("*") ? "all events" : events.join(",")} ${lastText}${why} [${str14(h.id)}]`;
24112
24403
  });
24113
24404
  }
24114
24405
  function deliveryLines(raw, now) {
24115
24406
  const j = clean(raw);
24116
24407
  const rows = Array.isArray(j.deliveries) ? j.deliveries : [];
24117
- const name = str13(j.webhook?.name);
24408
+ const name = str14(j.webhook?.name);
24118
24409
  if (!rows.length)
24119
24410
  return [`${name}: nothing sent yet`];
24120
24411
  return [
24121
24412
  `${name} — the last ${rows.length}`,
24122
24413
  ...rows.map((d) => {
24123
- const result = d.status !== null && d.status !== undefined ? String(d.status) : str13(d.error) || "—";
24124
- return `${str13(d.state).padEnd(9)} ${result.padEnd(5)} ${str13(d.event).padEnd(20)} ${str13(d.key) || "—"} ${ago3(Number(d.at), now)} tries ${d.attempts} ${str13(d.deliveryId)}`;
24414
+ const result = d.status !== null && d.status !== undefined ? String(d.status) : str14(d.error) || "—";
24415
+ return `${str14(d.state).padEnd(9)} ${result.padEnd(5)} ${str14(d.event).padEnd(20)} ${str14(d.key) || "—"} ${ago3(Number(d.at), now)} tries ${d.attempts} ${str14(d.deliveryId)}`;
24125
24416
  })
24126
24417
  ];
24127
24418
  }
@@ -24152,7 +24443,7 @@ var webhooksCmd = async (o) => {
24152
24443
  return 1;
24153
24444
  }
24154
24445
  if (r.status !== 200 || !r.json) {
24155
- const why = str13(r.json?.error) || `HTTP ${r.status}`;
24446
+ const why = str14(r.json?.error) || `HTTP ${r.status}`;
24156
24447
  error(r.status === 404 && !r.json ? "SLEDS does not know this seat — run `sleds init`" : why);
24157
24448
  return 1;
24158
24449
  }
@@ -24166,7 +24457,7 @@ var webhooksCmd = async (o) => {
24166
24457
 
24167
24458
  // src/config.ts
24168
24459
  init_settings();
24169
- import { chmodSync as chmodSync7, existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync30, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
24460
+ import { chmodSync as chmodSync7, existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
24170
24461
  import { join as join26, resolve as resolve10 } from "node:path";
24171
24462
  var configDir2 = (home, env = process.env) => {
24172
24463
  const o = env.SLEDS_HOME?.trim();
@@ -24306,7 +24597,7 @@ var readConfig2 = (home) => {
24306
24597
  const p = configPath2(home);
24307
24598
  if (!existsSync29(p))
24308
24599
  return {};
24309
- return parseConfig2(JSON.parse(readFileSync30(p, "utf8")));
24600
+ return parseConfig2(JSON.parse(readFileSync31(p, "utf8")));
24310
24601
  };
24311
24602
 
24312
24603
  // src/runners/exec.ts
@@ -24666,8 +24957,12 @@ var main = async (argv) => {
24666
24957
  return engineLogCmd({ home, argv: argv.slice(2) });
24667
24958
  if (cmd === "engine" && argv[1] === "list")
24668
24959
  return engineListCmd({ home, argv: argv.slice(2) });
24669
- if (cmd === "engine" && argv[1] === "install")
24960
+ if (cmd === "engine" && argv[1] === "install") {
24961
+ const ask = process.stdin.isTTY ? promptStdin : undefined;
24962
+ if (!await ensureReposDir({ home, argv: argv.slice(2), ...ask ? { ask } : {} }))
24963
+ return 1;
24670
24964
  return engineInstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
24965
+ }
24671
24966
  if (cmd === "engine" && argv[1] === "uninstall")
24672
24967
  return engineUninstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
24673
24968
  if (cmd === "engine" && argv[1] === "profile")
@@ -24853,7 +25148,7 @@ ${renderHelpText(argv[1])}`);
24853
25148
  case "version":
24854
25149
  case "--version":
24855
25150
  case "-v":
24856
- console.log("sleds 0.1.41");
25151
+ console.log("sleds 0.1.43");
24857
25152
  return 0;
24858
25153
  default:
24859
25154
  console.error(`unknown command: ${cmd}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleds/cli",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
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",