@sleds/cli 0.1.38 → 0.1.40

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 +591 -197
  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" ? {
@@ -8210,6 +8238,7 @@ init_intake();
8210
8238
  init_template();
8211
8239
  init_statusMap();
8212
8240
  init_confluence();
8241
+ init_fieldMap();
8213
8242
  init_whatsNew();
8214
8243
  init_cheatSheet();
8215
8244
  init_pack2();
@@ -9385,10 +9414,10 @@ import {
9385
9414
  existsSync as existsSync16,
9386
9415
  lstatSync,
9387
9416
  mkdtempSync,
9388
- readFileSync as readFileSync15,
9417
+ readFileSync as readFileSync16,
9389
9418
  readlinkSync,
9390
- rmSync as rmSync7,
9391
- writeFileSync as writeFileSync16
9419
+ rmSync as rmSync8,
9420
+ writeFileSync as writeFileSync17
9392
9421
  } from "node:fs";
9393
9422
  import { tmpdir } from "node:os";
9394
9423
  import { join as join19, resolve as resolve8 } from "node:path";
@@ -9880,6 +9909,14 @@ var withCodexSandbox = (agent, gitCommonDir) => {
9880
9909
  return line;
9881
9910
  return `${line} --sandbox workspace-write --add-dir ${shq(gitCommonDir)} -c sandbox_workspace_write.network_access=true`;
9882
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)}` : "";
9883
9920
  var shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
9884
9921
 
9885
9922
  // src/runners/exec.ts
@@ -9904,21 +9941,23 @@ var agentShellCommand = (o) => {
9904
9941
  const prompt = `"$(cat ${shq2(o.promptFile)})"`;
9905
9942
  const agent = o.agent?.trim();
9906
9943
  if (agent)
9907
- return `${sandboxedAgent(agent, o.sandbox)} ${prompt}`;
9944
+ return `${sandboxedAgent(agent, o.sandbox, o.mainCheckout)} ${prompt}`;
9908
9945
  const model = o.model ? ` --model ${shq2(o.model)}` : "";
9909
9946
  const effort = o.effort ? ` --effort ${shq2(o.effort)}` : "";
9910
- 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}`;
9911
9948
  };
