@sleds/cli 0.1.37 → 0.1.39

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 +746 -198
  2. package/package.json +1 -1
package/dist/sleds.mjs CHANGED
@@ -894,7 +894,7 @@ var init_verbs = __esm(() => {
894
894
  mcp: "a `Worth:` line in the spec you estimate (`Worth: high · $5k–20k · Acme renewal`)",
895
895
  claudeCode: "`SLEDS-Worth:` line in the PR body",
896
896
  app: "Work › the item › Worth…",
897
- how: "A level, dollars when you know them, a note. Yours to state; the tracker's priority stands in at low confidence; blank stays blank."
897
+ how: "A level, dollars when you know them, a note. Yours to state — the tracker's priority is urgency, never worth; blank stays blank."
898
898
  },
899
899
  {
900
900
  id: "intake",
@@ -5524,6 +5524,21 @@ var init_confluence = __esm(() => {
5524
5524
  CONFLUENCE_READ_SCOPES_LINE = CONFLUENCE_READ_SCOPES.join(", ");
5525
5525
  });
5526
5526
 
5527
+ // ../../packages/shared/src/tracker/fieldMap.ts
5528
+ var FILLED_ON_CREATE;
5529
+ var init_fieldMap = __esm(() => {
5530
+ FILLED_ON_CREATE = new Set([
5531
+ "summary",
5532
+ "description",
5533
+ "project",
5534
+ "issuetype",
5535
+ "parent",
5536
+ "reporter",
5537
+ "labels",
5538
+ "priority"
5539
+ ]);
5540
+ });
5541
+
5527
5542
  // ../../packages/shared/src/help/whatsNew.ts
5528
5543
  var SEPT_30 = "/whats-new/2026-09-30", WHATS_NEW;
5529
5544
  var init_whatsNew = __esm(() => {
@@ -7153,7 +7168,10 @@ var BASE_URL_KEY = "ANTHROPIC_BASE_URL", AUTH_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
7153
7168
  if (!list.some(ours))
7154
7169
  return { ...settings };
7155
7170
  return withEventHooks(settings, event, list.filter((e) => !ours(e)));
7156
- }, applySessionHook = (settings, command) => applyOnce(settings, SESSION_EVENT, isSledsSessionHook, sessionHookEntry(command)), removeSessionHook = (settings) => removeOurs(settings, SESSION_EVENT, isSledsSessionHook), hasSessionHook = (settings) => eventHooks(settings, SESSION_EVENT).some(isSledsSessionHook), applyStopHook = (settings, command) => applyOnce(settings, STOP_EVENT, isSledsStopHook, stopHookEntry(command)), removeStopHook = (settings) => removeOurs(settings, STOP_EVENT, isSledsStopHook), hasStopHook = (settings) => eventHooks(settings, STOP_EVENT).some(isSledsStopHook), isSeatToken = (token) => /^sled_[A-Za-z0-9_-]+$/.test(token), maskToken = (token) => token.length <= 9 ? `${token.slice(0, 5)}…` : `${token.slice(0, 9)}…`, serialize = (settings) => `${JSON.stringify(settings, null, 2)}
7171
+ }, applySessionHook = (settings, command) => applyOnce(settings, SESSION_EVENT, isSledsSessionHook, sessionHookEntry(command)), removeSessionHook = (settings) => removeOurs(settings, SESSION_EVENT, isSledsSessionHook), hasSessionHook = (settings) => eventHooks(settings, SESSION_EVENT).some(isSledsSessionHook), applyStopHook = (settings, command) => applyOnce(settings, STOP_EVENT, isSledsStopHook, stopHookEntry(command)), removeStopHook = (settings) => removeOurs(settings, STOP_EVENT, isSledsStopHook), hasStopHook = (settings) => eventHooks(settings, STOP_EVENT).some(isSledsStopHook), PERMISSION_EVENT_MATCHER = "permission_prompt", PERMISSION_EVENT_TIMEOUT_S = 5, isSledsPermissionHook, permissionHookEntry = (command) => ({
7172
+ matcher: PERMISSION_EVENT_MATCHER,
7173
+ hooks: [{ type: "command", command, timeout: PERMISSION_EVENT_TIMEOUT_S }]
7174
+ }), applyPermissionHook = (settings, command) => applyOnce(settings, NOTIFICATION_EVENT, isSledsPermissionHook, permissionHookEntry(command)), isSeatToken = (token) => /^sled_[A-Za-z0-9_-]+$/.test(token), maskToken = (token) => token.length <= 9 ? `${token.slice(0, 5)}…` : `${token.slice(0, 9)}…`, serialize = (settings) => `${JSON.stringify(settings, null, 2)}
7157
7175
  `, hasClaudeCode = (home, env = process.env) => existsSync(claudeDir(home, env)), readSettings = (home, env = process.env) => {
7158
7176
  const p = settingsPath(home, env);
7159
7177
  if (!existsSync(p))
@@ -7193,6 +7211,7 @@ var init_settings = __esm(() => {
7193
7211
  ORG_EXPORT = /(?:^|\s)SLEDS_ORG=(?:'[^']*'|\S+)(?=\s)/;
7194
7212
  isSledsSessionHook = isSledsHookFor("session-event");
7195
7213
  isSledsStopHook = isSledsHookFor("stop-event");
7214
+ isSledsPermissionHook = isSledsHookFor("permission-event");
7196
7215
  });
7197
7216
 
7198
7217
  // src/config.ts
@@ -7520,6 +7539,7 @@ var init_src = __esm(() => {
7520
7539
  init_template();
7521
7540
  init_statusMap();
7522
7541
  init_confluence();
7542
+ init_fieldMap();
7523
7543
  init_whatsNew();
7524
7544
  init_cheatSheet();
7525
7545
  init_pack2();
@@ -7935,6 +7955,14 @@ function parseRun(v) {
7935
7955
  ...typeof v.seenTicks === "number" ? { seenTicks: v.seenTicks } : {},
7936
7956
  ...typeof v.answeredAt === "number" ? { answeredAt: v.answeredAt } : {},
7937
7957
  ...typeof v.band === "string" ? { band: v.band } : {},
7958
+ ...isObject8(v.permission) && typeof v.permission.decisionId === "string" && typeof v.permission.at === "number" ? {
7959
+ permission: {
7960
+ decisionId: v.permission.decisionId,
7961
+ options: Array.isArray(v.permission.options) ? v.permission.options.filter((x) => typeof x === "string") : [],
7962
+ at: v.permission.at,
7963
+ ...v.permission.onScreen === true ? { onScreen: true } : {}
7964
+ }
7965
+ } : {},
7938
7966
  ...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 } } : {},
7939
7967
  ...parked ? { parked } : {},
7940
7968
  ...isObject8(v.pr) && typeof v.pr.url === "string" ? {
@@ -7952,7 +7980,8 @@ function parseRun(v) {
7952
7980
  ...typeof v.pr.conflict === "string" ? { conflict: v.pr.conflict } : {},
7953
7981
  ...typeof v.pr.resolving === "string" ? { resolving: v.pr.resolving } : {},
7954
7982
  ...conflicted ? { conflicted } : {},
7955
- ...typeof v.pr.reviewed === "string" ? { reviewed: v.pr.reviewed } : {}
7983
+ ...typeof v.pr.reviewed === "string" ? { reviewed: v.pr.reviewed } : {},
7984
+ ...typeof v.pr.look === "string" ? { look: v.pr.look } : {}
7956
7985
  }
7957
7986
  } : {}
7958
7987
  };
@@ -8023,6 +8052,7 @@ function parseLock(text) {
8023
8052
  runner: typeof v.runner === "string" ? v.runner : "",
8024
8053
  atOnce: n(v.atOnce, 1),
8025
8054
  interval: n(v.interval, 0),
8055
+ ...typeof v.cliVersion === "string" && v.cliVersion ? { cliVersion: v.cliVersion } : {},
8026
8056
  ...typeof v.checkedAt === "number" ? { checkedAt: v.checkedAt } : {},
8027
8057
  ...typeof v.nextCheckAt === "number" ? { nextCheckAt: v.nextCheckAt } : {},
8028
8058
  ...machine ? { machine } : {},
@@ -8208,6 +8238,7 @@ init_intake();
8208
8238
  init_template();
8209
8239
  init_statusMap();
8210
8240
  init_confluence();
8241
+ init_fieldMap();
8211
8242
  init_whatsNew();
8212
8243
  init_cheatSheet();
8213
8244
  init_pack2();
@@ -9383,10 +9414,10 @@ import {
9383
9414
  existsSync as existsSync16,
9384
9415
  lstatSync,
9385
9416
  mkdtempSync,
9386
- readFileSync as readFileSync15,
9417
+ readFileSync as readFileSync16,
9387
9418
  readlinkSync,
9388
- rmSync as rmSync7,
9389
- writeFileSync as writeFileSync16
9419
+ rmSync as rmSync8,
9420
+ writeFileSync as writeFileSync17
9390
9421
  } from "node:fs";
9391
9422
  import { tmpdir } from "node:os";
9392
9423
  import { join as join19, resolve as resolve8 } from "node:path";
@@ -9878,6 +9909,14 @@ var withCodexSandbox = (agent, gitCommonDir) => {
9878
9909
  return line;
9879
9910
  return `${line} --sandbox workspace-write --add-dir ${shq(gitCommonDir)} -c sandbox_workspace_write.network_access=true`;
9880
9911
  };
9912
+ var mainCheckoutOf = (gitCommonDir, cwd) => {
9913
+ const dir = gitCommonDir.replace(/\/+$/, "");
9914
+ if (!dir.endsWith("/.git"))
9915
+ return null;
9916
+ const main = dir.slice(0, -"/.git".length);
9917
+ return main && main !== cwd.replace(/\/+$/, "") ? main : null;
9918
+ };
9919
+ var claudeAddDirFlag = (mainCheckout) => mainCheckout ? ` --add-dir=${shq(mainCheckout)}` : "";
9881
9920
  var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
9882
9921
 
9883
9922
  // src/runners/exec.ts
@@ -9902,21 +9941,23 @@ var agentShellCommand = (o) => {
9902
9941
  const prompt = `"$(cat ${shq2(o.promptFile)})"`;
9903
9942
  const agent = o.agent?.trim();
9904
9943
  if (agent)
9905
- return `${sandboxedAgent(agent, o.sandbox)} ${prompt}`;
9944
+ return `${sandboxedAgent(agent, o.sandbox, o.mainCheckout)} ${prompt}`;
9906
9945
  const model = o.model ? ` --model ${shq2(o.model)}` : "";
9907
9946
  const effort = o.effort ? ` --effort ${shq2(o.effort)}` : "";
9908
- return `${sandboxedAgent("claude", o.sandbox)}${mcpConfigFlag(o)} --permission-mode ${shq2(o.permissionMode)}${model}${effort} ${prompt}`;
9947
+ return `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)}${hookSettingsFlag(o)}${mcpConfigFlag(o)} --permission-mode ${shq2(o.permissionMode)}${model}${effort} ${prompt}`;
9909
9948
  };
9910
- var sandboxedAgent = (agent, sandbox) => {
9911
- if (!sandbox)
9912
- return agent;
9949
+ var sandboxedAgent = (agent, sandbox, mainCheckout) => {
9913
9950
  const kind = sandboxAgentKind(agent);
9951
+ const line = kind === "claude" ? `${agent}${claudeAddDirFlag(mainCheckout)}` : agent;
9952
+ if (!sandbox)
9953
+ return line;
9914
9954
  if (kind === "claude")
9915
- return `env -u SLEDS_TOKEN ${agent} --settings ${shq2(sandbox.settingsFile)}`;
9955
+ return `env -u SLEDS_TOKEN ${line} --settings ${shq2(sandbox.settingsFile)}`;
9916
9956
  if (kind === "codex")
9917
9957
  return `env -u SLEDS_TOKEN ${withCodexSandbox(agent, sandbox.gitCommonDir)}`;
9918
9958
  return agent;
9919
9959
  };
9960
+ var hookSettingsFlag = (o) => o.settingsFile && !o.sandbox ? ` --settings ${shq2(o.settingsFile)}` : "";
9920
9961
  var mcpConfigFlag = (o) => o.mcpConfig ? ` --mcp-config ${shq2(o.mcpConfig)}${o.strictMcp ? " --strict-mcp-config" : ""}` : "";
9921
9962
  var defaultSpawnDetached = (cmd, args, o) => {
9922
9963
  const child = spawn(cmd, [...args], { cwd: o.cwd, detached: true, stdio: "ignore" });
@@ -9969,6 +10010,15 @@ var tailText = (s, bytes = 4096) => {
9969
10010
  return nl >= 0 && nl < cut.length - 1 ? cut.slice(nl + 1) : cut;
9970
10011
  };
9971
10012
 
10013
+ // src/runners/types.ts
10014
+ var watchTarget = (h) => ({
10015
+ runner: h.runner,
10016
+ handle: h.id,
10017
+ ...h.log ? { log: h.log } : {},
10018
+ ...h.group ? { group: h.group } : {}
10019
+ });
10020
+ var isRunnerKey = (key) => /^(?:[1-9]|esc)$/.test(key);
10021
+
9972
10022
  // src/runners/cmux.ts
9973
10023
  var CMUX_APP_BIN = "/Applications/cmux.app/Contents/Resources/bin/cmux";
9974
10024
  var resolveCmuxBin = (exists = existsSync11, path = process.env.PATH ?? "") => {
@@ -10275,6 +10325,13 @@ function cmuxRunner(exec, deps = {}) {
10275
10325
  return;
10276
10326
  return pending() ? `the Enter was swallowed; pressed it ${again} more times and the text is still in the input` : `the Enter was swallowed; pressed it again${again > 1 ? ` (${again}×)` : ""}`;
10277
10327
  },
10328
+ sendKey(h, key) {
10329
+ if (!isRunnerKey(key))
10330
+ throw new Error(`cmux send-key ${h.id}: no key ${JSON.stringify(key)}`);
10331
+ const r = cmux(["send-key", "--workspace", h.id, key === "esc" ? "escape" : key]);
10332
+ if (r.status !== 0)
10333
+ throw new Error(`cmux send-key ${h.id} failed: ${failed(r)}`);
10334
+ },
10278
10335
  stop(h) {
10279
10336
  cmux(["close-workspace", "--workspace", h.id]);
10280
10337
  if (!h.group)
@@ -10325,12 +10382,12 @@ init_server();
10325
10382
  init_config();
10326
10383
  import {
10327
10384
  appendFileSync as appendFileSync2,
10328
- chmodSync as chmodSync3,
10329
- mkdirSync as mkdirSync11,
10330
- readFileSync as readFileSync12,
10385
+ chmodSync as chmodSync4,
10386
+ mkdirSync as mkdirSync12,
10387
+ readFileSync as readFileSync13,
10331
10388
  existsSync as realExists,
10332
- rmSync as rmSync3,
10333
- writeFileSync as writeFileSync12
10389
+ rmSync as rmSync4,
10390
+ writeFileSync as writeFileSync13
10334
10391
  } from "node:fs";
10335
10392
  import { hostname as hostname2 } from "node:os";
10336
10393
  import { join as join15, resolve as resolve7 } from "node:path";
@@ -10345,7 +10402,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
10345
10402
  name: flags.name,
10346
10403
  os: host.os ?? process.platform,
10347
10404
  runner: flags.runner,
10348
- version: host.version ?? "0.1.37" ?? "dev",
10405
+ version: host.version ?? "0.1.39" ?? "dev",
10349
10406
  running: live.length,
10350
10407
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
10351
10408
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -10657,6 +10714,15 @@ function prForBranch(exec, repoPath, branch) {
10657
10714
  return { error: e instanceof Error ? e.message : String(e) };
10658
10715
  }
10659
10716
  }
10717
+ var URL_RE = /https:\/\/[^\s)>\]"']+/;
10718
+ var ARTIFACT_RE = /https:\/\/claude\.ai\/[^\s)>\]"']*artifact\/[^\s)>\]"']+/;
10719
+ function mockOf(body) {
10720
+ const header = /<!--\s*sleds:header\s*-->([\s\S]*?)<!--\s*\/sleds:header\s*-->/.exec(body)?.[1];
10721
+ const line = header?.split(`
10722
+ `).find((l) => /\*\*Mocks\*\*\s*:/i.test(l));
10723
+ const fromHeader = line ? URL_RE.exec(line)?.[0] : undefined;
10724
+ return fromHeader ?? ARTIFACT_RE.exec(body)?.[0];
10725
+ }
10660
10726
  var FAILED = new Set([
10661
10727
  "FAILURE",
10662
10728
  "ERROR",
@@ -10702,6 +10768,7 @@ function parsePrView(stdout) {
10702
10768
  pending++;
10703
10769
  }
10704
10770
  const mergeCommit = typeof o.mergeCommit === "object" && o.mergeCommit !== null ? o.mergeCommit.oid : undefined;
10771
+ const mock = typeof o.body === "string" ? mockOf(o.body) : undefined;
10705
10772
  return {
10706
10773
  state: o.state,
10707
10774
  author: loginOf(o.author),
@@ -10712,6 +10779,8 @@ function parsePrView(stdout) {
10712
10779
  reviewDecision: typeof o.reviewDecision === "string" ? o.reviewDecision : "",
10713
10780
  feedback: 0,
10714
10781
  authorFeedback: 0,
10782
+ draft: o.isDraft === true,
10783
+ ...mock ? { mock } : {},
10715
10784
  ...typeof o.headRefOid === "string" && o.headRefOid ? { head: o.headRefOid } : {},
10716
10785
  ...typeof mergeCommit === "string" && mergeCommit ? { mergeCommit } : {}
10717
10786
  };
@@ -10766,7 +10835,7 @@ function prView(exec, repoPath, url) {
10766
10835
  "view",
10767
10836
  url,
10768
10837
  "--json",
10769
- "state,author,statusCheckRollup,reviewDecision,comments,reviews,headRefOid,mergeable,baseRefName,mergeCommit"
10838
+ "state,author,statusCheckRollup,reviewDecision,comments,reviews,headRefOid,mergeable,baseRefName,mergeCommit,isDraft,body"
10770
10839
  ], { cwd: repoPath });
10771
10840
  const view = r.status === 0 ? parsePrView(r.stdout) : null;
10772
10841
  if (!view || view.state !== "OPEN")
@@ -11078,13 +11147,40 @@ function jiraFromEnv(epic, env = process.env) {
11078
11147
  // src/engine/loop.ts
11079
11148
  init_src();
11080
11149
 
11081
- // src/runners/types.ts
11082
- var watchTarget = (h) => ({
11083
- runner: h.runner,
11084
- handle: h.id,
11085
- ...h.log ? { log: h.log } : {},
11086
- ...h.group ? { group: h.group } : {}
11087
- });
11150
+ // src/commands/permissionEvent.ts
11151
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync3, writeFileSync as writeFileSync11 } from "node:fs";
11152
+
11153
+ // src/commands/seatEvent.ts
11154
+ init_config();
11155
+ init_meter();
11156
+ var HOOK_STDIN_TIMEOUT_MS = 1000;
11157
+ var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11158
+ var notificationType = (text) => {
11159
+ try {
11160
+ const raw = JSON.parse(text);
11161
+ if (!isObject11(raw))
11162
+ return;
11163
+ return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
11164
+ } catch {
11165
+ return;
11166
+ }
11167
+ };
11168
+
11169
+ // src/commands/permissionEvent.ts
11170
+ var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11171
+ var readPermissionMark = (file) => {
11172
+ try {
11173
+ const raw = JSON.parse(readFileSync10(file, "utf8"));
11174
+ if (!isObject12(raw) || typeof raw.at !== "number")
11175
+ return null;
11176
+ return { message: typeof raw.message === "string" ? raw.message : "", at: raw.at };
11177
+ } catch {
11178
+ return null;
11179
+ }
11180
+ };
11181
+ var clearPermissionMark = (file) => {
11182
+ rmSync3(file, { force: true });
11183
+ };
11088
11184
 
11089
11185
  // src/engine/answerer.ts
11090
11186
  init_src();
@@ -11232,6 +11328,14 @@ init_machine();
11232
11328
 
11233
11329
  // src/engine/sledsSource.ts
11234
11330
  init_src();
11331
+ function looksOf(s) {
11332
+ const l = s;
11333
+ return l && typeof l.openLook === "function" && typeof l.clearLook === "function" ? s : null;
11334
+ }
11335
+ function permissionsOf(s) {
11336
+ const p = s;
11337
+ return p && typeof p.openPermission === "function" && typeof p.clearPermission === "function" && typeof p.decisionOf === "function" ? s : null;
11338
+ }
11235
11339
  var FAIL_LOG_MAX = 2000;
11236
11340
  var UNREACHABLE2 = "SLEDS unreachable";
11237
11341
  function keysIn(states, state) {
@@ -11373,20 +11477,34 @@ var PROFILE_TEXT = [
11373
11477
  "reposDir",
11374
11478
  "engine"
11375
11479
  ];
11480
+ var SHELL_TEXT = ["agent", "launcher"];
11376
11481
  function profileOf(r) {
11377
11482
  if (!r || r.ok === false || !isObject8(r.profile))
11378
11483
  return null;
11379
11484
  const p = r.profile;
11485
+ const member = isObject8(r.member) ? r.member : {};
11380
11486
  const out = {
11381
11487
  pickup: p.pickup === "auto" ? "auto" : "approve",
11382
11488
  ...HOLD_DEFAULTS2,
11383
11489
  holdOff: p.holdOff === true
11384
11490
  };
11385
11491
  for (const k of PROFILE_TEXT) {
11492
+ if (SHELL_TEXT.includes(k))
11493
+ continue;
11386
11494
  const v = str6(p[k])?.trim();
11387
11495
  if (v)
11388
11496
  out[k] = v;
11389
11497
  }
11498
+ const notYours = [];
11499
+ for (const k of SHELL_TEXT) {
11500
+ const mine = str6(member[k])?.trim();
11501
+ if (mine)
11502
+ out[k] = mine;
11503
+ else if (str6(p[k])?.trim())
11504
+ notYours.push(k);
11505
+ }
11506
+ if (notYours.length)
11507
+ out.notYours = notYours;
11390
11508
  const atOnce = num5(p.atOnce);
11391
11509
  if (atOnce !== undefined && atOnce >= 1)
11392
11510
  out.atOnce = Math.round(atOnce);
@@ -11746,6 +11864,57 @@ function sledsSource(client, o) {
11746
11864
  throw new Error(serverMessage(r));
11747
11865
  return eventOpenOf(r.json);
11748
11866
  },
11867
+ async openLook(runId, look, engine) {
11868
+ const r = await client.postFull("/proxy/run", {
11869
+ runId,
11870
+ engine,
11871
+ event: {
11872
+ kind: "look",
11873
+ md: look.md,
11874
+ about: { prs: [look.pr], ...look.mock ? { mock: look.mock } : {} }
11875
+ }
11876
+ });
11877
+ if (!r)
11878
+ throw new Error(`${UNREACHABLE2} — the look not raised`);
11879
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11880
+ throw new Error(serverMessage(r));
11881
+ const decisionId = str6(r.json.decisionId);
11882
+ const state = str6(r.json.state);
11883
+ return {
11884
+ opened: r.json.opened === true,
11885
+ ...decisionId ? { decisionId } : {},
11886
+ ...state ? { state } : {}
11887
+ };
11888
+ },
11889
+ async clearLook(runId, clear, engine) {
11890
+ const r = await client.postFull("/proxy/run", { runId, engine, clearLook: clear });
11891
+ if (!r)
11892
+ throw new Error(`${UNREACHABLE2} — the look not cleared`);
11893
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11894
+ throw new Error(serverMessage(r));
11895
+ return r.json.cleared === true;
11896
+ },
11897
+ async openPermission(runId, p, engine) {
11898
+ const r = await client.postFull("/proxy/run", {
11899
+ runId,
11900
+ engine,
11901
+ event: { kind: "permission", md: p.md, about: { options: p.options } }
11902
+ });
11903
+ if (!r)
11904
+ throw new Error(`${UNREACHABLE2} — the permission not raised`);
11905
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11906
+ throw new Error(serverMessage(r));
11907
+ const decisionId = str6(r.json.decisionId);
11908
+ return { opened: r.json.opened === true, ...decisionId ? { decisionId } : {} };
11909
+ },
11910
+ async clearPermission(runId, why, engine) {
11911
+ const r = await client.postFull("/proxy/run", { runId, engine, clearPermission: { why } });
11912
+ if (!r)
11913
+ throw new Error(`${UNREACHABLE2} — the permission not cleared`);
11914
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11915
+ throw new Error(serverMessage(r));
11916
+ return r.json.cleared === true;
11917
+ },
11749
11918
  async decisionOf(item, decisionId) {
11750
11919
  const route = await routeOf(item);
11751
11920
  const decisions = Array.isArray(route?.decisions) ? route.decisions.filter(isObject8) : [];
@@ -11877,6 +12046,63 @@ function askedAndStopped(screen) {
11877
12046
  const last = (paras[paras.length - 1] ?? "").replace(/\s*\n\s*/g, " ");
11878
12047
  return ASKS.test(last) || CONFIRMS.test(last);
11879
12048
  }
12049
+ var DIALOG_ASKS = /^Do you want to .+\?$/;
12050
+ var DIALOG_OPTION = /^(❯\s*)?(\d)\.\s+(.+)$/;
12051
+ var DIALOG_FOOTER = /^(?:esc to |tab to |enter to |ctrl\+)/i;
12052
+ var unbox = (line) => line.trimEnd().replace(/^\s*[│┃║]\s?/, "").replace(/\s*[│┃║]$/, "");
12053
+ function permissionDialog(screen) {
12054
+ const raw = screen.split(`
12055
+ `);
12056
+ const lines = raw.map(unbox);
12057
+ let q = -1;
12058
+ for (let i = lines.length - 1;i >= 0; i--)
12059
+ if (DIALOG_ASKS.test(lines[i].trim())) {
12060
+ q = i;
12061
+ break;
12062
+ }
12063
+ if (q < 0)
12064
+ return null;
12065
+ const options = [];
12066
+ let cursors = 0;
12067
+ let i = q + 1;
12068
+ for (;i < lines.length; i++) {
12069
+ const t = lines[i].trim();
12070
+ const m = DIALOG_OPTION.exec(t);
12071
+ if (m) {
12072
+ if (m[2] !== String(options.length + 1))
12073
+ return null;
12074
+ if (m[1])
12075
+ cursors++;
12076
+ options.push({ key: m[2], label: m[3].replace(/\s*\(esc\)$/i, "") });
12077
+ } else if (t && options.length && !isRule(raw[i]) && !isPrompt(t) && !isFooter(t) && !DIALOG_FOOTER.test(t)) {
12078
+ const last = options[options.length - 1];
12079
+ last.label = `${last.label} ${t.replace(/\s*\(esc\)$/i, "")}`;
12080
+ } else
12081
+ break;
12082
+ }
12083
+ if (options.length < 2 || cursors !== 1)
12084
+ return null;
12085
+ for (;i < lines.length; i++) {
12086
+ const t = lines[i].trim();
12087
+ if (t && !isRule(raw[i]) && !DIALOG_FOOTER.test(t) && !isFooter(t))
12088
+ return null;
12089
+ }
12090
+ let from = 0;
12091
+ for (let j = q - 1;j >= 0; j--) {
12092
+ const t = lines[j].trim();
12093
+ if (isRule(raw[j]) || /^⏺/.test(t) || /^⎿/.test(t)) {
12094
+ from = j + 1;
12095
+ break;
12096
+ }
12097
+ }
12098
+ const body = lines.slice(from, q + 1);
12099
+ const indent = Math.min(...body.filter((l) => l.trim()).map((l) => /^\s*/.exec(l)[0].length));
12100
+ const text = body.map((l) => l.slice(indent)).join(`
12101
+ `).replace(/\n{3,}/g, `
12102
+
12103
+ `).trim();
12104
+ return { text, options };
12105
+ }
11880
12106
  var PROVIDER_FAILURES = [
11881
12107
  ["connection lost", /\bconnection (?:lost|error)\b/i],
11882
12108
  ["credit balance too low", /\bcredit balance (?:is )?too low\b/i],
@@ -11899,7 +12125,7 @@ function providerFailure(screen) {
11899
12125
 
11900
12126
  // src/engine/worktree.ts
11901
12127
  init_src();
11902
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync10 } from "node:fs";
12128
+ import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync11 } from "node:fs";
11903
12129
  import { dirname as dirname11, isAbsolute, join as join14 } from "node:path";
11904
12130
  function worktreePath(repoPath, name) {
11905
12131
  return join14(repoPath, ".worktrees", name);
@@ -11925,7 +12151,7 @@ function defaultBranch(exec, repoPath) {
11925
12151
  var realWorktreeFs = {
11926
12152
  exists: existsSync12,
11927
12153
  append: (p, text) => {
11928
- mkdirSync10(dirname11(p), { recursive: true });
12154
+ mkdirSync11(dirname11(p), { recursive: true });
11929
12155
  appendFileSync(p, text);
11930
12156
  }
11931
12157
  };
@@ -12151,6 +12377,15 @@ function sandboxFor(d, name, cwd) {
12151
12377
  throw new Error("sessions are sandboxed, but nothing writes their settings — not started");
12152
12378
  return { sandbox: d.writeSandboxSettings(name, cwd) };
12153
12379
  }
12380
+ function hooksFor(d, name) {
12381
+ if (d.sandbox || !d.writeHookSettings)
12382
+ return {};
12383
+ return { settingsFile: d.writeHookSettings(name) };
12384
+ }
12385
+ function mainCheckoutFor(d, cwd) {
12386
+ const main = d.mainCheckoutOf?.(cwd);
12387
+ return main ? { mainCheckout: main } : {};
12388
+ }
12154
12389
  function engineMcpConfigFor(d, name, runToken, at, cwd) {
12155
12390
  if (d.source.name !== "sleds" || d.agent?.trim() || !d.writeMcpConfig)
12156
12391
  return;
@@ -12207,9 +12442,13 @@ async function tick(deps) {
12207
12442
  deps.providerHold = d.providerHold;
12208
12443
  deps.atOnce = d.atOnce;
12209
12444
  }
12445
+ removeEndedMcpConfigs(deps, made);
12210
12446
  }
12447
+ return report;
12448
+ }
12449
+ function removeEndedMcpConfigs(deps, made) {
12211
12450
  if (!deps.removeMcpConfig || made.size === 0)
12212
- return report;
12451
+ return;
12213
12452
  const state = deps.readState();
12214
12453
  const live = liveMcpConfigs(state);
12215
12454
  for (const p of made) {
@@ -12227,7 +12466,6 @@ async function tick(deps) {
12227
12466
  r.mcpConfig = undefined;
12228
12467
  deps.writeState(state);
12229
12468
  }
12230
- return report;
12231
12469
  }
12232
12470
  async function tickOnce(d) {
12233
12471
  d.tickProfile = profileOnce(d.readProfile);
@@ -12293,6 +12531,7 @@ async function tickOnce(d) {
12293
12531
  }
12294
12532
  continue;
12295
12533
  } else if (pr) {
12534
+ await dropPermission(d, run, "the session opened its PR");
12296
12535
  const outcome = { kind: "pr", ref: pr.url };
12297
12536
  try {
12298
12537
  await d.source.report(claim, outcome, d.engine);
@@ -12316,6 +12555,7 @@ async function tickOnce(d) {
12316
12555
  }
12317
12556
  }
12318
12557
  if (status === "exited") {
12558
+ await dropPermission(d, run, "the session exited");
12319
12559
  let log = "";
12320
12560
  try {
12321
12561
  log = runner.read(run.handle);
@@ -12365,6 +12605,11 @@ async function tickOnce(d) {
12365
12605
  }
12366
12606
  }
12367
12607
  }
12608
+ if (await settlePermission(d, runner, run, shown)) {
12609
+ idle = false;
12610
+ asked = false;
12611
+ failing = null;
12612
+ }
12368
12613
  const retry = failing !== null && (run.provider?.tries ?? 0) < PROVIDER_RETRIES;
12369
12614
  if (idle || failing || needsHeartbeat(run.leaseUntil, d.now(), d.leaseMin)) {
12370
12615
  let beat;
@@ -12615,6 +12860,8 @@ async function tickOnce(d) {
12615
12860
  ...launch.agent ? { agent: launch.agent } : {},
12616
12861
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
12617
12862
  ...sandboxFor(d, name, worktree),
12863
+ ...hooksFor(d, name),
12864
+ ...mainCheckoutFor(d, worktree),
12618
12865
  ...claim.group ? { group: claim.group } : {}
12619
12866
  };
12620
12867
  } catch (e) {
@@ -12769,6 +13016,115 @@ function parkQuestion(screen, idleMin) {
12769
13016
  function askedQuestion(screen) {
12770
13017
  return `The session stopped and is waiting on an answer, with no PR. ${lastMessage(screen)}`;
12771
13018
  }
13019
+ var MARK_OPTIONS = ["1 · Allow once", "esc · Deny"];
13020
+ function permissionKey(answer) {
13021
+ const m = /^\s*(\d|esc)\s*·/i.exec(answer);
13022
+ return m ? m[1].toLowerCase() : null;
13023
+ }
13024
+ async function settlePermission(d, runner, run, shown) {
13025
+ const perms = permissionsOf(d.source);
13026
+ if (!perms || !run.runId)
13027
+ return false;
13028
+ const at = ref(run);
13029
+ const markFile = d.permissionMark?.(sessionName(run.item, run.node));
13030
+ const dialog = runner.readsSession ? permissionDialog(shown) : null;
13031
+ const p = run.permission;
13032
+ if (p) {
13033
+ const gone = runner.readsSession && (p.onScreen ? !dialog : (run.seenAt ?? 0) > p.at);
13034
+ let now;
13035
+ try {
13036
+ now = await perms.decisionOf(run.item, p.decisionId);
13037
+ } catch (e) {
13038
+ d.log(`${at}: could not read the permission decision, asking next tick: ${message(e)}`);
13039
+ return true;
13040
+ }
13041
+ if (now?.state === "answered" && now.answer) {
13042
+ if (gone)
13043
+ d.log(`${at}: the permission was answered after its dialog closed — not pressed`);
13044
+ else if (!pressAnswer(d, runner, run, p.options, now.answer))
13045
+ return true;
13046
+ run.permission = undefined;
13047
+ run.answeredAt = d.now();
13048
+ if (markFile)
13049
+ clearPermissionMark(markFile);
13050
+ return true;
13051
+ }
13052
+ if (now?.state !== "open" && now?.state !== "escalated") {
13053
+ run.permission = undefined;
13054
+ return false;
13055
+ }
13056
+ if (!gone)
13057
+ return true;
13058
+ try {
13059
+ await perms.clearPermission(run.runId, "the dialog closed in the terminal", d.engine);
13060
+ } catch (e) {
13061
+ d.log(`${at}: could not clear the permission, trying next tick: ${message(e)}`);
13062
+ return true;
13063
+ }
13064
+ d.log(`${at}: the permission dialog closed before an answer — cleared`);
13065
+ run.permission = undefined;
13066
+ return false;
13067
+ }
13068
+ const mark = markFile ? readPermissionMark(markFile) : null;
13069
+ if (!dialog && !mark)
13070
+ return false;
13071
+ if (!dialog && runner.readsSession && !run.seenTicks)
13072
+ return false;
13073
+ const options = dialog ? dialog.options.map((o) => `${o.key} · ${o.label}`) : MARK_OPTIONS;
13074
+ const md = dialog?.text || mark?.message || "The session stopped at a permission dialog.";
13075
+ let opened;
13076
+ try {
13077
+ opened = await perms.openPermission(run.runId, { md, options }, d.engine);
13078
+ } catch (e) {
13079
+ d.log(`${at}: could not raise the permission dialog, trying next tick: ${message(e)}`);
13080
+ return true;
13081
+ }
13082
+ if (!opened.decisionId)
13083
+ return true;
13084
+ run.permission = {
13085
+ decisionId: opened.decisionId,
13086
+ options,
13087
+ at: d.now(),
13088
+ ...dialog ? { onScreen: true } : {}
13089
+ };
13090
+ if (markFile)
13091
+ clearPermissionMark(markFile);
13092
+ d.log(`${at}: stopped at a permission dialog — it waits for a person (${runner.watchHint(run.handle)})`);
13093
+ return true;
13094
+ }
13095
+ function pressAnswer(d, runner, run, options, answer) {
13096
+ const at = ref(run);
13097
+ const key = permissionKey(answer);
13098
+ const offered = key !== null && options.some((o) => permissionKey(o) === key);
13099
+ try {
13100
+ if (key !== null && offered) {
13101
+ if (runner.sendKey)
13102
+ runner.sendKey(run.handle, key);
13103
+ else
13104
+ runner.send(run.handle, key);
13105
+ d.log(`${at}: the permission is answered — pressed ${key}; the session goes on`);
13106
+ } else {
13107
+ runner.sendKey?.(run.handle, "esc");
13108
+ const note = runner.send(run.handle, answer);
13109
+ d.log(`${at}: the permission is answered in words — Esc, then typed in${note ? ` (${note})` : ""}`);
13110
+ }
13111
+ return true;
13112
+ } catch (e) {
13113
+ d.log(`${at}: could not press the permission's answer, trying next tick: ${message(e)}`);
13114
+ return false;
13115
+ }
13116
+ }
13117
+ async function dropPermission(d, run, why) {
13118
+ const perms = permissionsOf(d.source);
13119
+ if (!run.permission || !perms || !run.runId)
13120
+ return;
13121
+ try {
13122
+ await perms.clearPermission(run.runId, why, d.engine);
13123
+ } catch (e) {
13124
+ d.log(`${ref(run)}: could not clear the permission (${why}): ${message(e)}`);
13125
+ }
13126
+ run.permission = undefined;
13127
+ }
12772
13128
  var STILL_TICKS = 2;
