@sleds/cli 0.1.42 → 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 +359 -86
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -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?",
@@ -1769,7 +1850,7 @@ function buildRoute(drafts, opts = {}) {
1769
1850
  problems.push({ level: "error", text: "a route needs at least one node" });
1770
1851
  if (drafts.length > MAX_NODES)
1771
1852
  problems.push({ level: "error", text: `a route holds at most ${MAX_NODES} nodes` });
1772
- const given = drafts.map((d) => d.key ? norm(d.key) : "");
1853
+ const given = drafts.map((d) => d.key ? norm2(d.key) : "");
1773
1854
  const fresh = mintKeys(given.filter((k) => !k).length, given.filter((k) => k));
1774
1855
  let f = 0;
1775
1856
  const keys = given.map((k) => k || fresh[f++]);
@@ -1784,7 +1865,7 @@ function buildRoute(drafts, opts = {}) {
1784
1865
  const title = stripControl(d.title.replace(/\s+/g, " ")).trim().slice(0, 160);
1785
1866
  if (!title)
1786
1867
  problems.push({ level: "error", node: key, text: `node ${key} has no title` });
1787
- const blockedBy = [...new Set((d.blockedBy ?? []).map(norm).filter(Boolean))];
1868
+ const blockedBy = [...new Set((d.blockedBy ?? []).map(norm2).filter(Boolean))];
1788
1869
  for (const b of blockedBy) {
1789
1870
  if (b === key)
1790
1871
  problems.push({ level: "error", node: key, text: `node ${key} blocks itself` });
@@ -1961,7 +2042,7 @@ ${state}`)}, fillcolor="${fill}", color="${border}"${style}];`);
1961
2042
  `)}
1962
2043
  `;
1963
2044
  }
1964
- 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}`.";
1965
2046
  var init_route = __esm(() => {
1966
2047
  CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
1967
2048
  AREA_ROOTS = new Set(["apps", "packages", "services", "libs", "crates", "modules"]);
@@ -5546,6 +5627,44 @@ var init_fieldMap = __esm(() => {
5546
5627
  var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
5547
5628
  var init_whatsNew = __esm(() => {
5548
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
+ },
5549
5668
  {
5550
5669
  date: "2026-10-20",
5551
5670
  title: "Intake: the gate gets its own page",
@@ -6575,7 +6694,7 @@ function nodePrompt(o) {
6575
6694
  `- Work on branch \`${o.branch}\` from the latest \`origin/${base}\` (create it if this checkout is not on it). One branch, one PR.`,
6576
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.`,
6577
6696
  ...claim.pack.includes(PR_HEADER_OPEN) ? [
6578
- `- 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.`
6579
6698
  ] : [],
6580
6699
  "- Run the repo's own gate before you push. Never merge: a person (or the musher, when the attachment says so) does.",
6581
6700
  "- Stay inside this node's scope. Work another node needs goes in the PR description, not in this branch.",
@@ -7553,6 +7672,7 @@ var init_src = __esm(() => {
7553
7672
  init_guides();
7554
7673
  init_verbs();
7555
7674
  init_brief();
7675
+ init_forms();
7556
7676
  init_related();
7557
7677
  init_rank();
7558
7678
  init_markdown2();
@@ -8253,6 +8373,7 @@ init_cowork();
8253
8373
  init_guides();
8254
8374
  init_verbs();
8255
8375
  init_brief();
8376
+ init_forms();
8256
8377
  init_related();
8257
8378
  init_rank();
8258
8379
  init_markdown2();
@@ -9232,12 +9353,13 @@ var scanTranscriptForCredits = (path, prior) => {
9232
9353
  return { hit: false, next: prior && prior.path === path ? prior : { path, offset: 0 } };
9233
9354
  }
9234
9355
  };
9235
- var seatWindowBody = (w) => ({
9356
+ var seatWindowBody = (w, account) => ({
9236
9357
  capturedAt: w.capturedAt,
9237
9358
  source: "claude",
9238
9359
  ...w.fiveHour ? { fiveHour: w.fiveHour } : {},
9239
9360
  ...w.sevenDay ? { sevenDay: w.sevenDay } : {},
9240
- ...w.spendLimit ? { spendLimit: w.spendLimit } : {}
9361
+ ...w.spendLimit ? { spendLimit: w.spendLimit } : {},
9362
+ ...account ? { account } : {}
9241
9363
  });
9242
9364
  var postJson = async (o, path, body) => {
9243
9365
  const fetchFn = o.fetchFn ?? fetch;
@@ -9253,7 +9375,7 @@ var postJson = async (o, path, body) => {
9253
9375
  return false;
9254
9376
  }
9255
9377
  };
9256
- var postSeatWindow = (o, w) => postJson(o, "/proxy/seat", seatWindowBody(w));
9378
+ var postSeatWindow = (o, w, account) => postJson(o, "/proxy/seat", seatWindowBody(w, account));
9257
9379
  var postSeatWall = (o, input) => postJson(o, "/proxy/seat-wall", { source: "claude", ...input });
9258
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.";
9259
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.";
@@ -9447,7 +9569,7 @@ import {
9447
9569
  existsSync as existsSync16,
9448
9570
  lstatSync,
9449
9571
  mkdtempSync,
9450
- readFileSync as readFileSync16,
9572
+ readFileSync as readFileSync17,
9451
9573
  readlinkSync,
9452
9574
  rmSync as rmSync8,
9453
9575
  writeFileSync as writeFileSync17
@@ -9850,7 +9972,7 @@ function resolveRepoPath(o) {
9850
9972
  const want = o.repos.length ? o.repos.join(", ") : "this node";
9851
9973
  const dir = names[0] ? ` (the folder that holds ${names[0]})` : "";
9852
9974
  return {
9853
- 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.`
9854
9976
  };
