@sleds/cli 0.1.68 → 0.1.70

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 +319 -90
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -295,6 +295,16 @@ var init_sledSort = __esm(() => {
295
295
  function slugify(text, max = 63) {
296
296
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, max);
297
297
  }
298
+
299
+ // ../../packages/shared/src/workspaceLook.ts
300
+ var init_workspaceLook = () => {};
301
+
302
+ // ../../packages/shared/src/org/brand.ts
303
+ var BRAND_LOGO_MAX_BYTES;
304
+ var init_brand = __esm(() => {
305
+ init_workspaceLook();
306
+ BRAND_LOGO_MAX_BYTES = 1024 * 1024;
307
+ });
298
308
  // ../../packages/shared/src/seat/agents.ts
299
309
  var init_agents = () => {};
300
310
  // ../../packages/shared/src/seat/burn.ts
@@ -1107,6 +1117,9 @@ function norm(label) {
1107
1117
  function plain(s) {
1108
1118
  return s.replace(/[*_`~]+/g, "").replace(/\s+/g, " ").trim();
1109
1119
  }
1120
+ function isNoAnswer(s) {
1121
+ return !s || /^(n\/?a|none|no|nil|null|-+|—|tbd|\?+)\.?$/i.test(s.trim());
1122
+ }
1110
1123
  function titleOf(s, max = 80) {
1111
1124
  const t = plain(s);
1112
1125
  const first = /^(.+?[.!?])(\s|$)/.exec(t)?.[1] ?? t;
@@ -1290,9 +1303,9 @@ function draftBrief(input) {
1290
1303
  const byKey = form ? {
1291
1304
  touches: form.touches,
1292
1305
  demand: form.demand,
1293
- ...form.expected ? { done: form.expected } : {}
1306
+ ...form.expected && !isNoAnswer(form.expected) ? { done: form.expected } : {}
1294
1307
  } : parsed.byKey;
1295
- const fromText = form?.summary ? form.summary : form?.ask ? form.ask : askFrom(text, preamble);
1308
+ const fromText = form?.summary && !isNoAnswer(form.summary) ? form.summary : form?.ask ? form.ask : askFrom(text, preamble);
1296
1309
  const askText = a.ask?.trim() || (fromText ? titleOf(fromText) : undefined);
1297
1310
  if (!askText)
1298
1311
  return null;
@@ -4481,6 +4494,7 @@ async function actualTool(ctx, args, to = { path: "/proxy/estimate/actual" }) {
4481
4494
  ...num3(args.usd) !== undefined ? { actualUsd: num3(args.usd) } : {},
4482
4495
  ...args.usd === null ? { actualUsd: null } : {},
4483
4496
  ...str5(args.state) ? { state: str5(args.state) } : {},
4497
+ ...args.devVerified === true ? { devVerified: true } : {},
4484
4498
  ...Object.keys(work).length > 0 ? { work } : {}
4485
4499
  };
4486
4500
  const res = await ctx.client.postFull(to.path, body);
@@ -5529,6 +5543,10 @@ var init_server = __esm(() => {
5529
5543
  type: "string",
5530
5544
  description: "started · done · merged · shipped · parked (default: done when minutes or finishedAt are given)."
5531
5545
  },
5546
+ devVerified: {
5547
+ type: "boolean",
5548
+ description: "true when you verified it yourself — the finish skips verification (the workspace's QA column) and the merge lands it as before. Never set it by rule."
5549
+ },
5532
5550
  phases: {
5533
5551
  type: "object",
5534
5552
  description: "Minutes by phase; total defaults to their sum when `minutes` is absent.",
@@ -5619,6 +5637,10 @@ var init_server = __esm(() => {
5619
5637
  type: "string",
5620
5638
  description: "started · done · merged · shipped · parked (default: done when minutes or finishedAt are given)."
5621
5639
  },
5640
+ devVerified: {
5641
+ type: "boolean",
5642
+ description: "true when you verified it yourself — the finish skips verification (the workspace's QA column) and the merge lands it as before. Never set it by rule."
5643
+ },
5622
5644
  phases: {
5623
5645
  type: "object",
5624
5646
  description: "Minutes by phase; total defaults to their sum when `minutes` is absent.",
@@ -5668,6 +5690,10 @@ var init_server = __esm(() => {
5668
5690
  feature: { type: "string" },
5669
5691
  ref: { type: "string", description: "Where the output lives: PR url, commit, doc." },
5670
5692
  state: { type: "string", description: "done · merged · shipped (default: done)." },
5693
+ devVerified: {
5694
+ type: "boolean",
5695
+ description: "true when you verified it yourself — the finish skips verification (the workspace's QA column) and the merge lands it as before. Never set it by rule."
5696
+ },
5671
5697
  model: { type: "string" },
5672
5698
  sessionId: { type: "string" },
5673
5699
  node: {
@@ -8174,9 +8200,6 @@ var init_effort = __esm(() => {
8174
8200
  EFFORTS = ["low", "medium", "high", "xhigh", "max"];
8175
8201
  });
8176
8202
 
8177
- // ../../packages/shared/src/workspaceLook.ts
8178
- var init_workspaceLook = () => {};
8179
-
8180
8203
  // src/settings.ts
8181
8204
  import { execFileSync } from "node:child_process";
8182
8205
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
@@ -8806,6 +8829,7 @@ var init_src = __esm(() => {
8806
8829
  init_sledConfig();
8807
8830
  init_sledSort();
8808
8831
  init_gate();
8832
+ init_brand();
8809
8833
  init_agents();
8810
8834
  init_burn();
8811
8835
  init_cowork();
@@ -9301,6 +9325,7 @@ function parseRun(v) {
9301
9325
  }
9302
9326
  } : {},
9303
9327
  ...isObject8(v.provider) && typeof v.provider.why === "string" && typeof v.provider.since === "number" && typeof v.provider.tries === "number" ? { provider: { why: v.provider.why, since: v.provider.since, tries: v.provider.tries } } : {},
9328
+ ...isObject8(v.atPrompt) && typeof v.atPrompt.since === "number" && typeof v.atPrompt.text === "string" ? { atPrompt: { since: v.atPrompt.since, text: v.atPrompt.text } } : {},
9304
9329
  ...parked ? { parked } : {},
9305
9330
  ...isObject8(v.pr) && typeof v.pr.url === "string" ? {
9306
9331
  pr: {
@@ -9586,6 +9611,7 @@ init_envelope();
9586
9611
  init_sledConfig();
9587
9612
  init_sledSort();
9588
9613
  init_gate();
9614
+ init_brand();
9589
9615
  init_agents();
9590
9616
  init_burn();
9591
9617
  init_cowork();
@@ -11563,7 +11589,20 @@ var mainCheckoutOf = (gitCommonDir, cwd) => {
11563
11589
  const main = dir.slice(0, -"/.git".length);
11564
11590
  return main && main !== cwd.replace(/\/+$/, "") ? main : null;
11565
11591
  };
11566
- var claudeAddDirFlag = (mainCheckout) => mainCheckout ? ` --add-dir=${shq(mainCheckout)}` : "";
11592
+ var trimDir = (dir) => dir.replace(/\/+$/, "") || "/";
11593
+ var within = (path, dir) => {
11594
+ const p = trimDir(path);
11595
+ const d = trimDir(dir);
11596
+ return p === d || p.startsWith(d === "/" ? "/" : `${d}/`);
11597
+ };
11598
+ var sessionReadDirs = (o) => {
11599
+ const cwd = trimDir(o.cwd);
11600
+ const dirs = [o.mainCheckout, ...o.worktrees ?? [], o.pnpmStore].filter((d) => !!d?.trim()).map(trimDir);
11601
+ return [...new Set(dirs)].filter((d) => d !== cwd);
11602
+ };
11603
+ var readOnlyDirs = (readDirs, cwd, keep = []) => readDirs.filter((d) => ![cwd, ...keep].some((p) => within(p, d)));
11604
+ var readOnlyDenyRules = (dirs) => dirs.map((d) => `Edit(${absRule(d)})`);
11605
+ var claudeAddDirFlag = (dirs = []) => dirs.map((d) => ` --add-dir=${shq(d)}`).join("");
11567
11606
  var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
11568
11607
 
11569
11608
  // src/runners/exec.ts
@@ -11609,15 +11648,15 @@ var agentShellCommand = (o) => {
11609
11648
  const prompt = `"$(cat ${shq2(o.promptFile)})"`;
11610
11649
  const agent = o.agent?.trim();
11611
11650
  if (agent)
11612
- return `${sandboxedAgent(agent, o.sandbox, o.mainCheckout)} ${prompt}`;
11651
+ return `${sandboxedAgent(agent, o.sandbox, o.readDirs)} ${prompt}`;
11613
11652
  const model = o.model ? ` --model ${shq2(o.model)}` : "";
11614
11653
  const effort = o.effort ? ` --effort ${shq2(o.effort)}` : "";
11615
- return `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)}${hookSettingsFlag(o)}${mcpConfigFlag(o)} --permission-mode ${shq2(o.permissionMode)}${model}${effort} ${prompt}`;
11654
+ return `${sandboxedAgent("claude", o.sandbox, o.readDirs)}${hookSettingsFlag(o)}${mcpConfigFlag(o)} --permission-mode ${shq2(o.permissionMode)}${model}${effort} ${prompt}`;
11616
11655
  };
11617
11656
  var execLine = (cmd) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(cmd) ? `exec env ${cmd}` : `exec ${cmd}`;
11618
- var sandboxedAgent = (agent, sandbox, mainCheckout) => {
11657
+ var sandboxedAgent = (agent, sandbox, readDirs) => {
11619
11658
  const kind = sandboxAgentKind(agent);
11620
- const line = kind === "claude" ? `${agent}${claudeAddDirFlag(mainCheckout)}` : agent;
11659
+ const line = kind === "claude" ? `${agent}${claudeAddDirFlag(readDirs)}` : agent;
11621
11660
  if (!sandbox)
11622
11661
  return line;
11623
11662
  if (kind === "claude")
@@ -12340,7 +12379,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
12340
12379
  name: flags.name,
12341
12380
  os: host.os ?? process.platform,
12342
12381
  runner: flags.runner,
12343
- version: host.version ?? "0.1.68" ?? "dev",
12382
+ version: host.version ?? "0.1.70" ?? "dev",
12344
12383
  running: live.length,
12345
12384
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
12346
12385
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -14580,21 +14619,65 @@ function permissionDialog(screen) {
14580
14619
  if (t && !isRule(raw[i]) && !DIALOG_FOOTER.test(t) && !isFooter(t))
14581
14620
  return null;
14582
14621
  }
14622
+ return { text: dialogBody(raw, lines, q), options };
14623
+ }
14624
+ function dialogBody(raw, lines, last) {
14583
14625
  let from = 0;
14584
- for (let j = q - 1;j >= 0; j--) {
14626
+ for (let j = last;j >= 0; j--) {
14585
14627
  const t = lines[j].trim();
14586
14628
  if (isRule(raw[j]) || /^⏺/.test(t) || /^⎿/.test(t)) {
14587
14629
  from = j + 1;
14588
14630
  break;
14589
14631
  }
14590
14632
  }
14591
- const body = lines.slice(from, q + 1);
14592
- const indent = Math.min(...body.filter((l) => l.trim()).map((l) => /^\s*/.exec(l)[0].length));
14593
- const text = body.map((l) => l.slice(indent)).join(`
14633
+ const body = lines.slice(from, last + 1);
14634
+ const inked = body.filter((l) => l.trim());
14635
+ if (!inked.length)
14636
+ return "";
14637
+ const indent = Math.min(...inked.map((l) => /^\s*/.exec(l)[0].length));
14638
+ return body.map((l) => l.slice(indent)).join(`
14594
14639
  `).replace(/\n{3,}/g, `
14595
14640
 
14596
14641
  `).trim();
14597
- return { text, options };
14642
+ }
14643
+ var CURSOR_CHOICE = /^❯\s*\d\.\s/;
14644
+ var CANCEL_FOOTER = /\besc to cancel\b/i;
14645
+ function stoppedAtPrompt(screen) {
14646
+ const dialog = permissionDialog(screen);
14647
+ if (dialog)
14648
+ return { text: dialog.text };
14649
+ const raw = screen.split(`
14650
+ `);
14651
+ const lines = raw.map(unbox);
14652
+ let c = -1;
14653
+ for (let i = lines.length - 1;i >= 0; i--)
14654
+ if (CURSOR_CHOICE.test(lines[i].trim())) {
14655
+ c = i;
14656
+ break;
14657
+ }
14658
+ if (c < 0)
14659
+ return null;
14660
+ let footer = -1;
14661
+ for (let i = c + 1;i < lines.length; i++) {
14662
+ const t = lines[i].trim();
14663
+ if (/^⏺/.test(t) || isPrompt(t) && !CURSOR_CHOICE.test(t))
14664
+ return null;
14665
+ if (footer < 0 && CANCEL_FOOTER.test(t))
14666
+ footer = i;
14667
+ else if (footer >= 0 && t && !isRule(raw[i]) && !isFooter(t) && !DIALOG_FOOTER.test(t))
14668
+ return null;
14669
+ }
14670
+ if (footer < 0 && !choicePicker(screen))
14671
+ return null;
14672
+ let first = c;
14673
+ for (let i = c;i >= 0 && c - i < PICKER_REACH; i--)
14674
+ if (/^(?:❯\s*)?1\.\s/.test(lines[i].trim())) {
14675
+ first = i;
14676
+ break;
14677
+ }
14678
+ const text = dialogBody(raw, lines, first - 1) || lines.slice(first, c + 1).map((l) => l.trim()).join(`
14679
+ `);
14680
+ return { text };
14598
14681
  }
14599
14682
  var PROVIDER_FAILURES = [
14600
14683
  ["connection lost", /\bconnection (?:lost|error)\b/i],
@@ -14955,21 +15038,29 @@ function fingerprint(text) {
14955
15038
  function wordsPrint(screen) {
14956
15039
  return fingerprint(stripTuiChrome(screen));
14957
15040
  }
14958
- function sandboxFor(d, name, cwd, allow) {
15041
+ function sandboxFor(d, name, cwd, allow, readOnly) {
14959
15042
  if (!d.sandbox)
14960
15043
  return {};
14961
15044
  if (!d.writeSandboxSettings)
14962
15045
  throw new Error("sessions are sandboxed, but nothing writes their settings — not started");
14963
- return { sandbox: d.writeSandboxSettings(name, cwd, allow) };
15046
+ return { sandbox: d.writeSandboxSettings(name, cwd, allow, readOnly) };
14964
15047
  }
14965
- function hooksFor(d, name, allow) {
15048
+ function hooksFor(d, name, allow, readOnly) {
14966
15049
  if (d.sandbox || !d.writeHookSettings)
14967
15050
  return {};
14968
- return { settingsFile: d.writeHookSettings(name, allow) };
15051
+ return { settingsFile: d.writeHookSettings(name, allow, readOnly) };
14969
15052
  }
14970
- function mainCheckoutFor(d, cwd) {
14971
- const main = d.mainCheckoutOf?.(cwd);
14972
- return main ? { mainCheckout: main } : {};
15053
+ function wallsFor(d, name, cwd, allow) {
15054
+ let readDirs = [];
15055
+ try {
15056
+ readDirs = d.readDirsOf?.(cwd) ?? [];
15057
+ } catch {}
15058
+ const readOnly = readOnlyDirs(readDirs, cwd);
15059
+ return {
15060
+ ...sandboxFor(d, name, cwd, allow, readOnly),
15061
+ ...hooksFor(d, name, allow, readOnly),
15062
+ ...readDirs.length ? { readDirs } : {}
15063
+ };
14973
15064
  }
14974
15065
  function engineMcpConfigFor(d, name, runToken, at, cwd) {
14975
15066
  if (d.source.name !== "sleds" || d.agent?.trim() || !d.writeMcpConfig)
@@ -15216,7 +15307,9 @@ async function tickOnce(d) {
15216
15307
  idle = false;
15217
15308
  asked = false;
15218
15309
  failing = null;
15219
- }
15310
+ run.atPrompt = undefined;
15311
+ } else if (runner.readsSession)
15312
+ notePrompt(d, runner, run, shown);
15220
15313
  const retry = failing !== null && (run.provider?.tries ?? 0) < PROVIDER_RETRIES;
15221
15314
  if (idle || failing || needsHeartbeat(run.leaseUntil, d.now(), d.leaseMin)) {
15222
15315
  let beat;
@@ -15519,9 +15612,7 @@ async function tickOnce(d) {
15519
15612
  ...launch.effort && !launch.agent ? { effort: launch.effort } : {},
15520
15613
  ...launch.agent ? { agent: launch.agent } : {},
15521
15614
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
15522
- ...sandboxFor(d, name, worktree, allow),
15523
- ...hooksFor(d, name, allow),
15524
- ...mainCheckoutFor(d, worktree),
15615
+ ...wallsFor(d, name, worktree, allow),
15525
15616
  ...claim.group ? { group: claim.group } : {}
15526
15617
  };
15527
15618
  } catch (e) {
@@ -15813,6 +15904,17 @@ async function settlePermission(d, runner, run, shown) {
15813
15904
  d.log(`${at}: stopped at a permission dialog — it waits for a person (${runner.watchHint(run.handle)})`);
15814
15905
  return true;
15815
15906
  }
15907
+ function notePrompt(d, runner, run, shown) {
15908
+ if (!shown)
15909
+ return;
15910
+ const prompt = stoppedAtPrompt(shown);
15911
+ if (!prompt || run.atPrompt && run.atPrompt.text !== prompt.text)
15912
+ run.atPrompt = undefined;
15913
+ if (!prompt || run.atPrompt || !run.seenTicks)
15914
+ return;
15915
+ run.atPrompt = { since: d.now(), text: prompt.text };
15916
+ d.log(`${ref(run)}: stopped at a prompt the engine cannot answer — answer it in the terminal (${runner.watchHint(run.handle)})`);
15917
+ }
15816
15918
  var PRESS_TRIES = 3;
15817
15919
  function pressAnswer(d, runner, run, options, answer) {
15818
15920
  const at = ref(run);
@@ -16398,9 +16500,7 @@ async function startAnswerer(d, answers, run) {
16398
16500
  permissionMode: d.permissionMode,
16399
16501
  ...launchOptions(launch),
16400
16502
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
16401
- ...sandboxFor(d, name, run.handle.cwd),
16402
- ...hooksFor(d, name),
16403
- ...mainCheckoutFor(d, run.handle.cwd),
16503
+ ...wallsFor(d, name, run.handle.cwd),
16404
16504
  ...run.group ? { group: run.group } : {}
16405
16505
  });
16406
16506
  } catch (e) {
@@ -16608,9 +16708,7 @@ async function startDraft(d, claim, why, state, started, finished) {
16608
16708
  permissionMode: d.permissionMode,
16609
16709
  ...launchOptions(launch),
16610
16710
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
16611
- ...sandboxFor(d, name, worktree),
16612
- ...hooksFor(d, name),
16613
- ...mainCheckoutFor(d, worktree)
16711
+ ...wallsFor(d, name, worktree)
16614
16712
  });
16615
16713
  } catch (e) {
16616
16714
  d.log(`${at}: could not start the drafter: ${message(e)}`);
@@ -16789,9 +16887,7 @@ async function startMock(d, claim, state, started, finished) {
16789
16887
  promptFile: d.writePrompt(name, prompt),
16790
16888
  permissionMode: d.permissionMode,
16791
16889
  ...launchOptions(launch),
16792
- ...sandboxFor(d, name, worktree, allow),
16793
- ...hooksFor(d, name, allow),
16794
- ...mainCheckoutFor(d, worktree)
16890
+ ...wallsFor(d, name, worktree, allow)
16795
16891
  });
16796
16892
  } catch (e) {
16797
16893
  await fail(`the engine could not start the session: ${message(e)}`);
@@ -17678,9 +17774,7 @@ function restartInWorktree(d, run, name, prompt, base) {
17678
17774
  permissionMode: d.permissionMode,
17679
17775
  ...launchOptions(launch),
17680
17776
  ...run.mcpConfig ? { mcpConfig: run.mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
17681
- ...sandboxFor(d, name, run.handle.cwd),
17682
- ...hooksFor(d, name),
17683
- ...mainCheckoutFor(d, run.handle.cwd),
17777
+ ...wallsFor(d, name, run.handle.cwd),
17684
17778
  ...run.group ? { group: run.group } : {}
17685
17779
  };
17686
17780
  run.handle = runner.makesWorktree ? runner.reopen(run.handle, o) : runner.start(o);
@@ -17814,9 +17908,7 @@ async function startEventAnswerer(d, answers, state, dec, decided) {
17814
17908
  permissionMode: d.permissionMode,
17815
17909
  ...launchOptions(launch),
17816
17910
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
17817
- ...sandboxFor(d, name, worktree),
17818
- ...hooksFor(d, name),
17819
- ...mainCheckoutFor(d, worktree)
17911
+ ...wallsFor(d, name, worktree)
17820
17912
  });
17821
17913
  } catch (e) {
17822
17914
  d.log(`${at}: could not start the ${KIND_SAYS[kind]} answerer — it goes to a person: ${message(e)}`);
@@ -18344,8 +18436,8 @@ ${renderTranscript(messages)}`;
18344
18436
  var headlessScript = (o, log) => {
18345
18437
  const prompt = `"$(cat ${shq2(o.promptFile)})"`;
18346
18438
  const agent = o.agent?.trim();
18347
- const cmd = agent ? `${sandboxedAgent(agent, o.sandbox, o.mainCheckout)} ${prompt}` : [
18348
- `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)} -p`,
18439
+ const cmd = agent ? `${sandboxedAgent(agent, o.sandbox, o.readDirs)} ${prompt}` : [
18440
+ `${sandboxedAgent("claude", o.sandbox, o.readDirs)} -p`,
18349
18441
  prompt,
18350
18442
  ...o.mcpConfig ? [mcpConfigFlag(o).trim()] : [],
18351
18443
  "--output-format stream-json --verbose",
@@ -19224,7 +19316,7 @@ async function fetchVersion(o) {
19224
19316
  const platform = o.platform ?? process.platform;
19225
19317
  const arch = o.arch ?? process.arch;
19226
19318
  const publicKey = o.publicKey ?? RELEASE_PUBLIC_KEY;
19227
- const running = o.running ?? "0.1.68";
19319
+ const running = o.running ?? "0.1.70";
19228
19320
  if (!publicKey)
19229
19321
  return { ok: false, why: "no release key yet" };
19230
19322
  if (!VERSION_RE2.test(o.version))
@@ -20450,7 +20542,7 @@ function engineUpdater(o) {
20450
20542
 
20451
20543
  // src/version.ts
20452
20544
  function cliVersion(env) {
20453
- return "0.1.68";
20545
+ return "0.1.70";
20454
20546
  }
20455
20547
 
20456
20548
  // src/commands/update.ts
@@ -20836,7 +20928,7 @@ function engineClient(o, org, breaker) {
20836
20928
  ...src,
20837
20929
  ...breaker ? { fetchFn: breaker.fetch } : {},
20838
20930
  timeoutMs: ENGINE_TIMEOUT_MS,
20839
- version: "0.1.68"
20931
+ version: "0.1.70"
20840
20932
  });
20841
20933
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
20842
20934
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -20912,6 +21004,7 @@ function engineDeps(o, flags, source, runner, stateName) {
20912
21004
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve9(cwd, d));
20913
21005
  const { branch, prTitle } = flags;
20914
21006
  const hooksDir = join25(stateDir, "hooks");
21007
+ const pnpmStores = new Map;
20915
21008
  const permissionMark = (name) => join25(hooksDir, `${name}.permission.json`);
20916
21009
  const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
20917
21010
  return {
@@ -20935,7 +21028,7 @@ function engineDeps(o, flags, source, runner, stateName) {
20935
21028
  ...flags.strictMcp ? { strictMcp: true } : {},
20936
21029
  ...flags.sandbox ? {
20937
21030
  sandbox: true,
20938
- writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
21031
+ writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow, readOnly) => {
20939
21032
  const gitCommonDir = gitCommonDirOf(exec, cwd);
20940
21033
  const dir = join25(stateDir, "sandbox");
20941
21034
  mkdirSync18(dir, { recursive: true, mode: 448 });
@@ -20947,6 +21040,12 @@ function engineDeps(o, flags, source, runner, stateName) {
20947
21040
  walls.permissions.allow = [
20948
21041
  ...new Set([...walls.permissions.allow, ...allow ?? []])
20949
21042
  ];
21043
+ walls.permissions.deny = [
21044
+ ...new Set([
21045
+ ...walls.permissions.deny,
21046
+ ...readOnlyDenyRules(readOnlyDirs(readOnly ?? [], cwd, [gitCommonDir]))
21047
+ ])
21048
+ ];
20950
21049
  const settings = withHook(name, { ...walls });
20951
21050
  writeFileSync16(p, `${JSON.stringify(settings, null, 2)}
20952
21051
  `, { mode: 384 });
@@ -20954,23 +21053,22 @@ function engineDeps(o, flags, source, runner, stateName) {
20954
21053
  return { settingsFile: p, gitCommonDir };
20955
21054
  })
20956
21055
  } : {},
20957
- writeHookSettings: (name, allow) => {
21056
+ writeHookSettings: (name, allow, readOnly) => {
20958
21057
  mkdirSync18(hooksDir, { recursive: true, mode: 448 });
20959
21058
  const p = join25(hooksDir, `${name}.json`);
20960
- const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
21059
+ const deny = readOnlyDenyRules(readOnly ?? []);
21060
+ const permissions = {
21061
+ ...allow?.length ? { allow: [...allow] } : {},
21062
+ ...deny.length ? { deny } : {}
21063
+ };
21064
+ const base = Object.keys(permissions).length ? { permissions } : {};
20961
21065
  writeFileSync16(p, `${JSON.stringify(withHook(name, base), null, 2)}
20962
21066
  `, { mode: 384 });
20963
21067
  chmodSync5(p, 384);
20964
21068
  return p;
20965
21069
  },
20966
21070
  permissionMark,
20967
- mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
20968
- try {
20969
- return mainCheckoutOf(gitCommonDirOf(exec, cwd), cwd);
20970
- } catch {
20971
- return null;
20972
- }
20973
- }),
21071
+ readDirsOf: o.readDirsOf ?? ((dir) => engineReadDirs(exec, dir, exists, pnpmStores)),
20974
21072
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
20975
21073
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
20976
21074
  now: o.now ?? Date.now,
@@ -21072,6 +21170,52 @@ function gitCommonDirOf(exec, cwd) {
21072
21170
  throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
21073
21171
  return resolve9(cwd, dir);
21074
21172
  }
21173
+ function worktreeListPaths(porcelain) {
21174
+ return porcelain.split(/\n\s*\n/).map((block) => block.split(`
21175
+ `).map((l) => l.trim())).filter((lines) => !lines.some((l) => l === "bare" || l.startsWith("prunable"))).flatMap((lines) => {
21176
+ const wt = lines.find((l) => l.startsWith("worktree "));
21177
+ return wt ? [wt.slice("worktree ".length)] : [];
21178
+ });
21179
+ }
21180
+ var PNPM_STORE_TIMEOUT_MS = 1e4;
21181
+ function engineReadDirs(exec, cwd, exists, stores) {
21182
+ let common = null;
21183
+ try {
21184
+ common = gitCommonDirOf(exec, cwd);
21185
+ } catch {}
21186
+ const worktrees = (() => {
21187
+ try {
21188
+ const r = exec("git", ["-C", cwd, "worktree", "list", "--porcelain"]);
21189
+ return r.status === 0 ? worktreeListPaths(r.stdout) : [];
21190
+ } catch {
21191
+ return [];
21192
+ }
21193
+ })();
21194
+ const pnpmStore = (() => {
21195
+ try {
21196
+ if (!exists(join25(cwd, "pnpm-lock.yaml")))
21197
+ return null;
21198
+ const key = common ?? cwd;
21199
+ const known = stores.get(key);
21200
+ if (known)
21201
+ return known;
21202
+ const r = exec("pnpm", ["store", "path"], { cwd, timeoutMs: PNPM_STORE_TIMEOUT_MS });
21203
+ const dir = r.status === 0 ? r.stdout.split(`
21204
+ `).map((l) => l.trim()).filter((l) => l.startsWith("/")).pop() : undefined;
21205
+ if (dir)
21206
+ stores.set(key, dir);
21207
+ return dir ?? null;
21208
+ } catch {
21209
+ return null;
21210
+ }
21211
+ })();
21212
+ return sessionReadDirs({
21213
+ mainCheckout: common ? mainCheckoutOf(common, cwd) : null,
21214
+ worktrees,
21215
+ pnpmStore,
21216
+ cwd
21217
+ });
21218
+ }
21075
21219
  var LENT_NEEDS_SANDBOX = "a lent engine runs every session sandboxed (spec 48 rule 10) — add --sandbox to --lent-by";
21076
21220
  function lenderHome(accountHome, lentBy) {
21077
21221
  return isAbsolute3(lentBy) ? resolve9(lentBy) : join25(dirname16(accountHome), lentBy);
@@ -21142,7 +21286,7 @@ async function driveEngine(o, flags, deps, until = {}) {
21142
21286
  runner: deps.runner.name,
21143
21287
  atOnce: flags.atOnce,
21144
21288
  interval: flags.interval,
21145
- cliVersion: "0.1.68"
21289
+ cliVersion: "0.1.70"
21146
21290
  };
21147
21291
  const beat = (checkedAt) => {
21148
21292
  const m = deps.machine;
@@ -23020,7 +23164,7 @@ function engineClient2(o, org, breaker) {
23020
23164
  ...src,
23021
23165
  ...breaker ? { fetchFn: breaker.fetch } : {},
23022
23166
  timeoutMs: ENGINE_TIMEOUT_MS2,
23023
- version: "0.1.68"
23167
+ version: "0.1.70"
23024
23168
  });
23025
23169
  const asked = org?.trim() || env.SLEDS_ORG?.trim();
23026
23170
  if (asked && !selectOrg(readConfig(o.home), org, env).seat)
@@ -23118,6 +23262,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
23118
23262
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve11(cwd, d));
23119
23263
  const { branch, prTitle } = flags;
23120
23264
  const hooksDir = join28(stateDir, "hooks");
23265
+ const pnpmStores = new Map;
23121
23266
  const permissionMark = (name) => join28(hooksDir, `${name}.permission.json`);
23122
23267
  const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
23123
23268
  return {
@@ -23141,7 +23286,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
23141
23286
  ...flags.strictMcp ? { strictMcp: true } : {},
23142
23287
  ...flags.sandbox ? {
23143
23288
  sandbox: true,
23144
- writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow) => {
23289
+ writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd, allow, readOnly) => {
23145
23290
  const gitCommonDir = gitCommonDirOf2(exec, cwd);
23146
23291
  const dir = join28(stateDir, "sandbox");
23147
23292
  mkdirSync21(dir, { recursive: true, mode: 448 });
@@ -23153,6 +23298,12 @@ function engineDeps2(o, flags, source, runner, stateName) {
23153
23298
  walls.permissions.allow = [
23154
23299
  ...new Set([...walls.permissions.allow, ...allow ?? []])
23155
23300
  ];
23301
+ walls.permissions.deny = [
23302
+ ...new Set([
23303
+ ...walls.permissions.deny,
23304
+ ...readOnlyDenyRules(readOnlyDirs(readOnly ?? [], cwd, [gitCommonDir]))
23305
+ ])
23306
+ ];
23156
23307
  const settings = withHook(name, { ...walls });
23157
23308
  writeFileSync20(p, `${JSON.stringify(settings, null, 2)}
23158
23309
  `, { mode: 384 });
@@ -23160,23 +23311,22 @@ function engineDeps2(o, flags, source, runner, stateName) {
23160
23311
  return { settingsFile: p, gitCommonDir };
23161
23312
  })
23162
23313
  } : {},
23163
- writeHookSettings: (name, allow) => {
23314
+ writeHookSettings: (name, allow, readOnly) => {
23164
23315
  mkdirSync21(hooksDir, { recursive: true, mode: 448 });
23165
23316
  const p = join28(hooksDir, `${name}.json`);
23166
- const base = allow?.length ? { permissions: { allow: [...allow] } } : {};
23317
+ const deny = readOnlyDenyRules(readOnly ?? []);
23318
+ const permissions = {
23319
+ ...allow?.length ? { allow: [...allow] } : {},
23320
+ ...deny.length ? { deny } : {}
23321
+ };
23322
+ const base = Object.keys(permissions).length ? { permissions } : {};
23167
23323
  writeFileSync20(p, `${JSON.stringify(withHook(name, base), null, 2)}
23168
23324
  `, { mode: 384 });
23169
23325
  chmodSync6(p, 384);
23170
23326
  return p;
23171
23327
  },
23172
23328
  permissionMark,
23173
- mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
23174
- try {
23175
- return mainCheckoutOf(gitCommonDirOf2(exec, cwd), cwd);
23176
- } catch {
23177
- return null;
23178
- }
23179
- }),
23329
+ readDirsOf: o.readDirsOf ?? ((dir) => engineReadDirs2(exec, dir, exists, pnpmStores)),
23180
23330
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
23181
23331
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
23182
23332
  now: o.now ?? Date.now,
@@ -23278,6 +23428,52 @@ function gitCommonDirOf2(exec, cwd) {
23278
23428
  throw new Error(`git has no common dir for ${cwd}, so the sandbox has nowhere to commit`);
23279
23429
  return resolve11(cwd, dir);
23280
23430
  }
23431
+ function worktreeListPaths2(porcelain) {
23432
+ return porcelain.split(/\n\s*\n/).map((block) => block.split(`
23433
+ `).map((l) => l.trim())).filter((lines) => !lines.some((l) => l === "bare" || l.startsWith("prunable"))).flatMap((lines) => {
23434
+ const wt = lines.find((l) => l.startsWith("worktree "));
23435
+ return wt ? [wt.slice("worktree ".length)] : [];
23436
+ });
23437
+ }
23438
+ var PNPM_STORE_TIMEOUT_MS2 = 1e4;
23439
+ function engineReadDirs2(exec, cwd, exists, stores) {
23440
+ let common = null;
23441
+ try {
23442
+ common = gitCommonDirOf2(exec, cwd);
23443
+ } catch {}
23444
+ const worktrees = (() => {
23445
+ try {
23446
+ const r = exec("git", ["-C", cwd, "worktree", "list", "--porcelain"]);
23447
+ return r.status === 0 ? worktreeListPaths2(r.stdout) : [];
23448
+ } catch {
23449
+ return [];
23450
+ }
23451
+ })();
23452
+ const pnpmStore = (() => {
23453
+ try {
23454
+ if (!exists(join28(cwd, "pnpm-lock.yaml")))
23455
+ return null;
23456
+ const key = common ?? cwd;
23457
+ const known = stores.get(key);
23458
+ if (known)
23459
+ return known;
23460
+ const r = exec("pnpm", ["store", "path"], { cwd, timeoutMs: PNPM_STORE_TIMEOUT_MS2 });
23461
+ const dir = r.status === 0 ? r.stdout.split(`
23462
+ `).map((l) => l.trim()).filter((l) => l.startsWith("/")).pop() : undefined;
23463
+ if (dir)
23464
+ stores.set(key, dir);
23465
+ return dir ?? null;
23466
+ } catch {
23467
+ return null;
23468
+ }
23469
+ })();
23470
+ return sessionReadDirs({
23471
+ mainCheckout: common ? mainCheckoutOf(common, cwd) : null,
23472
+ worktrees,
23473
+ pnpmStore,
23474
+ cwd
23475
+ });
23476
+ }
23281
23477
  function sandboxRefusal(flags) {
23282
23478
  if (flags.lentBy && !flags.sandbox)
23283
23479
  return LENT_NEEDS_SANDBOX2;
@@ -23361,7 +23557,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
23361
23557
  runner: deps.runner.name,
23362
23558
  atOnce: flags.atOnce,
23363
23559
  interval: flags.interval,
23364
- cliVersion: "0.1.68"
23560
+ cliVersion: "0.1.70"
23365
23561
  };
23366
23562
  const beat = (checkedAt) => {
23367
23563
  const m = deps.machine;
@@ -24582,7 +24778,8 @@ function engineStatus(i) {
24582
24778
  ...opt("leaseUntil", iso2(r.leaseUntil)),
24583
24779
  ...opt("runId", r.runId),
24584
24780
  ...opt("parked", r.parked?.question),
24585
- ...opt("answerer", answererOf(r))
24781
+ ...opt("answerer", answererOf(r)),
24782
+ ...opt("atPrompt", r.atPrompt ? { since: iso2(r.atPrompt.since) ?? new Date(0).toISOString(), text: r.atPrompt.text } : undefined)
24586
24783
  });
24587
24784
  }
24588
24785
  return {
@@ -24639,7 +24836,7 @@ function statusLines(s, now) {
24639
24836
  }
24640
24837
  lines.push(`running ${s.running.length}`);
24641
24838
  for (const r of s.running)
24642
- lines.push(` ${r.item}/${r.node} ${r.title} · ${r.answerer ? `parked, the decider answers${r.answerer.model ? ` on ${r.answerer.model}` : ""}${r.answerer.effort ? ` · ${r.answerer.effort}` : ""}` : r.parked ? "parked, waiting for your answer" : `${r.kind === "mock" ? "drawing a mock · " : ""}${ago(r.startedAt)}`} · watch: ${r.watch}`);
24839
+ lines.push(` ${r.item}/${r.node} ${r.title} · ${r.answerer ? `parked, the decider answers${r.answerer.model ? ` on ${r.answerer.model}` : ""}${r.answerer.effort ? ` · ${r.answerer.effort}` : ""}` : r.parked ? "parked, waiting for your answer" : r.atPrompt ? `at a prompt · ${ago(r.atPrompt.since)}` : `${r.kind === "mock" ? "drawing a mock · " : ""}${ago(r.startedAt)}`} · watch: ${r.watch}`);
24643
24840
  lines.push(`following ${s.following.length}`);
24644
24841
  for (const f of s.following) {
24645
24842
  lines.push(` ${f.item}/${f.node} ${f.pr}${f.checks ? ` checks ${f.checks}` : ""}${f.review ? ` · ${f.review}` : ""}${f.mergeable ? " · ready for your merge" : ""}${f.queued ? " · queued to merge" : f.autoMerge ? " · merge when ready" : ""}`);
@@ -25472,7 +25669,7 @@ function contextFor(deps) {
25472
25669
  ...src,
25473
25670
  fetchFn: deps.fetchFn,
25474
25671
  timeoutMs: deps.timeoutMs,
25475
- version: "0.1.68"
25672
+ version: "0.1.70"
25476
25673
  }), deps.home, now) : null;
25477
25674
  return {
25478
25675
  channel: "code",
@@ -30288,16 +30485,20 @@ function readOrgInfo(json) {
30288
30485
  const org = isObj7(json.org) ? str16(json.org.slug) : str16(json.org);
30289
30486
  if (!org)
30290
30487
  return "no org";
30291
- const environments = [];
30292
- for (const e of Array.isArray(json.environments) ? json.environments : []) {
30488
+ const bySlug = new Map;
30489
+ for (const e of Array.isArray(json.envs) ? json.envs : []) {
30293
30490
  if (!isObj7(e) || !str16(e.slug))
30294
30491
  continue;
30295
- const env = { slug: str16(e.slug).toLowerCase(), production: e.production === true };
30492
+ const slug = str16(e.slug).toLowerCase();
30493
+ const env = bySlug.get(slug) ?? { slug, production: false };
30494
+ if (e.production === true)
30495
+ env.production = true;
30296
30496
  const ld = str16(e.launchDarklyEnv);
30297
- if (ld)
30497
+ if (ld && !env.launchDarklyEnv)
30298
30498
  env.launchDarklyEnv = ld;
30299
- environments.push(env);
30499
+ bySlug.set(slug, env);
30300
30500
  }
30501
+ const environments = [...bySlug.values()];
30301
30502
  const strings = (v) => Array.isArray(v) ? v.map((x) => str16(x)).filter(Boolean) : [];
30302
30503
  return {
30303
30504
  org,
@@ -30337,17 +30538,28 @@ function pickLdProject(given, info) {
30337
30538
  };
30338
30539
  }
30339
30540
  var num9 = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
30340
- var countsOf = (json) => {
30341
- const c = { inserted: 0, existing: 0, skipped: 0 };
30541
+ function countsOf(json) {
30542
+ const c = { inserted: 0, existing: 0, skipped: 0, pulls: 0, skips: [] };
30342
30543
  if (!isObj7(json))
30343
30544
  return c;
30344
- const parts = ["inserted", "existing", "skipped"].some((k) => typeof json[k] === "number") ? [json] : [json.deploys, json.flags].filter(isObj7);
30345
- for (const p of parts) {
30346
- c.inserted += num9(p.inserted);
30347
- c.existing += num9(p.existing);
30348
- c.skipped += Array.isArray(p.skipped) ? p.skipped.length : num9(p.skipped);
30545
+ const rows = (v) => isObj7(v) ? num9(v.deploys) + num9(v.flags) : num9(v);
30546
+ c.inserted = rows(json.inserted);
30547
+ c.existing = rows(json.existing);
30548
+ c.skipped = rows(json.skipped);
30549
+ c.pulls = isObj7(json.inserted) ? num9(json.inserted.pulls) : num9(json.pulls);
30550
+ for (const s of Array.isArray(json.skips) ? json.skips : []) {
30551
+ const w = isObj7(s) ? str16(s.why) : "";
30552
+ const count = isObj7(s) ? num9(s.count) : 0;
30553
+ if (w && count > 0)
30554
+ c.skips.push({ why: w, count });
30349
30555
  }
30350
30556
  return c;
30557
+ }
30558
+ var mergeSkips = (...lists) => {
30559
+ const by = new Map;
30560
+ for (const s of lists.flat())
30561
+ by.set(s.why, (by.get(s.why) ?? 0) + s.count);
30562
+ return [...by.entries()].map(([w, count]) => ({ why: w, count })).sort((a, b) => b.count - a.count);
30351
30563
  };
30352
30564
  var why3 = (json, status) => isObj7(json) && typeof json.error === "string" ? json.error : `HTTP ${status}`;
30353
30565
  async function importSleuth(o) {
@@ -30506,7 +30718,9 @@ async function importSleuth(o) {
30506
30718
  posted: 0,
30507
30719
  inserted: 0,
30508
30720
  existing: 0,
30509
- skipped
30721
+ skipped,
30722
+ prs: 0,
30723
+ serverSkips: []
30510
30724
  };
30511
30725
  o.onMapping?.(result);
30512
30726
  for (let b = 0;b < result.batches; b++) {
@@ -30531,6 +30745,8 @@ async function importSleuth(o) {
30531
30745
  result.inserted += n.inserted;
30532
30746
  result.existing += n.existing;
30533
30747
  result.skipped += n.skipped;
30748
+ result.prs += n.pulls;
30749
+ result.serverSkips = mergeSkips(result.serverSkips, n.skips);
30534
30750
  if ((b + 1) % 10 === 0 || b + 1 === result.batches)
30535
30751
  log(` ${at} · ${result.posted} rows sent`);
30536
30752
  }
@@ -30555,6 +30771,18 @@ function historyLine(r) {
30555
30771
  return ` PR history: ${missing} merged PRs without a first commit — SLEDS is reading them from GitHub, paced; lead time fills in as it goes`;
30556
30772
  return missing ? ` PR history: ${missing} merged PRs without a first commit — not reading them: connect GitHub (Settings › Integrations), then re-run` : " PR history: every merged PR has its first commit";
30557
30773
  }
30774
+ function summaryLines(r) {
30775
+ const tail = `already there ${r.existing}, skipped ${r.skipped}`;
30776
+ const out = [
30777
+ r.dryRun ? ` would write ~${r.inserted}, ${tail} · ~${r.prs} PRs — run again without --dry-run to write` : ` wrote ${r.inserted}, ${tail} · ${r.prs} PRs`
30778
+ ];
30779
+ if (r.serverSkips.length) {
30780
+ out.push("", " server skipped, and why:");
30781
+ for (const s of r.serverSkips)
30782
+ out.push(` ${String(s.count).padStart(6)} ${s.why}`);
30783
+ }
30784
+ return out;
30785
+ }
30558
30786
  function mappingTable(r) {
30559
30787
  const head = [
30560
30788
  "Sleuth deployment",
@@ -31162,7 +31390,8 @@ var importCmd = async (o, { words, flags, all }) => {
31162
31390
  log(JSON.stringify(result));
31163
31391
  return code;
31164
31392
  }
31165
- log(dryRun ? ` would write ${result.inserted}, already there ${result.existing}, skipped ${result.skipped} — run again without --dry-run to write` : ` wrote ${result.inserted}, already there ${result.existing}, skipped ${result.skipped}`);
31393
+ for (const l of summaryLines(result))
31394
+ log(l);
31166
31395
  const history = historyLine(result);
31167
31396
  if (history)
31168
31397
  (result.historyError ? error : log)(history);
@@ -32040,7 +32269,7 @@ var realPidAlive4 = (pid) => {
32040
32269
  };
32041
32270
  function machineClient(home, env) {
32042
32271
  const src = meterSource(home, undefined, { env });
32043
- return src ? fetchClient({ ...src, version: "0.1.68" }) : null;
32272
+ return src ? fetchClient({ ...src, version: "0.1.70" }) : null;
32044
32273
  }
32045
32274
  async function noteAnswer2(home, a, now) {
32046
32275
  try {
@@ -32991,7 +33220,7 @@ var main = async (argv) => {
32991
33220
  now: Date.now(),
32992
33221
  stderr: (line) => console.error(line),
32993
33222
  isTTY: !!process.stderr.isTTY,
32994
- version: "0.1.68"
33223
+ version: "0.1.70"
32995
33224
  });
32996
33225
  switch (cmd) {
32997
33226
  case "update":
@@ -33255,7 +33484,7 @@ ${renderHelpText(argv[1])}`);
33255
33484
  case "version":
33256
33485
  case "--version":
33257
33486
  case "-v":
33258
- console.log("sleds 0.1.68");
33487
+ console.log("sleds 0.1.70");
33259
33488
  return 0;
33260
33489
  default:
33261
33490
  console.error(`unknown command: ${cmd}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleds/cli",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
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",