12773
13129
  function lastMessage(screen) {
12774
13130
  const shown = stripTuiChrome(screen);
@@ -13202,6 +13558,8 @@ async function startAnswerer(d, answers, run) {
13202
13558
  ...launchOptions(launch),
13203
13559
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
13204
13560
  ...sandboxFor(d, name, run.handle.cwd),
13561
+ ...hooksFor(d, name),
13562
+ ...mainCheckoutFor(d, run.handle.cwd),
13205
13563
  ...run.group ? { group: run.group } : {}
13206
13564
  });
13207
13565
  } catch (e) {
@@ -13376,7 +13734,9 @@ async function startDraft(d, claim, why, state, started, finished) {
13376
13734
  permissionMode: d.permissionMode,
13377
13735
  ...launchOptions(launch),
13378
13736
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
13379
- ...sandboxFor(d, name, worktree)
13737
+ ...sandboxFor(d, name, worktree),
13738
+ ...hooksFor(d, name),
13739
+ ...mainCheckoutFor(d, worktree)
13380
13740
  });
13381
13741
  } catch (e) {
13382
13742
  d.log(`${at}: could not start the drafter: ${message(e)}`);
@@ -13513,11 +13873,47 @@ var SELF_NUDGES = 2;
13513
13873
  function reviewNudge(url, count) {
13514
13874
  return `New review feedback on your PR (${count} ${count === 1 ? "comment" : "comments"} from people so far): ${url}. Read all of it (gh pr view ${url} --comments, and the inline threads), answer each comment on its own thread with the fix or a reasoned reply, push, and leave the threads for the reviewer to resolve. End every comment and reply you post on the PR with the line ${ENGINE_MARK} so the engine knows it is yours and not the reviewer's.`;
13515
13875
  }
13876
+ function lookQuestion(url, mock) {
13877
+ return `A draft PR waits for your look: ${url}.${mock ? ` The mock: ${mock}.` : " Its body names no mock; the PR is the place to look."} Looks good marks it ready for review.`;
13878
+ }
13879
+ async function judgeLook(d, run, view) {
13880
+ const looks = looksOf(d.source);
13881
+ const pr = run.pr;
13882
+ if (!looks || !run.runId)
13883
+ return;
13884
+ const draft = view.state === "OPEN" && view.draft;
13885
+ if (draft && !pr.look) {
13886
+ try {
13887
+ const r = await looks.openLook(run.runId, {
13888
+ md: lookQuestion(pr.url, view.mock),
13889
+ pr: pr.url,
13890
+ ...view.mock ? { mock: view.mock } : {}
13891
+ }, d.engine);
13892
+ if (!r.decisionId)
13893
+ return;
13894
+ pr.look = r.decisionId;
13895
+ if (r.opened)
13896
+ d.log(`${ref(run)}: ${pr.url} is a draft — a look waits for a person`);
13897
+ } catch (e) {
13898
+ d.log(`${ref(run)}: could not raise the look on ${pr.url}: ${message(e)}`);
13899
+ }
13900
+ } else if (!draft && pr.look) {
13901
+ const why = view.state === "MERGED" ? "the PR merged" : view.state === "CLOSED" ? "the PR closed without a merge" : "the PR left draft";
13902
+ try {
13903
+ if (await looks.clearLook(run.runId, { pr: pr.url, why }, d.engine))
13904
+ d.log(`${ref(run)}: the look on ${pr.url} is cleared — ${why}`);
13905
+ pr.look = undefined;
13906
+ } catch (e) {
13907
+ d.log(`${ref(run)}: could not clear the look on ${pr.url}: ${message(e)}`);
13908
+ }
13909
+ }
13910
+ }
13516
13911
  async function follow(d, state, run, claim, runner, finished, attention, changes, decided) {
13517
13912
  const pr = run.pr;
13518
13913
  const view = d.prView(d.exec, run.repoPath, pr.url);
13519
13914
  if (!view)
13520
13915
  return true;
13916
+ await judgeLook(d, run, view);
13521
13917
  if (view.state === "MERGED") {
13522
13918
  const landed = await ripple(d, state, run, claim, runner, pr.url, finished, decided, view.head, view.mergeCommit);
13523
13919
  return !landed;
@@ -14113,6 +14509,8 @@ function startResolver(d, run, e, answer) {
14113
14509
  ...launchOptions(launch),
14114
14510
  ...run.mcpConfig ? { mcpConfig: run.mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
14115
14511
  ...sandboxFor(d, name, run.handle.cwd),
14512
+ ...hooksFor(d, name),
14513
+ ...mainCheckoutFor(d, run.handle.cwd),
14116
14514
  ...run.group ? { group: run.group } : {}
14117
14515
  });
14118
14516
  } catch (err) {
@@ -14187,7 +14585,9 @@ async function startEventAnswerer(d, answers, state, dec, decided) {
14187
14585
  permissionMode: d.permissionMode,
14188
14586
  ...launchOptions(launch),
14189
14587
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
14190
- ...sandboxFor(d, name, worktree)
14588
+ ...sandboxFor(d, name, worktree),
14589
+ ...hooksFor(d, name),
14590
+ ...mainCheckoutFor(d, worktree)
14191
14591
  });
14192
14592
  } catch (e) {
14193
14593
  d.log(`${at}: could not start the ${KIND_SAYS[kind]} answerer — it goes to a person: ${message(e)}`);
@@ -14407,7 +14807,7 @@ function commandRunner(template, deps = {}) {
14407
14807
  }
14408
14808
 
14409
14809
  // src/runners/conductor.ts
14410
- import { readFileSync as readFileSync10 } from "node:fs";
14810
+ import { readFileSync as readFileSync11 } from "node:fs";
14411
14811
  var obj = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
14412
14812
  var str8 = (v) => typeof v === "string" && v.length > 0 ? v : null;
14413
14813
  var CONDUCTOR_API = "https://api.conductor.build";
@@ -14505,7 +14905,7 @@ var ids = (h) => {
14505
14905
  var ENDED = new Set(["archived", "deleted"]);
14506
14906
  function conductorRunner(exec, deps = {}) {
14507
14907
  const env = deps.env ?? process.env;
14508
- const readFile = deps.readFile ?? ((p) => readFileSync10(p, "utf8"));
14908
+ const readFile = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
14509
14909
  const now = deps.now ?? Date.now;
14510
14910
  const base = env.CONDUCTOR_API_URL?.trim() || CONDUCTOR_API;
14511
14911
  const key = () => {
@@ -14624,8 +15024,8 @@ ${renderTranscript(messages)}`;
14624
15024
  var headlessScript = (o, log) => {
14625
15025
  const prompt = `"$(cat ${shq2(o.promptFile)})"`;
14626
15026
  const agent = o.agent?.trim();
14627
- const cmd = agent ? `${sandboxedAgent(agent, o.sandbox)} ${prompt}` : [
14628
- `${sandboxedAgent("claude", o.sandbox)} -p`,
15027
+ const cmd = agent ? `${sandboxedAgent(agent, o.sandbox, o.mainCheckout)} ${prompt}` : [
15028
+ `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)} -p`,
14629
15029
  prompt,
14630
15030
  ...o.mcpConfig ? [mcpConfigFlag(o).trim()] : [],
14631
15031
  "--output-format stream-json --verbose",
@@ -14696,7 +15096,7 @@ function headlessRunner(deps = {}) {
14696
15096
 
14697
15097
  // src/runners/managed.ts
14698
15098
  init_src();
14699
- import { readFileSync as readFileSync11 } from "node:fs";
15099
+ import { readFileSync as readFileSync12 } from "node:fs";
14700
15100
  var obj2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
14701
15101
  var str9 = (v) => typeof v === "string" && v.length > 0 ? v : null;
14702
15102
  var ANTHROPIC_API = "https://api.anthropic.com";
@@ -14832,7 +15232,7 @@ var statusLine2 = (session, events, now) => {
14832
15232
  var YES = /^\s*(?:y|yes|allow|ok|okay|go|go ahead|approve[d]?)\b/i;
14833
15233
  function managedRunner(exec, deps = {}) {
14834
15234
  const env = deps.env ?? process.env;
14835
- const readFile = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
15235
+ const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
14836
15236
  const now = deps.now ?? Date.now;
14837
15237
  const base = env.ANTHROPIC_BASE_URL?.trim() || ANTHROPIC_API;
14838
15238
  const need = (name, where) => {
@@ -15002,7 +15402,7 @@ ${renderTranscript(eventMessages(all))}`;
15002
15402
  }
15003
15403
 
15004
15404
  // src/runners/orca.ts
15005
- import { writeFileSync as writeFileSync11 } from "node:fs";
15405
+ import { writeFileSync as writeFileSync12 } from "node:fs";
15006
15406
  var obj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
15007
15407
  var str10 = (v) => typeof v === "string" && v.length > 0 ? v : null;
15008
15408
  var parseOrcaJson = (stdout) => {
@@ -15061,7 +15461,7 @@ var orcaTerminalLive = (status, stdout) => {
15061
15461
  };
15062
15462
  var launcherPath2 = (promptFile) => `${promptFile.replace(/\.md$/, "")}.orca.sh`;
15063
15463
  function orcaRunner(exec, deps = {}) {
15064
- const writeFile = deps.writeFile ?? ((p, t) => writeFileSync11(p, t, { mode: 384 }));
15464
+ const writeFile = deps.writeFile ?? ((p, t) => writeFileSync12(p, t, { mode: 384 }));
15065
15465
  const orca = (args, what) => {
15066
15466
  const r = exec("orca", args);
15067
15467
  const env = parseOrcaJson(r.stdout);
@@ -15201,6 +15601,14 @@ function tmuxRunner(exec) {
15201
15601
  }
15202
15602
  exec("tmux", ["send-keys", "-t", tmuxPane(h.id), "Enter"]);
15203
15603
  },
15604
+ sendKey(h, key) {
15605
+ if (!isRunnerKey(key))
15606
+ throw new Error(`tmux send-keys ${h.id}: no key ${JSON.stringify(key)}`);
15607
+ const r = exec("tmux", ["send-keys", "-t", tmuxPane(h.id), key === "esc" ? "Escape" : key]);
15608
+ if (r.status !== 0) {
15609
+ throw new Error(`tmux send-keys ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
15610
+ }
15611
+ },
15204
15612
  stop(h) {
15205
15613
  exec("tmux", ["kill-session", "-t", tmuxSession(h.id)]);
15206
15614
  },
@@ -15429,6 +15837,8 @@ function flagsFromProfile(flags, given, profile, home) {
15429
15837
  const out = { ...flags };
15430
15838
  const used = [];
15431
15839
  const skipped = [];
15840
+ for (const k of profile.notYours ?? [])
15841
+ skipped.push(`${k} (the workspace default — the engine runs only one you set yourself)`);
15432
15842
  if (profile.atOnce !== undefined && !has("at-once")) {
15433
15843
  out.atOnce = profile.atOnce;
15434
15844
  used.push(`at-once ${profile.atOnce}`);
@@ -15624,7 +16034,7 @@ function realPidAlive(pid) {
15624
16034
  function memberMcpConfigs(o, cwd) {
15625
16035
  const read = o.readJson ?? ((p) => {
15626
16036
  try {
15627
- return JSON.parse(readFileSync12(p, "utf8"));
16037
+ return JSON.parse(readFileSync13(p, "utf8"));
15628
16038
  } catch {
15629
16039
  return null;
15630
16040
  }
@@ -15666,6 +16076,9 @@ function engineDeps(o, flags, source, runner, stateName) {
15666
16076
  const repo = flags.repo ? resolve7(cwd, flags.repo) : undefined;
15667
16077
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve7(cwd, d));
15668
16078
  const { branch, prTitle } = flags;
16079
+ const hooksDir = join15(stateDir, "hooks");
16080
+ const permissionMark = (name) => join15(hooksDir, `${name}.permission.json`);
16081
+ const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
15669
16082
  return {
15670
16083
  source,
15671
16084
  runner,
@@ -15690,18 +16103,35 @@ function engineDeps(o, flags, source, runner, stateName) {
15690
16103
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
15691
16104
  const gitCommonDir = gitCommonDirOf(exec, cwd);
15692
16105
  const dir = join15(stateDir, "sandbox");
15693
- mkdirSync11(dir, { recursive: true, mode: 448 });
16106
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15694
16107
  const p = join15(dir, `${name}.json`);
15695
- const settings = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
16108
+ const walls = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
15696
16109
  ...SANDBOX_DEFAULT_ALLOW,
15697
16110
  ...flags.sandboxAllow ?? []
15698
16111
  ]);
15699
- writeFileSync12(p, `${JSON.stringify(settings, null, 2)}
16112
+ const settings = withHook(name, { ...walls });
16113
+ writeFileSync13(p, `${JSON.stringify(settings, null, 2)}
15700
16114
  `, { mode: 384 });
15701
- chmodSync3(p, 384);
16115
+ chmodSync4(p, 384);
15702
16116
  return { settingsFile: p, gitCommonDir };
15703
16117
  })
15704
16118
  } : {},
16119
+ writeHookSettings: (name) => {
16120
+ mkdirSync12(hooksDir, { recursive: true, mode: 448 });
16121
+ const p = join15(hooksDir, `${name}.json`);
16122
+ writeFileSync13(p, `${JSON.stringify(withHook(name, {}), null, 2)}
16123
+ `, { mode: 384 });
16124
+ chmodSync4(p, 384);
16125
+ return p;
16126
+ },
16127
+ permissionMark,
16128
+ mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
16129
+ try {
16130
+ return mainCheckoutOf(gitCommonDirOf(exec, cwd), cwd);
16131
+ } catch {
16132
+ return null;
16133
+ }
16134
+ }),
15705
16135
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
15706
16136
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
15707
16137
  now: o.now ?? Date.now,
@@ -15716,7 +16146,7 @@ function engineDeps(o, flags, source, runner, stateName) {
15716
16146
  }),
15717
16147
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
15718
16148
  const dir = join15(stateDir, "mcp");
15719
- mkdirSync11(dir, { recursive: true, mode: 448 });
16149
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15720
16150
  const p = join15(dir, `${name}.json`);
15721
16151
  let others = {};
15722
16152
  if (flags.strictMcp && flags.mcpAllow?.length) {
@@ -15727,16 +16157,16 @@ function engineDeps(o, flags, source, runner, stateName) {
15727
16157
  if (r.refused.length)
15728
16158
  log(`${name}: MCP pass-through refused ${r.refused.join(", ")} — the run's sleds server is the only SLEDS one`);
15729
16159
  }
15730
- writeFileSync12(p, pinOrg2(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
15731
- chmodSync3(p, 384);
16160
+ writeFileSync13(p, pinOrg2(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
16161
+ chmodSync4(p, 384);
15732
16162
  return p;
15733
16163
  }),
15734
- removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync3(p, { force: true })),
16164
+ removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync4(p, { force: true })),
15735
16165
  writePrompt: o.writePrompt ?? ((name, text) => {
15736
16166
  const dir = join15(stateDir, "prompts");
15737
- mkdirSync11(dir, { recursive: true, mode: 448 });
16167
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15738
16168
  const p = join15(dir, `${name}.md`);
15739
- writeFileSync12(p, text, { mode: 384 });
16169
+ writeFileSync13(p, text, { mode: 384 });
15740
16170
  return p;
15741
16171
  }),
15742
16172
  ensureWorktree: (e, repoPath, branch, path, base, install) => ensureWorktree(e, repoPath, branch, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }, { ...install ? { command: install } : {}, log: o.log ?? console.log }),
@@ -15819,7 +16249,8 @@ async function driveEngine(o, flags, deps, until = {}) {
15819
16249
  startedAt: (o.now ?? Date.now)(),
15820
16250
  runner: deps.runner.name,
15821
16251
  atOnce: flags.atOnce,
15822
- interval: flags.interval
16252
+ interval: flags.interval,
16253
+ cliVersion: "0.1.39"
15823
16254
  };
15824
16255
  const beat = (checkedAt) => {
15825
16256
  const m = deps.machine;
@@ -16111,7 +16542,7 @@ async function enginePauseCmd(o) {
16111
16542
  result.released++;
16112
16543
  result.kept.push(run.handle.cwd);
16113
16544
  if (run.mcpConfig)
16114
- rmSync3(run.mcpConfig, { force: true });
16545
+ rmSync4(run.mcpConfig, { force: true });
16115
16546
  }
16116
16547
  if (gone.size)
16117
16548
  writeState(file, { ...state, runs: state.runs.filter((r) => !gone.has(r)) });
@@ -16133,19 +16564,19 @@ init_config();
16133
16564
  init_state();
16134
16565
  import {
16135
16566
  existsSync as existsSync14,
16136
- mkdirSync as mkdirSync13,
16137
- readFileSync as readFileSync13,
16567
+ mkdirSync as mkdirSync14,
16568
+ readFileSync as readFileSync14,
16138
16569
  readdirSync as readdirSync2,
16139
16570
  realpathSync,
16140
- rmSync as rmSync5,
16141
- writeFileSync as writeFileSync14
16571
+ rmSync as rmSync6,
16572
+ writeFileSync as writeFileSync15
16142
16573
  } from "node:fs";
16143
16574
  import { hostname as hostname3 } from "node:os";
16144
16575
  import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join18 } from "node:path";
16145
16576
 
16146
16577
  // src/schedule.ts
16147
16578
  import { spawnSync as spawnSync4 } from "node:child_process";
16148
- import { existsSync as existsSync13, mkdirSync as mkdirSync12, rmSync as rmSync4, writeFileSync as writeFileSync13 } from "node:fs";
16579
+ import { existsSync as existsSync13, mkdirSync as mkdirSync13, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "node:fs";
16149
16580
  import { join as join16 } from "node:path";
16150
16581
  var REVERT_LABEL = "ai.sleds.revert";
16151
16582
  var defaultExec2 = (cmd, args) => {
@@ -16193,9 +16624,9 @@ var scheduleRevert = (d, deadlineMs) => {
16193
16624
  if (d.platform === "darwin") {
16194
16625
  const p = plistPath(d.home);
16195
16626
  try {
16196
- mkdirSync12(join16(d.home, "Library", "LaunchAgents"), { recursive: true });
16627
+ mkdirSync13(join16(d.home, "Library", "LaunchAgents"), { recursive: true });
16197
16628
  d.exec("launchctl", ["unload", p]);
16198
- writeFileSync13(p, buildLaunchdPlist(d.sledsArgv, deadline));
16629
+ writeFileSync14(p, buildLaunchdPlist(d.sledsArgv, deadline));
16199
16630
  const r = d.exec("launchctl", ["load", p]);
16200
16631
  if (r.ok)
16201
16632
  return { kind: "launchd", detail: `launchd agent ${REVERT_LABEL}` };
@@ -16224,7 +16655,7 @@ var cancelRevert = (d, kind) => {
16224
16655
  const p = plistPath(d.home);
16225
16656
  if (existsSync13(p)) {
16226
16657
  d.exec("launchctl", ["unload", p]);
16227
- rmSync4(p, { force: true });
16658
+ rmSync5(p, { force: true });
16228
16659
  }
16229
16660
  }
16230
16661
  if (kind === "systemd" || d.platform === "linux") {
@@ -16400,7 +16831,7 @@ function readEngines(dir, suffix, manager, parse) {
16400
16831
  const path = join18(dir, name);
16401
16832
  let parsed;
16402
16833
  try {
16403
- parsed = parse(readFileSync13(path, "utf8"), name);
16834
+ parsed = parse(readFileSync14(path, "utf8"), name);
16404
16835
  } catch {
16405
16836
  parsed = null;
16406
16837
  }
@@ -16454,7 +16885,7 @@ function engineInstall(o) {
16454
16885
  const stateDir = seatStateDir(orgStateDir(baseDir, parsed.org), seat);
16455
16886
  const label = engineLabel(parsed.name, parsed.org, seat);
16456
16887
  const logPath = engineLogPath(stateDir, parsed.name);
16457
- mkdirSync13(stateDir, { recursive: true, mode: 448 });
16888
+ mkdirSync14(stateDir, { recursive: true, mode: 448 });
16458
16889
  const sledsArgv = stableArgv(o.sledsArgv, o.realpath);
16459
16890
  const execPath = sledsArgv[0] ?? process.execPath;
16460
16891
  const agentEnv = {
@@ -16467,7 +16898,7 @@ function engineInstall(o) {
16467
16898
  if (platform === "linux")
16468
16899
  return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
16469
16900
  const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
16470
- mkdirSync13(dirname12(plist), { recursive: true });
16901
+ mkdirSync14(dirname12(plist), { recursive: true });
16471
16902
  if (existsSync14(plist))
16472
16903
  exec("launchctl", ["unload", plist]);
16473
16904
  for (const other of installedEngines(o.home)) {
@@ -16476,7 +16907,7 @@ function engineInstall(o) {
16476
16907
  if (engineArg(other.programArgs, "name") !== parsed.name)
16477
16908
  continue;
16478
16909
  exec("launchctl", ["unload", other.plist]);
16479
- rmSync5(other.plist, { force: true });
16910
+ rmSync6(other.plist, { force: true });
16480
16911
  log(`replaced ${other.label}, this engine as installed before`);
16481
16912
  }
16482
16913
  const legacy = enginePlistPath(o.home, parsed.name);
@@ -16484,12 +16915,12 @@ function engineInstall(o) {
16484
16915
  const cfg = readConfig(o.home);
16485
16916
  if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
16486
16917
  exec("launchctl", ["unload", legacy]);
16487
- rmSync5(legacy, { force: true });
16918
+ rmSync6(legacy, { force: true });
16488
16919
  log(`replaced ${engineLabel(parsed.name)}, the engine installed before orgs`);
16489
16920
  } else
16490
16921
  log(`note: ${engineLabel(parsed.name)} (installed before orgs) still runs — \`sleds engine uninstall --name ${parsed.name}\` removes it if it was this org's`);
16491
16922
  }
16492
- writeFileSync14(plist, buildEnginePlist({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
16923
+ writeFileSync15(plist, buildEnginePlist({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
16493
16924
  const r = exec("launchctl", ["load", plist]);
16494
16925
  if (!r.ok) {
16495
16926
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -16509,7 +16940,7 @@ function installUnit(a) {
16509
16940
  const unit = engineUnitPath(o.home, label);
16510
16941
  const unitName = basename4(unit);
16511
16942
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
16512
- mkdirSync13(dirname12(unit), { recursive: true });
16943
+ mkdirSync14(dirname12(unit), { recursive: true });
16513
16944
  if (existsSync14(unit))
16514
16945
  systemctl("stop", unitName);
16515
16946
  for (const other of installedEngines(o.home)) {
@@ -16518,10 +16949,10 @@ function installUnit(a) {
16518
16949
  if (engineArg(other.programArgs, "name") !== parsed.name)
16519
16950
  continue;
16520
16951
  systemctl("disable", "--now", basename4(other.plist));
16521
- rmSync5(other.plist, { force: true });
16952
+ rmSync6(other.plist, { force: true });
16522
16953
  log(`replaced ${other.label}, this engine as installed before`);
16523
16954
  }
16524
- writeFileSync14(unit, buildEngineUnit({
16955
+ writeFileSync15(unit, buildEngineUnit({
16525
16956
  label,
16526
16957
  name: parsed.name,
16527
16958
  sledsArgv: a.sledsArgv,
@@ -16579,7 +17010,7 @@ function engineUninstall(o) {
16579
17010
  exec("systemctl", ["--user", "disable", "--now", basename4(file)]);
16580
17011
  else
16581
17012
  exec("launchctl", ["unload", file]);
16582
- rmSync5(file, { force: true });
17013
+ rmSync6(file, { force: true });
16583
17014
  if (unit)
16584
17015
  exec("systemctl", ["--user", "daemon-reload"]);
16585
17016
  log(`engine:${name} removed: ${basename4(file, unit ? ".service" : ".plist")} — its sessions keep running`);
@@ -16587,7 +17018,7 @@ function engineUninstall(o) {
16587
17018
  }
16588
17019
 
16589
17020
  // src/commands/musher.ts
16590
- import { existsSync as existsSync15, mkdirSync as mkdirSync14, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync15 } from "node:fs";
17021
+ import { existsSync as existsSync15, mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
16591
17022
  init_config();
16592
17023
  init_meter();
16593
17024
  var defaultWhich = (cmd) => {
@@ -16620,7 +17051,7 @@ var musherAgentState = (home, env = process.env) => {
16620
17051
  if (!existsSync15(plist))
16621
17052
  return { installed: false, thisSeat: false };
16622
17053
  try {
16623
- const text = readFileSync14(plist, "utf8");
17054
+ const text = readFileSync15(plist, "utf8");
16624
17055
  const want = env.SLEDS_HOME?.trim();
16625
17056
  const has = /<key>SLEDS_HOME<\/key>\s*<string>([^<]*)<\/string>/.exec(text)?.[1];
16626
17057
  return { installed: true, thisSeat: (want ?? undefined) === (has ?? undefined) };
@@ -16980,7 +17411,7 @@ function sandboxChecks(o, env) {
16980
17411
  const probe = join19(dir, SANDBOX_PROBE);
16981
17412
  const work = mkdtempSync(join19(tmpdir(), "sleds-sandbox-"));
16982
17413
  try {
16983
- writeFileSync16(probe, `${nonce}
17414
+ writeFileSync17(probe, `${nonce}
16984
17415
  `, { mode: 384 });
16985
17416
  const settingsFile = join19(work, "settings.json");
16986
17417
  const settings = sandboxSettings({
@@ -16988,7 +17419,7 @@ function sandboxChecks(o, env) {
16988
17419
  gitCommonDir: work,
16989
17420
  allow: SANDBOX_DEFAULT_ALLOW
16990
17421
  });
16991
- writeFileSync16(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
17422
+ writeFileSync17(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
16992
17423
  const prompt = `Run exactly this one Bash command, once, then reply with its complete output verbatim and nothing else:
16993
17424
 
16994
17425
  ${SANDBOX_PROBE_COMMAND}`;
@@ -17010,8 +17441,8 @@ ${SANDBOX_PROBE_COMMAND}`;
17010
17441
  ], { cwd: work });
17011
17442
  return readSandboxProbe(run, nonce);
17012
17443
  } finally {
17013
- rmSync7(probe, { force: true });
17014
- rmSync7(work, { recursive: true, force: true });
17444
+ rmSync8(probe, { force: true });
17445
+ rmSync8(work, { recursive: true, force: true });
17015
17446
  }
17016
17447
  }
17017
17448
  function engineChecks(o, env) {
@@ -17128,7 +17559,7 @@ function boxChecks(o, env, which, need) {
17128
17559
  const file = boxEnvPath(o.home);
17129
17560
  let named = false;
17130
17561
  try {
17131
- named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync15(file, "utf8"));
17562
+ named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync16(file, "utf8"));
17132
17563
  } catch {
17133
17564
  named = false;
17134
17565
  }
@@ -17232,12 +17663,12 @@ init_server();
17232
17663
  init_config();
17233
17664
  import {
17234
17665
  appendFileSync as appendFileSync3,
17235
- chmodSync as chmodSync4,
17236
- mkdirSync as mkdirSync15,
17237
- readFileSync as readFileSync16,
17666
+ chmodSync as chmodSync5,
17667
+ mkdirSync as mkdirSync16,
17668
+ readFileSync as readFileSync17,
17238
17669
  existsSync as realExists2,
17239
- rmSync as rmSync8,
17240
- writeFileSync as writeFileSync17
17670
+ rmSync as rmSync9,
17671
+ writeFileSync as writeFileSync18
17241
17672
  } from "node:fs";
17242
17673
  import { hostname as hostname4 } from "node:os";
17243
17674
  import { join as join20, resolve as resolve9 } from "node:path";
@@ -17421,6 +17852,8 @@ function flagsFromProfile2(flags, given, profile, home) {
17421
17852
  const out = { ...flags };
17422
17853
  const used = [];
17423
17854
  const skipped = [];
17855
+ for (const k of profile.notYours ?? [])
17856
+ skipped.push(`${k} (the workspace default — the engine runs only one you set yourself)`);
17424
17857
  if (profile.atOnce !== undefined && !has("at-once")) {
17425
17858
  out.atOnce = profile.atOnce;
17426
17859
  used.push(`at-once ${profile.atOnce}`);
@@ -17645,7 +18078,7 @@ function realPidAlive2(pid) {
17645
18078
  function memberMcpConfigs2(o, cwd) {
17646
18079
  const read = o.readJson ?? ((p) => {
17647
18080
  try {
17648
- return JSON.parse(readFileSync16(p, "utf8"));
18081
+ return JSON.parse(readFileSync17(p, "utf8"));
17649
18082
  } catch {
17650
18083
  return null;
17651
18084
  }
@@ -17687,6 +18120,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
17687
18120
  const repo = flags.repo ? resolve9(cwd, flags.repo) : undefined;
17688
18121
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve9(cwd, d));
17689
18122
  const { branch, prTitle } = flags;
18123
+ const hooksDir = join20(stateDir, "hooks");
18124
+ const permissionMark = (name) => join20(hooksDir, `${name}.permission.json`);
18125
+ const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
17690
18126
  return {
17691
18127
  source,
17692
18128
  runner,
@@ -17711,18 +18147,35 @@ function engineDeps2(o, flags, source, runner, stateName) {
17711
18147
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
17712
18148
  const gitCommonDir = gitCommonDirOf2(exec, cwd);
17713
18149
  const dir = join20(stateDir, "sandbox");
17714
- mkdirSync15(dir, { recursive: true, mode: 448 });
18150
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17715
18151
  const p = join20(dir, `${name}.json`);
17716
- const settings = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
18152
+ const walls = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
17717
18153
  ...SANDBOX_DEFAULT_ALLOW,
17718
18154
  ...flags.sandboxAllow ?? []
17719
18155
  ]);
17720
- writeFileSync17(p, `${JSON.stringify(settings, null, 2)}
18156
+ const settings = withHook(name, { ...walls });
18157
+ writeFileSync18(p, `${JSON.stringify(settings, null, 2)}
17721
18158
  `, { mode: 384 });
17722
- chmodSync4(p, 384);
18159
+ chmodSync5(p, 384);
17723
18160
  return { settingsFile: p, gitCommonDir };
17724
18161
  })
17725
18162
  } : {},
18163
+ writeHookSettings: (name) => {
18164
+ mkdirSync16(hooksDir, { recursive: true, mode: 448 });
18165
+ const p = join20(hooksDir, `${name}.json`);
18166
+ writeFileSync18(p, `${JSON.stringify(withHook(name, {}), null, 2)}
18167
+ `, { mode: 384 });
18168
+ chmodSync5(p, 384);
18169
+ return p;
18170
+ },
18171
+ permissionMark,
18172
+ mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
18173
+ try {
18174
+ return mainCheckoutOf(gitCommonDirOf2(exec, cwd), cwd);
18175
+ } catch {
18176
+ return null;
18177
+ }
18178
+ }),
17726
18179
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
17727
18180
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
17728
18181
  now: o.now ?? Date.now,
@@ -17737,7 +18190,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
17737
18190
  }),
17738
18191
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
17739
18192
  const dir = join20(stateDir, "mcp");
17740
- mkdirSync15(dir, { recursive: true, mode: 448 });
18193
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17741
18194
  const p = join20(dir, `${name}.json`);
17742
18195
  let others = {};
17743
18196
  if (flags.strictMcp && flags.mcpAllow?.length) {
@@ -17748,16 +18201,16 @@ function engineDeps2(o, flags, source, runner, stateName) {
17748
18201
  if (r.refused.length)
17749
18202
  log(`${name}: MCP pass-through refused ${r.refused.join(", ")} — the run's sleds server is the only SLEDS one`);
17750
18203
  }
17751
- writeFileSync17(p, pinOrg3(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
17752
- chmodSync4(p, 384);
18204
+ writeFileSync18(p, pinOrg3(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
18205
+ chmodSync5(p, 384);
17753
18206
  return p;
17754
18207
  }),
17755
- removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync8(p, { force: true })),
18208
+ removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync9(p, { force: true })),
17756
18209
  writePrompt: o.writePrompt ?? ((name, text) => {
17757
18210
  const dir = join20(stateDir, "prompts");
17758
- mkdirSync15(dir, { recursive: true, mode: 448 });
18211
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17759
18212
  const p = join20(dir, `${name}.md`);
17760
- writeFileSync17(p, text, { mode: 384 });
18213
+ writeFileSync18(p, text, { mode: 384 });
17761
18214
  return p;
17762
18215
  }),
17763
18216
  ensureWorktree: (e, repoPath, branch, path, base, install) => ensureWorktree(e, repoPath, branch, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }, { ...install ? { command: install } : {}, log: o.log ?? console.log }),
@@ -17849,7 +18302,8 @@ async function driveEngine2(o, flags, deps, until = {}) {
17849
18302
  startedAt: (o.now ?? Date.now)(),
17850
18303
  runner: deps.runner.name,
17851
18304
  atOnce: flags.atOnce,
17852
- interval: flags.interval
18305
+ interval: flags.interval,
18306
+ cliVersion: "0.1.39"
17853
18307
  };
17854
18308
  const beat = (checkedAt) => {
17855
18309
  const m = deps.machine;
@@ -18255,7 +18709,7 @@ async function enginePauseCmd2(o) {
18255
18709
  result.released++;
18256
18710
  result.kept.push(run.handle.cwd);
18257
18711
  if (run.mcpConfig)
18258
- rmSync8(run.mcpConfig, { force: true });
18712
+ rmSync9(run.mcpConfig, { force: true });
18259
18713
  }
18260
18714
  if (gone.size)
18261
18715
  writeState(file, { ...state, runs: state.runs.filter((r) => !gone.has(r)) });
@@ -18277,12 +18731,12 @@ init_config();
18277
18731
  init_state();
18278
18732
  import {
18279
18733
  existsSync as existsSync17,
18280
- mkdirSync as mkdirSync16,
18281
- readFileSync as readFileSync17,
18734
+ mkdirSync as mkdirSync17,
18735
+ readFileSync as readFileSync18,
18282
18736
  readdirSync as readdirSync4,
18283
18737
  realpathSync as realpathSync2,
18284
- rmSync as rmSync9,
18285
- writeFileSync as writeFileSync18
18738
+ rmSync as rmSync10,
18739
+ writeFileSync as writeFileSync19
18286
18740
  } from "node:fs";
18287
18741
  import { hostname as hostname5 } from "node:os";
18288
18742
  import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join21 } from "node:path";
@@ -18404,7 +18858,7 @@ function readEngines2(dir, suffix, manager, parse) {
18404
18858
  const path = join21(dir, name);
18405
18859
  let parsed;
18406
18860
  try {
18407
- parsed = parse(readFileSync17(path, "utf8"), name);
18861
+ parsed = parse(readFileSync18(path, "utf8"), name);
18408
18862
  } catch {
18409
18863
  parsed = null;
18410
18864
  }
@@ -18458,7 +18912,7 @@ function engineInstall2(o) {
18458
18912
  const stateDir = seatStateDir(orgStateDir(baseDir, parsed.org), seat);
18459
18913
  const label = engineLabel2(parsed.name, parsed.org, seat);
18460
18914
  const logPath = engineLogPath2(stateDir, parsed.name);
18461
- mkdirSync16(stateDir, { recursive: true, mode: 448 });
18915
+ mkdirSync17(stateDir, { recursive: true, mode: 448 });
18462
18916
  const sledsArgv = stableArgv2(o.sledsArgv, o.realpath);
18463
18917
  const execPath = sledsArgv[0] ?? process.execPath;
18464
18918
  const agentEnv = {
@@ -18471,7 +18925,7 @@ function engineInstall2(o) {
18471
18925
  if (platform === "linux")
18472
18926
  return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
18473
18927
  const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
18474
- mkdirSync16(dirname13(plist), { recursive: true });
18928
+ mkdirSync17(dirname13(plist), { recursive: true });
18475
18929
  if (existsSync17(plist))
18476
18930
  exec("launchctl", ["unload", plist]);
18477
18931
  for (const other of installedEngines2(o.home)) {
@@ -18480,7 +18934,7 @@ function engineInstall2(o) {
18480
18934
  if (engineArg2(other.programArgs, "name") !== parsed.name)
18481
18935
  continue;
18482
18936
  exec("launchctl", ["unload", other.plist]);
18483
- rmSync9(other.plist, { force: true });
18937
+ rmSync10(other.plist, { force: true });
18484
18938
  log(`replaced ${other.label}, this engine as installed before`);
18485
18939
  }
18486
18940
  const legacy = enginePlistPath2(o.home, parsed.name);
@@ -18488,12 +18942,12 @@ function engineInstall2(o) {
18488
18942
  const cfg = readConfig(o.home);
18489
18943
  if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
18490
18944
  exec("launchctl", ["unload", legacy]);
18491
- rmSync9(legacy, { force: true });
18945
+ rmSync10(legacy, { force: true });
18492
18946
  log(`replaced ${engineLabel2(parsed.name)}, the engine installed before orgs`);
18493
18947
  } else
18494
18948
  log(`note: ${engineLabel2(parsed.name)} (installed before orgs) still runs — \`sleds engine uninstall --name ${parsed.name}\` removes it if it was this org's`);
18495
18949
  }
18496
- writeFileSync18(plist, buildEnginePlist2({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
18950
+ writeFileSync19(plist, buildEnginePlist2({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
18497
18951
  const r = exec("launchctl", ["load", plist]);
18498
18952
  if (!r.ok) {
18499
18953
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -18513,7 +18967,7 @@ function installUnit2(a) {
18513
18967
  const unit = engineUnitPath(o.home, label);
18514
18968
  const unitName = basename5(unit);
18515
18969
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
18516
- mkdirSync16(dirname13(unit), { recursive: true });
18970
+ mkdirSync17(dirname13(unit), { recursive: true });
18517
18971
  if (existsSync17(unit))
18518
18972
  systemctl("stop", unitName);
18519
18973
  for (const other of installedEngines2(o.home)) {
@@ -18522,10 +18976,10 @@ function installUnit2(a) {
18522
18976
  if (engineArg2(other.programArgs, "name") !== parsed.name)
18523
18977
  continue;
18524
18978
  systemctl("disable", "--now", basename5(other.plist));
18525
- rmSync9(other.plist, { force: true });
18979
+ rmSync10(other.plist, { force: true });
18526
18980
  log(`replaced ${other.label}, this engine as installed before`);
18527
18981
  }
18528
- writeFileSync18(unit, buildEngineUnit({
18982
+ writeFileSync19(unit, buildEngineUnit({
18529
18983
  label,
18530
18984
  name: parsed.name,
18531
18985
  sledsArgv: a.sledsArgv,
@@ -18583,7 +19037,7 @@ function engineUninstall2(o) {
18583
19037
  exec("systemctl", ["--user", "disable", "--now", basename5(file)]);
18584
19038
  else
18585
19039
  exec("launchctl", ["unload", file]);
18586
- rmSync9(file, { force: true });
19040
+ rmSync10(file, { force: true });
18587
19041
  if (unit)
18588
19042
  exec("systemctl", ["--user", "daemon-reload"]);
18589
19043
  log(`engine:${name} removed: ${basename5(file, unit ? ".service" : ".plist")} — its sessions keep running`);
@@ -18714,7 +19168,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
18714
19168
  import { hostname as hostname6 } from "node:os";
18715
19169
 
18716
19170
  // src/commands/engineStatus.ts
18717
- import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
19171
+ import { existsSync as existsSync18, readFileSync as readFileSync19 } from "node:fs";
18718
19172
  init_state();
18719
19173
 
18720
19174
  // src/engine/status.ts
@@ -18827,6 +19281,7 @@ function engineStatus(i) {
18827
19281
  ...opt("runner", lock.runner || undefined),
18828
19282
  atOnce: lock.atOnce,
18829
19283
  interval: lock.interval,
19284
+ cliVersion: lock.cliVersion ?? "",
18830
19285
  ...opt("checkedAt", iso2(lock.checkedAt)),
18831
19286
  ...opt("nextCheckAt", alive ? iso2(lock.nextCheckAt) : undefined),
18832
19287
  ...opt("machine", lock.machine ? { ...lock.machine, at: new Date(lock.machine.at).toISOString() } : undefined),
@@ -19003,7 +19458,7 @@ var engineLogCmd = (o) => {
19003
19458
 
19004
19459
  // src/commands/engineProfile.ts
19005
19460
  init_src();
19006
- import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
19461
+ import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
19007
19462
  import { hostname as hostname7 } from "node:os";
19008
19463
  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]";
19009
19464
  var PROFILE_FIELDS = [
@@ -19268,7 +19723,7 @@ async function engineProfileCmd(o) {
19268
19723
  const rest = words.slice(1);
19269
19724
  let stdin = null;
19270
19725
  if (fromStdin) {
19271
- const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync19(0, "utf8")))());
19726
+ const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync20(0, "utf8")))());
19272
19727
  if ("error" in taken || taken.password !== undefined && password !== undefined) {
19273
19728
  error("error" in taken ? taken.error : "the cmux password is given once");
19274
19729
  error(PROFILE_USAGE);
@@ -19353,7 +19808,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
19353
19808
 
19354
19809
  // src/commands/engineStatus.ts
19355
19810
  init_state();
19356
- import { existsSync as existsSync20, readFileSync as readFileSync20 } from "node:fs";
19811
+ import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
19357
19812
  import { hostname as hostname8 } from "node:os";
19358
19813
  var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
19359
19814
  function parseStatusArgs(argv, host) {
@@ -19409,7 +19864,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
19409
19864
  }
19410
19865
  function safeRead(path) {
19411
19866
  try {
19412
- return readFileSync20(path, "utf8");
19867
+ return readFileSync21(path, "utf8");
19413
19868
  } catch {
19414
19869
  return "";
19415
19870
  }
@@ -19530,7 +19985,7 @@ init_meter();
19530
19985
 
19531
19986
  // src/owed.ts
19532
19987
  init_config();
19533
- import { existsSync as existsSync21, mkdirSync as mkdirSync17, readFileSync as readFileSync21, renameSync as renameSync6, writeFileSync as writeFileSync19 } from "node:fs";
19988
+ import { existsSync as existsSync21, mkdirSync as mkdirSync18, readFileSync as readFileSync22, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
19534
19989
  import { dirname as dirname14, join as join22 } from "node:path";
19535
19990
  var owedPath = (home) => join22(configDir(home), "owed.json");
19536
19991
  var sessionPath = (home) => join22(configDir(home), "session.json");
@@ -19551,17 +20006,17 @@ var readJson = (p) => {
19551
20006
  if (!existsSync21(p))
19552
20007
  return null;
19553
20008
  try {
19554
- return JSON.parse(readFileSync21(p, "utf8"));
20009
+ return JSON.parse(readFileSync22(p, "utf8"));
19555
20010
  } catch {
19556
20011
  return null;
19557
20012
  }
19558
20013
  };
19559
20014
  var writeJson = (p, v) => {
19560
- mkdirSync17(dirname14(p), { recursive: true, mode: 448 });
20015
+ mkdirSync18(dirname14(p), { recursive: true, mode: 448 });
19561
20016
  const tmp = `${p}.${process.pid}.tmp`;
19562
- writeFileSync19(tmp, `${JSON.stringify(v, null, 2)}
20017
+ writeFileSync20(tmp, `${JSON.stringify(v, null, 2)}
19563
20018
  `, { mode: 384 });
19564
- renameSync6(tmp, p);
20019
+ renameSync7(tmp, p);
19565
20020
  };
19566
20021
  var readOwed = (home, now = Date.now()) => {
19567
20022
  const f = readJson(owedPath(home));
@@ -19766,7 +20221,7 @@ var hooksOrg = (home, env = process.env) => {
19766
20221
  };
19767
20222
 
19768
20223
  // src/commands/init.ts
19769
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "node:fs";
20224
+ import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync21 } from "node:fs";
19770
20225
  import { createInterface } from "node:readline";
19771
20226
  init_config();
19772
20227
  init_state();
@@ -20453,8 +20908,8 @@ var init = async (o) => {
20453
20908
  });
20454
20909
  if (mcpLine)
20455
20910
  wrote.push(mcpLine);
20456
- mkdirSync18(skillDir(o.home), { recursive: true });
20457
- writeFileSync20(skillPath(o.home), SKILL_MD);
20911
+ mkdirSync19(skillDir(o.home), { recursive: true });
20912
+ writeFileSync21(skillPath(o.home), SKILL_MD);
20458
20913
  wrote.push(`installed skill at ${skillPath(o.home)}`);
20459
20914
  if (o.installMusher) {
20460
20915
  const r = o.installMusher();
@@ -20578,7 +21033,7 @@ var login = async (o) => {
20578
21033
  // src/commands/logout.ts
20579
21034
  init_config();
20580
21035
  init_state();
20581
- import { existsSync as existsSync22, readFileSync as readFileSync22, rmSync as rmSync10 } from "node:fs";
21036
+ import { existsSync as existsSync22, readFileSync as readFileSync23, rmSync as rmSync11 } from "node:fs";
20582
21037
  import { hostname as hostname10 } from "node:os";
20583
21038
  init_settings();
20584
21039
  var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
@@ -20702,7 +21157,7 @@ async function logout(o) {
20702
21157
  continue;
20703
21158
  if (!e.org) {
20704
21159
  (o.exec ?? defaultExec2)("launchctl", ["unload", e.plist]);
20705
- rmSync10(e.plist, { force: true });
21160
+ rmSync11(e.plist, { force: true });
20706
21161
  continue;
20707
21162
  }
20708
21163
  engineUninstall({
@@ -20735,8 +21190,8 @@ async function logout(o) {
20735
21190
  };
20736
21191
  writeConfig(o.home, next);
20737
21192
  const tp = tokenPath(o.home, env);
20738
- if (existsSync22(tp) && readFileSync22(tp, "utf8").trim() === seat.token)
20739
- rmSync10(tp, { force: true });
21193
+ if (existsSync22(tp) && readFileSync23(tp, "utf8").trim() === seat.token)
21194
+ rmSync11(tp, { force: true });
20740
21195
  const active = next.activeOrg ?? next.org?.slug ?? null;
20741
21196
  const result = { ok: true, email, org: slug, paused, active };
20742
21197
  if (parsed.json)
@@ -20764,8 +21219,9 @@ var mcp = async (o) => {
20764
21219
  (o.warn ?? ((line) => process.stderr.write(`${line}
20765
21220
  `)))("sleds mcp: on this run's token (SLEDS_RUN_TOKEN), not the seat's");
20766
21221
  let buffer = "";
21222
+ const decoder = new TextDecoder;
20767
21223
  for await (const chunk of stdin) {
20768
- buffer += typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
21224
+ buffer += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
20769
21225
  let nl = buffer.indexOf(`
20770
21226
  `);
20771
21227
  while (nl !== -1) {
@@ -20778,6 +21234,7 @@ var mcp = async (o) => {
20778
21234
  `);
20779
21235
  }
20780
21236
  }
21237
+ buffer += decoder.decode();
20781
21238
  if (buffer.trim().length > 0) {
20782
21239
  const out = await handleLine2(buffer, { home: o.home, env });
20783
21240
  if (out !== null)
@@ -20811,7 +21268,7 @@ var meter = async (o) => {
20811
21268
  init_config();
20812
21269
  init_meter();
20813
21270
  import { spawnSync as spawnSync7 } from "node:child_process";
20814
- import { existsSync as existsSync23, mkdirSync as mkdirSync19, readFileSync as readFileSync23, rmSync as rmSync11, writeFileSync as writeFileSync21 } from "node:fs";
21271
+ import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync24, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
20815
21272
  import { hostname as hostname11 } from "node:os";
20816
21273
  import { dirname as dirname15 } from "node:path";
20817
21274
  var defaultWhich3 = (cmd) => {
@@ -20851,8 +21308,8 @@ var musherInstall = (o) => {
20851
21308
  const plist = musherPlistPath(o.home, env);
20852
21309
  const label = musherLabel(env);
20853
21310
  const dir = configDir(o.home, env);
20854
- mkdirSync19(dirname15(plist), { recursive: true });
20855
- mkdirSync19(dir, { recursive: true, mode: 448 });
21311
+ mkdirSync20(dirname15(plist), { recursive: true });
21312
+ mkdirSync20(dir, { recursive: true, mode: 448 });
20856
21313
  const execPath = o.sledsArgv[0] ?? process.execPath;
20857
21314
  const agentEnv = {
20858
21315
  PATH: musherPath(o.home, execPath, env.PATH ?? ""),
@@ -20863,7 +21320,7 @@ var musherInstall = (o) => {
20863
21320
  const exec = o.exec ?? defaultExecSync;
20864
21321
  if (existsSync23(plist))
20865
21322
  exec("launchctl", ["unload", plist]);
20866
- writeFileSync21(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
21323
+ writeFileSync22(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
20867
21324
  const r = exec("launchctl", ["load", plist]);
20868
21325
  if (!r.ok) {
20869
21326
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -20884,7 +21341,7 @@ var musherUninstall = (o) => {
20884
21341
  const exec = o.exec ?? defaultExecSync;
20885
21342
  if (existsSync23(plist)) {
20886
21343
  exec("launchctl", ["unload", plist]);
20887
- rmSync11(plist, { force: true });
21344
+ rmSync12(plist, { force: true });
20888
21345
  log(`musher removed: ${musherLabel(env)}`);
20889
21346
  } else
20890
21347
  log("musher was not installed.");
@@ -21147,7 +21604,7 @@ var on = (o) => {
21147
21604
  init_config();
21148
21605
  init_meter();
21149
21606
  init_settings();
21150
- import { existsSync as existsSync24, rmSync as rmSync12 } from "node:fs";
21607
+ import { existsSync as existsSync24, rmSync as rmSync13 } from "node:fs";
21151
21608
  var ORG_USAGE = "usage: sleds org list [--json] | sleds org use <slug>";
21152
21609
  var orgRows = (cfg, activeSlug) => {
21153
21610
  const seats = { ...cfg.orgs ?? {} };
@@ -21224,7 +21681,7 @@ var orgUse = (o) => {
21224
21681
  writeSettings(o.home, removeProxy(settings));
21225
21682
  }
21226
21683
  try {
21227
- rmSync12(meterCachePath(o.home), { force: true });
21684
+ rmSync13(meterCachePath(o.home), { force: true });
21228
21685
  } catch {}
21229
21686
  log(`Active org: ${seat.org?.name ? `${seat.org.name} (${slug})` : slug}${seat.email ? ` · ${seat.email}` : ""}.`);
21230
21687
  log("Claude Code, the status line and the hooks act as this org now.");
@@ -21250,7 +21707,7 @@ var orgCmd = (o) => {
21250
21707
  // src/commands/pack.ts
21251
21708
  init_src();
21252
21709
  import { spawnSync as spawnSync8 } from "node:child_process";
21253
- import { writeFileSync as writeFileSync22 } from "node:fs";
21710
+ import { writeFileSync as writeFileSync23 } from "node:fs";
21254
21711
  import { join as join23 } from "node:path";
21255
21712
  function packFileName(key) {
21256
21713
  return `pack-${slugify(key, 80) || "item"}.md`;
@@ -21280,22 +21737,43 @@ var packCmd = async (o) => {
21280
21737
  return 0;
21281
21738
  }
21282
21739
  const path = join23(o.cwd ?? process.cwd(), packFileName(key));
21283
- (o.write ?? ((p, t) => writeFileSync22(p, t.endsWith(`
21740
+ (o.write ?? ((p, t) => writeFileSync23(p, t.endsWith(`
21284
21741
  `) ? t : `${t}
21285
21742
  `)))(path, r.text);
21286
21743
  log(`Wrote ${path} — hand it to any model. \`sleds pack ${key} --run\` starts Claude Code with it.`);
21287
21744
  return 0;
21288
21745
  };
21289
21746
 
21290
- // src/commands/promptEvent.ts
21291
- init_config();
21292
-
21293
- // src/commands/seatEvent.ts
21294
- init_config();
21295
- init_meter();
21296
- var HOOK_STDIN_TIMEOUT_MS = 1000;
21747
+ // src/commands/permissionEvent.ts
21748
+ import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as readFileSync25, renameSync as renameSync8, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "node:fs";
21749
+ import { dirname as dirname16 } from "node:path";
21750
+ var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
21751
+ var messageOf = (text) => {
21752
+ try {
21753
+ const raw = JSON.parse(text);
21754
+ return isObject13(raw) && typeof raw.message === "string" ? raw.message : "";
21755
+ } catch {
21756
+ return "";
21757
+ }
21758
+ };
21759
+ var permissionEvent = async (o) => {
21760
+ try {
21761
+ const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
21762
+ if (!o.mark || notificationType(input) !== "permission_prompt")
21763
+ return 0;
21764
+ const mark = { message: messageOf(input), at: (o.now ?? Date.now)() };
21765
+ mkdirSync21(dirname16(o.mark), { recursive: true, mode: 448 });
21766
+ const tmp = `${o.mark}.${process.pid}.tmp`;
21767
+ writeFileSync24(tmp, `${JSON.stringify(mark)}
21768
+ `, { mode: 384 });
21769
+ renameSync8(tmp, o.mark);
21770
+ chmodSync6(o.mark, 384);
21771
+ } catch {}
21772
+ return 0;
21773
+ };
21297
21774
 
21298
21775
  // src/commands/promptEvent.ts
21776
+ init_config();
21299
21777
  var LABEL = {
21300
21778
  fiveHour: "5-hour",
21301
21779
  sevenDay: "weekly",
@@ -21378,16 +21856,16 @@ var promptEvent = async (o) => {
21378
21856
 
21379
21857
  // src/commands/route.ts
21380
21858
  init_src();
21381
- import { readFileSync as readFileSync24 } from "node:fs";
21859
+ import { readFileSync as readFileSync26 } from "node:fs";
21382
21860
  import { hostname as hostname12 } from "node:os";
21383
21861
  var ARM_USAGE = "usage: sleds route arm <key> [--off] [--json] [--name <engine>] [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--mode <permission-mode>] [--model <id>] [--effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--repos-dir <dir>]";
21384
- var USAGE = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route decisions [<key>] [--flagged] | sleds route decision <key> <id> --seen | --answer --stdin [--route accept|keep|redraft] | --keep-route | --apply [--answer --stdin] | --overrule --stdin | sleds route run <key> [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--interval <s>] [--lease <min>] [--idle <min>] [--repo <path>] [--repos-dir <dir>] [--mode <permission-mode>] [--model <id>] [--branch '<template>'] [--pr-title '<template>'] [--once]";
21862
+ var USAGE = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route decisions [<key>] [--flagged] | sleds route decision <key> <id> --seen | --answer --stdin [--route accept|keep|redraft] | --keep-route | --apply [--answer --stdin] | --overrule --stdin | --ready | sleds route run <key> [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--interval <s>] [--lease <min>] [--idle <min>] [--repo <path>] [--repos-dir <dir>] [--mode <permission-mode>] [--model <id>] [--branch '<template>'] [--pr-title '<template>'] [--once]";
21385
21863
  var ANSWER_USAGE = "usage: sleds route answer <key>/<node> <answer…> | sleds route answer <key>/<node> --stdin [--json]";
21386
21864
  var PROPOSE_USAGE = "usage: sleds route propose <key> (--file <nodes.json> | --stdin) [--name <engine>] [--json]";
21387
21865
  var APPROVE_USAGE = "usage: sleds route approve <key> [--file <nodes.json>] [--json]";
21388
21866
  var REDRAFT_USAGE = "usage: sleds route redraft <key> [--json]";
21389
21867
  var SINGLE_USAGE = "usage: sleds route single <key> [--json]";
21390
- var DECISION_USAGE = "usage: sleds route decision <key> <id> --seen | sleds route decision <key> <id> (--answer | --overrule) (<answer…> | --stdin) | sleds route decision <key> <id> [--answer (<answer…> | --stdin)] --route accept|keep|redraft | sleds route decision <key> <id> --keep-route | sleds route decision <key> <id> --apply [--answer (<answer…> | --stdin)] [--json]";
21868
+ var DECISION_USAGE = "usage: sleds route decision <key> <id> --seen | sleds route decision <key> <id> (--answer | --overrule) (<answer…> | --stdin) | sleds route decision <key> <id> [--answer (<answer…> | --stdin)] --route accept|keep|redraft | sleds route decision <key> <id> --keep-route | sleds route decision <key> <id> --apply [--answer (<answer…> | --stdin)] | sleds route decision <key> <id> --ready [--json]";
21391
21869
  var ROUTE_VERDICTS = ["accept", "keep", "redraft"];
21392
21870
  function nodesFromJson(text) {
21393
21871
  let parsed;
@@ -21551,7 +22029,7 @@ async function answerCmd(o, argv) {
21551
22029
  const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
21552
22030
  const ref = words[0] ?? "";
21553
22031
  const slash = ref.lastIndexOf("/");
21554
- const answer = (fromStdin ? (o.stdin ?? (() => readFileSync24(0, "utf8")))() : words.slice(1).join(" ")).trim();
22032
+ const answer = (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : words.slice(1).join(" ")).trim();
21555
22033
  if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
21556
22034
  error(ANSWER_USAGE);
21557
22035
  return 2;
@@ -21716,6 +22194,8 @@ function appliedLine(key, applied) {
21716
22194
  return a.changed === false ? `${key}: ${what} was already set — answered` : `${key}: set ${what} — answered`;
21717
22195
  }
21718
22196
  async function decisionCmd(o, argv) {
22197
+ if (argv.includes("--ready"))
22198
+ return readyCmd(o, argv);
21719
22199
  const log = o.log ?? console.log;
21720
22200
  const error = o.error ?? console.error;
21721
22201
  const json = argv.includes("--json");
@@ -21746,7 +22226,7 @@ async function decisionCmd(o, argv) {
21746
22226
  error(DECISION_USAGE);
21747
22227
  return 2;
21748
22228
  }
21749
- const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync24(0, "utf8")))() : rest.join(" ")).trim();
22229
+ const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : rest.join(" ")).trim();
21750
22230
  if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
21751
22231
  error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
21752
22232
  return 2;
@@ -21769,6 +22249,68 @@ async function decisionCmd(o, argv) {
21769
22249
  }
21770
22250
  return said(o, key, json, r, () => log(seen ? `${key}: marked seen` : overrule ? `${key}: overruled — your answer stands in the log` : apply ? appliedLine(key, r.json?.applied) : answeredLine(key, r.json?.route ?? route, r.json?.held)));
21771
22251
  }
22252
+ var LOOK_READY_ANSWER = "Looks good — marked ready for review.";
22253
+ function lookToReady(route, id) {
22254
+ const rows = Array.isArray(route?.decisions) ? route.decisions : [];
22255
+ const d = rows.find((r) => r.id === id);
22256
+ if (!d)
22257
+ return { error: `no decision ${id} on this item` };
22258
+ if (d.kind !== "look")
22259
+ return { error: `decision ${id} is not a look — --ready marks a look` };
22260
+ if (d.state !== "escalated")
22261
+ return { error: `decision ${id} is ${String(d.state)} — not a look you owe` };
22262
+ const about = d.about && typeof d.about === "object" ? d.about : {};
22263
+ const pr = Array.isArray(about.prs) ? about.prs.find((p) => typeof p === "string" && p) : undefined;
22264
+ return typeof pr === "string" ? { pr } : { error: `decision ${id} names no PR` };
22265
+ }
22266
+ async function readyCmd(o, argv) {
22267
+ const log = o.log ?? console.log;
22268
+ const error = o.error ?? console.error;
22269
+ const json = argv.includes("--json");
22270
+ const words = argv.filter((a) => a !== "--ready" && a !== "--json");
22271
+ const [key, id] = words;
22272
+ if (!key || !id || words.length !== 2 || words.some((w) => w.startsWith("--"))) {
22273
+ error(DECISION_USAGE);
22274
+ return 2;
22275
+ }
22276
+ const client = engineClient(o);
22277
+ if ("error" in client) {
22278
+ error(client.error);
22279
+ return 1;
22280
+ }
22281
+ const reader = routeReader(o);
22282
+ if ("error" in reader) {
22283
+ error(reader.error);
22284
+ return 1;
22285
+ }
22286
+ const route = await reader.read(key).catch((e) => {
22287
+ error(e instanceof Error ? e.message : String(e));
22288
+ return;
22289
+ });
22290
+ if (route === undefined)
22291
+ return 1;
22292
+ const look = lookToReady(route, id);
22293
+ if ("error" in look) {
22294
+ error(`${key}: ${look.error}`);
22295
+ return 1;
22296
+ }
22297
+ const gh = (o.exec ?? defaultExec)("gh", ["pr", "ready", look.pr]);
22298
+ if (gh.status !== 0) {
22299
+ error(gh.stderr.trim() || `gh pr ready ${look.pr} exited ${gh.status}`);
22300
+ error(`${key}: the look is not answered`);
22301
+ return 1;
22302
+ }
22303
+ const r = await client.postFull("/proxy/route/decision", {
22304
+ project: key,
22305
+ id,
22306
+ answer: LOOK_READY_ANSWER
22307
+ });
22308
+ if (!r) {
22309
+ error(`${key}: ${look.pr} is ready for review, but SLEDS is unreachable — the look is not answered`);
22310
+ return 1;
22311
+ }
22312
+ return said(o, key, json, r, () => log(`${key}: ${look.pr} marked ready for review — the look is answered`));
22313
+ }
21772
22314
  var DECISIONS_USAGE = "usage: sleds route decisions [<key>] [--flagged] [--json]";
21773
22315
  function decisionsLines(raw) {
21774
22316
  const j = clean(raw);
@@ -21825,7 +22367,7 @@ function approvedLine(key, j) {
21825
22367
  function readNodes(o, a) {
21826
22368
  let text;
21827
22369
  try {
21828
- text = a.file ? (o.readFile ?? ((p) => readFileSync24(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync24(0, "utf8")))();
22370
+ text = a.file ? (o.readFile ?? ((p) => readFileSync26(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync26(0, "utf8")))();
21829
22371
  } catch (e) {
21830
22372
  return { error: e instanceof Error ? e.message : String(e) };
21831
22373
  }
@@ -22099,11 +22641,11 @@ var run = async (o) => {
22099
22641
  init_config();
22100
22642
  init_meter();
22101
22643
  var HOOK_STDIN_TIMEOUT_MS2 = 1000;
22102
- var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22103
- var notificationType = (text) => {
22644
+ var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22645
+ var notificationType2 = (text) => {
22104
22646
  try {
22105
22647
  const raw = JSON.parse(text);
22106
- if (!isObject11(raw))
22648
+ if (!isObject14(raw))
22107
22649
  return;
22108
22650
  return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
22109
22651
  } catch {
@@ -22114,7 +22656,7 @@ var seatEvent = async (o) => {
22114
22656
  const log = o.log ?? console.log;
22115
22657
  const now = (o.now ?? Date.now)();
22116
22658
  const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS2);
22117
- const kind = notificationType(input);
22659
+ const kind = notificationType2(input);
22118
22660
  try {
22119
22661
  const key = seatKeyFor(o.home, process.env);
22120
22662
  const state = readSeatState(o.home, key);
@@ -22136,12 +22678,12 @@ var seatEvent = async (o) => {
22136
22678
 
22137
22679
  // src/commands/seatSync.ts
22138
22680
  import { execFileSync as execFileSync4 } from "node:child_process";
22139
- import { existsSync as existsSync25, mkdirSync as mkdirSync20, readFileSync as readFileSync25, readdirSync as readdirSync5, rmSync as rmSync14, writeFileSync as writeFileSync23 } from "node:fs";
22681
+ import { existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync27, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
22140
22682
  import { platform as platform2 } from "node:os";
22141
- import { dirname as dirname16, join as join25 } from "node:path";
22683
+ import { dirname as dirname17, join as join25 } from "node:path";
22142
22684
 
22143
22685
  // src/cookieDb.ts
22144
- import { copyFileSync, rmSync as rmSync13 } from "node:fs";
22686
+ import { copyFileSync, rmSync as rmSync15 } from "node:fs";
22145
22687
  import { tmpdir as tmpdir2 } from "node:os";
22146
22688
  import { join as join24 } from "node:path";
22147
22689
  var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
@@ -22179,7 +22721,7 @@ async function readCookieRows(dbPath) {
22179
22721
  return rows.map((r) => ({ name: r.name, encrypted_value: Buffer.from(r.ev, "hex") }));
22180
22722
  } finally {
22181
22723
  try {
22182
- rmSync13(copy, { force: true });
22724
+ rmSync15(copy, { force: true });
22183
22725
  } catch {}
22184
22726
  }
22185
22727
  }
@@ -22264,8 +22806,8 @@ function resolveProfileDir(localStateJson, profile) {
22264
22806
  if (localStateJson) {
22265
22807
  try {
22266
22808
  const raw = JSON.parse(localStateJson);
22267
- const p = isObject12(raw) && isObject12(raw.profile) ? raw.profile : null;
22268
- if (p && isObject12(p.info_cache))
22809
+ const p = isObject15(raw) && isObject15(raw.profile) ? raw.profile : null;
22810
+ if (p && isObject15(p.info_cache))
22269
22811
  cache = p.info_cache;
22270
22812
  } catch {}
22271
22813
  }
@@ -22274,7 +22816,7 @@ function resolveProfileDir(localStateJson, profile) {
22274
22816
  const want = profile.toLowerCase();
22275
22817
  const hits = [];
22276
22818
  for (const [dir, info] of Object.entries(cache)) {
22277
- if (!isObject12(info))
22819
+ if (!isObject15(info))
22278
22820
  continue;
22279
22821
  const match = ["name", "user_name", "gaia_name"].some((k) => {
22280
22822
  const v = info[k];
@@ -22313,15 +22855,15 @@ function sourceArgs(source) {
22313
22855
  function seatTokenPath(home, source) {
22314
22856
  return `${home}/.config/sleds/${launchLabel(source).slice("ai.sleds.".length)}.token`;
22315
22857
  }
22316
- var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22858
+ var isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22317
22859
  var num7 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
22318
22860
  var round22 = (n) => Math.round(n * 100) / 100;
22319
22861
  function parseUsageResponse(raw, now = Date.now()) {
22320
- if (!isObject12(raw))
22862
+ if (!isObject15(raw))
22321
22863
  return null;
22322
22864
  const out = { capturedAt: now, models: {} };
22323
22865
  for (const [k, v] of Object.entries(raw)) {
22324
- if (!isObject12(v))
22866
+ if (!isObject15(v))
22325
22867
  continue;
22326
22868
  if (k === "extra_usage") {
22327
22869
  const credits = num7(v.used_credits);
@@ -22486,7 +23028,7 @@ function findCookieDb(home, source) {
22486
23028
  return db;
22487
23029
  }
22488
23030
  const localStatePath = join25(root, "Local State");
22489
- const localState = existsSync25(localStatePath) ? readFileSync25(localStatePath, "utf8") : null;
23031
+ const localState = existsSync25(localStatePath) ? readFileSync27(localStatePath, "utf8") : null;
22490
23032
  const wanted = source.profile ?? "Default";
22491
23033
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
22492
23034
  if (ambiguous)
@@ -22569,16 +23111,16 @@ function install(home, argv, source, log) {
22569
23111
  return 1;
22570
23112
  }
22571
23113
  const dir = join25(home, "Library", "LaunchAgents");
22572
- mkdirSync20(dir, { recursive: true });
23114
+ mkdirSync22(dir, { recursive: true });
22573
23115
  const path = plistPath2(home, source);
22574
23116
  const tokenPath = seatTokenPath(home, source);
22575
- rmSync14(tokenPath, { force: true });
23117
+ rmSync16(tokenPath, { force: true });
22576
23118
  if (source.token) {
22577
- mkdirSync20(dirname16(tokenPath), { recursive: true, mode: 448 });
22578
- writeFileSync23(tokenPath, `${source.token}
23119
+ mkdirSync22(dirname17(tokenPath), { recursive: true, mode: 448 });
23120
+ writeFileSync25(tokenPath, `${source.token}
22579
23121
  `, { mode: 384 });
22580
23122
  }
22581
- writeFileSync23(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
23123
+ writeFileSync25(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
22582
23124
  try {
22583
23125
  execFileSync4("launchctl", ["unload", path], { stdio: "ignore" });
22584
23126
  } catch {}
@@ -22591,7 +23133,7 @@ function uninstall(home, source, log) {
22591
23133
  try {
22592
23134
  execFileSync4("launchctl", ["unload", "-w", path], { stdio: "ignore" });
22593
23135
  } catch {}
22594
- rmSync14(seatTokenPath(home, source), { force: true });
23136
+ rmSync16(seatTokenPath(home, source), { force: true });
22595
23137
  log(existsSync25(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
22596
23138
  return 0;
22597
23139
  }
@@ -22666,7 +23208,7 @@ var seatSync = async (o) => {
22666
23208
  };
22667
23209
  function readSeatToken(home, source) {
22668
23210
  try {
22669
- return readFileSync25(seatTokenPath(home, source), "utf8").trim() || undefined;
23211
+ return readFileSync27(seatTokenPath(home, source), "utf8").trim() || undefined;
22670
23212
  } catch {
22671
23213
  return;
22672
23214
  }
@@ -22749,7 +23291,7 @@ var sessionEvent = async (o) => {
22749
23291
 
22750
23292
  // src/commands/specEvent.ts
22751
23293
  init_meter();
22752
- import { readFileSync as readFileSync26, statSync as statSync2 } from "node:fs";
23294
+ import { readFileSync as readFileSync28, statSync as statSync2 } from "node:fs";
22753
23295
  import { basename as basename6, isAbsolute as isAbsolute4, relative } from "node:path";
22754
23296
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
22755
23297
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -22805,7 +23347,7 @@ function readFileOrNull(path) {
22805
23347
  try {
22806
23348
  if (statSync2(path).size > 2000000)
22807
23349
  return null;
22808
- return readFileSync26(path, "utf8");
23350
+ return readFileSync28(path, "utf8");
22809
23351
  } catch {
22810
23352
  return null;
22811
23353
  }
@@ -23134,10 +23676,10 @@ var announce = (o, events) => {
23134
23676
 
23135
23677
  // src/commands/stopEvent.ts
23136
23678
  init_meter();
23137
- import { existsSync as existsSync27, readFileSync as readFileSync27 } from "node:fs";
23679
+ import { existsSync as existsSync27, readFileSync as readFileSync29 } from "node:fs";
23138
23680
 
23139
23681
  // src/transcript.ts
23140
- var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23682
+ var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23141
23683
  var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
23142
23684
  function sumTranscript(text) {
23143
23685
  const seen = new Set;
@@ -23158,7 +23700,7 @@ function sumTranscript(text) {
23158
23700
  } catch {
23159
23701
  continue;
23160
23702
  }
23161
- if (!isObject13(j) || j.type !== "assistant" || !isObject13(j.message))
23703
+ if (!isObject16(j) || j.type !== "assistant" || !isObject16(j.message))
23162
23704
  continue;
23163
23705
  const m = j.message;
23164
23706
  const id = typeof m.id === "string" ? m.id : typeof j.uuid === "string" ? j.uuid : undefined;
@@ -23167,7 +23709,7 @@ function sumTranscript(text) {
23167
23709
  continue;
23168
23710
  seen.add(id);
23169
23711
  }
23170
- const u = isObject13(m.usage) ? m.usage : undefined;
23712
+ const u = isObject16(m.usage) ? m.usage : undefined;
23171
23713
  if (!u)
23172
23714
  continue;
23173
23715
  const input = n(u.input_tokens);
@@ -23230,7 +23772,7 @@ async function postTranscript(o, payload, now) {
23230
23772
  const src = meterSource(o.home);
23231
23773
  if (!src)
23232
23774
  return false;
23233
- const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync27(p, "utf8") : null);
23775
+ const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync29(p, "utf8") : null);
23234
23776
  let text;
23235
23777
  try {
23236
23778
  text = read(path);
@@ -23308,7 +23850,7 @@ var stopEvent = async (o) => {
23308
23850
 
23309
23851
  // src/commands/uninstall.ts
23310
23852
  import { spawnSync as spawnSync9 } from "node:child_process";
23311
- import { existsSync as existsSync28, rmSync as rmSync15 } from "node:fs";
23853
+ import { existsSync as existsSync28, rmSync as rmSync17 } from "node:fs";
23312
23854
  init_config();
23313
23855
  init_meter();
23314
23856
  init_settings();
@@ -23369,19 +23911,19 @@ var uninstall2 = (o) => {
23369
23911
  }
23370
23912
  for (const file of [meterCachePath(o.home), ...allSeatStatePaths(o.home)]) {
23371
23913
  if (existsSync28(file)) {
23372
- rmSync15(file, { force: true });
23914
+ rmSync17(file, { force: true });
23373
23915
  removed.push(file);
23374
23916
  }
23375
23917
  }
23376
23918
  const skill = skillDir(o.home);
23377
23919
  if (existsSync28(skill)) {
23378
- rmSync15(skill, { recursive: true, force: true });
23920
+ rmSync17(skill, { recursive: true, force: true });
23379
23921
  removed.push(skill);
23380
23922
  }
23381
23923
  const plist = musherPlistPath(o.home);
23382
23924
  if (existsSync28(plist)) {
23383
23925
  (o.unloadAgent ?? defaultUnload)(plist);
23384
- rmSync15(plist, { force: true });
23926
+ rmSync17(plist, { force: true });
23385
23927
  removed.push(`launchd agent ${musherLabel()}`);
23386
23928
  }
23387
23929
  if (removed.length === 0) {
@@ -23534,17 +24076,17 @@ var webhooksCmd = async (o) => {
23534
24076
 
23535
24077
  // src/config.ts
23536
24078
  init_settings();
23537
- import { chmodSync as chmodSync5, existsSync as existsSync29, mkdirSync as mkdirSync21, readFileSync as readFileSync28, renameSync as renameSync7, writeFileSync as writeFileSync24 } from "node:fs";
24079
+ import { chmodSync as chmodSync7, existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync30, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
23538
24080
  import { join as join26, resolve as resolve10 } from "node:path";
23539
24081
  var configDir2 = (home, env = process.env) => {
23540
24082
  const o = env.SLEDS_HOME?.trim();
23541
24083
  return o ? resolve10(home, o) : join26(home, ".config", "sleds");
23542
24084
  };
23543
24085
  var configPath2 = (home) => join26(configDir2(home), "config.json");
23544
- var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
24086
+ var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23545
24087
  var optString2 = (v) => typeof v === "string" ? v : undefined;
23546
24088
  var parsePriorEnv2 = (v) => {
23547
- if (!isObject14(v))
24089
+ if (!isObject17(v))
23548
24090
  return;
23549
24091
  const out = {};
23550
24092
  const url = optString2(v[BASE_URL_KEY]);
@@ -23559,7 +24101,7 @@ var parsePriorEnv2 = (v) => {
23559
24101
  };
23560
24102
  var parseSchedule2 = (v) => v === "launchd" || v === "systemd" || v === "manual" ? v : undefined;
23561
24103
  var parseConfig2 = (raw) => {
23562
- if (!isObject14(raw))
24104
+ if (!isObject17(raw))
23563
24105
  return {};
23564
24106
  const cfg = {};
23565
24107
  const token = optString2(raw.token);
@@ -23609,11 +24151,11 @@ var parseConfig2 = (raw) => {
23609
24151
  return cfg;
23610
24152
  };
23611
24153
  var parseOrgs2 = (raw) => {
23612
- if (!isObject14(raw))
24154
+ if (!isObject17(raw))
23613
24155
  return;
23614
24156
  const out = {};
23615
24157
  for (const [slug, v] of Object.entries(raw)) {
23616
- if (!isObject14(v))
24158
+ if (!isObject17(v))
23617
24159
  continue;
23618
24160
  const token = optString2(v.token);
23619
24161
  if (!token)
@@ -23632,7 +24174,7 @@ var parseOrgs2 = (raw) => {
23632
24174
  return Object.keys(out).length > 0 ? out : undefined;
23633
24175
  };
23634
24176
  var parseMusher2 = (raw) => {
23635
- if (!isObject14(raw))
24177
+ if (!isObject17(raw))
23636
24178
  return;
23637
24179
  const out = {};
23638
24180
  const pm = optString2(raw.permissionMode);
@@ -23648,7 +24190,7 @@ var parseMusher2 = (raw) => {
23648
24190
  return out;
23649
24191
  };
23650
24192
  var parseOrgInfo2 = (raw) => {
23651
- if (!isObject14(raw) || !isObject14(raw.capabilities))
24193
+ if (!isObject17(raw) || !isObject17(raw.capabilities))
23652
24194
  return;
23653
24195
  const name = optString2(raw.name);
23654
24196
  const slug = optString2(raw.slug);
@@ -23674,7 +24216,7 @@ var readConfig2 = (home) => {
23674
24216
  const p = configPath2(home);
23675
24217
  if (!existsSync29(p))
23676
24218
  return {};
23677
- return parseConfig2(JSON.parse(readFileSync28(p, "utf8")));
24219
+ return parseConfig2(JSON.parse(readFileSync30(p, "utf8")));
23678
24220
  };
23679
24221
 
23680
24222
  // src/runners/exec.ts
@@ -23726,7 +24268,7 @@ var defaultExec5 = (cmd, args) => {
23726
24268
  };
23727
24269
 
23728
24270
  // src/settings.ts
23729
- import { dirname as dirname17, join as join27, resolve as resolve11 } from "node:path";
24271
+ import { dirname as dirname18, join as join27, resolve as resolve11 } from "node:path";
23730
24272
  var withSeatEnv2 = (command, home, env = process.env, org) => {
23731
24273
  const parts = [];
23732
24274
  const sh = env.SLEDS_HOME?.trim();
@@ -23749,15 +24291,16 @@ var pinOrg4 = (command, org) => {
23749
24291
  return `${lead}SLEDS_ORG=${shellWord2(slug)} ${bare.slice(lead.length)}`;
23750
24292
  };
23751
24293
  var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
23752
- var isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
24294
+ var isObject18 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23753
24295
  var isSledsHookFor2 = (sub) => (entry) => {
23754
- if (!isObject15(entry) || !Array.isArray(entry.hooks))
24296
+ if (!isObject18(entry) || !Array.isArray(entry.hooks))
23755
24297
  return false;
23756
24298
  const re = new RegExp(`\\b${sub}\\b`);
23757
- return entry.hooks.some((h) => isObject15(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
24299
+ return entry.hooks.some((h) => isObject18(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
23758
24300
  };
23759
24301
  var isSledsSessionHook2 = isSledsHookFor2("session-event");
23760
24302
  var isSledsStopHook2 = isSledsHookFor2("stop-event");
24303
+ var isSledsPermissionHook2 = isSledsHookFor2("permission-event");
23761
24304
 
23762
24305
  // src/index.ts
23763
24306
  var USAGE3 = `usage: sleds <command> [options]
@@ -23907,6 +24450,8 @@ commands:
23907
24450
  statusline Print the one-line meter Claude Code shows (installed by init; 10s cache).
23908
24451
  hooks Register the Claude Code Notification hook for seat limits (init does this too).
23909
24452
  seat-event Claude Code hook: record a seat wall and say how to continue. Reads hook JSON on stdin.
24453
+ permission-event --mark <file> Claude Code Notification hook the engine gives its sessions: on
24454
+ a permission dialog, write {message, at} to <file> for the engine. Local; exits 0.
23910
24455
  session-event Claude Code SessionStart hook: states the loop and the rows this machine still
23911
24456
  owes before the first turn. Reads hook JSON on stdin.
23912
24457
  stop-event Claude Code Stop hook: a session that estimated a row and posted no actual is asked
@@ -24051,7 +24596,8 @@ var main = async (argv) => {
24051
24596
  exec: defaultExec4,
24052
24597
  runnerFor: runnerFor3,
24053
24598
  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
24054
- shouldStop: () => stopping
24599
+ shouldStop: () => stopping,
24600
+ sledsArgv: sledsArgv()
24055
24601
  };
24056
24602
  return cmd === "engine" ? engineCmd(io) : routeCmd(io);
24057
24603
  }
@@ -24158,6 +24704,8 @@ var main = async (argv) => {
24158
24704
  });
24159
24705
  case "seat-event":
24160
24706
  return seatEvent({ home });
24707
+ case "permission-event":
24708
+ return permissionEvent({ mark: stringFlag(flags, "mark") });
24161
24709
  case "spec-event":
24162
24710
  return specEvent({ home });
24163
24711
  case "prompt-event":
@@ -24215,7 +24763,7 @@ ${renderHelpText(argv[1])}`);
24215
24763
  case "version":
24216
24764
  case "--version":
24217
24765
  case "-v":
24218
- console.log("sleds 0.1.37");
24766
+ console.log("sleds 0.1.39");
24219
24767
  return 0;
24220
24768
  default:
24221
24769
  console.error(`unknown command: ${cmd}