9855
9977
  }
9856
9978
  for (const name of names) {
@@ -10375,7 +10497,7 @@ function cmuxRunner(exec, deps = {}) {
10375
10497
  throw new Error(`cmux send-key ${h.id} failed: ${failed(r)}`);
10376
10498
  },
10377
10499
  stop(h) {
10378
- cmux(["close-workspace", "--workspace", h.id]);
10500
+ closeWorkspace(h.id);
10379
10501
  if (!h.group)
10380
10502
  return;
10381
10503
  const l = cmux(LIST_GROUPS);
@@ -10444,7 +10566,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
10444
10566
  name: flags.name,
10445
10567
  os: host.os ?? process.platform,
10446
10568
  runner: flags.runner,
10447
- version: host.version ?? "0.1.42" ?? "dev",
10569
+ version: host.version ?? "0.1.43" ?? "dev",
10448
10570
  running: live.length,
10449
10571
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
10450
10572
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -10463,10 +10585,13 @@ function beatBody(state, flags, host = {}, cap = {}) {
10463
10585
  }
10464
10586
  async function postBeat(client, body) {
10465
10587
  try {
10466
- const r = await client.post("/proxy/engine/beat", { ...body });
10467
- 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 };
10468
10593
  } catch {
10469
- return false;
10594
+ return { ok: false };
10470
10595
  }
10471
10596
  }
10472
10597
  function beater(client, now, everyMs = BEAT_EVERY_MS) {
@@ -11254,6 +11379,11 @@ var notificationType = (text) => {
11254
11379
 
11255
11380
  // src/commands/permissionEvent.ts
11256
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
+ };
11257
11387
  var readPermissionMark = (file) => {
11258
11388
  try {
11259
11389
  const raw = JSON.parse(readFileSync10(file, "utf8"));
@@ -12136,7 +12266,36 @@ var DIALOG_ASKS = /^Do you want to .+\?$/;
12136
12266
  var DIALOG_OPTION = /^(❯\s*)?(\d)\.\s+(.+)$/;
12137
12267
  var DIALOG_FOOTER = /^(?:esc to |tab to |enter to |ctrl\+)/i;
12138
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
+ }
12139
12296
  function permissionDialog(screen) {
12297
+ if (choicePicker(screen))
12298
+ return null;
12140
12299
  const raw = screen.split(`
12141
12300
  `);
12142
12301
  const lines = raw.map(unbox);
@@ -13151,7 +13310,11 @@ async function settlePermission(d, runner, run, shown) {
13151
13310
  run.permission = undefined;
13152
13311
  return false;
13153
13312
  }
13154
- 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
+ }
13155
13318
  if (!dialog && !mark)
13156
13319
  return false;
13157
13320
  if (!dialog && runner.readsSession && !run.seenTicks)
@@ -15315,7 +15478,7 @@ var statusLine2 = (session, events, now) => {
15315
15478
  return `managed session: idle — its turn ended${money}`;
15316
15479
  }
15317
15480
  };
15318
- 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;
15319
15482
  function managedRunner(exec, deps = {}) {
15320
15483
  const env = deps.env ?? process.env;
15321
15484
  const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
@@ -15440,7 +15603,7 @@ ${renderTranscript(eventMessages(all))}`;
15440
15603
  const idle = lastIdle(all);
15441
15604
  const asks = str9(obj2(idle?.stop_reason)?.type) === "requires_action" ? pendingAsks(all, idle) : [];
15442
15605
  if (asks.length) {
15443
- const allow = YES.test(text);
15606
+ const allow = YES2.test(text);
15444
15607
  api("POST", `/v1/sessions/${h.id}/events`, {
15445
15608
  events: asks.map((e) => ({
15446
15609
  type: "user.tool_confirmation",
@@ -16336,7 +16499,7 @@ async function driveEngine(o, flags, deps, until = {}) {
16336
16499
  runner: deps.runner.name,
16337
16500
  atOnce: flags.atOnce,
16338
16501
  interval: flags.interval,
16339
- cliVersion: "0.1.42"
16502
+ cliVersion: "0.1.43"
16340
16503
  };
16341
16504
  const beat = (checkedAt) => {
16342
16505
  const m = deps.machine;
@@ -16647,7 +16810,6 @@ async function enginePauseCmd(o) {
16647
16810
 
16648
16811
  // src/commands/engineInstall.ts
16649
16812
  init_config();
16650
- init_state();
16651
16813
  import {
16652
16814
  existsSync as existsSync14,
16653
16815
  mkdirSync as mkdirSync14,
@@ -16659,6 +16821,7 @@ import {
16659
16821
  } from "node:fs";
16660
16822
  import { hostname as hostname3 } from "node:os";
16661
16823
  import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join18 } from "node:path";
16824
+ init_state();
16662
16825
 
16663
16826
  // src/schedule.ts
16664
16827
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -17146,6 +17309,42 @@ var musherAgentState = (home, env = process.env) => {
17146
17309
  }
17147
17310
  };
17148
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
+
17149
17348
  // src/commands/status.ts
17150
17349
  init_config();
17151
17350
  init_settings();
@@ -17630,12 +17829,12 @@ function engineChecks(o, env) {
17630
17829
  level: "warn",
17631
17830
  name: "repos dir",
17632
17831
  detail: `${dirs.join(" → ")} — ${missing.join(" and ")} ${missing.length === 1 ? "does" : "do"} not exist`,
17633
- 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"
17634
17833
  } : dirs.length ? { level: "ok", name: "repos dir", detail: dirs.join(" → ") } : repo ? { level: "ok", name: "repos dir", detail: repo } : {
17635
17834
  level: "warn",
17636
17835
  name: "repos dir",
17637
- detail: `${e.label} names no --repos-dir — it works only if Settings › Engine sets Repos dir`,
17638
- 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"
17639
17838
  });
17640
17839
  }
17641
17840
  return checks;
@@ -17645,7 +17844,7 @@ function boxChecks(o, env, which, need) {
17645
17844
  const file = boxEnvPath(o.home);
17646
17845
  let named = false;
17647
17846
  try {
17648
- 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"));
17649
17848
  } catch {
17650
17849
  named = false;
17651
17850
  }
@@ -17751,7 +17950,7 @@ import {
17751
17950
  appendFileSync as appendFileSync3,
17752
17951
  chmodSync as chmodSync5,
17753
17952
  mkdirSync as mkdirSync16,
17754
- readFileSync as readFileSync17,
17953
+ readFileSync as readFileSync18,
17755
17954
  existsSync as realExists2,
17756
17955
  rmSync as rmSync9,
17757
17956
  writeFileSync as writeFileSync18
@@ -18173,7 +18372,7 @@ function realPidAlive2(pid) {
18173
18372
  function memberMcpConfigs2(o, cwd) {
18174
18373
  const read = o.readJson ?? ((p) => {
18175
18374
  try {
18176
- return JSON.parse(readFileSync17(p, "utf8"));
18375
+ return JSON.parse(readFileSync18(p, "utf8"));
18177
18376
  } catch {
18178
18377
  return null;
18179
18378
  }
@@ -18398,7 +18597,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
18398
18597
  runner: deps.runner.name,
18399
18598
  atOnce: flags.atOnce,
18400
18599
  interval: flags.interval,
18401
- cliVersion: "0.1.42"
18600
+ cliVersion: "0.1.43"
18402
18601
  };
18403
18602
  const beat = (checkedAt) => {
18404
18603
  const m = deps.machine;
@@ -18705,6 +18904,13 @@ var engineCmd = async (o) => {
18705
18904
  if (client) {
18706
18905
  const send = beater(client, o.now ?? Date.now);
18707
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
+ };
18708
18914
  afterTick = () => {
18709
18915
  try {
18710
18916
  const m = deps.machine;
@@ -18712,7 +18918,7 @@ var engineCmd = async (o) => {
18712
18918
  atOnce: deps.atOnce,
18713
18919
  ...m?.last ? { machine: m.last } : {},
18714
18920
  ...m?.hold && !m.off ? { hold: m.hold } : {}
18715
- }));
18921
+ }))?.then(sayRefused, () => {});
18716
18922
  } catch {}
18717
18923
  };
18718
18924
  }
@@ -18840,11 +19046,10 @@ async function enginePauseCmd2(o) {
18840
19046
 
18841
19047
  // src/commands/engineInstall.ts
18842
19048
  init_config();
18843
- init_state();
18844
19049
  import {
18845
19050
  existsSync as existsSync17,
18846
19051
  mkdirSync as mkdirSync17,
18847
- readFileSync as readFileSync18,
19052
+ readFileSync as readFileSync19,
18848
19053
  readdirSync as readdirSync4,
18849
19054
  realpathSync as realpathSync2,
18850
19055
  rmSync as rmSync10,
@@ -18852,6 +19057,7 @@ import {
18852
19057
  } from "node:fs";
18853
19058
  import { hostname as hostname5 } from "node:os";
18854
19059
  import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join21 } from "node:path";
19060
+ init_state();
18855
19061
  var ENGINE_LABEL2 = "ai.sleds.engine";
18856
19062
  var ENGINE_INSTALL_USAGE2 = `usage: sleds engine install [engine flags] | sleds engine uninstall [--name <n>] [--org <slug>]
18857
19063
  ${ENGINE_USAGE}`;
@@ -18970,7 +19176,7 @@ function readEngines2(dir, suffix, manager, parse) {
18970
19176
  const path = join21(dir, name);
18971
19177
  let parsed;
18972
19178
  try {
18973
- parsed = parse(readFileSync18(path, "utf8"), name);
19179
+ parsed = parse(readFileSync19(path, "utf8"), name);
18974
19180
  } catch {
18975
19181
  parsed = null;
18976
19182
  }
@@ -18995,6 +19201,50 @@ function installedEngines2(home) {
18995
19201
  ];
18996
19202
  }
18997
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
+ }
18998
19248
  function engineInstall2(o) {
18999
19249
  const log = o.log ?? console.log;
19000
19250
  const error = o.error ?? console.error;
@@ -19169,13 +19419,13 @@ function ago(at, now) {
19169
19419
 
19170
19420
  // src/commands/engineList.ts
19171
19421
  var ENGINE_LIST_USAGE = "usage: sleds engine list [--json]";
19172
- var str11 = (x) => typeof x === "string" ? x : "";
19422
+ var str12 = (x) => typeof x === "string" ? x : "";
19173
19423
  var numOrNull = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
19174
19424
  function machineOf(x) {
19175
19425
  if (!x || typeof x !== "object")
19176
19426
  return null;
19177
19427
  const m = x;
19178
- const out = { pressure: str11(m.pressure) || "unknown" };
19428
+ const out = { pressure: str12(m.pressure) || "unknown" };
19179
19429
  for (const k of ["cpuPct", "load1", "cores", "loadPerCore"]) {
19180
19430
  const n = numOrNull(m[k]);
19181
19431
  if (n !== null)
@@ -19191,8 +19441,8 @@ function workOf(x) {
19191
19441
  if (!w || typeof w !== "object")
19192
19442
  return [];
19193
19443
  const o = w;
19194
- const item = str11(o.item);
19195
- 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 }] : [];
19196
19446
  });
19197
19447
  }
19198
19448
  function engineRows(raw) {
@@ -19201,16 +19451,16 @@ function engineRows(raw) {
19201
19451
  if (!r || typeof r !== "object")
19202
19452
  return [];
19203
19453
  const e = r;
19204
- const name = str11(e.name);
19454
+ const name = str12(e.name);
19205
19455
  if (!name)
19206
19456
  return [];
19207
19457
  return [
19208
19458
  {
19209
19459
  name,
19210
- email: str11(e.email),
19211
- os: str11(e.os),
19212
- runner: str11(e.runner),
19213
- version: str11(e.version),
19460
+ email: str12(e.email),
19461
+ os: str12(e.os),
19462
+ runner: str12(e.runner),
19463
+ version: str12(e.version),
19214
19464
  running: typeof e.running === "number" ? e.running : 0,
19215
19465
  items: Array.isArray(e.items) ? e.items.filter((i) => typeof i === "string") : [],
19216
19466
  seatKind: typeof e.seatKind === "string" ? e.seatKind : null,
@@ -19263,7 +19513,7 @@ var engineListCmd = async (o) => {
19263
19513
  return 1;
19264
19514
  }
19265
19515
  if (r.status !== 200 || !r.json || r.json.ok === false) {
19266
- error(str11(r.json?.error) || `HTTP ${r.status}`);
19516
+ error(str12(r.json?.error) || `HTTP ${r.status}`);
19267
19517
  return 1;
19268
19518
  }
19269
19519
  const rows = engineRows(r.json);
@@ -19280,7 +19530,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
19280
19530
  import { hostname as hostname6 } from "node:os";
19281
19531
 
19282
19532
  // src/commands/engineStatus.ts
19283
- import { existsSync as existsSync18, readFileSync as readFileSync19 } from "node:fs";
19533
+ import { existsSync as existsSync18, readFileSync as readFileSync20 } from "node:fs";
19284
19534
  init_state();
19285
19535
 
19286
19536
  // src/engine/status.ts
@@ -19570,7 +19820,7 @@ var engineLogCmd = (o) => {
19570
19820
 
19571
19821
  // src/commands/engineProfile.ts
19572
19822
  init_src();
19573
- import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
19823
+ import { existsSync as existsSync19, readFileSync as readFileSync21 } from "node:fs";
19574
19824
  import { hostname as hostname7 } from "node:os";
19575
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]";
19576
19826
  var PROFILE_FIELDS = [
@@ -19835,7 +20085,7 @@ async function engineProfileCmd(o) {
19835
20085
  const rest = words.slice(1);
19836
20086
  let stdin = null;
19837
20087
  if (fromStdin) {
19838
- const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync20(0, "utf8")))());
20088
+ const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync21(0, "utf8")))());
19839
20089
  if ("error" in taken || taken.password !== undefined && password !== undefined) {
19840
20090
  error("error" in taken ? taken.error : "the cmux password is given once");
19841
20091
  error(PROFILE_USAGE);
@@ -19920,7 +20170,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
19920
20170
 
19921
20171
  // src/commands/engineStatus.ts
19922
20172
  init_state();
19923
- import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
20173
+ import { existsSync as existsSync20, readFileSync as readFileSync22 } from "node:fs";
19924
20174
  import { hostname as hostname8 } from "node:os";
19925
20175
  var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
19926
20176
  function parseStatusArgs(argv, host) {
@@ -19976,7 +20226,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
19976
20226
  }
19977
20227
  function safeRead(path) {
19978
20228
  try {
19979
- return readFileSync21(path, "utf8");
20229
+ return readFileSync22(path, "utf8");
19980
20230
  } catch {
19981
20231
  return "";
19982
20232
  }
@@ -20097,28 +20347,28 @@ init_meter();
20097
20347
 
20098
20348
  // src/owed.ts
20099
20349
  init_config();
20100
- 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";
20101
20351
  import { dirname as dirname14, join as join22 } from "node:path";
20102
20352
  var owedPath = (home) => join22(configDir(home), "owed.json");
20103
20353
  var sessionPath = (home) => join22(configDir(home), "session.json");
20104
20354
  var OWED_TTL_MS = 3 * 24 * 60 * 60000;
20105
- var str12 = (v) => typeof v === "string" ? v : undefined;
20355
+ var str13 = (v) => typeof v === "string" ? v : undefined;
20106
20356
  function sourceKey(s) {
20107
20357
  const trim = (x) => x.trim().slice(0, 120);
20108
- const kind = str12(s.kind) ?? "manual";
20109
- const project = trim(str12(s.project) ?? "");
20110
- const doc = str12(s.doc);
20111
- 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);
20112
20362
  return `${kind}:${project}${doc ? `/${trim(doc)}` : ""}${item ? `#${trim(item)}` : ""}`;
20113
20363
  }
20114
20364
  function sourceText2(s) {
20115
- 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(" › ");
20116
20366
  }
20117
20367
  var readJson = (p) => {
20118
20368
  if (!existsSync21(p))
20119
20369
  return null;
20120
20370
  try {
20121
- return JSON.parse(readFileSync22(p, "utf8"));
20371
+ return JSON.parse(readFileSync23(p, "utf8"));
20122
20372
  } catch {
20123
20373
  return null;
20124
20374
  }
@@ -21145,7 +21395,7 @@ var login = async (o) => {
21145
21395
  // src/commands/logout.ts
21146
21396
  init_config();
21147
21397
  init_state();
21148
- 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";
21149
21399
  import { hostname as hostname10 } from "node:os";
21150
21400
  init_settings();
21151
21401
  var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
@@ -21302,7 +21552,7 @@ async function logout(o) {
21302
21552
  };
21303
21553
  writeConfig(o.home, next);
21304
21554
  const tp = tokenPath(o.home, env);
21305
- if (existsSync22(tp) && readFileSync23(tp, "utf8").trim() === seat.token)
21555
+ if (existsSync22(tp) && readFileSync24(tp, "utf8").trim() === seat.token)
21306
21556
  rmSync11(tp, { force: true });
21307
21557
  const active = next.activeOrg ?? next.org?.slug ?? null;
21308
21558
  const result = { ok: true, email, org: slug, paused, active };
@@ -21380,7 +21630,7 @@ var meter = async (o) => {
21380
21630
  init_config();
21381
21631
  init_meter();
21382
21632
  import { spawnSync as spawnSync7 } from "node:child_process";
21383
- 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";
21384
21634
  import { hostname as hostname11 } from "node:os";
21385
21635
  import { dirname as dirname15 } from "node:path";
21386
21636
  var defaultWhich3 = (cmd) => {
@@ -21857,7 +22107,7 @@ var packCmd = async (o) => {
21857
22107
  };
21858
22108
 
21859
22109
  // src/commands/permissionEvent.ts
21860
- 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";
21861
22111
  import { dirname as dirname16 } from "node:path";
21862
22112
  var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
21863
22113
  var messageOf = (text) => {
@@ -21868,12 +22118,20 @@ var messageOf = (text) => {
21868
22118
  return "";
21869
22119
  }
21870
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
+ };
21871
22126
  var permissionEvent = async (o) => {
21872
22127
  try {
21873
22128
  const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
21874
22129
  if (!o.mark || notificationType(input) !== "permission_prompt")
21875
22130
  return 0;
21876
- 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)() };
21877
22135
  mkdirSync21(dirname16(o.mark), { recursive: true, mode: 448 });
21878
22136
  const tmp = `${o.mark}.${process.pid}.tmp`;
21879
22137
  writeFileSync24(tmp, `${JSON.stringify(mark)}
@@ -21968,7 +22226,7 @@ var promptEvent = async (o) => {
21968
22226
 
21969
22227
  // src/commands/route.ts
21970
22228
  init_src();
21971
- import { readFileSync as readFileSync26 } from "node:fs";
22229
+ import { readFileSync as readFileSync27 } from "node:fs";
21972
22230
  import { hostname as hostname12 } from "node:os";
21973
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>]";
21974
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]";
@@ -22141,7 +22399,7 @@ async function answerCmd(o, argv) {
22141
22399
  const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
22142
22400
  const ref = words[0] ?? "";
22143
22401
  const slash = ref.lastIndexOf("/");
22144
- 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();
22145
22403
  if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
22146
22404
  error(ANSWER_USAGE);
22147
22405
  return 2;
@@ -22338,7 +22596,7 @@ async function decisionCmd(o, argv) {
22338
22596
  error(DECISION_USAGE);
22339
22597
  return 2;
22340
22598
  }
22341
- 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();
22342
22600
  if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
22343
22601
  error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
22344
22602
  return 2;
@@ -22479,7 +22737,7 @@ function approvedLine(key, j) {
22479
22737
  function readNodes(o, a) {
22480
22738
  let text;
22481
22739
  try {
22482
- 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")))();
22483
22741
  } catch (e) {
22484
22742
  return { error: e instanceof Error ? e.message : String(e) };
22485
22743
  }
@@ -22790,7 +23048,7 @@ var seatEvent = async (o) => {
22790
23048
 
22791
23049
  // src/commands/seatSync.ts
22792
23050
  import { execFileSync as execFileSync4 } from "node:child_process";
22793
- 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";
22794
23052
  import { platform as platform2 } from "node:os";
22795
23053
  import { dirname as dirname17, join as join25 } from "node:path";
22796
23054
 
@@ -23140,7 +23398,7 @@ function findCookieDb(home, source) {
23140
23398
  return db;
23141
23399
  }
23142
23400
  const localStatePath = join25(root, "Local State");
23143
- const localState = existsSync25(localStatePath) ? readFileSync27(localStatePath, "utf8") : null;
23401
+ const localState = existsSync25(localStatePath) ? readFileSync28(localStatePath, "utf8") : null;
23144
23402
  const wanted = source.profile ?? "Default";
23145
23403
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
23146
23404
  if (ambiguous)
@@ -23320,7 +23578,7 @@ var seatSync = async (o) => {
23320
23578
  };
23321
23579
  function readSeatToken(home, source) {
23322
23580
  try {
23323
- return readFileSync27(seatTokenPath(home, source), "utf8").trim() || undefined;
23581
+ return readFileSync28(seatTokenPath(home, source), "utf8").trim() || undefined;
23324
23582
  } catch {
23325
23583
  return;
23326
23584
  }
@@ -23403,7 +23661,7 @@ var sessionEvent = async (o) => {
23403
23661
 
23404
23662
  // src/commands/specEvent.ts
23405
23663
  init_meter();
23406
- import { readFileSync as readFileSync28, statSync as statSync2 } from "node:fs";
23664
+ import { readFileSync as readFileSync29, statSync as statSync2 } from "node:fs";
23407
23665
  import { basename as basename6, isAbsolute as isAbsolute4, relative } from "node:path";
23408
23666
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
23409
23667
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -23459,7 +23717,7 @@ function readFileOrNull(path) {
23459
23717
  try {
23460
23718
  if (statSync2(path).size > 2000000)
23461
23719
  return null;
23462
- return readFileSync28(path, "utf8");
23720
+ return readFileSync29(path, "utf8");
23463
23721
  } catch {
23464
23722
  return null;
23465
23723
  }
@@ -23518,9 +23776,9 @@ var specEvent = async (o) => {
23518
23776
  };
23519
23777
 
23520
23778
  // src/commands/status.ts
23779
+ import { existsSync as existsSync26 } from "node:fs";
23521
23780
  init_config();
23522
23781
  init_settings();
23523
- import { existsSync as existsSync26 } from "node:fs";
23524
23782
  var HEALTH_TIMEOUT_MS2 = 4000;
23525
23783
  var checkHealth2 = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_MS2) => {
23526
23784
  try {
@@ -23550,6 +23808,13 @@ var fetchProxyStatus = async (url, fetchFn = fetch, timeoutMs = HEALTH_TIMEOUT_M
23550
23808
  }
23551
23809
  };
23552
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
+ };
23553
23818
  var formatStatus = (s) => {
23554
23819
  const lines = [];
23555
23820
  const stale = s.mode === "on" && !s.pointed;
@@ -23582,6 +23847,8 @@ var formatStatus = (s) => {
23582
23847
  lines.push(row2("Seat:", `${s.email}${s.agentKind ? ` · ${s.agentKind}` : ""}`));
23583
23848
  else if (s.agentKind)
23584
23849
  lines.push(row2("Agent:", s.agentKind));
23850
+ if (s.claude !== undefined)
23851
+ lines.push(row2("Claude:", claudeLine(s.claude, s.email)));
23585
23852
  if (s.org)
23586
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" : ""}`));
23587
23854
  if (s.otherOrgs)
@@ -23627,6 +23894,8 @@ var status3 = async (o) => {
23627
23894
  info.agentKind = cfg.agentKind;
23628
23895
  if (cfg.wiring)
23629
23896
  info.wiring = cfg.wiring;
23897
+ if (cfg.agentKind !== "codex")
23898
+ info.claude = readClaudeAccount(o.home, o.env ?? process.env);
23630
23899
  if (org)
23631
23900
  info.org = {
23632
23901
  name: org.name,
@@ -23748,7 +24017,7 @@ var rememberSeat = (o, key, window, session, now, input) => {
23748
24017
  const posts = [];
23749
24018
  if (src && shouldPostSeat(prior, window, now)) {
23750
24019
  state = markPosted(state, window, now);
23751
- 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)));
23752
24021
  }
23753
24022
  const events = seatWallEvents(prior, window);
23754
24023
  const credits = creditsEvent(o, prior, input, now);
@@ -23788,7 +24057,7 @@ var announce = (o, events) => {
23788
24057
 
23789
24058
  // src/commands/stopEvent.ts
23790
24059
  init_meter();
23791
- import { existsSync as existsSync27, readFileSync as readFileSync29 } from "node:fs";
24060
+ import { existsSync as existsSync27, readFileSync as readFileSync30 } from "node:fs";
23792
24061
 
23793
24062
  // src/transcript.ts
23794
24063
  var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -23884,7 +24153,7 @@ async function postTranscript(o, payload, now) {
23884
24153
  const src = meterSource(o.home);
23885
24154
  if (!src)
23886
24155
  return false;
23887
- const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync29(p, "utf8") : null);
24156
+ const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync30(p, "utf8") : null);
23888
24157
  let text;
23889
24158
  try {
23890
24159
  text = read(path);
@@ -24105,7 +24374,7 @@ var verify = async (o) => {
24105
24374
 
24106
24375
  // src/commands/webhooks.ts
24107
24376
  var USAGE2 = "usage: sleds webhooks [--json] | sleds webhooks deliveries <id or name> [--json]";
24108
- var str13 = (x) => typeof x === "string" ? x : "";
24377
+ var str14 = (x) => typeof x === "string" ? x : "";
24109
24378
  function ago3(at, now) {
24110
24379
  const m = Math.max(0, Math.floor((now - at) / 60000));
24111
24380
  if (m < 1)
@@ -24121,7 +24390,7 @@ function webhookLines(raw, now) {
24121
24390
  if (!rows.length)
24122
24391
  return ["no webhooks — add one in the app: Settings › Integrations › Webhooks"];
24123
24392
  return rows.map((h) => {
24124
- let host = str13(h.url);
24393
+ let host = str14(h.url);
24125
24394
  try {
24126
24395
  host = new URL(host.replace(/\{[a-z.]+\}/g, "x")).host;
24127
24396
  } catch {}
@@ -24129,21 +24398,21 @@ function webhookLines(raw, now) {
24129
24398
  const last = h.lastDelivery;
24130
24399
  const lastText = last?.at ? `last ${last.status ?? "—"} ${last.state ?? ""} ${ago3(last.at, now)}`.replace(/\s+/g, " ") : "nothing sent yet";
24131
24400
  const off = h.enabled ? "on " : "off";
24132
- const why = !h.enabled && h.disabledWhy ? ` (${str13(h.disabledWhy)})` : "";
24133
- 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)}]`;
24134
24403
  });
24135
24404
  }
24136
24405
  function deliveryLines(raw, now) {
24137
24406
  const j = clean(raw);
24138
24407
  const rows = Array.isArray(j.deliveries) ? j.deliveries : [];
24139
- const name = str13(j.webhook?.name);
24408
+ const name = str14(j.webhook?.name);
24140
24409
  if (!rows.length)
24141
24410
  return [`${name}: nothing sent yet`];
24142
24411
  return [
24143
24412
  `${name} — the last ${rows.length}`,
24144
24413
  ...rows.map((d) => {
24145
- const result = d.status !== null && d.status !== undefined ? String(d.status) : str13(d.error) || "—";
24146
- 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)}`;
24147
24416
  })
24148
24417
  ];
24149
24418
  }
@@ -24174,7 +24443,7 @@ var webhooksCmd = async (o) => {
24174
24443
  return 1;
24175
24444
  }
24176
24445
  if (r.status !== 200 || !r.json) {
24177
- const why = str13(r.json?.error) || `HTTP ${r.status}`;
24446
+ const why = str14(r.json?.error) || `HTTP ${r.status}`;
24178
24447
  error(r.status === 404 && !r.json ? "SLEDS does not know this seat — run `sleds init`" : why);
24179
24448
  return 1;
24180
24449
  }
@@ -24188,7 +24457,7 @@ var webhooksCmd = async (o) => {
24188
24457
 
24189
24458
  // src/config.ts
24190
24459
  init_settings();
24191
- 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";
24192
24461
  import { join as join26, resolve as resolve10 } from "node:path";
24193
24462
  var configDir2 = (home, env = process.env) => {
24194
24463
  const o = env.SLEDS_HOME?.trim();
@@ -24328,7 +24597,7 @@ var readConfig2 = (home) => {
24328
24597
  const p = configPath2(home);
24329
24598
  if (!existsSync29(p))
24330
24599
  return {};
24331
- return parseConfig2(JSON.parse(readFileSync30(p, "utf8")));
24600
+ return parseConfig2(JSON.parse(readFileSync31(p, "utf8")));
24332
24601
  };
24333
24602
 
24334
24603
  // src/runners/exec.ts
@@ -24688,8 +24957,12 @@ var main = async (argv) => {
24688
24957
  return engineLogCmd({ home, argv: argv.slice(2) });
24689
24958
  if (cmd === "engine" && argv[1] === "list")
24690
24959
  return engineListCmd({ home, argv: argv.slice(2) });
24691
- 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;
24692
24964
  return engineInstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
24965
+ }
24693
24966
  if (cmd === "engine" && argv[1] === "uninstall")
24694
24967
  return engineUninstall2({ home, argv: argv.slice(2), sledsArgv: sledsArgv() });
24695
24968
  if (cmd === "engine" && argv[1] === "profile")
@@ -24875,7 +25148,7 @@ ${renderHelpText(argv[1])}`);
24875
25148
  case "version":
24876
25149
  case "--version":
24877
25150
  case "-v":
24878
- console.log("sleds 0.1.42");
25151
+ console.log("sleds 0.1.43");
24879
25152
  return 0;
24880
25153
  default:
24881
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.42",
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",