9912
- var sandboxedAgent = (agent, sandbox) => {
9913
- if (!sandbox)
9914
- return agent;
9949
+ var sandboxedAgent = (agent, sandbox, mainCheckout) => {
9915
9950
  const kind = sandboxAgentKind(agent);
9951
+ const line = kind === "claude" ? `${agent}${claudeAddDirFlag(mainCheckout)}` : agent;
9952
+ if (!sandbox)
9953
+ return line;
9916
9954
  if (kind === "claude")
9917
- return `env -u SLEDS_TOKEN ${agent} --settings ${shq2(sandbox.settingsFile)}`;
9955
+ return `env -u SLEDS_TOKEN ${line} --settings ${shq2(sandbox.settingsFile)}`;
9918
9956
  if (kind === "codex")
9919
9957
  return `env -u SLEDS_TOKEN ${withCodexSandbox(agent, sandbox.gitCommonDir)}`;
9920
9958
  return agent;
9921
9959
  };
9960
+ var hookSettingsFlag = (o) => o.settingsFile && !o.sandbox ? ` --settings ${shq2(o.settingsFile)}` : "";
9922
9961
  var mcpConfigFlag = (o) => o.mcpConfig ? ` --mcp-config ${shq2(o.mcpConfig)}${o.strictMcp ? " --strict-mcp-config" : ""}` : "";
9923
9962
  var defaultSpawnDetached = (cmd, args, o) => {
9924
9963
  const child = spawn(cmd, [...args], { cwd: o.cwd, detached: true, stdio: "ignore" });
@@ -9971,6 +10010,15 @@ var tailText = (s, bytes = 4096) => {
9971
10010
  return nl >= 0 && nl < cut.length - 1 ? cut.slice(nl + 1) : cut;
9972
10011
  };
9973
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
+
9974
10022
  // src/runners/cmux.ts
9975
10023
  var CMUX_APP_BIN = "/Applications/cmux.app/Contents/Resources/bin/cmux";
9976
10024
  var resolveCmuxBin = (exists = existsSync11, path = process.env.PATH ?? "") => {
@@ -10112,6 +10160,10 @@ var cmuxProbeReason = (r) => {
10112
10160
  };
10113
10161
  var cmuxAuthRefused = (r) => r.status !== 0 && /\b(auth_required|unauthorized|authentication required|invalid password)\b/i.test(`${r.stderr}
10114
10162
  ${r.stdout}`);
10163
+ var cmuxNeedsForce = (r) => r.status !== 0 && /\bconfirmation_required\b|--force\b/i.test(`${r.stderr}
10164
+ ${r.stdout}`);
10165
+ var CMUX_LIST_TRIES = 20;
10166
+ var CMUX_LIST_WAIT_MS = 250;
10115
10167
  var CMUX_SETTLE_MS = 300;
10116
10168
  var CMUX_ENTER_RETRIES = 2;
10117
10169
  var PROMPT_LINE = /^[\s│|]*[>❯›]\s?(.*)$/;
@@ -10165,6 +10217,12 @@ function cmuxRunner(exec, deps = {}) {
10165
10217
  return pw ? run(args) : r;
10166
10218
  };
10167
10219
  const failed = cmuxFailure;
10220
+ const closeWorkspace = (id) => {
10221
+ const c = cmux(["close-workspace", "--workspace", id]);
10222
+ if (!cmuxNeedsForce(c))
10223
+ return c.status === 0;
10224
+ return cmux(["close-workspace", "--workspace", id, "--force"]).status === 0;
10225
+ };
10168
10226
  return {
10169
10227
  name: "cmux",
10170
10228
  makesWorktree: false,
@@ -10204,19 +10262,29 @@ function cmuxRunner(exec, deps = {}) {
10204
10262
  const why = failed(r);
10205
10263
  const l = cmux(["--id-format", "both", "list-workspaces"]);
10206
10264
  const stray = l.status === 0 ? cmuxIdForName(l.stdout, o.name) : null;
10207
- const left = l.status !== 0 || (stray ? cmux(["close-workspace", "--workspace", stray]).status !== 0 : namedLines(l.stdout, o.name).length > 0);
10265
+ const left = l.status !== 0 || (stray ? !closeWorkspace(stray) : namedLines(l.stdout, o.name).length > 0);
10208
10266
  throw new Error(`cmux workspace create ${o.name} failed: ${why}${left ? `; ${UNWATCHED}` : ""}`);
10209
10267
  }
10210
10268
  let id = parseCmuxWorkspace(r.stdout);
10211
10269
  const ref = id ? null : parseCmuxRef(r.stdout);
10212
10270
  let closed = false;
10213
10271
  if (ref) {
10214
- const l = cmux(["--id-format", "both", "list-workspaces"]);
10215
- if (l.status === 0) {
10216
- id = cmuxIdForRef(l.stdout, ref, o.name);
10217
- const stray = id ? null : cmuxIdForName(l.stdout, o.name);
10272
+ let listed = null;
10273
+ for (let i = 0;i < CMUX_LIST_TRIES; i++) {
10274
+ if (i > 0)
10275
+ sleep(CMUX_LIST_WAIT_MS);
10276
+ const l = cmux(["--id-format", "both", "list-workspaces"]);
10277
+ if (l.status !== 0)
10278
+ continue;
10279
+ listed = l.stdout;
10280
+ if (namedLines(l.stdout, o.name).length > 0)
10281
+ break;
10282
+ }
10283
+ if (listed !== null) {
10284
+ id = cmuxIdForRef(listed, ref, o.name);
10285
+ const stray = id ? null : cmuxIdForName(listed, o.name);
10218
10286
  if (stray)
10219
- closed = cmux(["close-workspace", "--workspace", stray]).status === 0;
10287
+ closed = closeWorkspace(stray);
10220
10288
  }
10221
10289
  }
10222
10290
  if (!id) {
@@ -10277,6 +10345,13 @@ function cmuxRunner(exec, deps = {}) {
10277
10345
  return;
10278
10346
  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}×)` : ""}`;
10279
10347
  },
10348
+ sendKey(h, key) {
10349
+ if (!isRunnerKey(key))
10350
+ throw new Error(`cmux send-key ${h.id}: no key ${JSON.stringify(key)}`);
10351
+ const r = cmux(["send-key", "--workspace", h.id, key === "esc" ? "escape" : key]);
10352
+ if (r.status !== 0)
10353
+ throw new Error(`cmux send-key ${h.id} failed: ${failed(r)}`);
10354
+ },
10280
10355
  stop(h) {
10281
10356
  cmux(["close-workspace", "--workspace", h.id]);
10282
10357
  if (!h.group)
@@ -10327,12 +10402,12 @@ init_server();
10327
10402
  init_config();
10328
10403
  import {
10329
10404
  appendFileSync as appendFileSync2,
10330
- chmodSync as chmodSync3,
10331
- mkdirSync as mkdirSync11,
10332
- readFileSync as readFileSync12,
10405
+ chmodSync as chmodSync4,
10406
+ mkdirSync as mkdirSync12,
10407
+ readFileSync as readFileSync13,
10333
10408
  existsSync as realExists,
10334
- rmSync as rmSync3,
10335
- writeFileSync as writeFileSync12
10409
+ rmSync as rmSync4,
10410
+ writeFileSync as writeFileSync13
10336
10411
  } from "node:fs";
10337
10412
  import { hostname as hostname2 } from "node:os";
10338
10413
  import { join as join15, resolve as resolve7 } from "node:path";
@@ -10347,7 +10422,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
10347
10422
  name: flags.name,
10348
10423
  os: host.os ?? process.platform,
10349
10424
  runner: flags.runner,
10350
- version: host.version ?? "0.1.38" ?? "dev",
10425
+ version: host.version ?? "0.1.40" ?? "dev",
10351
10426
  running: live.length,
10352
10427
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
10353
10428
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -11092,13 +11167,40 @@ function jiraFromEnv(epic, env = process.env) {
11092
11167
  // src/engine/loop.ts
11093
11168
  init_src();
11094
11169
 
11095
- // src/runners/types.ts
11096
- var watchTarget = (h) => ({
11097
- runner: h.runner,
11098
- handle: h.id,
11099
- ...h.log ? { log: h.log } : {},
11100
- ...h.group ? { group: h.group } : {}
11101
- });
11170
+ // src/commands/permissionEvent.ts
11171
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync3, writeFileSync as writeFileSync11 } from "node:fs";
11172
+
11173
+ // src/commands/seatEvent.ts
11174
+ init_config();
11175
+ init_meter();
11176
+ var HOOK_STDIN_TIMEOUT_MS = 1000;
11177
+ var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11178
+ var notificationType = (text) => {
11179
+ try {
11180
+ const raw = JSON.parse(text);
11181
+ if (!isObject11(raw))
11182
+ return;
11183
+ return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
11184
+ } catch {
11185
+ return;
11186
+ }
11187
+ };
11188
+
11189
+ // src/commands/permissionEvent.ts
11190
+ var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11191
+ var readPermissionMark = (file) => {
11192
+ try {
11193
+ const raw = JSON.parse(readFileSync10(file, "utf8"));
11194
+ if (!isObject12(raw) || typeof raw.at !== "number")
11195
+ return null;
11196
+ return { message: typeof raw.message === "string" ? raw.message : "", at: raw.at };
11197
+ } catch {
11198
+ return null;
11199
+ }
11200
+ };
11201
+ var clearPermissionMark = (file) => {
11202
+ rmSync3(file, { force: true });
11203
+ };
11102
11204
 
11103
11205
  // src/engine/answerer.ts
11104
11206
  init_src();
@@ -11250,6 +11352,10 @@ function looksOf(s) {
11250
11352
  const l = s;
11251
11353
  return l && typeof l.openLook === "function" && typeof l.clearLook === "function" ? s : null;
11252
11354
  }
11355
+ function permissionsOf(s) {
11356
+ const p = s;
11357
+ return p && typeof p.openPermission === "function" && typeof p.clearPermission === "function" && typeof p.decisionOf === "function" ? s : null;
11358
+ }
11253
11359
  var FAIL_LOG_MAX = 2000;
11254
11360
  var UNREACHABLE2 = "SLEDS unreachable";
11255
11361
  function keysIn(states, state) {
@@ -11808,6 +11914,27 @@ function sledsSource(client, o) {
11808
11914
  throw new Error(serverMessage(r));
11809
11915
  return r.json.cleared === true;
11810
11916
  },
11917
+ async openPermission(runId, p, engine) {
11918
+ const r = await client.postFull("/proxy/run", {
11919
+ runId,
11920
+ engine,
11921
+ event: { kind: "permission", md: p.md, about: { options: p.options } }
11922
+ });
11923
+ if (!r)
11924
+ throw new Error(`${UNREACHABLE2} — the permission not raised`);
11925
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11926
+ throw new Error(serverMessage(r));
11927
+ const decisionId = str6(r.json.decisionId);
11928
+ return { opened: r.json.opened === true, ...decisionId ? { decisionId } : {} };
11929
+ },
11930
+ async clearPermission(runId, why, engine) {
11931
+ const r = await client.postFull("/proxy/run", { runId, engine, clearPermission: { why } });
11932
+ if (!r)
11933
+ throw new Error(`${UNREACHABLE2} — the permission not cleared`);
11934
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11935
+ throw new Error(serverMessage(r));
11936
+ return r.json.cleared === true;
11937
+ },
11811
11938
  async decisionOf(item, decisionId) {
11812
11939
  const route = await routeOf(item);
11813
11940
  const decisions = Array.isArray(route?.decisions) ? route.decisions.filter(isObject8) : [];
@@ -11939,6 +12066,63 @@ function askedAndStopped(screen) {
11939
12066
  const last = (paras[paras.length - 1] ?? "").replace(/\s*\n\s*/g, " ");
11940
12067
  return ASKS.test(last) || CONFIRMS.test(last);
11941
12068
  }
12069
+ var DIALOG_ASKS = /^Do you want to .+\?$/;
12070
+ var DIALOG_OPTION = /^(❯\s*)?(\d)\.\s+(.+)$/;
12071
+ var DIALOG_FOOTER = /^(?:esc to |tab to |enter to |ctrl\+)/i;
12072
+ var unbox = (line) => line.trimEnd().replace(/^\s*[│┃║]\s?/, "").replace(/\s*[│┃║]$/, "");
12073
+ function permissionDialog(screen) {
12074
+ const raw = screen.split(`
12075
+ `);
12076
+ const lines = raw.map(unbox);
12077
+ let q = -1;
12078
+ for (let i = lines.length - 1;i >= 0; i--)
12079
+ if (DIALOG_ASKS.test(lines[i].trim())) {
12080
+ q = i;
12081
+ break;
12082
+ }
12083
+ if (q < 0)
12084
+ return null;
12085
+ const options = [];
12086
+ let cursors = 0;
12087
+ let i = q + 1;
12088
+ for (;i < lines.length; i++) {
12089
+ const t = lines[i].trim();
12090
+ const m = DIALOG_OPTION.exec(t);
12091
+ if (m) {
12092
+ if (m[2] !== String(options.length + 1))
12093
+ return null;
12094
+ if (m[1])
12095
+ cursors++;
12096
+ options.push({ key: m[2], label: m[3].replace(/\s*\(esc\)$/i, "") });
12097
+ } else if (t && options.length && !isRule(raw[i]) && !isPrompt(t) && !isFooter(t) && !DIALOG_FOOTER.test(t)) {
12098
+ const last = options[options.length - 1];
12099
+ last.label = `${last.label} ${t.replace(/\s*\(esc\)$/i, "")}`;
12100
+ } else
12101
+ break;
12102
+ }
12103
+ if (options.length < 2 || cursors !== 1)
12104
+ return null;
12105
+ for (;i < lines.length; i++) {
12106
+ const t = lines[i].trim();
12107
+ if (t && !isRule(raw[i]) && !DIALOG_FOOTER.test(t) && !isFooter(t))
12108
+ return null;
12109
+ }
12110
+ let from = 0;
12111
+ for (let j = q - 1;j >= 0; j--) {
12112
+ const t = lines[j].trim();
12113
+ if (isRule(raw[j]) || /^⏺/.test(t) || /^⎿/.test(t)) {
12114
+ from = j + 1;
12115
+ break;
12116
+ }
12117
+ }
12118
+ const body = lines.slice(from, q + 1);
12119
+ const indent = Math.min(...body.filter((l) => l.trim()).map((l) => /^\s*/.exec(l)[0].length));
12120
+ const text = body.map((l) => l.slice(indent)).join(`
12121
+ `).replace(/\n{3,}/g, `
12122
+
12123
+ `).trim();
12124
+ return { text, options };
12125
+ }
11942
12126
  var PROVIDER_FAILURES = [
11943
12127
  ["connection lost", /\bconnection (?:lost|error)\b/i],
11944
12128
  ["credit balance too low", /\bcredit balance (?:is )?too low\b/i],
@@ -11961,7 +12145,7 @@ function providerFailure(screen) {
11961
12145
 
11962
12146
  // src/engine/worktree.ts
11963
12147
  init_src();
11964
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync10 } from "node:fs";
12148
+ import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync11 } from "node:fs";
11965
12149
  import { dirname as dirname11, isAbsolute, join as join14 } from "node:path";
11966
12150
  function worktreePath(repoPath, name) {
11967
12151
  return join14(repoPath, ".worktrees", name);
@@ -11987,7 +12171,7 @@ function defaultBranch(exec, repoPath) {
11987
12171
  var realWorktreeFs = {
11988
12172
  exists: existsSync12,
11989
12173
  append: (p, text) => {
11990
- mkdirSync10(dirname11(p), { recursive: true });
12174
+ mkdirSync11(dirname11(p), { recursive: true });
11991
12175
  appendFileSync(p, text);
11992
12176
  }
11993
12177
  };
@@ -12213,6 +12397,15 @@ function sandboxFor(d, name, cwd) {
12213
12397
  throw new Error("sessions are sandboxed, but nothing writes their settings — not started");
12214
12398
  return { sandbox: d.writeSandboxSettings(name, cwd) };
12215
12399
  }
12400
+ function hooksFor(d, name) {
12401
+ if (d.sandbox || !d.writeHookSettings)
12402
+ return {};
12403
+ return { settingsFile: d.writeHookSettings(name) };
12404
+ }
12405
+ function mainCheckoutFor(d, cwd) {
12406
+ const main = d.mainCheckoutOf?.(cwd);
12407
+ return main ? { mainCheckout: main } : {};
12408
+ }
12216
12409
  function engineMcpConfigFor(d, name, runToken, at, cwd) {
12217
12410
  if (d.source.name !== "sleds" || d.agent?.trim() || !d.writeMcpConfig)
12218
12411
  return;
@@ -12358,6 +12551,7 @@ async function tickOnce(d) {
12358
12551
  }
12359
12552
  continue;
12360
12553
  } else if (pr) {
12554
+ await dropPermission(d, run, "the session opened its PR");
12361
12555
  const outcome = { kind: "pr", ref: pr.url };
12362
12556
  try {
12363
12557
  await d.source.report(claim, outcome, d.engine);
@@ -12381,6 +12575,7 @@ async function tickOnce(d) {
12381
12575
  }
12382
12576
  }
12383
12577
  if (status === "exited") {
12578
+ await dropPermission(d, run, "the session exited");
12384
12579
  let log = "";
12385
12580
  try {
12386
12581
  log = runner.read(run.handle);
@@ -12430,6 +12625,11 @@ async function tickOnce(d) {
12430
12625
  }
12431
12626
  }
12432
12627
  }
12628
+ if (await settlePermission(d, runner, run, shown)) {
12629
+ idle = false;
12630
+ asked = false;
12631
+ failing = null;
12632
+ }
12433
12633
  const retry = failing !== null && (run.provider?.tries ?? 0) < PROVIDER_RETRIES;
12434
12634
  if (idle || failing || needsHeartbeat(run.leaseUntil, d.now(), d.leaseMin)) {
12435
12635
  let beat;
@@ -12680,6 +12880,8 @@ async function tickOnce(d) {
12680
12880
  ...launch.agent ? { agent: launch.agent } : {},
12681
12881
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
12682
12882
  ...sandboxFor(d, name, worktree),
12883
+ ...hooksFor(d, name),
12884
+ ...mainCheckoutFor(d, worktree),
12683
12885
  ...claim.group ? { group: claim.group } : {}
12684
12886
  };
12685
12887
  } catch (e) {
@@ -12834,6 +13036,115 @@ function parkQuestion(screen, idleMin) {
12834
13036
  function askedQuestion(screen) {
12835
13037
  return `The session stopped and is waiting on an answer, with no PR. ${lastMessage(screen)}`;
12836
13038
  }
13039
+ var MARK_OPTIONS = ["1 · Allow once", "esc · Deny"];
13040
+ function permissionKey(answer) {
13041
+ const m = /^\s*(\d|esc)\s*·/i.exec(answer);
13042
+ return m ? m[1].toLowerCase() : null;
13043
+ }
13044
+ async function settlePermission(d, runner, run, shown) {
13045
+ const perms = permissionsOf(d.source);
13046
+ if (!perms || !run.runId)
13047
+ return false;
13048
+ const at = ref(run);
13049
+ const markFile = d.permissionMark?.(sessionName(run.item, run.node));
13050
+ const dialog = runner.readsSession ? permissionDialog(shown) : null;
13051
+ const p = run.permission;
13052
+ if (p) {
13053
+ const gone = runner.readsSession && (p.onScreen ? !dialog : (run.seenAt ?? 0) > p.at);
13054
+ let now;
13055
+ try {
13056
+ now = await perms.decisionOf(run.item, p.decisionId);
13057
+ } catch (e) {
13058
+ d.log(`${at}: could not read the permission decision, asking next tick: ${message(e)}`);
13059
+ return true;
13060
+ }
13061
+ if (now?.state === "answered" && now.answer) {
13062
+ if (gone)
13063
+ d.log(`${at}: the permission was answered after its dialog closed — not pressed`);
13064
+ else if (!pressAnswer(d, runner, run, p.options, now.answer))
13065
+ return true;
13066
+ run.permission = undefined;
13067
+ run.answeredAt = d.now();
13068
+ if (markFile)
13069
+ clearPermissionMark(markFile);
13070
+ return true;
13071
+ }
13072
+ if (now?.state !== "open" && now?.state !== "escalated") {
13073
+ run.permission = undefined;
13074
+ return false;
13075
+ }
13076
+ if (!gone)
13077
+ return true;
13078
+ try {
13079
+ await perms.clearPermission(run.runId, "the dialog closed in the terminal", d.engine);
13080
+ } catch (e) {
13081
+ d.log(`${at}: could not clear the permission, trying next tick: ${message(e)}`);
13082
+ return true;
13083
+ }
13084
+ d.log(`${at}: the permission dialog closed before an answer — cleared`);
13085
+ run.permission = undefined;
13086
+ return false;
13087
+ }
13088
+ const mark = markFile ? readPermissionMark(markFile) : null;
13089
+ if (!dialog && !mark)
13090
+ return false;
13091
+ if (!dialog && runner.readsSession && !run.seenTicks)
13092
+ return false;
13093
+ const options = dialog ? dialog.options.map((o) => `${o.key} · ${o.label}`) : MARK_OPTIONS;
13094
+ const md = dialog?.text || mark?.message || "The session stopped at a permission dialog.";
13095
+ let opened;
13096
+ try {
13097
+ opened = await perms.openPermission(run.runId, { md, options }, d.engine);
13098
+ } catch (e) {
13099
+ d.log(`${at}: could not raise the permission dialog, trying next tick: ${message(e)}`);
13100
+ return true;
13101
+ }
13102
+ if (!opened.decisionId)
13103
+ return true;
13104
+ run.permission = {
13105
+ decisionId: opened.decisionId,
13106
+ options,
13107
+ at: d.now(),
13108
+ ...dialog ? { onScreen: true } : {}
13109
+ };
13110
+ if (markFile)
13111
+ clearPermissionMark(markFile);
13112
+ d.log(`${at}: stopped at a permission dialog — it waits for a person (${runner.watchHint(run.handle)})`);
13113
+ return true;
13114
+ }
13115
+ function pressAnswer(d, runner, run, options, answer) {
13116
+ const at = ref(run);
13117
+ const key = permissionKey(answer);
13118
+ const offered = key !== null && options.some((o) => permissionKey(o) === key);
13119
+ try {
13120
+ if (key !== null && offered) {
13121
+ if (runner.sendKey)
13122
+ runner.sendKey(run.handle, key);
13123
+ else
13124
+ runner.send(run.handle, key);
13125
+ d.log(`${at}: the permission is answered — pressed ${key}; the session goes on`);
13126
+ } else {
13127
+ runner.sendKey?.(run.handle, "esc");
13128
+ const note = runner.send(run.handle, answer);
13129
+ d.log(`${at}: the permission is answered in words — Esc, then typed in${note ? ` (${note})` : ""}`);
13130
+ }
13131
+ return true;
13132
+ } catch (e) {
13133
+ d.log(`${at}: could not press the permission's answer, trying next tick: ${message(e)}`);
13134
+ return false;
13135
+ }
13136
+ }
13137
+ async function dropPermission(d, run, why) {
13138
+ const perms = permissionsOf(d.source);
13139
+ if (!run.permission || !perms || !run.runId)
13140
+ return;
13141
+ try {
13142
+ await perms.clearPermission(run.runId, why, d.engine);
13143
+ } catch (e) {
13144
+ d.log(`${ref(run)}: could not clear the permission (${why}): ${message(e)}`);
13145
+ }
13146
+ run.permission = undefined;
13147
+ }
12837
13148
  var STILL_TICKS = 2;
12838
13149
  function lastMessage(screen) {
12839
13150
  const shown = stripTuiChrome(screen);
@@ -13267,6 +13578,8 @@ async function startAnswerer(d, answers, run) {
13267
13578
  ...launchOptions(launch),
13268
13579
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
13269
13580
  ...sandboxFor(d, name, run.handle.cwd),
13581
+ ...hooksFor(d, name),
13582
+ ...mainCheckoutFor(d, run.handle.cwd),
13270
13583
  ...run.group ? { group: run.group } : {}
13271
13584
  });
13272
13585
  } catch (e) {
@@ -13441,7 +13754,9 @@ async function startDraft(d, claim, why, state, started, finished) {
13441
13754
  permissionMode: d.permissionMode,
13442
13755
  ...launchOptions(launch),
13443
13756
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
13444
- ...sandboxFor(d, name, worktree)
13757
+ ...sandboxFor(d, name, worktree),
13758
+ ...hooksFor(d, name),
13759
+ ...mainCheckoutFor(d, worktree)
13445
13760
  });
13446
13761
  } catch (e) {
13447
13762
  d.log(`${at}: could not start the drafter: ${message(e)}`);
@@ -14214,6 +14529,8 @@ function startResolver(d, run, e, answer) {
14214
14529
  ...launchOptions(launch),
14215
14530
  ...run.mcpConfig ? { mcpConfig: run.mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
14216
14531
  ...sandboxFor(d, name, run.handle.cwd),
14532
+ ...hooksFor(d, name),
14533
+ ...mainCheckoutFor(d, run.handle.cwd),
14217
14534
  ...run.group ? { group: run.group } : {}
14218
14535
  });
14219
14536
  } catch (err) {
@@ -14288,7 +14605,9 @@ async function startEventAnswerer(d, answers, state, dec, decided) {
14288
14605
  permissionMode: d.permissionMode,
14289
14606
  ...launchOptions(launch),
14290
14607
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
14291
- ...sandboxFor(d, name, worktree)
14608
+ ...sandboxFor(d, name, worktree),
14609
+ ...hooksFor(d, name),
14610
+ ...mainCheckoutFor(d, worktree)
14292
14611
  });
14293
14612
  } catch (e) {
14294
14613
  d.log(`${at}: could not start the ${KIND_SAYS[kind]} answerer — it goes to a person: ${message(e)}`);
@@ -14508,7 +14827,7 @@ function commandRunner(template, deps = {}) {
14508
14827
  }
14509
14828
 
14510
14829
  // src/runners/conductor.ts
14511
- import { readFileSync as readFileSync10 } from "node:fs";
14830
+ import { readFileSync as readFileSync11 } from "node:fs";
14512
14831
  var obj = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
14513
14832
  var str8 = (v) => typeof v === "string" && v.length > 0 ? v : null;
14514
14833
  var CONDUCTOR_API = "https://api.conductor.build";
@@ -14606,7 +14925,7 @@ var ids = (h) => {
14606
14925
  var ENDED = new Set(["archived", "deleted"]);
14607
14926
  function conductorRunner(exec, deps = {}) {
14608
14927
  const env = deps.env ?? process.env;
14609
- const readFile = deps.readFile ?? ((p) => readFileSync10(p, "utf8"));
14928
+ const readFile = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
14610
14929
  const now = deps.now ?? Date.now;
14611
14930
  const base = env.CONDUCTOR_API_URL?.trim() || CONDUCTOR_API;
14612
14931
  const key = () => {
@@ -14725,8 +15044,8 @@ ${renderTranscript(messages)}`;
14725
15044
  var headlessScript = (o, log) => {
14726
15045
  const prompt = `"$(cat ${shq2(o.promptFile)})"`;
14727
15046
  const agent = o.agent?.trim();
14728
- const cmd = agent ? `${sandboxedAgent(agent, o.sandbox)} ${prompt}` : [
14729
- `${sandboxedAgent("claude", o.sandbox)} -p`,
15047
+ const cmd = agent ? `${sandboxedAgent(agent, o.sandbox, o.mainCheckout)} ${prompt}` : [
15048
+ `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)} -p`,
14730
15049
  prompt,
14731
15050
  ...o.mcpConfig ? [mcpConfigFlag(o).trim()] : [],
14732
15051
  "--output-format stream-json --verbose",
@@ -14797,7 +15116,7 @@ function headlessRunner(deps = {}) {
14797
15116
 
14798
15117
  // src/runners/managed.ts
14799
15118
  init_src();
14800
- import { readFileSync as readFileSync11 } from "node:fs";
15119
+ import { readFileSync as readFileSync12 } from "node:fs";
14801
15120
  var obj2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
14802
15121
  var str9 = (v) => typeof v === "string" && v.length > 0 ? v : null;
14803
15122
  var ANTHROPIC_API = "https://api.anthropic.com";
@@ -14933,7 +15252,7 @@ var statusLine2 = (session, events, now) => {
14933
15252
  var YES = /^\s*(?:y|yes|allow|ok|okay|go|go ahead|approve[d]?)\b/i;
14934
15253
  function managedRunner(exec, deps = {}) {
14935
15254
  const env = deps.env ?? process.env;
14936
- const readFile = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
15255
+ const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
14937
15256
  const now = deps.now ?? Date.now;
14938
15257
  const base = env.ANTHROPIC_BASE_URL?.trim() || ANTHROPIC_API;
14939
15258
  const need = (name, where) => {
@@ -15103,7 +15422,7 @@ ${renderTranscript(eventMessages(all))}`;
15103
15422
  }
15104
15423
 
15105
15424
  // src/runners/orca.ts
15106
- import { writeFileSync as writeFileSync11 } from "node:fs";
15425
+ import { writeFileSync as writeFileSync12 } from "node:fs";
15107
15426
  var obj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
15108
15427
  var str10 = (v) => typeof v === "string" && v.length > 0 ? v : null;
15109
15428
  var parseOrcaJson = (stdout) => {
@@ -15162,7 +15481,7 @@ var orcaTerminalLive = (status, stdout) => {
15162
15481
  };
15163
15482
  var launcherPath2 = (promptFile) => `${promptFile.replace(/\.md$/, "")}.orca.sh`;
15164
15483
  function orcaRunner(exec, deps = {}) {
15165
- const writeFile = deps.writeFile ?? ((p, t) => writeFileSync11(p, t, { mode: 384 }));
15484
+ const writeFile = deps.writeFile ?? ((p, t) => writeFileSync12(p, t, { mode: 384 }));
15166
15485
  const orca = (args, what) => {
15167
15486
  const r = exec("orca", args);
15168
15487
  const env = parseOrcaJson(r.stdout);
@@ -15302,6 +15621,14 @@ function tmuxRunner(exec) {
15302
15621
  }
15303
15622
  exec("tmux", ["send-keys", "-t", tmuxPane(h.id), "Enter"]);
15304
15623
  },
15624
+ sendKey(h, key) {
15625
+ if (!isRunnerKey(key))
15626
+ throw new Error(`tmux send-keys ${h.id}: no key ${JSON.stringify(key)}`);
15627
+ const r = exec("tmux", ["send-keys", "-t", tmuxPane(h.id), key === "esc" ? "Escape" : key]);
15628
+ if (r.status !== 0) {
15629
+ throw new Error(`tmux send-keys ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
15630
+ }
15631
+ },
15305
15632
  stop(h) {
15306
15633
  exec("tmux", ["kill-session", "-t", tmuxSession(h.id)]);
15307
15634
  },
@@ -15727,7 +16054,7 @@ function realPidAlive(pid) {
15727
16054
  function memberMcpConfigs(o, cwd) {
15728
16055
  const read = o.readJson ?? ((p) => {
15729
16056
  try {
15730
- return JSON.parse(readFileSync12(p, "utf8"));
16057
+ return JSON.parse(readFileSync13(p, "utf8"));
15731
16058
  } catch {
15732
16059
  return null;
15733
16060
  }
@@ -15769,6 +16096,9 @@ function engineDeps(o, flags, source, runner, stateName) {
15769
16096
  const repo = flags.repo ? resolve7(cwd, flags.repo) : undefined;
15770
16097
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve7(cwd, d));
15771
16098
  const { branch, prTitle } = flags;
16099
+ const hooksDir = join15(stateDir, "hooks");
16100
+ const permissionMark = (name) => join15(hooksDir, `${name}.permission.json`);
16101
+ const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
15772
16102
  return {
15773
16103
  source,
15774
16104
  runner,
@@ -15793,18 +16123,35 @@ function engineDeps(o, flags, source, runner, stateName) {
15793
16123
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
15794
16124
  const gitCommonDir = gitCommonDirOf(exec, cwd);
15795
16125
  const dir = join15(stateDir, "sandbox");
15796
- mkdirSync11(dir, { recursive: true, mode: 448 });
16126
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15797
16127
  const p = join15(dir, `${name}.json`);
15798
- const settings = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
16128
+ const walls = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
15799
16129
  ...SANDBOX_DEFAULT_ALLOW,
15800
16130
  ...flags.sandboxAllow ?? []
15801
16131
  ]);
15802
- writeFileSync12(p, `${JSON.stringify(settings, null, 2)}
16132
+ const settings = withHook(name, { ...walls });
16133
+ writeFileSync13(p, `${JSON.stringify(settings, null, 2)}
15803
16134
  `, { mode: 384 });
15804
- chmodSync3(p, 384);
16135
+ chmodSync4(p, 384);
15805
16136
  return { settingsFile: p, gitCommonDir };
15806
16137
  })
15807
16138
  } : {},
16139
+ writeHookSettings: (name) => {
16140
+ mkdirSync12(hooksDir, { recursive: true, mode: 448 });
16141
+ const p = join15(hooksDir, `${name}.json`);
16142
+ writeFileSync13(p, `${JSON.stringify(withHook(name, {}), null, 2)}
16143
+ `, { mode: 384 });
16144
+ chmodSync4(p, 384);
16145
+ return p;
16146
+ },
16147
+ permissionMark,
16148
+ mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
16149
+ try {
16150
+ return mainCheckoutOf(gitCommonDirOf(exec, cwd), cwd);
16151
+ } catch {
16152
+ return null;
16153
+ }
16154
+ }),
15808
16155
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
15809
16156
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
15810
16157
  now: o.now ?? Date.now,
@@ -15819,7 +16166,7 @@ function engineDeps(o, flags, source, runner, stateName) {
15819
16166
  }),
15820
16167
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
15821
16168
  const dir = join15(stateDir, "mcp");
15822
- mkdirSync11(dir, { recursive: true, mode: 448 });
16169
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15823
16170
  const p = join15(dir, `${name}.json`);
15824
16171
  let others = {};
15825
16172
  if (flags.strictMcp && flags.mcpAllow?.length) {
@@ -15830,16 +16177,16 @@ function engineDeps(o, flags, source, runner, stateName) {
15830
16177
  if (r.refused.length)
15831
16178
  log(`${name}: MCP pass-through refused ${r.refused.join(", ")} — the run's sleds server is the only SLEDS one`);
15832
16179
  }
15833
- writeFileSync12(p, pinOrg2(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
15834
- chmodSync3(p, 384);
16180
+ writeFileSync13(p, pinOrg2(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
16181
+ chmodSync4(p, 384);
15835
16182
  return p;
15836
16183
  }),
15837
- removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync3(p, { force: true })),
16184
+ removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync4(p, { force: true })),
15838
16185
  writePrompt: o.writePrompt ?? ((name, text) => {
15839
16186
  const dir = join15(stateDir, "prompts");
15840
- mkdirSync11(dir, { recursive: true, mode: 448 });
16187
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15841
16188
  const p = join15(dir, `${name}.md`);
15842
- writeFileSync12(p, text, { mode: 384 });
16189
+ writeFileSync13(p, text, { mode: 384 });
15843
16190
  return p;
15844
16191
  }),
15845
16192
  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 }),
@@ -15923,7 +16270,7 @@ async function driveEngine(o, flags, deps, until = {}) {
15923
16270
  runner: deps.runner.name,
15924
16271
  atOnce: flags.atOnce,
15925
16272
  interval: flags.interval,
15926
- cliVersion: "0.1.38"
16273
+ cliVersion: "0.1.40"
15927
16274
  };
15928
16275
  const beat = (checkedAt) => {
15929
16276
  const m = deps.machine;
@@ -16215,7 +16562,7 @@ async function enginePauseCmd(o) {
16215
16562
  result.released++;
16216
16563
  result.kept.push(run.handle.cwd);
16217
16564
  if (run.mcpConfig)
16218
- rmSync3(run.mcpConfig, { force: true });
16565
+ rmSync4(run.mcpConfig, { force: true });
16219
16566
  }
16220
16567
  if (gone.size)
16221
16568
  writeState(file, { ...state, runs: state.runs.filter((r) => !gone.has(r)) });
@@ -16237,19 +16584,19 @@ init_config();
16237
16584
  init_state();
16238
16585
  import {
16239
16586
  existsSync as existsSync14,
16240
- mkdirSync as mkdirSync13,
16241
- readFileSync as readFileSync13,
16587
+ mkdirSync as mkdirSync14,
16588
+ readFileSync as readFileSync14,
16242
16589
  readdirSync as readdirSync2,
16243
16590
  realpathSync,
16244
- rmSync as rmSync5,
16245
- writeFileSync as writeFileSync14
16591
+ rmSync as rmSync6,
16592
+ writeFileSync as writeFileSync15
16246
16593
  } from "node:fs";
16247
16594
  import { hostname as hostname3 } from "node:os";
16248
16595
  import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join18 } from "node:path";
16249
16596
 
16250
16597
  // src/schedule.ts
16251
16598
  import { spawnSync as spawnSync4 } from "node:child_process";
16252
- import { existsSync as existsSync13, mkdirSync as mkdirSync12, rmSync as rmSync4, writeFileSync as writeFileSync13 } from "node:fs";
16599
+ import { existsSync as existsSync13, mkdirSync as mkdirSync13, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "node:fs";
16253
16600
  import { join as join16 } from "node:path";
16254
16601
  var REVERT_LABEL = "ai.sleds.revert";
16255
16602
  var defaultExec2 = (cmd, args) => {
@@ -16297,9 +16644,9 @@ var scheduleRevert = (d, deadlineMs) => {
16297
16644
  if (d.platform === "darwin") {
16298
16645
  const p = plistPath(d.home);
16299
16646
  try {
16300
- mkdirSync12(join16(d.home, "Library", "LaunchAgents"), { recursive: true });
16647
+ mkdirSync13(join16(d.home, "Library", "LaunchAgents"), { recursive: true });
16301
16648
  d.exec("launchctl", ["unload", p]);
16302
- writeFileSync13(p, buildLaunchdPlist(d.sledsArgv, deadline));
16649
+ writeFileSync14(p, buildLaunchdPlist(d.sledsArgv, deadline));
16303
16650
  const r = d.exec("launchctl", ["load", p]);
16304
16651
  if (r.ok)
16305
16652
  return { kind: "launchd", detail: `launchd agent ${REVERT_LABEL}` };
@@ -16328,7 +16675,7 @@ var cancelRevert = (d, kind) => {
16328
16675
  const p = plistPath(d.home);
16329
16676
  if (existsSync13(p)) {
16330
16677
  d.exec("launchctl", ["unload", p]);
16331
- rmSync4(p, { force: true });
16678
+ rmSync5(p, { force: true });
16332
16679
  }
16333
16680
  }
16334
16681
  if (kind === "systemd" || d.platform === "linux") {
@@ -16504,7 +16851,7 @@ function readEngines(dir, suffix, manager, parse) {
16504
16851
  const path = join18(dir, name);
16505
16852
  let parsed;
16506
16853
  try {
16507
- parsed = parse(readFileSync13(path, "utf8"), name);
16854
+ parsed = parse(readFileSync14(path, "utf8"), name);
16508
16855
  } catch {
16509
16856
  parsed = null;
16510
16857
  }
@@ -16558,7 +16905,7 @@ function engineInstall(o) {
16558
16905
  const stateDir = seatStateDir(orgStateDir(baseDir, parsed.org), seat);
16559
16906
  const label = engineLabel(parsed.name, parsed.org, seat);
16560
16907
  const logPath = engineLogPath(stateDir, parsed.name);
16561
- mkdirSync13(stateDir, { recursive: true, mode: 448 });
16908
+ mkdirSync14(stateDir, { recursive: true, mode: 448 });
16562
16909
  const sledsArgv = stableArgv(o.sledsArgv, o.realpath);
16563
16910
  const execPath = sledsArgv[0] ?? process.execPath;
16564
16911
  const agentEnv = {
@@ -16571,7 +16918,7 @@ function engineInstall(o) {
16571
16918
  if (platform === "linux")
16572
16919
  return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
16573
16920
  const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
16574
- mkdirSync13(dirname12(plist), { recursive: true });
16921
+ mkdirSync14(dirname12(plist), { recursive: true });
16575
16922
  if (existsSync14(plist))
16576
16923
  exec("launchctl", ["unload", plist]);
16577
16924
  for (const other of installedEngines(o.home)) {
@@ -16580,7 +16927,7 @@ function engineInstall(o) {
16580
16927
  if (engineArg(other.programArgs, "name") !== parsed.name)
16581
16928
  continue;
16582
16929
  exec("launchctl", ["unload", other.plist]);
16583
- rmSync5(other.plist, { force: true });
16930
+ rmSync6(other.plist, { force: true });
16584
16931
  log(`replaced ${other.label}, this engine as installed before`);
16585
16932
  }
16586
16933
  const legacy = enginePlistPath(o.home, parsed.name);
@@ -16588,12 +16935,12 @@ function engineInstall(o) {
16588
16935
  const cfg = readConfig(o.home);
16589
16936
  if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
16590
16937
  exec("launchctl", ["unload", legacy]);
16591
- rmSync5(legacy, { force: true });
16938
+ rmSync6(legacy, { force: true });
16592
16939
  log(`replaced ${engineLabel(parsed.name)}, the engine installed before orgs`);
16593
16940
  } else
16594
16941
  log(`note: ${engineLabel(parsed.name)} (installed before orgs) still runs — \`sleds engine uninstall --name ${parsed.name}\` removes it if it was this org's`);
16595
16942
  }
16596
- writeFileSync14(plist, buildEnginePlist({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
16943
+ writeFileSync15(plist, buildEnginePlist({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
16597
16944
  const r = exec("launchctl", ["load", plist]);
16598
16945
  if (!r.ok) {
16599
16946
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -16613,7 +16960,7 @@ function installUnit(a) {
16613
16960
  const unit = engineUnitPath(o.home, label);
16614
16961
  const unitName = basename4(unit);
16615
16962
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
16616
- mkdirSync13(dirname12(unit), { recursive: true });
16963
+ mkdirSync14(dirname12(unit), { recursive: true });
16617
16964
  if (existsSync14(unit))
16618
16965
  systemctl("stop", unitName);
16619
16966
  for (const other of installedEngines(o.home)) {
@@ -16622,10 +16969,10 @@ function installUnit(a) {
16622
16969
  if (engineArg(other.programArgs, "name") !== parsed.name)
16623
16970
  continue;
16624
16971
  systemctl("disable", "--now", basename4(other.plist));
16625
- rmSync5(other.plist, { force: true });
16972
+ rmSync6(other.plist, { force: true });
16626
16973
  log(`replaced ${other.label}, this engine as installed before`);
16627
16974
  }
16628
- writeFileSync14(unit, buildEngineUnit({
16975
+ writeFileSync15(unit, buildEngineUnit({
16629
16976
  label,
16630
16977
  name: parsed.name,
16631
16978
  sledsArgv: a.sledsArgv,
@@ -16683,7 +17030,7 @@ function engineUninstall(o) {
16683
17030
  exec("systemctl", ["--user", "disable", "--now", basename4(file)]);
16684
17031
  else
16685
17032
  exec("launchctl", ["unload", file]);
16686
- rmSync5(file, { force: true });
17033
+ rmSync6(file, { force: true });
16687
17034
  if (unit)
16688
17035
  exec("systemctl", ["--user", "daemon-reload"]);
16689
17036
  log(`engine:${name} removed: ${basename4(file, unit ? ".service" : ".plist")} — its sessions keep running`);
@@ -16691,7 +17038,7 @@ function engineUninstall(o) {
16691
17038
  }
16692
17039
 
16693
17040
  // src/commands/musher.ts
16694
- import { existsSync as existsSync15, mkdirSync as mkdirSync14, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync15 } from "node:fs";
17041
+ import { existsSync as existsSync15, mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
16695
17042
  init_config();
16696
17043
  init_meter();
16697
17044
  var defaultWhich = (cmd) => {
@@ -16724,7 +17071,7 @@ var musherAgentState = (home, env = process.env) => {
16724
17071
  if (!existsSync15(plist))
16725
17072
  return { installed: false, thisSeat: false };
16726
17073
  try {
16727
- const text = readFileSync14(plist, "utf8");
17074
+ const text = readFileSync15(plist, "utf8");
16728
17075
  const want = env.SLEDS_HOME?.trim();
16729
17076
  const has = /<key>SLEDS_HOME<\/key>\s*<string>([^<]*)<\/string>/.exec(text)?.[1];
16730
17077
  return { installed: true, thisSeat: (want ?? undefined) === (has ?? undefined) };
@@ -17084,7 +17431,7 @@ function sandboxChecks(o, env) {
17084
17431
  const probe = join19(dir, SANDBOX_PROBE);
17085
17432
  const work = mkdtempSync(join19(tmpdir(), "sleds-sandbox-"));
17086
17433
  try {
17087
- writeFileSync16(probe, `${nonce}
17434
+ writeFileSync17(probe, `${nonce}
17088
17435
  `, { mode: 384 });
17089
17436
  const settingsFile = join19(work, "settings.json");
17090
17437
  const settings = sandboxSettings({
@@ -17092,7 +17439,7 @@ function sandboxChecks(o, env) {
17092
17439
  gitCommonDir: work,
17093
17440
  allow: SANDBOX_DEFAULT_ALLOW
17094
17441
  });
17095
- writeFileSync16(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
17442
+ writeFileSync17(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
17096
17443
  const prompt = `Run exactly this one Bash command, once, then reply with its complete output verbatim and nothing else:
17097
17444
 
17098
17445
  ${SANDBOX_PROBE_COMMAND}`;
@@ -17114,8 +17461,8 @@ ${SANDBOX_PROBE_COMMAND}`;
17114
17461
  ], { cwd: work });
17115
17462
  return readSandboxProbe(run, nonce);
17116
17463
  } finally {
17117
- rmSync7(probe, { force: true });
17118
- rmSync7(work, { recursive: true, force: true });
17464
+ rmSync8(probe, { force: true });
17465
+ rmSync8(work, { recursive: true, force: true });
17119
17466
  }
17120
17467
  }
17121
17468
  function engineChecks(o, env) {
@@ -17232,7 +17579,7 @@ function boxChecks(o, env, which, need) {
17232
17579
  const file = boxEnvPath(o.home);
17233
17580
  let named = false;
17234
17581
  try {
17235
- named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync15(file, "utf8"));
17582
+ named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync16(file, "utf8"));
17236
17583
  } catch {
17237
17584
  named = false;
17238
17585
  }
@@ -17336,12 +17683,12 @@ init_server();
17336
17683
  init_config();
17337
17684
  import {
17338
17685
  appendFileSync as appendFileSync3,
17339
- chmodSync as chmodSync4,
17340
- mkdirSync as mkdirSync15,
17341
- readFileSync as readFileSync16,
17686
+ chmodSync as chmodSync5,
17687
+ mkdirSync as mkdirSync16,
17688
+ readFileSync as readFileSync17,
17342
17689
  existsSync as realExists2,
17343
- rmSync as rmSync8,
17344
- writeFileSync as writeFileSync17
17690
+ rmSync as rmSync9,
17691
+ writeFileSync as writeFileSync18
17345
17692
  } from "node:fs";
17346
17693
  import { hostname as hostname4 } from "node:os";
17347
17694
  import { join as join20, resolve as resolve9 } from "node:path";
@@ -17751,7 +18098,7 @@ function realPidAlive2(pid) {
17751
18098
  function memberMcpConfigs2(o, cwd) {
17752
18099
  const read = o.readJson ?? ((p) => {
17753
18100
  try {
17754
- return JSON.parse(readFileSync16(p, "utf8"));
18101
+ return JSON.parse(readFileSync17(p, "utf8"));
17755
18102
  } catch {
17756
18103
  return null;
17757
18104
  }
@@ -17793,6 +18140,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
17793
18140
  const repo = flags.repo ? resolve9(cwd, flags.repo) : undefined;
17794
18141
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve9(cwd, d));
17795
18142
  const { branch, prTitle } = flags;
18143
+ const hooksDir = join20(stateDir, "hooks");
18144
+ const permissionMark = (name) => join20(hooksDir, `${name}.permission.json`);
18145
+ const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
17796
18146
  return {
17797
18147
  source,
17798
18148
  runner,
@@ -17817,18 +18167,35 @@ function engineDeps2(o, flags, source, runner, stateName) {
17817
18167
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
17818
18168
  const gitCommonDir = gitCommonDirOf2(exec, cwd);
17819
18169
  const dir = join20(stateDir, "sandbox");
17820
- mkdirSync15(dir, { recursive: true, mode: 448 });
18170
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17821
18171
  const p = join20(dir, `${name}.json`);
17822
- const settings = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
18172
+ const walls = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
17823
18173
  ...SANDBOX_DEFAULT_ALLOW,
17824
18174
  ...flags.sandboxAllow ?? []
17825
18175
  ]);
17826
- writeFileSync17(p, `${JSON.stringify(settings, null, 2)}
18176
+ const settings = withHook(name, { ...walls });
18177
+ writeFileSync18(p, `${JSON.stringify(settings, null, 2)}
17827
18178
  `, { mode: 384 });
17828
- chmodSync4(p, 384);
18179
+ chmodSync5(p, 384);
17829
18180
  return { settingsFile: p, gitCommonDir };
17830
18181
  })
17831
18182
  } : {},
18183
+ writeHookSettings: (name) => {
18184
+ mkdirSync16(hooksDir, { recursive: true, mode: 448 });
18185
+ const p = join20(hooksDir, `${name}.json`);
18186
+ writeFileSync18(p, `${JSON.stringify(withHook(name, {}), null, 2)}
18187
+ `, { mode: 384 });
18188
+ chmodSync5(p, 384);
18189
+ return p;
18190
+ },
18191
+ permissionMark,
18192
+ mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
18193
+ try {
18194
+ return mainCheckoutOf(gitCommonDirOf2(exec, cwd), cwd);
18195
+ } catch {
18196
+ return null;
18197
+ }
18198
+ }),
17832
18199
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
17833
18200
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
17834
18201
  now: o.now ?? Date.now,
@@ -17843,7 +18210,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
17843
18210
  }),
17844
18211
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
17845
18212
  const dir = join20(stateDir, "mcp");
17846
- mkdirSync15(dir, { recursive: true, mode: 448 });
18213
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17847
18214
  const p = join20(dir, `${name}.json`);
17848
18215
  let others = {};
17849
18216
  if (flags.strictMcp && flags.mcpAllow?.length) {
@@ -17854,16 +18221,16 @@ function engineDeps2(o, flags, source, runner, stateName) {
17854
18221
  if (r.refused.length)
17855
18222
  log(`${name}: MCP pass-through refused ${r.refused.join(", ")} — the run's sleds server is the only SLEDS one`);
17856
18223
  }
17857
- writeFileSync17(p, pinOrg3(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
17858
- chmodSync4(p, 384);
18224
+ writeFileSync18(p, pinOrg3(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
18225
+ chmodSync5(p, 384);
17859
18226
  return p;
17860
18227
  }),
17861
- removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync8(p, { force: true })),
18228
+ removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync9(p, { force: true })),
17862
18229
  writePrompt: o.writePrompt ?? ((name, text) => {
17863
18230
  const dir = join20(stateDir, "prompts");
17864
- mkdirSync15(dir, { recursive: true, mode: 448 });
18231
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17865
18232
  const p = join20(dir, `${name}.md`);
17866
- writeFileSync17(p, text, { mode: 384 });
18233
+ writeFileSync18(p, text, { mode: 384 });
17867
18234
  return p;
17868
18235
  }),
17869
18236
  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 }),
@@ -17956,7 +18323,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
17956
18323
  runner: deps.runner.name,
17957
18324
  atOnce: flags.atOnce,
17958
18325
  interval: flags.interval,
17959
- cliVersion: "0.1.38"
18326
+ cliVersion: "0.1.40"
17960
18327
  };
17961
18328
  const beat = (checkedAt) => {
17962
18329
  const m = deps.machine;
@@ -18362,7 +18729,7 @@ async function enginePauseCmd2(o) {
18362
18729
  result.released++;
18363
18730
  result.kept.push(run.handle.cwd);
18364
18731
  if (run.mcpConfig)
18365
- rmSync8(run.mcpConfig, { force: true });
18732
+ rmSync9(run.mcpConfig, { force: true });
18366
18733
  }
18367
18734
  if (gone.size)
18368
18735
  writeState(file, { ...state, runs: state.runs.filter((r) => !gone.has(r)) });
@@ -18384,12 +18751,12 @@ init_config();
18384
18751
  init_state();
18385
18752
  import {
18386
18753
  existsSync as existsSync17,
18387
- mkdirSync as mkdirSync16,
18388
- readFileSync as readFileSync17,
18754
+ mkdirSync as mkdirSync17,
18755
+ readFileSync as readFileSync18,
18389
18756
  readdirSync as readdirSync4,
18390
18757
  realpathSync as realpathSync2,
18391
- rmSync as rmSync9,
18392
- writeFileSync as writeFileSync18
18758
+ rmSync as rmSync10,
18759
+ writeFileSync as writeFileSync19
18393
18760
  } from "node:fs";
18394
18761
  import { hostname as hostname5 } from "node:os";
18395
18762
  import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join21 } from "node:path";
@@ -18511,7 +18878,7 @@ function readEngines2(dir, suffix, manager, parse) {
18511
18878
  const path = join21(dir, name);
18512
18879
  let parsed;
18513
18880
  try {
18514
- parsed = parse(readFileSync17(path, "utf8"), name);
18881
+ parsed = parse(readFileSync18(path, "utf8"), name);
18515
18882
  } catch {
18516
18883
  parsed = null;
18517
18884
  }
@@ -18565,7 +18932,7 @@ function engineInstall2(o) {
18565
18932
  const stateDir = seatStateDir(orgStateDir(baseDir, parsed.org), seat);
18566
18933
  const label = engineLabel2(parsed.name, parsed.org, seat);
18567
18934
  const logPath = engineLogPath2(stateDir, parsed.name);
18568
- mkdirSync16(stateDir, { recursive: true, mode: 448 });
18935
+ mkdirSync17(stateDir, { recursive: true, mode: 448 });
18569
18936
  const sledsArgv = stableArgv2(o.sledsArgv, o.realpath);
18570
18937
  const execPath = sledsArgv[0] ?? process.execPath;
18571
18938
  const agentEnv = {
@@ -18578,7 +18945,7 @@ function engineInstall2(o) {
18578
18945
  if (platform === "linux")
18579
18946
  return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
18580
18947
  const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
18581
- mkdirSync16(dirname13(plist), { recursive: true });
18948
+ mkdirSync17(dirname13(plist), { recursive: true });
18582
18949
  if (existsSync17(plist))
18583
18950
  exec("launchctl", ["unload", plist]);
18584
18951
  for (const other of installedEngines2(o.home)) {
@@ -18587,7 +18954,7 @@ function engineInstall2(o) {
18587
18954
  if (engineArg2(other.programArgs, "name") !== parsed.name)
18588
18955
  continue;
18589
18956
  exec("launchctl", ["unload", other.plist]);
18590
- rmSync9(other.plist, { force: true });
18957
+ rmSync10(other.plist, { force: true });
18591
18958
  log(`replaced ${other.label}, this engine as installed before`);
18592
18959
  }
18593
18960
  const legacy = enginePlistPath2(o.home, parsed.name);
@@ -18595,12 +18962,12 @@ function engineInstall2(o) {
18595
18962
  const cfg = readConfig(o.home);
18596
18963
  if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
18597
18964
  exec("launchctl", ["unload", legacy]);
18598
- rmSync9(legacy, { force: true });
18965
+ rmSync10(legacy, { force: true });
18599
18966
  log(`replaced ${engineLabel2(parsed.name)}, the engine installed before orgs`);
18600
18967
  } else
18601
18968
  log(`note: ${engineLabel2(parsed.name)} (installed before orgs) still runs — \`sleds engine uninstall --name ${parsed.name}\` removes it if it was this org's`);
18602
18969
  }
18603
- writeFileSync18(plist, buildEnginePlist2({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
18970
+ writeFileSync19(plist, buildEnginePlist2({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
18604
18971
  const r = exec("launchctl", ["load", plist]);
18605
18972
  if (!r.ok) {
18606
18973
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -18620,7 +18987,7 @@ function installUnit2(a) {
18620
18987
  const unit = engineUnitPath(o.home, label);
18621
18988
  const unitName = basename5(unit);
18622
18989
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
18623
- mkdirSync16(dirname13(unit), { recursive: true });
18990
+ mkdirSync17(dirname13(unit), { recursive: true });
18624
18991
  if (existsSync17(unit))
18625
18992
  systemctl("stop", unitName);
18626
18993
  for (const other of installedEngines2(o.home)) {
@@ -18629,10 +18996,10 @@ function installUnit2(a) {
18629
18996
  if (engineArg2(other.programArgs, "name") !== parsed.name)
18630
18997
  continue;
18631
18998
  systemctl("disable", "--now", basename5(other.plist));
18632
- rmSync9(other.plist, { force: true });
18999
+ rmSync10(other.plist, { force: true });
18633
19000
  log(`replaced ${other.label}, this engine as installed before`);
18634
19001
  }
18635
- writeFileSync18(unit, buildEngineUnit({
19002
+ writeFileSync19(unit, buildEngineUnit({
18636
19003
  label,
18637
19004
  name: parsed.name,
18638
19005
  sledsArgv: a.sledsArgv,
@@ -18690,7 +19057,7 @@ function engineUninstall2(o) {
18690
19057
  exec("systemctl", ["--user", "disable", "--now", basename5(file)]);
18691
19058
  else
18692
19059
  exec("launchctl", ["unload", file]);
18693
- rmSync9(file, { force: true });
19060
+ rmSync10(file, { force: true });
18694
19061
  if (unit)
18695
19062
  exec("systemctl", ["--user", "daemon-reload"]);
18696
19063
  log(`engine:${name} removed: ${basename5(file, unit ? ".service" : ".plist")} — its sessions keep running`);
@@ -18821,7 +19188,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
18821
19188
  import { hostname as hostname6 } from "node:os";
18822
19189
 
18823
19190
  // src/commands/engineStatus.ts
18824
- import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
19191
+ import { existsSync as existsSync18, readFileSync as readFileSync19 } from "node:fs";
18825
19192
  init_state();
18826
19193
 
18827
19194
  // src/engine/status.ts
@@ -19111,7 +19478,7 @@ var engineLogCmd = (o) => {
19111
19478
 
19112
19479
  // src/commands/engineProfile.ts
19113
19480
  init_src();
19114
- import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
19481
+ import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
19115
19482
  import { hostname as hostname7 } from "node:os";
19116
19483
  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]";
19117
19484
  var PROFILE_FIELDS = [
@@ -19376,7 +19743,7 @@ async function engineProfileCmd(o) {
19376
19743
  const rest = words.slice(1);
19377
19744
  let stdin = null;
19378
19745
  if (fromStdin) {
19379
- const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync19(0, "utf8")))());
19746
+ const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync20(0, "utf8")))());
19380
19747
  if ("error" in taken || taken.password !== undefined && password !== undefined) {
19381
19748
  error("error" in taken ? taken.error : "the cmux password is given once");
19382
19749
  error(PROFILE_USAGE);
@@ -19461,7 +19828,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
19461
19828
 
19462
19829
  // src/commands/engineStatus.ts
19463
19830
  init_state();
19464
- import { existsSync as existsSync20, readFileSync as readFileSync20 } from "node:fs";
19831
+ import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
19465
19832
  import { hostname as hostname8 } from "node:os";
19466
19833
  var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
19467
19834
  function parseStatusArgs(argv, host) {
@@ -19517,7 +19884,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
19517
19884
  }
19518
19885
  function safeRead(path) {
19519
19886
  try {
19520
- return readFileSync20(path, "utf8");
19887
+ return readFileSync21(path, "utf8");
19521
19888
  } catch {
19522
19889
  return "";
19523
19890
  }
@@ -19638,7 +20005,7 @@ init_meter();
19638
20005
 
19639
20006
  // src/owed.ts
19640
20007
  init_config();
19641
- import { existsSync as existsSync21, mkdirSync as mkdirSync17, readFileSync as readFileSync21, renameSync as renameSync6, writeFileSync as writeFileSync19 } from "node:fs";
20008
+ import { existsSync as existsSync21, mkdirSync as mkdirSync18, readFileSync as readFileSync22, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
19642
20009
  import { dirname as dirname14, join as join22 } from "node:path";
19643
20010
  var owedPath = (home) => join22(configDir(home), "owed.json");
19644
20011
  var sessionPath = (home) => join22(configDir(home), "session.json");
@@ -19659,17 +20026,17 @@ var readJson = (p) => {
19659
20026
  if (!existsSync21(p))
19660
20027
  return null;
19661
20028
  try {
19662
- return JSON.parse(readFileSync21(p, "utf8"));
20029
+ return JSON.parse(readFileSync22(p, "utf8"));
19663
20030
  } catch {
19664
20031
  return null;
19665
20032
  }
19666
20033
  };
19667
20034
  var writeJson = (p, v) => {
19668
- mkdirSync17(dirname14(p), { recursive: true, mode: 448 });
20035
+ mkdirSync18(dirname14(p), { recursive: true, mode: 448 });
19669
20036
  const tmp = `${p}.${process.pid}.tmp`;
19670
- writeFileSync19(tmp, `${JSON.stringify(v, null, 2)}
20037
+ writeFileSync20(tmp, `${JSON.stringify(v, null, 2)}
19671
20038
  `, { mode: 384 });
19672
- renameSync6(tmp, p);
20039
+ renameSync7(tmp, p);
19673
20040
  };
19674
20041
  var readOwed = (home, now = Date.now()) => {
19675
20042
  const f = readJson(owedPath(home));
@@ -19874,7 +20241,7 @@ var hooksOrg = (home, env = process.env) => {
19874
20241
  };
19875
20242
 
19876
20243
  // src/commands/init.ts
19877
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "node:fs";
20244
+ import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync21 } from "node:fs";
19878
20245
  import { createInterface } from "node:readline";
19879
20246
  init_config();
19880
20247
  init_state();
@@ -20561,8 +20928,8 @@ var init = async (o) => {
20561
20928
  });
20562
20929
  if (mcpLine)
20563
20930
  wrote.push(mcpLine);
20564
- mkdirSync18(skillDir(o.home), { recursive: true });
20565
- writeFileSync20(skillPath(o.home), SKILL_MD);
20931
+ mkdirSync19(skillDir(o.home), { recursive: true });
20932
+ writeFileSync21(skillPath(o.home), SKILL_MD);
20566
20933
  wrote.push(`installed skill at ${skillPath(o.home)}`);
20567
20934
  if (o.installMusher) {
20568
20935
  const r = o.installMusher();
@@ -20686,7 +21053,7 @@ var login = async (o) => {
20686
21053
  // src/commands/logout.ts
20687
21054
  init_config();
20688
21055
  init_state();
20689
- import { existsSync as existsSync22, readFileSync as readFileSync22, rmSync as rmSync10 } from "node:fs";
21056
+ import { existsSync as existsSync22, readFileSync as readFileSync23, rmSync as rmSync11 } from "node:fs";
20690
21057
  import { hostname as hostname10 } from "node:os";
20691
21058
  init_settings();
20692
21059
  var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
@@ -20810,7 +21177,7 @@ async function logout(o) {
20810
21177
  continue;
20811
21178
  if (!e.org) {
20812
21179
  (o.exec ?? defaultExec2)("launchctl", ["unload", e.plist]);
20813
- rmSync10(e.plist, { force: true });
21180
+ rmSync11(e.plist, { force: true });
20814
21181
  continue;
20815
21182
  }
20816
21183
  engineUninstall({
@@ -20843,8 +21210,8 @@ async function logout(o) {
20843
21210
  };
20844
21211
  writeConfig(o.home, next);
20845
21212
  const tp = tokenPath(o.home, env);
20846
- if (existsSync22(tp) && readFileSync22(tp, "utf8").trim() === seat.token)
20847
- rmSync10(tp, { force: true });
21213
+ if (existsSync22(tp) && readFileSync23(tp, "utf8").trim() === seat.token)
21214
+ rmSync11(tp, { force: true });
20848
21215
  const active = next.activeOrg ?? next.org?.slug ?? null;
20849
21216
  const result = { ok: true, email, org: slug, paused, active };
20850
21217
  if (parsed.json)
@@ -20921,7 +21288,7 @@ var meter = async (o) => {
20921
21288
  init_config();
20922
21289
  init_meter();
20923
21290
  import { spawnSync as spawnSync7 } from "node:child_process";
20924
- import { existsSync as existsSync23, mkdirSync as mkdirSync19, readFileSync as readFileSync23, rmSync as rmSync11, writeFileSync as writeFileSync21 } from "node:fs";
21291
+ import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync24, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
20925
21292
  import { hostname as hostname11 } from "node:os";
20926
21293
  import { dirname as dirname15 } from "node:path";
20927
21294
  var defaultWhich3 = (cmd) => {
@@ -20961,8 +21328,8 @@ var musherInstall = (o) => {
20961
21328
  const plist = musherPlistPath(o.home, env);
20962
21329
  const label = musherLabel(env);
20963
21330
  const dir = configDir(o.home, env);
20964
- mkdirSync19(dirname15(plist), { recursive: true });
20965
- mkdirSync19(dir, { recursive: true, mode: 448 });
21331
+ mkdirSync20(dirname15(plist), { recursive: true });
21332
+ mkdirSync20(dir, { recursive: true, mode: 448 });
20966
21333
  const execPath = o.sledsArgv[0] ?? process.execPath;
20967
21334
  const agentEnv = {
20968
21335
  PATH: musherPath(o.home, execPath, env.PATH ?? ""),
@@ -20973,7 +21340,7 @@ var musherInstall = (o) => {
20973
21340
  const exec = o.exec ?? defaultExecSync;
20974
21341
  if (existsSync23(plist))
20975
21342
  exec("launchctl", ["unload", plist]);
20976
- writeFileSync21(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
21343
+ writeFileSync22(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
20977
21344
  const r = exec("launchctl", ["load", plist]);
20978
21345
  if (!r.ok) {
20979
21346
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -20994,7 +21361,7 @@ var musherUninstall = (o) => {
20994
21361
  const exec = o.exec ?? defaultExecSync;
20995
21362
  if (existsSync23(plist)) {
20996
21363
  exec("launchctl", ["unload", plist]);
20997
- rmSync11(plist, { force: true });
21364
+ rmSync12(plist, { force: true });
20998
21365
  log(`musher removed: ${musherLabel(env)}`);
20999
21366
  } else
21000
21367
  log("musher was not installed.");
@@ -21257,7 +21624,7 @@ var on = (o) => {
21257
21624
  init_config();
21258
21625
  init_meter();
21259
21626
  init_settings();
21260
- import { existsSync as existsSync24, rmSync as rmSync12 } from "node:fs";
21627
+ import { existsSync as existsSync24, rmSync as rmSync13 } from "node:fs";
21261
21628
  var ORG_USAGE = "usage: sleds org list [--json] | sleds org use <slug>";
21262
21629
  var orgRows = (cfg, activeSlug) => {
21263
21630
  const seats = { ...cfg.orgs ?? {} };
@@ -21334,7 +21701,7 @@ var orgUse = (o) => {
21334
21701
  writeSettings(o.home, removeProxy(settings));
21335
21702
  }
21336
21703
  try {
21337
- rmSync12(meterCachePath(o.home), { force: true });
21704
+ rmSync13(meterCachePath(o.home), { force: true });
21338
21705
  } catch {}
21339
21706
  log(`Active org: ${seat.org?.name ? `${seat.org.name} (${slug})` : slug}${seat.email ? ` · ${seat.email}` : ""}.`);
21340
21707
  log("Claude Code, the status line and the hooks act as this org now.");
@@ -21360,7 +21727,7 @@ var orgCmd = (o) => {
21360
21727
  // src/commands/pack.ts
21361
21728
  init_src();
21362
21729
  import { spawnSync as spawnSync8 } from "node:child_process";
21363
- import { writeFileSync as writeFileSync22 } from "node:fs";
21730
+ import { writeFileSync as writeFileSync23 } from "node:fs";
21364
21731
  import { join as join23 } from "node:path";
21365
21732
  function packFileName(key) {
21366
21733
  return `pack-${slugify(key, 80) || "item"}.md`;
@@ -21390,22 +21757,43 @@ var packCmd = async (o) => {
21390
21757
  return 0;
21391
21758
  }
21392
21759
  const path = join23(o.cwd ?? process.cwd(), packFileName(key));
21393
- (o.write ?? ((p, t) => writeFileSync22(p, t.endsWith(`
21760
+ (o.write ?? ((p, t) => writeFileSync23(p, t.endsWith(`
21394
21761
  `) ? t : `${t}
21395
21762
  `)))(path, r.text);
21396
21763
  log(`Wrote ${path} — hand it to any model. \`sleds pack ${key} --run\` starts Claude Code with it.`);
21397
21764
  return 0;
21398
21765
  };
21399
21766
 
21400
- // src/commands/promptEvent.ts
21401
- init_config();
21402
-
21403
- // src/commands/seatEvent.ts
21404
- init_config();
21405
- init_meter();
21406
- var HOOK_STDIN_TIMEOUT_MS = 1000;
21767
+ // src/commands/permissionEvent.ts
21768
+ import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as readFileSync25, renameSync as renameSync8, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "node:fs";
21769
+ import { dirname as dirname16 } from "node:path";
21770
+ var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
21771
+ var messageOf = (text) => {
21772
+ try {
21773
+ const raw = JSON.parse(text);
21774
+ return isObject13(raw) && typeof raw.message === "string" ? raw.message : "";
21775
+ } catch {
21776
+ return "";
21777
+ }
21778
+ };
21779
+ var permissionEvent = async (o) => {
21780
+ try {
21781
+ const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
21782
+ if (!o.mark || notificationType(input) !== "permission_prompt")
21783
+ return 0;
21784
+ const mark = { message: messageOf(input), at: (o.now ?? Date.now)() };
21785
+ mkdirSync21(dirname16(o.mark), { recursive: true, mode: 448 });
21786
+ const tmp = `${o.mark}.${process.pid}.tmp`;
21787
+ writeFileSync24(tmp, `${JSON.stringify(mark)}
21788
+ `, { mode: 384 });
21789
+ renameSync8(tmp, o.mark);
21790
+ chmodSync6(o.mark, 384);
21791
+ } catch {}
21792
+ return 0;
21793
+ };
21407
21794
 
21408
21795
  // src/commands/promptEvent.ts
21796
+ init_config();
21409
21797
  var LABEL = {
21410
21798
  fiveHour: "5-hour",
21411
21799
  sevenDay: "weekly",
@@ -21488,7 +21876,7 @@ var promptEvent = async (o) => {
21488
21876
 
21489
21877
  // src/commands/route.ts
21490
21878
  init_src();
21491
- import { readFileSync as readFileSync24 } from "node:fs";
21879
+ import { readFileSync as readFileSync26 } from "node:fs";
21492
21880
  import { hostname as hostname12 } from "node:os";
21493
21881
  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>]";
21494
21882
  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]";
@@ -21661,7 +22049,7 @@ async function answerCmd(o, argv) {
21661
22049
  const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
21662
22050
  const ref = words[0] ?? "";
21663
22051
  const slash = ref.lastIndexOf("/");
21664
- const answer = (fromStdin ? (o.stdin ?? (() => readFileSync24(0, "utf8")))() : words.slice(1).join(" ")).trim();
22052
+ const answer = (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : words.slice(1).join(" ")).trim();
21665
22053
  if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
21666
22054
  error(ANSWER_USAGE);
21667
22055
  return 2;
@@ -21858,7 +22246,7 @@ async function decisionCmd(o, argv) {
21858
22246
  error(DECISION_USAGE);
21859
22247
  return 2;
21860
22248
  }
21861
- const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync24(0, "utf8")))() : rest.join(" ")).trim();
22249
+ const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : rest.join(" ")).trim();
21862
22250
  if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
21863
22251
  error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
21864
22252
  return 2;
@@ -21999,7 +22387,7 @@ function approvedLine(key, j) {
21999
22387
  function readNodes(o, a) {
22000
22388
  let text;
22001
22389
  try {
22002
- text = a.file ? (o.readFile ?? ((p) => readFileSync24(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync24(0, "utf8")))();
22390
+ text = a.file ? (o.readFile ?? ((p) => readFileSync26(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync26(0, "utf8")))();
22003
22391
  } catch (e) {
22004
22392
  return { error: e instanceof Error ? e.message : String(e) };
22005
22393
  }
@@ -22273,11 +22661,11 @@ var run = async (o) => {
22273
22661
  init_config();
22274
22662
  init_meter();
22275
22663
  var HOOK_STDIN_TIMEOUT_MS2 = 1000;
22276
- var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22277
- var notificationType = (text) => {
22664
+ var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22665
+ var notificationType2 = (text) => {
22278
22666
  try {
22279
22667
  const raw = JSON.parse(text);
22280
- if (!isObject11(raw))
22668
+ if (!isObject14(raw))
22281
22669
  return;
22282
22670
  return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
22283
22671
  } catch {
@@ -22288,7 +22676,7 @@ var seatEvent = async (o) => {
22288
22676
  const log = o.log ?? console.log;
22289
22677
  const now = (o.now ?? Date.now)();
22290
22678
  const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS2);
22291
- const kind = notificationType(input);
22679
+ const kind = notificationType2(input);
22292
22680
  try {
22293
22681
  const key = seatKeyFor(o.home, process.env);
22294
22682
  const state = readSeatState(o.home, key);
@@ -22310,12 +22698,12 @@ var seatEvent = async (o) => {
22310
22698
 
22311
22699
  // src/commands/seatSync.ts
22312
22700
  import { execFileSync as execFileSync4 } from "node:child_process";
22313
- import { existsSync as existsSync25, mkdirSync as mkdirSync20, readFileSync as readFileSync25, readdirSync as readdirSync5, rmSync as rmSync14, writeFileSync as writeFileSync23 } from "node:fs";
22701
+ import { existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync27, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
22314
22702
  import { platform as platform2 } from "node:os";
22315
- import { dirname as dirname16, join as join25 } from "node:path";
22703
+ import { dirname as dirname17, join as join25 } from "node:path";
22316
22704
 
22317
22705
  // src/cookieDb.ts
22318
- import { copyFileSync, rmSync as rmSync13 } from "node:fs";
22706
+ import { copyFileSync, rmSync as rmSync15 } from "node:fs";
22319
22707
  import { tmpdir as tmpdir2 } from "node:os";
22320
22708
  import { join as join24 } from "node:path";
22321
22709
  var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
@@ -22353,7 +22741,7 @@ async function readCookieRows(dbPath) {
22353
22741
  return rows.map((r) => ({ name: r.name, encrypted_value: Buffer.from(r.ev, "hex") }));
22354
22742
  } finally {
22355
22743
  try {
22356
- rmSync13(copy, { force: true });
22744
+ rmSync15(copy, { force: true });
22357
22745
  } catch {}
22358
22746
  }
22359
22747
  }
@@ -22438,8 +22826,8 @@ function resolveProfileDir(localStateJson, profile) {
22438
22826
  if (localStateJson) {
22439
22827
  try {
22440
22828
  const raw = JSON.parse(localStateJson);
22441
- const p = isObject12(raw) && isObject12(raw.profile) ? raw.profile : null;
22442
- if (p && isObject12(p.info_cache))
22829
+ const p = isObject15(raw) && isObject15(raw.profile) ? raw.profile : null;
22830
+ if (p && isObject15(p.info_cache))
22443
22831
  cache = p.info_cache;
22444
22832
  } catch {}
22445
22833
  }
@@ -22448,7 +22836,7 @@ function resolveProfileDir(localStateJson, profile) {
22448
22836
  const want = profile.toLowerCase();
22449
22837
  const hits = [];
22450
22838
  for (const [dir, info] of Object.entries(cache)) {
22451
- if (!isObject12(info))
22839
+ if (!isObject15(info))
22452
22840
  continue;
22453
22841
  const match = ["name", "user_name", "gaia_name"].some((k) => {
22454
22842
  const v = info[k];
@@ -22487,15 +22875,15 @@ function sourceArgs(source) {
22487
22875
  function seatTokenPath(home, source) {
22488
22876
  return `${home}/.config/sleds/${launchLabel(source).slice("ai.sleds.".length)}.token`;
22489
22877
  }
22490
- var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22878
+ var isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22491
22879
  var num7 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
22492
22880
  var round22 = (n) => Math.round(n * 100) / 100;
22493
22881
  function parseUsageResponse(raw, now = Date.now()) {
22494
- if (!isObject12(raw))
22882
+ if (!isObject15(raw))
22495
22883
  return null;
22496
22884
  const out = { capturedAt: now, models: {} };
22497
22885
  for (const [k, v] of Object.entries(raw)) {
22498
- if (!isObject12(v))
22886
+ if (!isObject15(v))
22499
22887
  continue;
22500
22888
  if (k === "extra_usage") {
22501
22889
  const credits = num7(v.used_credits);
@@ -22660,7 +23048,7 @@ function findCookieDb(home, source) {
22660
23048
  return db;
22661
23049
  }
22662
23050
  const localStatePath = join25(root, "Local State");
22663
- const localState = existsSync25(localStatePath) ? readFileSync25(localStatePath, "utf8") : null;
23051
+ const localState = existsSync25(localStatePath) ? readFileSync27(localStatePath, "utf8") : null;
22664
23052
  const wanted = source.profile ?? "Default";
22665
23053
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
22666
23054
  if (ambiguous)
@@ -22743,16 +23131,16 @@ function install(home, argv, source, log) {
22743
23131
  return 1;
22744
23132
  }
22745
23133
  const dir = join25(home, "Library", "LaunchAgents");
22746
- mkdirSync20(dir, { recursive: true });
23134
+ mkdirSync22(dir, { recursive: true });
22747
23135
  const path = plistPath2(home, source);
22748
23136
  const tokenPath = seatTokenPath(home, source);
22749
- rmSync14(tokenPath, { force: true });
23137
+ rmSync16(tokenPath, { force: true });
22750
23138
  if (source.token) {
22751
- mkdirSync20(dirname16(tokenPath), { recursive: true, mode: 448 });
22752
- writeFileSync23(tokenPath, `${source.token}
23139
+ mkdirSync22(dirname17(tokenPath), { recursive: true, mode: 448 });
23140
+ writeFileSync25(tokenPath, `${source.token}
22753
23141
  `, { mode: 384 });
22754
23142
  }
22755
- writeFileSync23(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
23143
+ writeFileSync25(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
22756
23144
  try {
22757
23145
  execFileSync4("launchctl", ["unload", path], { stdio: "ignore" });
22758
23146
  } catch {}
@@ -22765,7 +23153,7 @@ function uninstall(home, source, log) {
22765
23153
  try {
22766
23154
  execFileSync4("launchctl", ["unload", "-w", path], { stdio: "ignore" });
22767
23155
  } catch {}
22768
- rmSync14(seatTokenPath(home, source), { force: true });
23156
+ rmSync16(seatTokenPath(home, source), { force: true });
22769
23157
  log(existsSync25(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
22770
23158
  return 0;
22771
23159
  }
@@ -22840,7 +23228,7 @@ var seatSync = async (o) => {
22840
23228
  };
22841
23229
  function readSeatToken(home, source) {
22842
23230
  try {
22843
- return readFileSync25(seatTokenPath(home, source), "utf8").trim() || undefined;
23231
+ return readFileSync27(seatTokenPath(home, source), "utf8").trim() || undefined;
22844
23232
  } catch {
22845
23233
  return;
22846
23234
  }
@@ -22923,7 +23311,7 @@ var sessionEvent = async (o) => {
22923
23311
 
22924
23312
  // src/commands/specEvent.ts
22925
23313
  init_meter();
22926
- import { readFileSync as readFileSync26, statSync as statSync2 } from "node:fs";
23314
+ import { readFileSync as readFileSync28, statSync as statSync2 } from "node:fs";
22927
23315
  import { basename as basename6, isAbsolute as isAbsolute4, relative } from "node:path";
22928
23316
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
22929
23317
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -22979,7 +23367,7 @@ function readFileOrNull(path) {
22979
23367
  try {
22980
23368
  if (statSync2(path).size > 2000000)
22981
23369
  return null;
22982
- return readFileSync26(path, "utf8");
23370
+ return readFileSync28(path, "utf8");
22983
23371
  } catch {
22984
23372
  return null;
22985
23373
  }
@@ -23308,10 +23696,10 @@ var announce = (o, events) => {
23308
23696
 
23309
23697
  // src/commands/stopEvent.ts
23310
23698
  init_meter();
23311
- import { existsSync as existsSync27, readFileSync as readFileSync27 } from "node:fs";
23699
+ import { existsSync as existsSync27, readFileSync as readFileSync29 } from "node:fs";
23312
23700
 
23313
23701
  // src/transcript.ts
23314
- var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23702
+ var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23315
23703
  var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
23316
23704
  function sumTranscript(text) {
23317
23705
  const seen = new Set;
@@ -23332,7 +23720,7 @@ function sumTranscript(text) {
23332
23720
  } catch {
23333
23721
  continue;
23334
23722
  }
23335
- if (!isObject13(j) || j.type !== "assistant" || !isObject13(j.message))
23723
+ if (!isObject16(j) || j.type !== "assistant" || !isObject16(j.message))
23336
23724
  continue;
23337
23725
  const m = j.message;
23338
23726
  const id = typeof m.id === "string" ? m.id : typeof j.uuid === "string" ? j.uuid : undefined;
@@ -23341,7 +23729,7 @@ function sumTranscript(text) {
23341
23729
  continue;
23342
23730
  seen.add(id);
23343
23731
  }
23344
- const u = isObject13(m.usage) ? m.usage : undefined;
23732
+ const u = isObject16(m.usage) ? m.usage : undefined;
23345
23733
  if (!u)
23346
23734
  continue;
23347
23735
  const input = n(u.input_tokens);
@@ -23404,7 +23792,7 @@ async function postTranscript(o, payload, now) {
23404
23792
  const src = meterSource(o.home);
23405
23793
  if (!src)
23406
23794
  return false;
23407
- const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync27(p, "utf8") : null);
23795
+ const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync29(p, "utf8") : null);
23408
23796
  let text;
23409
23797
  try {
23410
23798
  text = read(path);
@@ -23482,7 +23870,7 @@ var stopEvent = async (o) => {
23482
23870
 
23483
23871
  // src/commands/uninstall.ts
23484
23872
  import { spawnSync as spawnSync9 } from "node:child_process";
23485
- import { existsSync as existsSync28, rmSync as rmSync15 } from "node:fs";
23873
+ import { existsSync as existsSync28, rmSync as rmSync17 } from "node:fs";
23486
23874
  init_config();
23487
23875
  init_meter();
23488
23876
  init_settings();
@@ -23543,19 +23931,19 @@ var uninstall2 = (o) => {
23543
23931
  }
23544
23932
  for (const file of [meterCachePath(o.home), ...allSeatStatePaths(o.home)]) {
23545
23933
  if (existsSync28(file)) {
23546
- rmSync15(file, { force: true });
23934
+ rmSync17(file, { force: true });
23547
23935
  removed.push(file);
23548
23936
  }
23549
23937
  }
23550
23938
  const skill = skillDir(o.home);
23551
23939
  if (existsSync28(skill)) {
23552
- rmSync15(skill, { recursive: true, force: true });
23940
+ rmSync17(skill, { recursive: true, force: true });
23553
23941
  removed.push(skill);
23554
23942
  }
23555
23943
  const plist = musherPlistPath(o.home);
23556
23944
  if (existsSync28(plist)) {
23557
23945
  (o.unloadAgent ?? defaultUnload)(plist);
23558
- rmSync15(plist, { force: true });
23946
+ rmSync17(plist, { force: true });
23559
23947
  removed.push(`launchd agent ${musherLabel()}`);
23560
23948
  }
23561
23949
  if (removed.length === 0) {
@@ -23708,17 +24096,17 @@ var webhooksCmd = async (o) => {
23708
24096
 
23709
24097
  // src/config.ts
23710
24098
  init_settings();
23711
- import { chmodSync as chmodSync5, existsSync as existsSync29, mkdirSync as mkdirSync21, readFileSync as readFileSync28, renameSync as renameSync7, writeFileSync as writeFileSync24 } from "node:fs";
24099
+ import { chmodSync as chmodSync7, existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync30, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
23712
24100
  import { join as join26, resolve as resolve10 } from "node:path";
23713
24101
  var configDir2 = (home, env = process.env) => {
23714
24102
  const o = env.SLEDS_HOME?.trim();
23715
24103
  return o ? resolve10(home, o) : join26(home, ".config", "sleds");
23716
24104
  };
23717
24105
  var configPath2 = (home) => join26(configDir2(home), "config.json");
23718
- var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
24106
+ var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23719
24107
  var optString2 = (v) => typeof v === "string" ? v : undefined;
23720
24108
  var parsePriorEnv2 = (v) => {
23721
- if (!isObject14(v))
24109
+ if (!isObject17(v))
23722
24110
  return;
23723
24111
  const out = {};
23724
24112
  const url = optString2(v[BASE_URL_KEY]);
@@ -23733,7 +24121,7 @@ var parsePriorEnv2 = (v) => {
23733
24121
  };
23734
24122
  var parseSchedule2 = (v) => v === "launchd" || v === "systemd" || v === "manual" ? v : undefined;
23735
24123
  var parseConfig2 = (raw) => {
23736
- if (!isObject14(raw))
24124
+ if (!isObject17(raw))
23737
24125
  return {};
23738
24126
  const cfg = {};
23739
24127
  const token = optString2(raw.token);
@@ -23783,11 +24171,11 @@ var parseConfig2 = (raw) => {
23783
24171
  return cfg;
23784
24172
  };
23785
24173
  var parseOrgs2 = (raw) => {
23786
- if (!isObject14(raw))
24174
+ if (!isObject17(raw))
23787
24175
  return;
23788
24176
  const out = {};
23789
24177
  for (const [slug, v] of Object.entries(raw)) {
23790
- if (!isObject14(v))
24178
+ if (!isObject17(v))
23791
24179
  continue;
23792
24180
  const token = optString2(v.token);
23793
24181
  if (!token)
@@ -23806,7 +24194,7 @@ var parseOrgs2 = (raw) => {
23806
24194
  return Object.keys(out).length > 0 ? out : undefined;
23807
24195
  };
23808
24196
  var parseMusher2 = (raw) => {
23809
- if (!isObject14(raw))
24197
+ if (!isObject17(raw))
23810
24198
  return;
23811
24199
  const out = {};
23812
24200
  const pm = optString2(raw.permissionMode);
@@ -23822,7 +24210,7 @@ var parseMusher2 = (raw) => {
23822
24210
  return out;
23823
24211
  };
23824
24212
  var parseOrgInfo2 = (raw) => {
23825
- if (!isObject14(raw) || !isObject14(raw.capabilities))
24213
+ if (!isObject17(raw) || !isObject17(raw.capabilities))
23826
24214
  return;
23827
24215
  const name = optString2(raw.name);
23828
24216
  const slug = optString2(raw.slug);
@@ -23848,7 +24236,7 @@ var readConfig2 = (home) => {
23848
24236
  const p = configPath2(home);
23849
24237
  if (!existsSync29(p))
23850
24238
  return {};
23851
- return parseConfig2(JSON.parse(readFileSync28(p, "utf8")));
24239
+ return parseConfig2(JSON.parse(readFileSync30(p, "utf8")));
23852
24240
  };
23853
24241
 
23854
24242
  // src/runners/exec.ts
@@ -23900,7 +24288,7 @@ var defaultExec5 = (cmd, args) => {
23900
24288
  };
23901
24289
 
23902
24290
  // src/settings.ts
23903
- import { dirname as dirname17, join as join27, resolve as resolve11 } from "node:path";
24291
+ import { dirname as dirname18, join as join27, resolve as resolve11 } from "node:path";
23904
24292
  var withSeatEnv2 = (command, home, env = process.env, org) => {
23905
24293
  const parts = [];
23906
24294
  const sh = env.SLEDS_HOME?.trim();
@@ -23923,15 +24311,16 @@ var pinOrg4 = (command, org) => {
23923
24311
  return `${lead}SLEDS_ORG=${shellWord2(slug)} ${bare.slice(lead.length)}`;
23924
24312
  };
23925
24313
  var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
23926
- var isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
24314
+ var isObject18 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23927
24315
  var isSledsHookFor2 = (sub) => (entry) => {
23928
- if (!isObject15(entry) || !Array.isArray(entry.hooks))
24316
+ if (!isObject18(entry) || !Array.isArray(entry.hooks))
23929
24317
  return false;
23930
24318
  const re = new RegExp(`\\b${sub}\\b`);
23931
- return entry.hooks.some((h) => isObject15(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
24319
+ return entry.hooks.some((h) => isObject18(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
23932
24320
  };
23933
24321
  var isSledsSessionHook2 = isSledsHookFor2("session-event");
23934
24322
  var isSledsStopHook2 = isSledsHookFor2("stop-event");
24323
+ var isSledsPermissionHook2 = isSledsHookFor2("permission-event");
23935
24324
 
23936
24325
  // src/index.ts
23937
24326
  var USAGE3 = `usage: sleds <command> [options]
@@ -24081,6 +24470,8 @@ commands:
24081
24470
  statusline Print the one-line meter Claude Code shows (installed by init; 10s cache).
24082
24471
  hooks Register the Claude Code Notification hook for seat limits (init does this too).
24083
24472
  seat-event Claude Code hook: record a seat wall and say how to continue. Reads hook JSON on stdin.
24473
+ permission-event --mark <file> Claude Code Notification hook the engine gives its sessions: on
24474
+ a permission dialog, write {message, at} to <file> for the engine. Local; exits 0.
24084
24475
  session-event Claude Code SessionStart hook: states the loop and the rows this machine still
24085
24476
  owes before the first turn. Reads hook JSON on stdin.
24086
24477
  stop-event Claude Code Stop hook: a session that estimated a row and posted no actual is asked
@@ -24225,7 +24616,8 @@ var main = async (argv) => {
24225
24616
  exec: defaultExec4,
24226
24617
  runnerFor: runnerFor3,
24227
24618
  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
24228
- shouldStop: () => stopping
24619
+ shouldStop: () => stopping,
24620
+ sledsArgv: sledsArgv()
24229
24621
  };
24230
24622
  return cmd === "engine" ? engineCmd(io) : routeCmd(io);
24231
24623
  }
@@ -24332,6 +24724,8 @@ var main = async (argv) => {
24332
24724
  });
24333
24725
  case "seat-event":
24334
24726
  return seatEvent({ home });
24727
+ case "permission-event":
24728
+ return permissionEvent({ mark: stringFlag(flags, "mark") });
24335
24729
  case "spec-event":
24336
24730
  return specEvent({ home });
24337
24731
  case "prompt-event":
@@ -24389,7 +24783,7 @@ ${renderHelpText(argv[1])}`);
24389
24783
  case "version":
24390
24784
  case "--version":
24391
24785
  case "-v":
24392
- console.log("sleds 0.1.38");
24786
+ console.log("sleds 0.1.40");
24393
24787
  return 0;
24394
24788
  default:
24395
24789
  console.error(`unknown command: ${cmd}