@sleds/cli 0.1.38 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/sleds.mjs +565 -191
  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 ?? "") => {
@@ -10277,6 +10325,13 @@ function cmuxRunner(exec, deps = {}) {
10277
10325
  return;
10278
10326
  return pending() ? `the Enter was swallowed; pressed it ${again} more times and the text is still in the input` : `the Enter was swallowed; pressed it again${again > 1 ? ` (${again}×)` : ""}`;
10279
10327
  },
10328
+ sendKey(h, key) {
10329
+ if (!isRunnerKey(key))
10330
+ throw new Error(`cmux send-key ${h.id}: no key ${JSON.stringify(key)}`);
10331
+ const r = cmux(["send-key", "--workspace", h.id, key === "esc" ? "escape" : key]);
10332
+ if (r.status !== 0)
10333
+ throw new Error(`cmux send-key ${h.id} failed: ${failed(r)}`);
10334
+ },
10280
10335
  stop(h) {
10281
10336
  cmux(["close-workspace", "--workspace", h.id]);
10282
10337
  if (!h.group)
@@ -10327,12 +10382,12 @@ init_server();
10327
10382
  init_config();
10328
10383
  import {
10329
10384
  appendFileSync as appendFileSync2,
10330
- chmodSync as chmodSync3,
10331
- mkdirSync as mkdirSync11,
10332
- readFileSync as readFileSync12,
10385
+ chmodSync as chmodSync4,
10386
+ mkdirSync as mkdirSync12,
10387
+ readFileSync as readFileSync13,
10333
10388
  existsSync as realExists,
10334
- rmSync as rmSync3,
10335
- writeFileSync as writeFileSync12
10389
+ rmSync as rmSync4,
10390
+ writeFileSync as writeFileSync13
10336
10391
  } from "node:fs";
10337
10392
  import { hostname as hostname2 } from "node:os";
10338
10393
  import { join as join15, resolve as resolve7 } from "node:path";
@@ -10347,7 +10402,7 @@ function beatBody(state, flags, host = {}, cap = {}) {
10347
10402
  name: flags.name,
10348
10403
  os: host.os ?? process.platform,
10349
10404
  runner: flags.runner,
10350
- version: host.version ?? "0.1.38" ?? "dev",
10405
+ version: host.version ?? "0.1.39" ?? "dev",
10351
10406
  running: live.length,
10352
10407
  items: live.slice(0, BEAT_ITEMS).map((r) => `${r.item}/${r.node}`),
10353
10408
  ...cap.atOnce !== undefined ? { atOnce: cap.atOnce } : {},
@@ -11092,13 +11147,40 @@ function jiraFromEnv(epic, env = process.env) {
11092
11147
  // src/engine/loop.ts
11093
11148
  init_src();
11094
11149
 
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
- });
11150
+ // src/commands/permissionEvent.ts
11151
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync as rmSync3, writeFileSync as writeFileSync11 } from "node:fs";
11152
+
11153
+ // src/commands/seatEvent.ts
11154
+ init_config();
11155
+ init_meter();
11156
+ var HOOK_STDIN_TIMEOUT_MS = 1000;
11157
+ var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11158
+ var notificationType = (text) => {
11159
+ try {
11160
+ const raw = JSON.parse(text);
11161
+ if (!isObject11(raw))
11162
+ return;
11163
+ return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
11164
+ } catch {
11165
+ return;
11166
+ }
11167
+ };
11168
+
11169
+ // src/commands/permissionEvent.ts
11170
+ var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
11171
+ var readPermissionMark = (file) => {
11172
+ try {
11173
+ const raw = JSON.parse(readFileSync10(file, "utf8"));
11174
+ if (!isObject12(raw) || typeof raw.at !== "number")
11175
+ return null;
11176
+ return { message: typeof raw.message === "string" ? raw.message : "", at: raw.at };
11177
+ } catch {
11178
+ return null;
11179
+ }
11180
+ };
11181
+ var clearPermissionMark = (file) => {
11182
+ rmSync3(file, { force: true });
11183
+ };
11102
11184
 
11103
11185
  // src/engine/answerer.ts
11104
11186
  init_src();
@@ -11250,6 +11332,10 @@ function looksOf(s) {
11250
11332
  const l = s;
11251
11333
  return l && typeof l.openLook === "function" && typeof l.clearLook === "function" ? s : null;
11252
11334
  }
11335
+ function permissionsOf(s) {
11336
+ const p = s;
11337
+ return p && typeof p.openPermission === "function" && typeof p.clearPermission === "function" && typeof p.decisionOf === "function" ? s : null;
11338
+ }
11253
11339
  var FAIL_LOG_MAX = 2000;
11254
11340
  var UNREACHABLE2 = "SLEDS unreachable";
11255
11341
  function keysIn(states, state) {
@@ -11808,6 +11894,27 @@ function sledsSource(client, o) {
11808
11894
  throw new Error(serverMessage(r));
11809
11895
  return r.json.cleared === true;
11810
11896
  },
11897
+ async openPermission(runId, p, engine) {
11898
+ const r = await client.postFull("/proxy/run", {
11899
+ runId,
11900
+ engine,
11901
+ event: { kind: "permission", md: p.md, about: { options: p.options } }
11902
+ });
11903
+ if (!r)
11904
+ throw new Error(`${UNREACHABLE2} — the permission not raised`);
11905
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11906
+ throw new Error(serverMessage(r));
11907
+ const decisionId = str6(r.json.decisionId);
11908
+ return { opened: r.json.opened === true, ...decisionId ? { decisionId } : {} };
11909
+ },
11910
+ async clearPermission(runId, why, engine) {
11911
+ const r = await client.postFull("/proxy/run", { runId, engine, clearPermission: { why } });
11912
+ if (!r)
11913
+ throw new Error(`${UNREACHABLE2} — the permission not cleared`);
11914
+ if (r.status >= 300 || !r.json || r.json.ok === false)
11915
+ throw new Error(serverMessage(r));
11916
+ return r.json.cleared === true;
11917
+ },
11811
11918
  async decisionOf(item, decisionId) {
11812
11919
  const route = await routeOf(item);
11813
11920
  const decisions = Array.isArray(route?.decisions) ? route.decisions.filter(isObject8) : [];
@@ -11939,6 +12046,63 @@ function askedAndStopped(screen) {
11939
12046
  const last = (paras[paras.length - 1] ?? "").replace(/\s*\n\s*/g, " ");
11940
12047
  return ASKS.test(last) || CONFIRMS.test(last);
11941
12048
  }
12049
+ var DIALOG_ASKS = /^Do you want to .+\?$/;
12050
+ var DIALOG_OPTION = /^(❯\s*)?(\d)\.\s+(.+)$/;
12051
+ var DIALOG_FOOTER = /^(?:esc to |tab to |enter to |ctrl\+)/i;
12052
+ var unbox = (line) => line.trimEnd().replace(/^\s*[│┃║]\s?/, "").replace(/\s*[│┃║]$/, "");
12053
+ function permissionDialog(screen) {
12054
+ const raw = screen.split(`
12055
+ `);
12056
+ const lines = raw.map(unbox);
12057
+ let q = -1;
12058
+ for (let i = lines.length - 1;i >= 0; i--)
12059
+ if (DIALOG_ASKS.test(lines[i].trim())) {
12060
+ q = i;
12061
+ break;
12062
+ }
12063
+ if (q < 0)
12064
+ return null;
12065
+ const options = [];
12066
+ let cursors = 0;
12067
+ let i = q + 1;
12068
+ for (;i < lines.length; i++) {
12069
+ const t = lines[i].trim();
12070
+ const m = DIALOG_OPTION.exec(t);
12071
+ if (m) {
12072
+ if (m[2] !== String(options.length + 1))
12073
+ return null;
12074
+ if (m[1])
12075
+ cursors++;
12076
+ options.push({ key: m[2], label: m[3].replace(/\s*\(esc\)$/i, "") });
12077
+ } else if (t && options.length && !isRule(raw[i]) && !isPrompt(t) && !isFooter(t) && !DIALOG_FOOTER.test(t)) {
12078
+ const last = options[options.length - 1];
12079
+ last.label = `${last.label} ${t.replace(/\s*\(esc\)$/i, "")}`;
12080
+ } else
12081
+ break;
12082
+ }
12083
+ if (options.length < 2 || cursors !== 1)
12084
+ return null;
12085
+ for (;i < lines.length; i++) {
12086
+ const t = lines[i].trim();
12087
+ if (t && !isRule(raw[i]) && !DIALOG_FOOTER.test(t) && !isFooter(t))
12088
+ return null;
12089
+ }
12090
+ let from = 0;
12091
+ for (let j = q - 1;j >= 0; j--) {
12092
+ const t = lines[j].trim();
12093
+ if (isRule(raw[j]) || /^⏺/.test(t) || /^⎿/.test(t)) {
12094
+ from = j + 1;
12095
+ break;
12096
+ }
12097
+ }
12098
+ const body = lines.slice(from, q + 1);
12099
+ const indent = Math.min(...body.filter((l) => l.trim()).map((l) => /^\s*/.exec(l)[0].length));
12100
+ const text = body.map((l) => l.slice(indent)).join(`
12101
+ `).replace(/\n{3,}/g, `
12102
+
12103
+ `).trim();
12104
+ return { text, options };
12105
+ }
11942
12106
  var PROVIDER_FAILURES = [
11943
12107
  ["connection lost", /\bconnection (?:lost|error)\b/i],
11944
12108
  ["credit balance too low", /\bcredit balance (?:is )?too low\b/i],
@@ -11961,7 +12125,7 @@ function providerFailure(screen) {
11961
12125
 
11962
12126
  // src/engine/worktree.ts
11963
12127
  init_src();
11964
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync10 } from "node:fs";
12128
+ import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync11 } from "node:fs";
11965
12129
  import { dirname as dirname11, isAbsolute, join as join14 } from "node:path";
11966
12130
  function worktreePath(repoPath, name) {
11967
12131
  return join14(repoPath, ".worktrees", name);
@@ -11987,7 +12151,7 @@ function defaultBranch(exec, repoPath) {
11987
12151
  var realWorktreeFs = {
11988
12152
  exists: existsSync12,
11989
12153
  append: (p, text) => {
11990
- mkdirSync10(dirname11(p), { recursive: true });
12154
+ mkdirSync11(dirname11(p), { recursive: true });
11991
12155
  appendFileSync(p, text);
11992
12156
  }
11993
12157
  };
@@ -12213,6 +12377,15 @@ function sandboxFor(d, name, cwd) {
12213
12377
  throw new Error("sessions are sandboxed, but nothing writes their settings — not started");
12214
12378
  return { sandbox: d.writeSandboxSettings(name, cwd) };
12215
12379
  }
12380
+ function hooksFor(d, name) {
12381
+ if (d.sandbox || !d.writeHookSettings)
12382
+ return {};
12383
+ return { settingsFile: d.writeHookSettings(name) };
12384
+ }
12385
+ function mainCheckoutFor(d, cwd) {
12386
+ const main = d.mainCheckoutOf?.(cwd);
12387
+ return main ? { mainCheckout: main } : {};
12388
+ }
12216
12389
  function engineMcpConfigFor(d, name, runToken, at, cwd) {
12217
12390
  if (d.source.name !== "sleds" || d.agent?.trim() || !d.writeMcpConfig)
12218
12391
  return;
@@ -12358,6 +12531,7 @@ async function tickOnce(d) {
12358
12531
  }
12359
12532
  continue;
12360
12533
  } else if (pr) {
12534
+ await dropPermission(d, run, "the session opened its PR");
12361
12535
  const outcome = { kind: "pr", ref: pr.url };
12362
12536
  try {
12363
12537
  await d.source.report(claim, outcome, d.engine);
@@ -12381,6 +12555,7 @@ async function tickOnce(d) {
12381
12555
  }
12382
12556
  }
12383
12557
  if (status === "exited") {
12558
+ await dropPermission(d, run, "the session exited");
12384
12559
  let log = "";
12385
12560
  try {
12386
12561
  log = runner.read(run.handle);
@@ -12430,6 +12605,11 @@ async function tickOnce(d) {
12430
12605
  }
12431
12606
  }
12432
12607
  }
12608
+ if (await settlePermission(d, runner, run, shown)) {
12609
+ idle = false;
12610
+ asked = false;
12611
+ failing = null;
12612
+ }
12433
12613
  const retry = failing !== null && (run.provider?.tries ?? 0) < PROVIDER_RETRIES;
12434
12614
  if (idle || failing || needsHeartbeat(run.leaseUntil, d.now(), d.leaseMin)) {
12435
12615
  let beat;
@@ -12680,6 +12860,8 @@ async function tickOnce(d) {
12680
12860
  ...launch.agent ? { agent: launch.agent } : {},
12681
12861
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
12682
12862
  ...sandboxFor(d, name, worktree),
12863
+ ...hooksFor(d, name),
12864
+ ...mainCheckoutFor(d, worktree),
12683
12865
  ...claim.group ? { group: claim.group } : {}
12684
12866
  };
12685
12867
  } catch (e) {
@@ -12834,6 +13016,115 @@ function parkQuestion(screen, idleMin) {
12834
13016
  function askedQuestion(screen) {
12835
13017
  return `The session stopped and is waiting on an answer, with no PR. ${lastMessage(screen)}`;
12836
13018
  }
13019
+ var MARK_OPTIONS = ["1 · Allow once", "esc · Deny"];
13020
+ function permissionKey(answer) {
13021
+ const m = /^\s*(\d|esc)\s*·/i.exec(answer);
13022
+ return m ? m[1].toLowerCase() : null;
13023
+ }
13024
+ async function settlePermission(d, runner, run, shown) {
13025
+ const perms = permissionsOf(d.source);
13026
+ if (!perms || !run.runId)
13027
+ return false;
13028
+ const at = ref(run);
13029
+ const markFile = d.permissionMark?.(sessionName(run.item, run.node));
13030
+ const dialog = runner.readsSession ? permissionDialog(shown) : null;
13031
+ const p = run.permission;
13032
+ if (p) {
13033
+ const gone = runner.readsSession && (p.onScreen ? !dialog : (run.seenAt ?? 0) > p.at);
13034
+ let now;
13035
+ try {
13036
+ now = await perms.decisionOf(run.item, p.decisionId);
13037
+ } catch (e) {
13038
+ d.log(`${at}: could not read the permission decision, asking next tick: ${message(e)}`);
13039
+ return true;
13040
+ }
13041
+ if (now?.state === "answered" && now.answer) {
13042
+ if (gone)
13043
+ d.log(`${at}: the permission was answered after its dialog closed — not pressed`);
13044
+ else if (!pressAnswer(d, runner, run, p.options, now.answer))
13045
+ return true;
13046
+ run.permission = undefined;
13047
+ run.answeredAt = d.now();
13048
+ if (markFile)
13049
+ clearPermissionMark(markFile);
13050
+ return true;
13051
+ }
13052
+ if (now?.state !== "open" && now?.state !== "escalated") {
13053
+ run.permission = undefined;
13054
+ return false;
13055
+ }
13056
+ if (!gone)
13057
+ return true;
13058
+ try {
13059
+ await perms.clearPermission(run.runId, "the dialog closed in the terminal", d.engine);
13060
+ } catch (e) {
13061
+ d.log(`${at}: could not clear the permission, trying next tick: ${message(e)}`);
13062
+ return true;
13063
+ }
13064
+ d.log(`${at}: the permission dialog closed before an answer — cleared`);
13065
+ run.permission = undefined;
13066
+ return false;
13067
+ }
13068
+ const mark = markFile ? readPermissionMark(markFile) : null;
13069
+ if (!dialog && !mark)
13070
+ return false;
13071
+ if (!dialog && runner.readsSession && !run.seenTicks)
13072
+ return false;
13073
+ const options = dialog ? dialog.options.map((o) => `${o.key} · ${o.label}`) : MARK_OPTIONS;
13074
+ const md = dialog?.text || mark?.message || "The session stopped at a permission dialog.";
13075
+ let opened;
13076
+ try {
13077
+ opened = await perms.openPermission(run.runId, { md, options }, d.engine);
13078
+ } catch (e) {
13079
+ d.log(`${at}: could not raise the permission dialog, trying next tick: ${message(e)}`);
13080
+ return true;
13081
+ }
13082
+ if (!opened.decisionId)
13083
+ return true;
13084
+ run.permission = {
13085
+ decisionId: opened.decisionId,
13086
+ options,
13087
+ at: d.now(),
13088
+ ...dialog ? { onScreen: true } : {}
13089
+ };
13090
+ if (markFile)
13091
+ clearPermissionMark(markFile);
13092
+ d.log(`${at}: stopped at a permission dialog — it waits for a person (${runner.watchHint(run.handle)})`);
13093
+ return true;
13094
+ }
13095
+ function pressAnswer(d, runner, run, options, answer) {
13096
+ const at = ref(run);
13097
+ const key = permissionKey(answer);
13098
+ const offered = key !== null && options.some((o) => permissionKey(o) === key);
13099
+ try {
13100
+ if (key !== null && offered) {
13101
+ if (runner.sendKey)
13102
+ runner.sendKey(run.handle, key);
13103
+ else
13104
+ runner.send(run.handle, key);
13105
+ d.log(`${at}: the permission is answered — pressed ${key}; the session goes on`);
13106
+ } else {
13107
+ runner.sendKey?.(run.handle, "esc");
13108
+ const note = runner.send(run.handle, answer);
13109
+ d.log(`${at}: the permission is answered in words — Esc, then typed in${note ? ` (${note})` : ""}`);
13110
+ }
13111
+ return true;
13112
+ } catch (e) {
13113
+ d.log(`${at}: could not press the permission's answer, trying next tick: ${message(e)}`);
13114
+ return false;
13115
+ }
13116
+ }
13117
+ async function dropPermission(d, run, why) {
13118
+ const perms = permissionsOf(d.source);
13119
+ if (!run.permission || !perms || !run.runId)
13120
+ return;
13121
+ try {
13122
+ await perms.clearPermission(run.runId, why, d.engine);
13123
+ } catch (e) {
13124
+ d.log(`${ref(run)}: could not clear the permission (${why}): ${message(e)}`);
13125
+ }
13126
+ run.permission = undefined;
13127
+ }
12837
13128
  var STILL_TICKS = 2;
12838
13129
  function lastMessage(screen) {
12839
13130
  const shown = stripTuiChrome(screen);
@@ -13267,6 +13558,8 @@ async function startAnswerer(d, answers, run) {
13267
13558
  ...launchOptions(launch),
13268
13559
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
13269
13560
  ...sandboxFor(d, name, run.handle.cwd),
13561
+ ...hooksFor(d, name),
13562
+ ...mainCheckoutFor(d, run.handle.cwd),
13270
13563
  ...run.group ? { group: run.group } : {}
13271
13564
  });
13272
13565
  } catch (e) {
@@ -13441,7 +13734,9 @@ async function startDraft(d, claim, why, state, started, finished) {
13441
13734
  permissionMode: d.permissionMode,
13442
13735
  ...launchOptions(launch),
13443
13736
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
13444
- ...sandboxFor(d, name, worktree)
13737
+ ...sandboxFor(d, name, worktree),
13738
+ ...hooksFor(d, name),
13739
+ ...mainCheckoutFor(d, worktree)
13445
13740
  });
13446
13741
  } catch (e) {
13447
13742
  d.log(`${at}: could not start the drafter: ${message(e)}`);
@@ -14214,6 +14509,8 @@ function startResolver(d, run, e, answer) {
14214
14509
  ...launchOptions(launch),
14215
14510
  ...run.mcpConfig ? { mcpConfig: run.mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
14216
14511
  ...sandboxFor(d, name, run.handle.cwd),
14512
+ ...hooksFor(d, name),
14513
+ ...mainCheckoutFor(d, run.handle.cwd),
14217
14514
  ...run.group ? { group: run.group } : {}
14218
14515
  });
14219
14516
  } catch (err) {
@@ -14288,7 +14585,9 @@ async function startEventAnswerer(d, answers, state, dec, decided) {
14288
14585
  permissionMode: d.permissionMode,
14289
14586
  ...launchOptions(launch),
14290
14587
  ...mcpConfig ? { mcpConfig, ...d.strictMcp ? { strictMcp: true } : {} } : {},
14291
- ...sandboxFor(d, name, worktree)
14588
+ ...sandboxFor(d, name, worktree),
14589
+ ...hooksFor(d, name),
14590
+ ...mainCheckoutFor(d, worktree)
14292
14591
  });
14293
14592
  } catch (e) {
14294
14593
  d.log(`${at}: could not start the ${KIND_SAYS[kind]} answerer — it goes to a person: ${message(e)}`);
@@ -14508,7 +14807,7 @@ function commandRunner(template, deps = {}) {
14508
14807
  }
14509
14808
 
14510
14809
  // src/runners/conductor.ts
14511
- import { readFileSync as readFileSync10 } from "node:fs";
14810
+ import { readFileSync as readFileSync11 } from "node:fs";
14512
14811
  var obj = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
14513
14812
  var str8 = (v) => typeof v === "string" && v.length > 0 ? v : null;
14514
14813
  var CONDUCTOR_API = "https://api.conductor.build";
@@ -14606,7 +14905,7 @@ var ids = (h) => {
14606
14905
  var ENDED = new Set(["archived", "deleted"]);
14607
14906
  function conductorRunner(exec, deps = {}) {
14608
14907
  const env = deps.env ?? process.env;
14609
- const readFile = deps.readFile ?? ((p) => readFileSync10(p, "utf8"));
14908
+ const readFile = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
14610
14909
  const now = deps.now ?? Date.now;
14611
14910
  const base = env.CONDUCTOR_API_URL?.trim() || CONDUCTOR_API;
14612
14911
  const key = () => {
@@ -14725,8 +15024,8 @@ ${renderTranscript(messages)}`;
14725
15024
  var headlessScript = (o, log) => {
14726
15025
  const prompt = `"$(cat ${shq2(o.promptFile)})"`;
14727
15026
  const agent = o.agent?.trim();
14728
- const cmd = agent ? `${sandboxedAgent(agent, o.sandbox)} ${prompt}` : [
14729
- `${sandboxedAgent("claude", o.sandbox)} -p`,
15027
+ const cmd = agent ? `${sandboxedAgent(agent, o.sandbox, o.mainCheckout)} ${prompt}` : [
15028
+ `${sandboxedAgent("claude", o.sandbox, o.mainCheckout)} -p`,
14730
15029
  prompt,
14731
15030
  ...o.mcpConfig ? [mcpConfigFlag(o).trim()] : [],
14732
15031
  "--output-format stream-json --verbose",
@@ -14797,7 +15096,7 @@ function headlessRunner(deps = {}) {
14797
15096
 
14798
15097
  // src/runners/managed.ts
14799
15098
  init_src();
14800
- import { readFileSync as readFileSync11 } from "node:fs";
15099
+ import { readFileSync as readFileSync12 } from "node:fs";
14801
15100
  var obj2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
14802
15101
  var str9 = (v) => typeof v === "string" && v.length > 0 ? v : null;
14803
15102
  var ANTHROPIC_API = "https://api.anthropic.com";
@@ -14933,7 +15232,7 @@ var statusLine2 = (session, events, now) => {
14933
15232
  var YES = /^\s*(?:y|yes|allow|ok|okay|go|go ahead|approve[d]?)\b/i;
14934
15233
  function managedRunner(exec, deps = {}) {
14935
15234
  const env = deps.env ?? process.env;
14936
- const readFile = deps.readFile ?? ((p) => readFileSync11(p, "utf8"));
15235
+ const readFile = deps.readFile ?? ((p) => readFileSync12(p, "utf8"));
14937
15236
  const now = deps.now ?? Date.now;
14938
15237
  const base = env.ANTHROPIC_BASE_URL?.trim() || ANTHROPIC_API;
14939
15238
  const need = (name, where) => {
@@ -15103,7 +15402,7 @@ ${renderTranscript(eventMessages(all))}`;
15103
15402
  }
15104
15403
 
15105
15404
  // src/runners/orca.ts
15106
- import { writeFileSync as writeFileSync11 } from "node:fs";
15405
+ import { writeFileSync as writeFileSync12 } from "node:fs";
15107
15406
  var obj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
15108
15407
  var str10 = (v) => typeof v === "string" && v.length > 0 ? v : null;
15109
15408
  var parseOrcaJson = (stdout) => {
@@ -15162,7 +15461,7 @@ var orcaTerminalLive = (status, stdout) => {
15162
15461
  };
15163
15462
  var launcherPath2 = (promptFile) => `${promptFile.replace(/\.md$/, "")}.orca.sh`;
15164
15463
  function orcaRunner(exec, deps = {}) {
15165
- const writeFile = deps.writeFile ?? ((p, t) => writeFileSync11(p, t, { mode: 384 }));
15464
+ const writeFile = deps.writeFile ?? ((p, t) => writeFileSync12(p, t, { mode: 384 }));
15166
15465
  const orca = (args, what) => {
15167
15466
  const r = exec("orca", args);
15168
15467
  const env = parseOrcaJson(r.stdout);
@@ -15302,6 +15601,14 @@ function tmuxRunner(exec) {
15302
15601
  }
15303
15602
  exec("tmux", ["send-keys", "-t", tmuxPane(h.id), "Enter"]);
15304
15603
  },
15604
+ sendKey(h, key) {
15605
+ if (!isRunnerKey(key))
15606
+ throw new Error(`tmux send-keys ${h.id}: no key ${JSON.stringify(key)}`);
15607
+ const r = exec("tmux", ["send-keys", "-t", tmuxPane(h.id), key === "esc" ? "Escape" : key]);
15608
+ if (r.status !== 0) {
15609
+ throw new Error(`tmux send-keys ${h.id} failed: ${r.stderr.trim() || `exit ${r.status}`}`);
15610
+ }
15611
+ },
15305
15612
  stop(h) {
15306
15613
  exec("tmux", ["kill-session", "-t", tmuxSession(h.id)]);
15307
15614
  },
@@ -15727,7 +16034,7 @@ function realPidAlive(pid) {
15727
16034
  function memberMcpConfigs(o, cwd) {
15728
16035
  const read = o.readJson ?? ((p) => {
15729
16036
  try {
15730
- return JSON.parse(readFileSync12(p, "utf8"));
16037
+ return JSON.parse(readFileSync13(p, "utf8"));
15731
16038
  } catch {
15732
16039
  return null;
15733
16040
  }
@@ -15769,6 +16076,9 @@ function engineDeps(o, flags, source, runner, stateName) {
15769
16076
  const repo = flags.repo ? resolve7(cwd, flags.repo) : undefined;
15770
16077
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve7(cwd, d));
15771
16078
  const { branch, prTitle } = flags;
16079
+ const hooksDir = join15(stateDir, "hooks");
16080
+ const permissionMark = (name) => join15(hooksDir, `${name}.permission.json`);
16081
+ const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
15772
16082
  return {
15773
16083
  source,
15774
16084
  runner,
@@ -15793,18 +16103,35 @@ function engineDeps(o, flags, source, runner, stateName) {
15793
16103
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
15794
16104
  const gitCommonDir = gitCommonDirOf(exec, cwd);
15795
16105
  const dir = join15(stateDir, "sandbox");
15796
- mkdirSync11(dir, { recursive: true, mode: 448 });
16106
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15797
16107
  const p = join15(dir, `${name}.json`);
15798
- const settings = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
16108
+ const walls = engineSandboxSettings(o.home, o.env ?? process.env, gitCommonDir, [
15799
16109
  ...SANDBOX_DEFAULT_ALLOW,
15800
16110
  ...flags.sandboxAllow ?? []
15801
16111
  ]);
15802
- writeFileSync12(p, `${JSON.stringify(settings, null, 2)}
16112
+ const settings = withHook(name, { ...walls });
16113
+ writeFileSync13(p, `${JSON.stringify(settings, null, 2)}
15803
16114
  `, { mode: 384 });
15804
- chmodSync3(p, 384);
16115
+ chmodSync4(p, 384);
15805
16116
  return { settingsFile: p, gitCommonDir };
15806
16117
  })
15807
16118
  } : {},
16119
+ writeHookSettings: (name) => {
16120
+ mkdirSync12(hooksDir, { recursive: true, mode: 448 });
16121
+ const p = join15(hooksDir, `${name}.json`);
16122
+ writeFileSync13(p, `${JSON.stringify(withHook(name, {}), null, 2)}
16123
+ `, { mode: 384 });
16124
+ chmodSync4(p, 384);
16125
+ return p;
16126
+ },
16127
+ permissionMark,
16128
+ mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
16129
+ try {
16130
+ return mainCheckoutOf(gitCommonDirOf(exec, cwd), cwd);
16131
+ } catch {
16132
+ return null;
16133
+ }
16134
+ }),
15808
16135
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
15809
16136
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
15810
16137
  now: o.now ?? Date.now,
@@ -15819,7 +16146,7 @@ function engineDeps(o, flags, source, runner, stateName) {
15819
16146
  }),
15820
16147
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
15821
16148
  const dir = join15(stateDir, "mcp");
15822
- mkdirSync11(dir, { recursive: true, mode: 448 });
16149
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15823
16150
  const p = join15(dir, `${name}.json`);
15824
16151
  let others = {};
15825
16152
  if (flags.strictMcp && flags.mcpAllow?.length) {
@@ -15830,16 +16157,16 @@ function engineDeps(o, flags, source, runner, stateName) {
15830
16157
  if (r.refused.length)
15831
16158
  log(`${name}: MCP pass-through refused ${r.refused.join(", ")} — the run's sleds server is the only SLEDS one`);
15832
16159
  }
15833
- writeFileSync12(p, pinOrg2(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
15834
- chmodSync3(p, 384);
16160
+ writeFileSync13(p, pinOrg2(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
16161
+ chmodSync4(p, 384);
15835
16162
  return p;
15836
16163
  }),
15837
- removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync3(p, { force: true })),
16164
+ removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync4(p, { force: true })),
15838
16165
  writePrompt: o.writePrompt ?? ((name, text) => {
15839
16166
  const dir = join15(stateDir, "prompts");
15840
- mkdirSync11(dir, { recursive: true, mode: 448 });
16167
+ mkdirSync12(dir, { recursive: true, mode: 448 });
15841
16168
  const p = join15(dir, `${name}.md`);
15842
- writeFileSync12(p, text, { mode: 384 });
16169
+ writeFileSync13(p, text, { mode: 384 });
15843
16170
  return p;
15844
16171
  }),
15845
16172
  ensureWorktree: (e, repoPath, branch, path, base, install) => ensureWorktree(e, repoPath, branch, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }, { ...install ? { command: install } : {}, log: o.log ?? console.log }),
@@ -15923,7 +16250,7 @@ async function driveEngine(o, flags, deps, until = {}) {
15923
16250
  runner: deps.runner.name,
15924
16251
  atOnce: flags.atOnce,
15925
16252
  interval: flags.interval,
15926
- cliVersion: "0.1.38"
16253
+ cliVersion: "0.1.39"
15927
16254
  };
15928
16255
  const beat = (checkedAt) => {
15929
16256
  const m = deps.machine;
@@ -16215,7 +16542,7 @@ async function enginePauseCmd(o) {
16215
16542
  result.released++;
16216
16543
  result.kept.push(run.handle.cwd);
16217
16544
  if (run.mcpConfig)
16218
- rmSync3(run.mcpConfig, { force: true });
16545
+ rmSync4(run.mcpConfig, { force: true });
16219
16546
  }
16220
16547
  if (gone.size)
16221
16548
  writeState(file, { ...state, runs: state.runs.filter((r) => !gone.has(r)) });
@@ -16237,19 +16564,19 @@ init_config();
16237
16564
  init_state();
16238
16565
  import {
16239
16566
  existsSync as existsSync14,
16240
- mkdirSync as mkdirSync13,
16241
- readFileSync as readFileSync13,
16567
+ mkdirSync as mkdirSync14,
16568
+ readFileSync as readFileSync14,
16242
16569
  readdirSync as readdirSync2,
16243
16570
  realpathSync,
16244
- rmSync as rmSync5,
16245
- writeFileSync as writeFileSync14
16571
+ rmSync as rmSync6,
16572
+ writeFileSync as writeFileSync15
16246
16573
  } from "node:fs";
16247
16574
  import { hostname as hostname3 } from "node:os";
16248
16575
  import { basename as basename4, dirname as dirname12, isAbsolute as isAbsolute2, join as join18 } from "node:path";
16249
16576
 
16250
16577
  // src/schedule.ts
16251
16578
  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";
16579
+ import { existsSync as existsSync13, mkdirSync as mkdirSync13, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "node:fs";
16253
16580
  import { join as join16 } from "node:path";
16254
16581
  var REVERT_LABEL = "ai.sleds.revert";
16255
16582
  var defaultExec2 = (cmd, args) => {
@@ -16297,9 +16624,9 @@ var scheduleRevert = (d, deadlineMs) => {
16297
16624
  if (d.platform === "darwin") {
16298
16625
  const p = plistPath(d.home);
16299
16626
  try {
16300
- mkdirSync12(join16(d.home, "Library", "LaunchAgents"), { recursive: true });
16627
+ mkdirSync13(join16(d.home, "Library", "LaunchAgents"), { recursive: true });
16301
16628
  d.exec("launchctl", ["unload", p]);
16302
- writeFileSync13(p, buildLaunchdPlist(d.sledsArgv, deadline));
16629
+ writeFileSync14(p, buildLaunchdPlist(d.sledsArgv, deadline));
16303
16630
  const r = d.exec("launchctl", ["load", p]);
16304
16631
  if (r.ok)
16305
16632
  return { kind: "launchd", detail: `launchd agent ${REVERT_LABEL}` };
@@ -16328,7 +16655,7 @@ var cancelRevert = (d, kind) => {
16328
16655
  const p = plistPath(d.home);
16329
16656
  if (existsSync13(p)) {
16330
16657
  d.exec("launchctl", ["unload", p]);
16331
- rmSync4(p, { force: true });
16658
+ rmSync5(p, { force: true });
16332
16659
  }
16333
16660
  }
16334
16661
  if (kind === "systemd" || d.platform === "linux") {
@@ -16504,7 +16831,7 @@ function readEngines(dir, suffix, manager, parse) {
16504
16831
  const path = join18(dir, name);
16505
16832
  let parsed;
16506
16833
  try {
16507
- parsed = parse(readFileSync13(path, "utf8"), name);
16834
+ parsed = parse(readFileSync14(path, "utf8"), name);
16508
16835
  } catch {
16509
16836
  parsed = null;
16510
16837
  }
@@ -16558,7 +16885,7 @@ function engineInstall(o) {
16558
16885
  const stateDir = seatStateDir(orgStateDir(baseDir, parsed.org), seat);
16559
16886
  const label = engineLabel(parsed.name, parsed.org, seat);
16560
16887
  const logPath = engineLogPath(stateDir, parsed.name);
16561
- mkdirSync13(stateDir, { recursive: true, mode: 448 });
16888
+ mkdirSync14(stateDir, { recursive: true, mode: 448 });
16562
16889
  const sledsArgv = stableArgv(o.sledsArgv, o.realpath);
16563
16890
  const execPath = sledsArgv[0] ?? process.execPath;
16564
16891
  const agentEnv = {
@@ -16571,7 +16898,7 @@ function engineInstall(o) {
16571
16898
  if (platform === "linux")
16572
16899
  return installUnit({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
16573
16900
  const plist = enginePlistPath(o.home, parsed.name, parsed.org, seat);
16574
- mkdirSync13(dirname12(plist), { recursive: true });
16901
+ mkdirSync14(dirname12(plist), { recursive: true });
16575
16902
  if (existsSync14(plist))
16576
16903
  exec("launchctl", ["unload", plist]);
16577
16904
  for (const other of installedEngines(o.home)) {
@@ -16580,7 +16907,7 @@ function engineInstall(o) {
16580
16907
  if (engineArg(other.programArgs, "name") !== parsed.name)
16581
16908
  continue;
16582
16909
  exec("launchctl", ["unload", other.plist]);
16583
- rmSync5(other.plist, { force: true });
16910
+ rmSync6(other.plist, { force: true });
16584
16911
  log(`replaced ${other.label}, this engine as installed before`);
16585
16912
  }
16586
16913
  const legacy = enginePlistPath(o.home, parsed.name);
@@ -16588,12 +16915,12 @@ function engineInstall(o) {
16588
16915
  const cfg = readConfig(o.home);
16589
16916
  if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
16590
16917
  exec("launchctl", ["unload", legacy]);
16591
- rmSync5(legacy, { force: true });
16918
+ rmSync6(legacy, { force: true });
16592
16919
  log(`replaced ${engineLabel(parsed.name)}, the engine installed before orgs`);
16593
16920
  } else
16594
16921
  log(`note: ${engineLabel(parsed.name)} (installed before orgs) still runs — \`sleds engine uninstall --name ${parsed.name}\` removes it if it was this org's`);
16595
16922
  }
16596
- writeFileSync14(plist, buildEnginePlist({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
16923
+ writeFileSync15(plist, buildEnginePlist({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
16597
16924
  const r = exec("launchctl", ["load", plist]);
16598
16925
  if (!r.ok) {
16599
16926
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -16613,7 +16940,7 @@ function installUnit(a) {
16613
16940
  const unit = engineUnitPath(o.home, label);
16614
16941
  const unitName = basename4(unit);
16615
16942
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
16616
- mkdirSync13(dirname12(unit), { recursive: true });
16943
+ mkdirSync14(dirname12(unit), { recursive: true });
16617
16944
  if (existsSync14(unit))
16618
16945
  systemctl("stop", unitName);
16619
16946
  for (const other of installedEngines(o.home)) {
@@ -16622,10 +16949,10 @@ function installUnit(a) {
16622
16949
  if (engineArg(other.programArgs, "name") !== parsed.name)
16623
16950
  continue;
16624
16951
  systemctl("disable", "--now", basename4(other.plist));
16625
- rmSync5(other.plist, { force: true });
16952
+ rmSync6(other.plist, { force: true });
16626
16953
  log(`replaced ${other.label}, this engine as installed before`);
16627
16954
  }
16628
- writeFileSync14(unit, buildEngineUnit({
16955
+ writeFileSync15(unit, buildEngineUnit({
16629
16956
  label,
16630
16957
  name: parsed.name,
16631
16958
  sledsArgv: a.sledsArgv,
@@ -16683,7 +17010,7 @@ function engineUninstall(o) {
16683
17010
  exec("systemctl", ["--user", "disable", "--now", basename4(file)]);
16684
17011
  else
16685
17012
  exec("launchctl", ["unload", file]);
16686
- rmSync5(file, { force: true });
17013
+ rmSync6(file, { force: true });
16687
17014
  if (unit)
16688
17015
  exec("systemctl", ["--user", "daemon-reload"]);
16689
17016
  log(`engine:${name} removed: ${basename4(file, unit ? ".service" : ".plist")} — its sessions keep running`);
@@ -16691,7 +17018,7 @@ function engineUninstall(o) {
16691
17018
  }
16692
17019
 
16693
17020
  // src/commands/musher.ts
16694
- import { existsSync as existsSync15, mkdirSync as mkdirSync14, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync15 } from "node:fs";
17021
+ import { existsSync as existsSync15, mkdirSync as mkdirSync15, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
16695
17022
  init_config();
16696
17023
  init_meter();
16697
17024
  var defaultWhich = (cmd) => {
@@ -16724,7 +17051,7 @@ var musherAgentState = (home, env = process.env) => {
16724
17051
  if (!existsSync15(plist))
16725
17052
  return { installed: false, thisSeat: false };
16726
17053
  try {
16727
- const text = readFileSync14(plist, "utf8");
17054
+ const text = readFileSync15(plist, "utf8");
16728
17055
  const want = env.SLEDS_HOME?.trim();
16729
17056
  const has = /<key>SLEDS_HOME<\/key>\s*<string>([^<]*)<\/string>/.exec(text)?.[1];
16730
17057
  return { installed: true, thisSeat: (want ?? undefined) === (has ?? undefined) };
@@ -17084,7 +17411,7 @@ function sandboxChecks(o, env) {
17084
17411
  const probe = join19(dir, SANDBOX_PROBE);
17085
17412
  const work = mkdtempSync(join19(tmpdir(), "sleds-sandbox-"));
17086
17413
  try {
17087
- writeFileSync16(probe, `${nonce}
17414
+ writeFileSync17(probe, `${nonce}
17088
17415
  `, { mode: 384 });
17089
17416
  const settingsFile = join19(work, "settings.json");
17090
17417
  const settings = sandboxSettings({
@@ -17092,7 +17419,7 @@ function sandboxChecks(o, env) {
17092
17419
  gitCommonDir: work,
17093
17420
  allow: SANDBOX_DEFAULT_ALLOW
17094
17421
  });
17095
- writeFileSync16(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
17422
+ writeFileSync17(settingsFile, JSON.stringify(settings, null, 2), { mode: 384 });
17096
17423
  const prompt = `Run exactly this one Bash command, once, then reply with its complete output verbatim and nothing else:
17097
17424
 
17098
17425
  ${SANDBOX_PROBE_COMMAND}`;
@@ -17114,8 +17441,8 @@ ${SANDBOX_PROBE_COMMAND}`;
17114
17441
  ], { cwd: work });
17115
17442
  return readSandboxProbe(run, nonce);
17116
17443
  } finally {
17117
- rmSync7(probe, { force: true });
17118
- rmSync7(work, { recursive: true, force: true });
17444
+ rmSync8(probe, { force: true });
17445
+ rmSync8(work, { recursive: true, force: true });
17119
17446
  }
17120
17447
  }
17121
17448
  function engineChecks(o, env) {
@@ -17232,7 +17559,7 @@ function boxChecks(o, env, which, need) {
17232
17559
  const file = boxEnvPath(o.home);
17233
17560
  let named = false;
17234
17561
  try {
17235
- named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync15(file, "utf8"));
17562
+ named = /^\s*(?:export\s+)?ANTHROPIC_API_KEY\s*=\s*\S/m.test(readFileSync16(file, "utf8"));
17236
17563
  } catch {
17237
17564
  named = false;
17238
17565
  }
@@ -17336,12 +17663,12 @@ init_server();
17336
17663
  init_config();
17337
17664
  import {
17338
17665
  appendFileSync as appendFileSync3,
17339
- chmodSync as chmodSync4,
17340
- mkdirSync as mkdirSync15,
17341
- readFileSync as readFileSync16,
17666
+ chmodSync as chmodSync5,
17667
+ mkdirSync as mkdirSync16,
17668
+ readFileSync as readFileSync17,
17342
17669
  existsSync as realExists2,
17343
- rmSync as rmSync8,
17344
- writeFileSync as writeFileSync17
17670
+ rmSync as rmSync9,
17671
+ writeFileSync as writeFileSync18
17345
17672
  } from "node:fs";
17346
17673
  import { hostname as hostname4 } from "node:os";
17347
17674
  import { join as join20, resolve as resolve9 } from "node:path";
@@ -17751,7 +18078,7 @@ function realPidAlive2(pid) {
17751
18078
  function memberMcpConfigs2(o, cwd) {
17752
18079
  const read = o.readJson ?? ((p) => {
17753
18080
  try {
17754
- return JSON.parse(readFileSync16(p, "utf8"));
18081
+ return JSON.parse(readFileSync17(p, "utf8"));
17755
18082
  } catch {
17756
18083
  return null;
17757
18084
  }
@@ -17793,6 +18120,9 @@ function engineDeps2(o, flags, source, runner, stateName) {
17793
18120
  const repo = flags.repo ? resolve9(cwd, flags.repo) : undefined;
17794
18121
  const reposDirs = splitReposDirs(flags.reposDir).map((d) => resolve9(cwd, d));
17795
18122
  const { branch, prTitle } = flags;
18123
+ const hooksDir = join20(stateDir, "hooks");
18124
+ const permissionMark = (name) => join20(hooksDir, `${name}.permission.json`);
18125
+ const withHook = (name, settings) => applyPermissionHook(settings, [...o.sledsArgv ?? ["sleds"], "permission-event", "--mark", permissionMark(name)].map(shq2).join(" "));
17796
18126
  return {
17797
18127
  source,
17798
18128
  runner,
@@ -17817,18 +18147,35 @@ function engineDeps2(o, flags, source, runner, stateName) {
17817
18147
  writeSandboxSettings: o.writeSandboxSettings ?? ((name, cwd) => {
17818
18148
  const gitCommonDir = gitCommonDirOf2(exec, cwd);
17819
18149
  const dir = join20(stateDir, "sandbox");
17820
- mkdirSync15(dir, { recursive: true, mode: 448 });
18150
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17821
18151
  const p = join20(dir, `${name}.json`);
17822
- const settings = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
18152
+ const walls = engineSandboxSettings2(o.home, o.env ?? process.env, gitCommonDir, [
17823
18153
  ...SANDBOX_DEFAULT_ALLOW,
17824
18154
  ...flags.sandboxAllow ?? []
17825
18155
  ]);
17826
- writeFileSync17(p, `${JSON.stringify(settings, null, 2)}
18156
+ const settings = withHook(name, { ...walls });
18157
+ writeFileSync18(p, `${JSON.stringify(settings, null, 2)}
17827
18158
  `, { mode: 384 });
17828
- chmodSync4(p, 384);
18159
+ chmodSync5(p, 384);
17829
18160
  return { settingsFile: p, gitCommonDir };
17830
18161
  })
17831
18162
  } : {},
18163
+ writeHookSettings: (name) => {
18164
+ mkdirSync16(hooksDir, { recursive: true, mode: 448 });
18165
+ const p = join20(hooksDir, `${name}.json`);
18166
+ writeFileSync18(p, `${JSON.stringify(withHook(name, {}), null, 2)}
18167
+ `, { mode: 384 });
18168
+ chmodSync5(p, 384);
18169
+ return p;
18170
+ },
18171
+ permissionMark,
18172
+ mainCheckoutOf: o.mainCheckoutOf ?? ((cwd) => {
18173
+ try {
18174
+ return mainCheckoutOf(gitCommonDirOf2(exec, cwd), cwd);
18175
+ } catch {
18176
+ return null;
18177
+ }
18178
+ }),
17832
18179
  ...branch ? { branchOf: (claim) => branchFor(branch, claim) } : {},
17833
18180
  ...prTitle ? { prTitleOf: (claim) => prTitleFor(prTitle, claim) } : {},
17834
18181
  now: o.now ?? Date.now,
@@ -17843,7 +18190,7 @@ function engineDeps2(o, flags, source, runner, stateName) {
17843
18190
  }),
17844
18191
  writeMcpConfig: o.writeMcpConfig ?? ((name, runToken, worktree) => {
17845
18192
  const dir = join20(stateDir, "mcp");
17846
- mkdirSync15(dir, { recursive: true, mode: 448 });
18193
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17847
18194
  const p = join20(dir, `${name}.json`);
17848
18195
  let others = {};
17849
18196
  if (flags.strictMcp && flags.mcpAllow?.length) {
@@ -17854,16 +18201,16 @@ function engineDeps2(o, flags, source, runner, stateName) {
17854
18201
  if (r.refused.length)
17855
18202
  log(`${name}: MCP pass-through refused ${r.refused.join(", ")} — the run's sleds server is the only SLEDS one`);
17856
18203
  }
17857
- writeFileSync17(p, pinOrg3(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
17858
- chmodSync4(p, 384);
18204
+ writeFileSync18(p, pinOrg3(engineMcpConfig(runToken, others), mcpOrg), { mode: 384 });
18205
+ chmodSync5(p, 384);
17859
18206
  return p;
17860
18207
  }),
17861
- removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync8(p, { force: true })),
18208
+ removeMcpConfig: o.removeMcpConfig ?? ((p) => rmSync9(p, { force: true })),
17862
18209
  writePrompt: o.writePrompt ?? ((name, text) => {
17863
18210
  const dir = join20(stateDir, "prompts");
17864
- mkdirSync15(dir, { recursive: true, mode: 448 });
18211
+ mkdirSync16(dir, { recursive: true, mode: 448 });
17865
18212
  const p = join20(dir, `${name}.md`);
17866
- writeFileSync17(p, text, { mode: 384 });
18213
+ writeFileSync18(p, text, { mode: 384 });
17867
18214
  return p;
17868
18215
  }),
17869
18216
  ensureWorktree: (e, repoPath, branch, path, base, install) => ensureWorktree(e, repoPath, branch, path, base, o.worktreeFs ?? { ...realWorktreeFs, exists }, { ...install ? { command: install } : {}, log: o.log ?? console.log }),
@@ -17956,7 +18303,7 @@ async function driveEngine2(o, flags, deps, until = {}) {
17956
18303
  runner: deps.runner.name,
17957
18304
  atOnce: flags.atOnce,
17958
18305
  interval: flags.interval,
17959
- cliVersion: "0.1.38"
18306
+ cliVersion: "0.1.39"
17960
18307
  };
17961
18308
  const beat = (checkedAt) => {
17962
18309
  const m = deps.machine;
@@ -18362,7 +18709,7 @@ async function enginePauseCmd2(o) {
18362
18709
  result.released++;
18363
18710
  result.kept.push(run.handle.cwd);
18364
18711
  if (run.mcpConfig)
18365
- rmSync8(run.mcpConfig, { force: true });
18712
+ rmSync9(run.mcpConfig, { force: true });
18366
18713
  }
18367
18714
  if (gone.size)
18368
18715
  writeState(file, { ...state, runs: state.runs.filter((r) => !gone.has(r)) });
@@ -18384,12 +18731,12 @@ init_config();
18384
18731
  init_state();
18385
18732
  import {
18386
18733
  existsSync as existsSync17,
18387
- mkdirSync as mkdirSync16,
18388
- readFileSync as readFileSync17,
18734
+ mkdirSync as mkdirSync17,
18735
+ readFileSync as readFileSync18,
18389
18736
  readdirSync as readdirSync4,
18390
18737
  realpathSync as realpathSync2,
18391
- rmSync as rmSync9,
18392
- writeFileSync as writeFileSync18
18738
+ rmSync as rmSync10,
18739
+ writeFileSync as writeFileSync19
18393
18740
  } from "node:fs";
18394
18741
  import { hostname as hostname5 } from "node:os";
18395
18742
  import { basename as basename5, dirname as dirname13, isAbsolute as isAbsolute3, join as join21 } from "node:path";
@@ -18511,7 +18858,7 @@ function readEngines2(dir, suffix, manager, parse) {
18511
18858
  const path = join21(dir, name);
18512
18859
  let parsed;
18513
18860
  try {
18514
- parsed = parse(readFileSync17(path, "utf8"), name);
18861
+ parsed = parse(readFileSync18(path, "utf8"), name);
18515
18862
  } catch {
18516
18863
  parsed = null;
18517
18864
  }
@@ -18565,7 +18912,7 @@ function engineInstall2(o) {
18565
18912
  const stateDir = seatStateDir(orgStateDir(baseDir, parsed.org), seat);
18566
18913
  const label = engineLabel2(parsed.name, parsed.org, seat);
18567
18914
  const logPath = engineLogPath2(stateDir, parsed.name);
18568
- mkdirSync16(stateDir, { recursive: true, mode: 448 });
18915
+ mkdirSync17(stateDir, { recursive: true, mode: 448 });
18569
18916
  const sledsArgv = stableArgv2(o.sledsArgv, o.realpath);
18570
18917
  const execPath = sledsArgv[0] ?? process.execPath;
18571
18918
  const agentEnv = {
@@ -18578,7 +18925,7 @@ function engineInstall2(o) {
18578
18925
  if (platform === "linux")
18579
18926
  return installUnit2({ o, parsed, label, sledsArgv, env: agentEnv, logPath, exec });
18580
18927
  const plist = enginePlistPath2(o.home, parsed.name, parsed.org, seat);
18581
- mkdirSync16(dirname13(plist), { recursive: true });
18928
+ mkdirSync17(dirname13(plist), { recursive: true });
18582
18929
  if (existsSync17(plist))
18583
18930
  exec("launchctl", ["unload", plist]);
18584
18931
  for (const other of installedEngines2(o.home)) {
@@ -18587,7 +18934,7 @@ function engineInstall2(o) {
18587
18934
  if (engineArg2(other.programArgs, "name") !== parsed.name)
18588
18935
  continue;
18589
18936
  exec("launchctl", ["unload", other.plist]);
18590
- rmSync9(other.plist, { force: true });
18937
+ rmSync10(other.plist, { force: true });
18591
18938
  log(`replaced ${other.label}, this engine as installed before`);
18592
18939
  }
18593
18940
  const legacy = enginePlistPath2(o.home, parsed.name);
@@ -18595,12 +18942,12 @@ function engineInstall2(o) {
18595
18942
  const cfg = readConfig(o.home);
18596
18943
  if (org.sole || parsed.org === (cfg.activeOrg ?? cfg.org?.slug)) {
18597
18944
  exec("launchctl", ["unload", legacy]);
18598
- rmSync9(legacy, { force: true });
18945
+ rmSync10(legacy, { force: true });
18599
18946
  log(`replaced ${engineLabel2(parsed.name)}, the engine installed before orgs`);
18600
18947
  } else
18601
18948
  log(`note: ${engineLabel2(parsed.name)} (installed before orgs) still runs — \`sleds engine uninstall --name ${parsed.name}\` removes it if it was this org's`);
18602
18949
  }
18603
- writeFileSync18(plist, buildEnginePlist2({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
18950
+ writeFileSync19(plist, buildEnginePlist2({ label, sledsArgv, args: parsed.args, env: agentEnv, logPath }));
18604
18951
  const r = exec("launchctl", ["load", plist]);
18605
18952
  if (!r.ok) {
18606
18953
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -18620,7 +18967,7 @@ function installUnit2(a) {
18620
18967
  const unit = engineUnitPath(o.home, label);
18621
18968
  const unitName = basename5(unit);
18622
18969
  const systemctl = (...args) => exec("systemctl", ["--user", ...args]);
18623
- mkdirSync16(dirname13(unit), { recursive: true });
18970
+ mkdirSync17(dirname13(unit), { recursive: true });
18624
18971
  if (existsSync17(unit))
18625
18972
  systemctl("stop", unitName);
18626
18973
  for (const other of installedEngines2(o.home)) {
@@ -18629,10 +18976,10 @@ function installUnit2(a) {
18629
18976
  if (engineArg2(other.programArgs, "name") !== parsed.name)
18630
18977
  continue;
18631
18978
  systemctl("disable", "--now", basename5(other.plist));
18632
- rmSync9(other.plist, { force: true });
18979
+ rmSync10(other.plist, { force: true });
18633
18980
  log(`replaced ${other.label}, this engine as installed before`);
18634
18981
  }
18635
- writeFileSync18(unit, buildEngineUnit({
18982
+ writeFileSync19(unit, buildEngineUnit({
18636
18983
  label,
18637
18984
  name: parsed.name,
18638
18985
  sledsArgv: a.sledsArgv,
@@ -18690,7 +19037,7 @@ function engineUninstall2(o) {
18690
19037
  exec("systemctl", ["--user", "disable", "--now", basename5(file)]);
18691
19038
  else
18692
19039
  exec("launchctl", ["unload", file]);
18693
- rmSync9(file, { force: true });
19040
+ rmSync10(file, { force: true });
18694
19041
  if (unit)
18695
19042
  exec("systemctl", ["--user", "daemon-reload"]);
18696
19043
  log(`engine:${name} removed: ${basename5(file, unit ? ".service" : ".plist")} — its sessions keep running`);
@@ -18821,7 +19168,7 @@ import { closeSync as closeSync3, fstatSync as fstatSync2, openSync as openSync3
18821
19168
  import { hostname as hostname6 } from "node:os";
18822
19169
 
18823
19170
  // src/commands/engineStatus.ts
18824
- import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
19171
+ import { existsSync as existsSync18, readFileSync as readFileSync19 } from "node:fs";
18825
19172
  init_state();
18826
19173
 
18827
19174
  // src/engine/status.ts
@@ -19111,7 +19458,7 @@ var engineLogCmd = (o) => {
19111
19458
 
19112
19459
  // src/commands/engineProfile.ts
19113
19460
  init_src();
19114
- import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
19461
+ import { existsSync as existsSync19, readFileSync as readFileSync20 } from "node:fs";
19115
19462
  import { hostname as hostname7 } from "node:os";
19116
19463
  var PROFILE_USAGE = "usage: sleds engine profile [--json] | sleds engine profile set <field>=<value>… [--workspace] [--json] | sleds engine profile set --stdin [--workspace] [--json] | sleds engine profile --strict-mcp [--mcp-allow <a,b>] | --no-strict-mcp [--workspace] [--json] | sleds engine profile --sandbox [--sandbox-allow <a,b>] | --no-sandbox [--workspace] [--json] | sleds engine profile set --hold on|off [--workspace] [--json] | sleds engine profile set cmuxPassword=<pw> | --cmux-password <pw> [--json]";
19117
19464
  var PROFILE_FIELDS = [
@@ -19376,7 +19723,7 @@ async function engineProfileCmd(o) {
19376
19723
  const rest = words.slice(1);
19377
19724
  let stdin = null;
19378
19725
  if (fromStdin) {
19379
- const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync19(0, "utf8")))());
19726
+ const taken = cmuxPasswordFromJson((o.stdin ?? (() => readFileSync20(0, "utf8")))());
19380
19727
  if ("error" in taken || taken.password !== undefined && password !== undefined) {
19381
19728
  error("error" in taken ? taken.error : "the cmux password is given once");
19382
19729
  error(PROFILE_USAGE);
@@ -19461,7 +19808,7 @@ var keychainLine = (k) => k === "set" ? "cmuxPassword: set (Keychain)" : k === "
19461
19808
 
19462
19809
  // src/commands/engineStatus.ts
19463
19810
  init_state();
19464
- import { existsSync as existsSync20, readFileSync as readFileSync20 } from "node:fs";
19811
+ import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
19465
19812
  import { hostname as hostname8 } from "node:os";
19466
19813
  var ENGINE_STATUS_USAGE = "usage: sleds engine status [--name <n>] [--org <slug>] [--json]";
19467
19814
  function parseStatusArgs(argv, host) {
@@ -19517,7 +19864,7 @@ function readEngineStatus(stateDir, engine, host, pidAlive, seat) {
19517
19864
  }
19518
19865
  function safeRead(path) {
19519
19866
  try {
19520
- return readFileSync20(path, "utf8");
19867
+ return readFileSync21(path, "utf8");
19521
19868
  } catch {
19522
19869
  return "";
19523
19870
  }
@@ -19638,7 +19985,7 @@ init_meter();
19638
19985
 
19639
19986
  // src/owed.ts
19640
19987
  init_config();
19641
- import { existsSync as existsSync21, mkdirSync as mkdirSync17, readFileSync as readFileSync21, renameSync as renameSync6, writeFileSync as writeFileSync19 } from "node:fs";
19988
+ import { existsSync as existsSync21, mkdirSync as mkdirSync18, readFileSync as readFileSync22, renameSync as renameSync7, writeFileSync as writeFileSync20 } from "node:fs";
19642
19989
  import { dirname as dirname14, join as join22 } from "node:path";
19643
19990
  var owedPath = (home) => join22(configDir(home), "owed.json");
19644
19991
  var sessionPath = (home) => join22(configDir(home), "session.json");
@@ -19659,17 +20006,17 @@ var readJson = (p) => {
19659
20006
  if (!existsSync21(p))
19660
20007
  return null;
19661
20008
  try {
19662
- return JSON.parse(readFileSync21(p, "utf8"));
20009
+ return JSON.parse(readFileSync22(p, "utf8"));
19663
20010
  } catch {
19664
20011
  return null;
19665
20012
  }
19666
20013
  };
19667
20014
  var writeJson = (p, v) => {
19668
- mkdirSync17(dirname14(p), { recursive: true, mode: 448 });
20015
+ mkdirSync18(dirname14(p), { recursive: true, mode: 448 });
19669
20016
  const tmp = `${p}.${process.pid}.tmp`;
19670
- writeFileSync19(tmp, `${JSON.stringify(v, null, 2)}
20017
+ writeFileSync20(tmp, `${JSON.stringify(v, null, 2)}
19671
20018
  `, { mode: 384 });
19672
- renameSync6(tmp, p);
20019
+ renameSync7(tmp, p);
19673
20020
  };
19674
20021
  var readOwed = (home, now = Date.now()) => {
19675
20022
  const f = readJson(owedPath(home));
@@ -19874,7 +20221,7 @@ var hooksOrg = (home, env = process.env) => {
19874
20221
  };
19875
20222
 
19876
20223
  // src/commands/init.ts
19877
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync20 } from "node:fs";
20224
+ import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync21 } from "node:fs";
19878
20225
  import { createInterface } from "node:readline";
19879
20226
  init_config();
19880
20227
  init_state();
@@ -20561,8 +20908,8 @@ var init = async (o) => {
20561
20908
  });
20562
20909
  if (mcpLine)
20563
20910
  wrote.push(mcpLine);
20564
- mkdirSync18(skillDir(o.home), { recursive: true });
20565
- writeFileSync20(skillPath(o.home), SKILL_MD);
20911
+ mkdirSync19(skillDir(o.home), { recursive: true });
20912
+ writeFileSync21(skillPath(o.home), SKILL_MD);
20566
20913
  wrote.push(`installed skill at ${skillPath(o.home)}`);
20567
20914
  if (o.installMusher) {
20568
20915
  const r = o.installMusher();
@@ -20686,7 +21033,7 @@ var login = async (o) => {
20686
21033
  // src/commands/logout.ts
20687
21034
  init_config();
20688
21035
  init_state();
20689
- import { existsSync as existsSync22, readFileSync as readFileSync22, rmSync as rmSync10 } from "node:fs";
21036
+ import { existsSync as existsSync22, readFileSync as readFileSync23, rmSync as rmSync11 } from "node:fs";
20690
21037
  import { hostname as hostname10 } from "node:os";
20691
21038
  init_settings();
20692
21039
  var LOGOUT_USAGE = "usage: sleds logout [--org <slug>] [--json]";
@@ -20810,7 +21157,7 @@ async function logout(o) {
20810
21157
  continue;
20811
21158
  if (!e.org) {
20812
21159
  (o.exec ?? defaultExec2)("launchctl", ["unload", e.plist]);
20813
- rmSync10(e.plist, { force: true });
21160
+ rmSync11(e.plist, { force: true });
20814
21161
  continue;
20815
21162
  }
20816
21163
  engineUninstall({
@@ -20843,8 +21190,8 @@ async function logout(o) {
20843
21190
  };
20844
21191
  writeConfig(o.home, next);
20845
21192
  const tp = tokenPath(o.home, env);
20846
- if (existsSync22(tp) && readFileSync22(tp, "utf8").trim() === seat.token)
20847
- rmSync10(tp, { force: true });
21193
+ if (existsSync22(tp) && readFileSync23(tp, "utf8").trim() === seat.token)
21194
+ rmSync11(tp, { force: true });
20848
21195
  const active = next.activeOrg ?? next.org?.slug ?? null;
20849
21196
  const result = { ok: true, email, org: slug, paused, active };
20850
21197
  if (parsed.json)
@@ -20921,7 +21268,7 @@ var meter = async (o) => {
20921
21268
  init_config();
20922
21269
  init_meter();
20923
21270
  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";
21271
+ import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync24, rmSync as rmSync12, writeFileSync as writeFileSync22 } from "node:fs";
20925
21272
  import { hostname as hostname11 } from "node:os";
20926
21273
  import { dirname as dirname15 } from "node:path";
20927
21274
  var defaultWhich3 = (cmd) => {
@@ -20961,8 +21308,8 @@ var musherInstall = (o) => {
20961
21308
  const plist = musherPlistPath(o.home, env);
20962
21309
  const label = musherLabel(env);
20963
21310
  const dir = configDir(o.home, env);
20964
- mkdirSync19(dirname15(plist), { recursive: true });
20965
- mkdirSync19(dir, { recursive: true, mode: 448 });
21311
+ mkdirSync20(dirname15(plist), { recursive: true });
21312
+ mkdirSync20(dir, { recursive: true, mode: 448 });
20966
21313
  const execPath = o.sledsArgv[0] ?? process.execPath;
20967
21314
  const agentEnv = {
20968
21315
  PATH: musherPath(o.home, execPath, env.PATH ?? ""),
@@ -20973,7 +21320,7 @@ var musherInstall = (o) => {
20973
21320
  const exec = o.exec ?? defaultExecSync;
20974
21321
  if (existsSync23(plist))
20975
21322
  exec("launchctl", ["unload", plist]);
20976
- writeFileSync21(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
21323
+ writeFileSync22(plist, buildMusherPlist({ sledsArgv: o.sledsArgv, env: agentEnv, logPath: musherLogPath(dir), label }));
20977
21324
  const r = exec("launchctl", ["load", plist]);
20978
21325
  if (!r.ok) {
20979
21326
  error(`launchctl load failed: ${r.stderr.trim() || "unknown"}`);
@@ -20994,7 +21341,7 @@ var musherUninstall = (o) => {
20994
21341
  const exec = o.exec ?? defaultExecSync;
20995
21342
  if (existsSync23(plist)) {
20996
21343
  exec("launchctl", ["unload", plist]);
20997
- rmSync11(plist, { force: true });
21344
+ rmSync12(plist, { force: true });
20998
21345
  log(`musher removed: ${musherLabel(env)}`);
20999
21346
  } else
21000
21347
  log("musher was not installed.");
@@ -21257,7 +21604,7 @@ var on = (o) => {
21257
21604
  init_config();
21258
21605
  init_meter();
21259
21606
  init_settings();
21260
- import { existsSync as existsSync24, rmSync as rmSync12 } from "node:fs";
21607
+ import { existsSync as existsSync24, rmSync as rmSync13 } from "node:fs";
21261
21608
  var ORG_USAGE = "usage: sleds org list [--json] | sleds org use <slug>";
21262
21609
  var orgRows = (cfg, activeSlug) => {
21263
21610
  const seats = { ...cfg.orgs ?? {} };
@@ -21334,7 +21681,7 @@ var orgUse = (o) => {
21334
21681
  writeSettings(o.home, removeProxy(settings));
21335
21682
  }
21336
21683
  try {
21337
- rmSync12(meterCachePath(o.home), { force: true });
21684
+ rmSync13(meterCachePath(o.home), { force: true });
21338
21685
  } catch {}
21339
21686
  log(`Active org: ${seat.org?.name ? `${seat.org.name} (${slug})` : slug}${seat.email ? ` · ${seat.email}` : ""}.`);
21340
21687
  log("Claude Code, the status line and the hooks act as this org now.");
@@ -21360,7 +21707,7 @@ var orgCmd = (o) => {
21360
21707
  // src/commands/pack.ts
21361
21708
  init_src();
21362
21709
  import { spawnSync as spawnSync8 } from "node:child_process";
21363
- import { writeFileSync as writeFileSync22 } from "node:fs";
21710
+ import { writeFileSync as writeFileSync23 } from "node:fs";
21364
21711
  import { join as join23 } from "node:path";
21365
21712
  function packFileName(key) {
21366
21713
  return `pack-${slugify(key, 80) || "item"}.md`;
@@ -21390,22 +21737,43 @@ var packCmd = async (o) => {
21390
21737
  return 0;
21391
21738
  }
21392
21739
  const path = join23(o.cwd ?? process.cwd(), packFileName(key));
21393
- (o.write ?? ((p, t) => writeFileSync22(p, t.endsWith(`
21740
+ (o.write ?? ((p, t) => writeFileSync23(p, t.endsWith(`
21394
21741
  `) ? t : `${t}
21395
21742
  `)))(path, r.text);
21396
21743
  log(`Wrote ${path} — hand it to any model. \`sleds pack ${key} --run\` starts Claude Code with it.`);
21397
21744
  return 0;
21398
21745
  };
21399
21746
 
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;
21747
+ // src/commands/permissionEvent.ts
21748
+ import { chmodSync as chmodSync6, mkdirSync as mkdirSync21, readFileSync as readFileSync25, renameSync as renameSync8, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "node:fs";
21749
+ import { dirname as dirname16 } from "node:path";
21750
+ var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
21751
+ var messageOf = (text) => {
21752
+ try {
21753
+ const raw = JSON.parse(text);
21754
+ return isObject13(raw) && typeof raw.message === "string" ? raw.message : "";
21755
+ } catch {
21756
+ return "";
21757
+ }
21758
+ };
21759
+ var permissionEvent = async (o) => {
21760
+ try {
21761
+ const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS);
21762
+ if (!o.mark || notificationType(input) !== "permission_prompt")
21763
+ return 0;
21764
+ const mark = { message: messageOf(input), at: (o.now ?? Date.now)() };
21765
+ mkdirSync21(dirname16(o.mark), { recursive: true, mode: 448 });
21766
+ const tmp = `${o.mark}.${process.pid}.tmp`;
21767
+ writeFileSync24(tmp, `${JSON.stringify(mark)}
21768
+ `, { mode: 384 });
21769
+ renameSync8(tmp, o.mark);
21770
+ chmodSync6(o.mark, 384);
21771
+ } catch {}
21772
+ return 0;
21773
+ };
21407
21774
 
21408
21775
  // src/commands/promptEvent.ts
21776
+ init_config();
21409
21777
  var LABEL = {
21410
21778
  fiveHour: "5-hour",
21411
21779
  sevenDay: "weekly",
@@ -21488,7 +21856,7 @@ var promptEvent = async (o) => {
21488
21856
 
21489
21857
  // src/commands/route.ts
21490
21858
  init_src();
21491
- import { readFileSync as readFileSync24 } from "node:fs";
21859
+ import { readFileSync as readFileSync26 } from "node:fs";
21492
21860
  import { hostname as hostname12 } from "node:os";
21493
21861
  var ARM_USAGE = "usage: sleds route arm <key> [--off] [--json] [--name <engine>] [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--mode <permission-mode>] [--model <id>] [--effort <e>] [--branch '<template>'] [--pr-title '<template>'] [--repos-dir <dir>]";
21494
21862
  var USAGE = "usage: sleds route <key> [--long | --dot | --json] | sleds route arm <key> [--off] [--json] | sleds route answer <key>/<node> --stdin | sleds route propose <key> --file <nodes.json> | sleds route approve <key> | sleds route redraft <key> | sleds route single <key> | sleds route decisions [<key>] [--flagged] | sleds route decision <key> <id> --seen | --answer --stdin [--route accept|keep|redraft] | --keep-route | --apply [--answer --stdin] | --overrule --stdin | --ready | sleds route run <key> [--at-once <n>] [--runner tmux|headless|command|orca|cmux] [--runner-cmd '<launcher>'] [--agent '<command>'] [--interval <s>] [--lease <min>] [--idle <min>] [--repo <path>] [--repos-dir <dir>] [--mode <permission-mode>] [--model <id>] [--branch '<template>'] [--pr-title '<template>'] [--once]";
@@ -21661,7 +22029,7 @@ async function answerCmd(o, argv) {
21661
22029
  const words = argv.filter((a) => a !== "--json" && a !== "--stdin");
21662
22030
  const ref = words[0] ?? "";
21663
22031
  const slash = ref.lastIndexOf("/");
21664
- const answer = (fromStdin ? (o.stdin ?? (() => readFileSync24(0, "utf8")))() : words.slice(1).join(" ")).trim();
22032
+ const answer = (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : words.slice(1).join(" ")).trim();
21665
22033
  if (slash <= 0 || slash === ref.length - 1 || fromStdin && words.length > 1) {
21666
22034
  error(ANSWER_USAGE);
21667
22035
  return 2;
@@ -21858,7 +22226,7 @@ async function decisionCmd(o, argv) {
21858
22226
  error(DECISION_USAGE);
21859
22227
  return 2;
21860
22228
  }
21861
- const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync24(0, "utf8")))() : rest.join(" ")).trim();
22229
+ const md = seen || routeAlone ? "" : (fromStdin ? (o.stdin ?? (() => readFileSync26(0, "utf8")))() : rest.join(" ")).trim();
21862
22230
  if (!seen && !routeAlone && (!md || fromStdin && rest.length > 0)) {
21863
22231
  error(md ? DECISION_USAGE : `${answer ? "an answer" : "an overrule"} needs some text`);
21864
22232
  return 2;
@@ -21999,7 +22367,7 @@ function approvedLine(key, j) {
21999
22367
  function readNodes(o, a) {
22000
22368
  let text;
22001
22369
  try {
22002
- text = a.file ? (o.readFile ?? ((p) => readFileSync24(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync24(0, "utf8")))();
22370
+ text = a.file ? (o.readFile ?? ((p) => readFileSync26(p, "utf8")))(a.file) : (o.stdin ?? (() => readFileSync26(0, "utf8")))();
22003
22371
  } catch (e) {
22004
22372
  return { error: e instanceof Error ? e.message : String(e) };
22005
22373
  }
@@ -22273,11 +22641,11 @@ var run = async (o) => {
22273
22641
  init_config();
22274
22642
  init_meter();
22275
22643
  var HOOK_STDIN_TIMEOUT_MS2 = 1000;
22276
- var isObject11 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22277
- var notificationType = (text) => {
22644
+ var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22645
+ var notificationType2 = (text) => {
22278
22646
  try {
22279
22647
  const raw = JSON.parse(text);
22280
- if (!isObject11(raw))
22648
+ if (!isObject14(raw))
22281
22649
  return;
22282
22650
  return typeof raw.notification_type === "string" ? raw.notification_type : undefined;
22283
22651
  } catch {
@@ -22288,7 +22656,7 @@ var seatEvent = async (o) => {
22288
22656
  const log = o.log ?? console.log;
22289
22657
  const now = (o.now ?? Date.now)();
22290
22658
  const input = o.stdin ?? await readStdin(HOOK_STDIN_TIMEOUT_MS2);
22291
- const kind = notificationType(input);
22659
+ const kind = notificationType2(input);
22292
22660
  try {
22293
22661
  const key = seatKeyFor(o.home, process.env);
22294
22662
  const state = readSeatState(o.home, key);
@@ -22310,12 +22678,12 @@ var seatEvent = async (o) => {
22310
22678
 
22311
22679
  // src/commands/seatSync.ts
22312
22680
  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";
22681
+ import { existsSync as existsSync25, mkdirSync as mkdirSync22, readFileSync as readFileSync27, readdirSync as readdirSync5, rmSync as rmSync16, writeFileSync as writeFileSync25 } from "node:fs";
22314
22682
  import { platform as platform2 } from "node:os";
22315
- import { dirname as dirname16, join as join25 } from "node:path";
22683
+ import { dirname as dirname17, join as join25 } from "node:path";
22316
22684
 
22317
22685
  // src/cookieDb.ts
22318
- import { copyFileSync, rmSync as rmSync13 } from "node:fs";
22686
+ import { copyFileSync, rmSync as rmSync15 } from "node:fs";
22319
22687
  import { tmpdir as tmpdir2 } from "node:os";
22320
22688
  import { join as join24 } from "node:path";
22321
22689
  var QUERY = "SELECT name, hex(encrypted_value) AS ev FROM cookies WHERE host_key LIKE '%claude.ai%'";
@@ -22353,7 +22721,7 @@ async function readCookieRows(dbPath) {
22353
22721
  return rows.map((r) => ({ name: r.name, encrypted_value: Buffer.from(r.ev, "hex") }));
22354
22722
  } finally {
22355
22723
  try {
22356
- rmSync13(copy, { force: true });
22724
+ rmSync15(copy, { force: true });
22357
22725
  } catch {}
22358
22726
  }
22359
22727
  }
@@ -22438,8 +22806,8 @@ function resolveProfileDir(localStateJson, profile) {
22438
22806
  if (localStateJson) {
22439
22807
  try {
22440
22808
  const raw = JSON.parse(localStateJson);
22441
- const p = isObject12(raw) && isObject12(raw.profile) ? raw.profile : null;
22442
- if (p && isObject12(p.info_cache))
22809
+ const p = isObject15(raw) && isObject15(raw.profile) ? raw.profile : null;
22810
+ if (p && isObject15(p.info_cache))
22443
22811
  cache = p.info_cache;
22444
22812
  } catch {}
22445
22813
  }
@@ -22448,7 +22816,7 @@ function resolveProfileDir(localStateJson, profile) {
22448
22816
  const want = profile.toLowerCase();
22449
22817
  const hits = [];
22450
22818
  for (const [dir, info] of Object.entries(cache)) {
22451
- if (!isObject12(info))
22819
+ if (!isObject15(info))
22452
22820
  continue;
22453
22821
  const match = ["name", "user_name", "gaia_name"].some((k) => {
22454
22822
  const v = info[k];
@@ -22487,15 +22855,15 @@ function sourceArgs(source) {
22487
22855
  function seatTokenPath(home, source) {
22488
22856
  return `${home}/.config/sleds/${launchLabel(source).slice("ai.sleds.".length)}.token`;
22489
22857
  }
22490
- var isObject12 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22858
+ var isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22491
22859
  var num7 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
22492
22860
  var round22 = (n) => Math.round(n * 100) / 100;
22493
22861
  function parseUsageResponse(raw, now = Date.now()) {
22494
- if (!isObject12(raw))
22862
+ if (!isObject15(raw))
22495
22863
  return null;
22496
22864
  const out = { capturedAt: now, models: {} };
22497
22865
  for (const [k, v] of Object.entries(raw)) {
22498
- if (!isObject12(v))
22866
+ if (!isObject15(v))
22499
22867
  continue;
22500
22868
  if (k === "extra_usage") {
22501
22869
  const credits = num7(v.used_credits);
@@ -22660,7 +23028,7 @@ function findCookieDb(home, source) {
22660
23028
  return db;
22661
23029
  }
22662
23030
  const localStatePath = join25(root, "Local State");
22663
- const localState = existsSync25(localStatePath) ? readFileSync25(localStatePath, "utf8") : null;
23031
+ const localState = existsSync25(localStatePath) ? readFileSync27(localStatePath, "utf8") : null;
22664
23032
  const wanted = source.profile ?? "Default";
22665
23033
  const { dir, ambiguous } = resolveProfileDir(localState, wanted);
22666
23034
  if (ambiguous)
@@ -22743,16 +23111,16 @@ function install(home, argv, source, log) {
22743
23111
  return 1;
22744
23112
  }
22745
23113
  const dir = join25(home, "Library", "LaunchAgents");
22746
- mkdirSync20(dir, { recursive: true });
23114
+ mkdirSync22(dir, { recursive: true });
22747
23115
  const path = plistPath2(home, source);
22748
23116
  const tokenPath = seatTokenPath(home, source);
22749
- rmSync14(tokenPath, { force: true });
23117
+ rmSync16(tokenPath, { force: true });
22750
23118
  if (source.token) {
22751
- mkdirSync20(dirname16(tokenPath), { recursive: true, mode: 448 });
22752
- writeFileSync23(tokenPath, `${source.token}
23119
+ mkdirSync22(dirname17(tokenPath), { recursive: true, mode: 448 });
23120
+ writeFileSync25(tokenPath, `${source.token}
22753
23121
  `, { mode: 384 });
22754
23122
  }
22755
- writeFileSync23(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
23123
+ writeFileSync25(path, launchAgentPlist(bun, script, home, source), { mode: 384 });
22756
23124
  try {
22757
23125
  execFileSync4("launchctl", ["unload", path], { stdio: "ignore" });
22758
23126
  } catch {}
@@ -22765,7 +23133,7 @@ function uninstall(home, source, log) {
22765
23133
  try {
22766
23134
  execFileSync4("launchctl", ["unload", "-w", path], { stdio: "ignore" });
22767
23135
  } catch {}
22768
- rmSync14(seatTokenPath(home, source), { force: true });
23136
+ rmSync16(seatTokenPath(home, source), { force: true });
22769
23137
  log(existsSync25(path) ? `seat-sync timer removed (${path} can be deleted)` : "no seat-sync timer");
22770
23138
  return 0;
22771
23139
  }
@@ -22840,7 +23208,7 @@ var seatSync = async (o) => {
22840
23208
  };
22841
23209
  function readSeatToken(home, source) {
22842
23210
  try {
22843
- return readFileSync25(seatTokenPath(home, source), "utf8").trim() || undefined;
23211
+ return readFileSync27(seatTokenPath(home, source), "utf8").trim() || undefined;
22844
23212
  } catch {
22845
23213
  return;
22846
23214
  }
@@ -22923,7 +23291,7 @@ var sessionEvent = async (o) => {
22923
23291
 
22924
23292
  // src/commands/specEvent.ts
22925
23293
  init_meter();
22926
- import { readFileSync as readFileSync26, statSync as statSync2 } from "node:fs";
23294
+ import { readFileSync as readFileSync28, statSync as statSync2 } from "node:fs";
22927
23295
  import { basename as basename6, isAbsolute as isAbsolute4, relative } from "node:path";
22928
23296
  var HOOK_STDIN_TIMEOUT_MS3 = 1000;
22929
23297
  var ESTIMATE_TIMEOUT_MS = 4000;
@@ -22979,7 +23347,7 @@ function readFileOrNull(path) {
22979
23347
  try {
22980
23348
  if (statSync2(path).size > 2000000)
22981
23349
  return null;
22982
- return readFileSync26(path, "utf8");
23350
+ return readFileSync28(path, "utf8");
22983
23351
  } catch {
22984
23352
  return null;
22985
23353
  }
@@ -23308,10 +23676,10 @@ var announce = (o, events) => {
23308
23676
 
23309
23677
  // src/commands/stopEvent.ts
23310
23678
  init_meter();
23311
- import { existsSync as existsSync27, readFileSync as readFileSync27 } from "node:fs";
23679
+ import { existsSync as existsSync27, readFileSync as readFileSync29 } from "node:fs";
23312
23680
 
23313
23681
  // src/transcript.ts
23314
- var isObject13 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23682
+ var isObject16 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23315
23683
  var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
23316
23684
  function sumTranscript(text) {
23317
23685
  const seen = new Set;
@@ -23332,7 +23700,7 @@ function sumTranscript(text) {
23332
23700
  } catch {
23333
23701
  continue;
23334
23702
  }
23335
- if (!isObject13(j) || j.type !== "assistant" || !isObject13(j.message))
23703
+ if (!isObject16(j) || j.type !== "assistant" || !isObject16(j.message))
23336
23704
  continue;
23337
23705
  const m = j.message;
23338
23706
  const id = typeof m.id === "string" ? m.id : typeof j.uuid === "string" ? j.uuid : undefined;
@@ -23341,7 +23709,7 @@ function sumTranscript(text) {
23341
23709
  continue;
23342
23710
  seen.add(id);
23343
23711
  }
23344
- const u = isObject13(m.usage) ? m.usage : undefined;
23712
+ const u = isObject16(m.usage) ? m.usage : undefined;
23345
23713
  if (!u)
23346
23714
  continue;
23347
23715
  const input = n(u.input_tokens);
@@ -23404,7 +23772,7 @@ async function postTranscript(o, payload, now) {
23404
23772
  const src = meterSource(o.home);
23405
23773
  if (!src)
23406
23774
  return false;
23407
- const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync27(p, "utf8") : null);
23775
+ const read = o.readFile ?? ((p) => existsSync27(p) ? readFileSync29(p, "utf8") : null);
23408
23776
  let text;
23409
23777
  try {
23410
23778
  text = read(path);
@@ -23482,7 +23850,7 @@ var stopEvent = async (o) => {
23482
23850
 
23483
23851
  // src/commands/uninstall.ts
23484
23852
  import { spawnSync as spawnSync9 } from "node:child_process";
23485
- import { existsSync as existsSync28, rmSync as rmSync15 } from "node:fs";
23853
+ import { existsSync as existsSync28, rmSync as rmSync17 } from "node:fs";
23486
23854
  init_config();
23487
23855
  init_meter();
23488
23856
  init_settings();
@@ -23543,19 +23911,19 @@ var uninstall2 = (o) => {
23543
23911
  }
23544
23912
  for (const file of [meterCachePath(o.home), ...allSeatStatePaths(o.home)]) {
23545
23913
  if (existsSync28(file)) {
23546
- rmSync15(file, { force: true });
23914
+ rmSync17(file, { force: true });
23547
23915
  removed.push(file);
23548
23916
  }
23549
23917
  }
23550
23918
  const skill = skillDir(o.home);
23551
23919
  if (existsSync28(skill)) {
23552
- rmSync15(skill, { recursive: true, force: true });
23920
+ rmSync17(skill, { recursive: true, force: true });
23553
23921
  removed.push(skill);
23554
23922
  }
23555
23923
  const plist = musherPlistPath(o.home);
23556
23924
  if (existsSync28(plist)) {
23557
23925
  (o.unloadAgent ?? defaultUnload)(plist);
23558
- rmSync15(plist, { force: true });
23926
+ rmSync17(plist, { force: true });
23559
23927
  removed.push(`launchd agent ${musherLabel()}`);
23560
23928
  }
23561
23929
  if (removed.length === 0) {
@@ -23708,17 +24076,17 @@ var webhooksCmd = async (o) => {
23708
24076
 
23709
24077
  // src/config.ts
23710
24078
  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";
24079
+ import { chmodSync as chmodSync7, existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync30, renameSync as renameSync9, writeFileSync as writeFileSync26 } from "node:fs";
23712
24080
  import { join as join26, resolve as resolve10 } from "node:path";
23713
24081
  var configDir2 = (home, env = process.env) => {
23714
24082
  const o = env.SLEDS_HOME?.trim();
23715
24083
  return o ? resolve10(home, o) : join26(home, ".config", "sleds");
23716
24084
  };
23717
24085
  var configPath2 = (home) => join26(configDir2(home), "config.json");
23718
- var isObject14 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
24086
+ var isObject17 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23719
24087
  var optString2 = (v) => typeof v === "string" ? v : undefined;
23720
24088
  var parsePriorEnv2 = (v) => {
23721
- if (!isObject14(v))
24089
+ if (!isObject17(v))
23722
24090
  return;
23723
24091
  const out = {};
23724
24092
  const url = optString2(v[BASE_URL_KEY]);
@@ -23733,7 +24101,7 @@ var parsePriorEnv2 = (v) => {
23733
24101
  };
23734
24102
  var parseSchedule2 = (v) => v === "launchd" || v === "systemd" || v === "manual" ? v : undefined;
23735
24103
  var parseConfig2 = (raw) => {
23736
- if (!isObject14(raw))
24104
+ if (!isObject17(raw))
23737
24105
  return {};
23738
24106
  const cfg = {};
23739
24107
  const token = optString2(raw.token);
@@ -23783,11 +24151,11 @@ var parseConfig2 = (raw) => {
23783
24151
  return cfg;
23784
24152
  };
23785
24153
  var parseOrgs2 = (raw) => {
23786
- if (!isObject14(raw))
24154
+ if (!isObject17(raw))
23787
24155
  return;
23788
24156
  const out = {};
23789
24157
  for (const [slug, v] of Object.entries(raw)) {
23790
- if (!isObject14(v))
24158
+ if (!isObject17(v))
23791
24159
  continue;
23792
24160
  const token = optString2(v.token);
23793
24161
  if (!token)
@@ -23806,7 +24174,7 @@ var parseOrgs2 = (raw) => {
23806
24174
  return Object.keys(out).length > 0 ? out : undefined;
23807
24175
  };
23808
24176
  var parseMusher2 = (raw) => {
23809
- if (!isObject14(raw))
24177
+ if (!isObject17(raw))
23810
24178
  return;
23811
24179
  const out = {};
23812
24180
  const pm = optString2(raw.permissionMode);
@@ -23822,7 +24190,7 @@ var parseMusher2 = (raw) => {
23822
24190
  return out;
23823
24191
  };
23824
24192
  var parseOrgInfo2 = (raw) => {
23825
- if (!isObject14(raw) || !isObject14(raw.capabilities))
24193
+ if (!isObject17(raw) || !isObject17(raw.capabilities))
23826
24194
  return;
23827
24195
  const name = optString2(raw.name);
23828
24196
  const slug = optString2(raw.slug);
@@ -23848,7 +24216,7 @@ var readConfig2 = (home) => {
23848
24216
  const p = configPath2(home);
23849
24217
  if (!existsSync29(p))
23850
24218
  return {};
23851
- return parseConfig2(JSON.parse(readFileSync28(p, "utf8")));
24219
+ return parseConfig2(JSON.parse(readFileSync30(p, "utf8")));
23852
24220
  };
23853
24221
 
23854
24222
  // src/runners/exec.ts
@@ -23900,7 +24268,7 @@ var defaultExec5 = (cmd, args) => {
23900
24268
  };
23901
24269
 
23902
24270
  // src/settings.ts
23903
- import { dirname as dirname17, join as join27, resolve as resolve11 } from "node:path";
24271
+ import { dirname as dirname18, join as join27, resolve as resolve11 } from "node:path";
23904
24272
  var withSeatEnv2 = (command, home, env = process.env, org) => {
23905
24273
  const parts = [];
23906
24274
  const sh = env.SLEDS_HOME?.trim();
@@ -23923,15 +24291,16 @@ var pinOrg4 = (command, org) => {
23923
24291
  return `${lead}SLEDS_ORG=${shellWord2(slug)} ${bare.slice(lead.length)}`;
23924
24292
  };
23925
24293
  var shellWord2 = (v) => /^[\w./~-]+$/.test(v) ? v : `'${v.replace(/'/g, "'\\''")}'`;
23926
- var isObject15 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
24294
+ var isObject18 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
23927
24295
  var isSledsHookFor2 = (sub) => (entry) => {
23928
- if (!isObject15(entry) || !Array.isArray(entry.hooks))
24296
+ if (!isObject18(entry) || !Array.isArray(entry.hooks))
23929
24297
  return false;
23930
24298
  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));
24299
+ return entry.hooks.some((h) => isObject18(h) && h.type === "command" && typeof h.command === "string" && re.test(h.command));
23932
24300
  };
23933
24301
  var isSledsSessionHook2 = isSledsHookFor2("session-event");
23934
24302
  var isSledsStopHook2 = isSledsHookFor2("stop-event");
24303
+ var isSledsPermissionHook2 = isSledsHookFor2("permission-event");
23935
24304
 
23936
24305
  // src/index.ts
23937
24306
  var USAGE3 = `usage: sleds <command> [options]
@@ -24081,6 +24450,8 @@ commands:
24081
24450
  statusline Print the one-line meter Claude Code shows (installed by init; 10s cache).
24082
24451
  hooks Register the Claude Code Notification hook for seat limits (init does this too).
24083
24452
  seat-event Claude Code hook: record a seat wall and say how to continue. Reads hook JSON on stdin.
24453
+ permission-event --mark <file> Claude Code Notification hook the engine gives its sessions: on
24454
+ a permission dialog, write {message, at} to <file> for the engine. Local; exits 0.
24084
24455
  session-event Claude Code SessionStart hook: states the loop and the rows this machine still
24085
24456
  owes before the first turn. Reads hook JSON on stdin.
24086
24457
  stop-event Claude Code Stop hook: a session that estimated a row and posted no actual is asked
@@ -24225,7 +24596,8 @@ var main = async (argv) => {
24225
24596
  exec: defaultExec4,
24226
24597
  runnerFor: runnerFor3,
24227
24598
  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
24228
- shouldStop: () => stopping
24599
+ shouldStop: () => stopping,
24600
+ sledsArgv: sledsArgv()
24229
24601
  };
24230
24602
  return cmd === "engine" ? engineCmd(io) : routeCmd(io);
24231
24603
  }
@@ -24332,6 +24704,8 @@ var main = async (argv) => {
24332
24704
  });
24333
24705
  case "seat-event":
24334
24706
  return seatEvent({ home });
24707
+ case "permission-event":
24708
+ return permissionEvent({ mark: stringFlag(flags, "mark") });
24335
24709
  case "spec-event":
24336
24710
  return specEvent({ home });
24337
24711
  case "prompt-event":
@@ -24389,7 +24763,7 @@ ${renderHelpText(argv[1])}`);
24389
24763
  case "version":
24390
24764
  case "--version":
24391
24765
  case "-v":
24392
- console.log("sleds 0.1.38");
24766
+ console.log("sleds 0.1.39");
24393
24767
  return 0;
24394
24768
  default:
24395
24769
  console.error(`unknown command: ${cmd}