@tablation/crew 0.72.2 → 0.72.3

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/cli.js +403 -344
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -130,21 +130,21 @@ function formatStaleOwnLock(scope, holder) {
130
130
 
131
131
  // src/selftest.ts
132
132
  import { spawn as spawn3 } from "child_process";
133
- import { existsSync as existsSync19, mkdirSync as mkdirSync9, mkdtempSync, readFileSync as readFileSync15, rmSync as rmSync9, writeFileSync as writeFileSync10 } from "fs";
133
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10, mkdtempSync, readFileSync as readFileSync16, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "fs";
134
134
  import { tmpdir as tmpdir2 } from "os";
135
- import { join as join18 } from "path";
135
+ import { join as join19 } from "path";
136
136
 
137
137
  // src/agent.ts
138
138
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
139
- import { readFileSync as readFileSync14, existsSync as existsSync18, statSync as statSync5, mkdirSync as mkdirSync8, writeFileSync as writeFileSync9, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
139
+ import { readFileSync as readFileSync15, existsSync as existsSync18, statSync as statSync5, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
140
140
  import { StringDecoder } from "string_decoder";
141
141
  import { finished } from "stream/promises";
142
- import { join as join17, dirname as dirname7, basename as basename7 } from "path";
143
- import { createHash as createHash2 } from "crypto";
142
+ import { join as join18, dirname as dirname7, basename as basename7 } from "path";
143
+ import { createHash as createHash3 } from "crypto";
144
144
 
145
145
  // src/config.ts
146
- import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
147
- import { resolve, dirname as dirname3, isAbsolute, join as join7, basename as basename3 } from "path";
146
+ import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
147
+ import { resolve, dirname as dirname3, isAbsolute, join as join8, basename as basename3 } from "path";
148
148
  import { homedir as homedir5, tmpdir } from "os";
149
149
  import { parse as parse2 } from "yaml";
150
150
 
@@ -294,10 +294,10 @@ var Emitter = class _Emitter {
294
294
  var eventFileFor = (stateDir) => join(stateDir, "events.jsonl");
295
295
 
296
296
  // src/pair-install.ts
297
- import { existsSync as existsSync5, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "fs";
297
+ import { existsSync as existsSync5, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
298
298
  import { spawnSync as spawnSync3 } from "child_process";
299
299
  import { homedir as homedir4 } from "os";
300
- import { join as join5 } from "path";
300
+ import { join as join6 } from "path";
301
301
 
302
302
  // src/stream.ts
303
303
  function toolTarget(name, input) {
@@ -349,9 +349,38 @@ function extractResult(line) {
349
349
 
350
350
  // src/claude-mcp.ts
351
351
  import { spawnSync } from "child_process";
352
- import { readFileSync as readFileSync2 } from "fs";
352
+ import { readFileSync as readFileSync3 } from "fs";
353
353
  import { homedir } from "os";
354
+ import { join as join3 } from "path";
355
+
356
+ // src/mcp-registrations.ts
357
+ import { createHash } from "crypto";
358
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
354
359
  import { join as join2 } from "path";
360
+ var fileFor = (stateDir) => join2(stateDir, "mcp-registrations.json");
361
+ var keyFingerprint = (key) => createHash("sha256").update(key).digest("hex").slice(0, 8);
362
+ function readMcpRegistrations(stateDir) {
363
+ try {
364
+ const j = JSON.parse(readFileSync2(fileFor(stateDir), "utf8"));
365
+ return Array.isArray(j) ? j : [];
366
+ } catch {
367
+ return [];
368
+ }
369
+ }
370
+ function recordMcpRegistration(stateDir, r, now = /* @__PURE__ */ new Date()) {
371
+ const rest = readMcpRegistrations(stateDir).filter((x) => !(x.harness === r.harness && x.scope === r.scope && x.name === r.name));
372
+ mkdirSync2(stateDir, { recursive: true });
373
+ writeFileSync(fileFor(stateDir), `${JSON.stringify([...rest, { ...r, at: now.toISOString() }], null, 2)}
374
+ `, { mode: 384 });
375
+ }
376
+ function findMcpRegistration(stateDir, r) {
377
+ const fp = keyFingerprint(r.key);
378
+ return readMcpRegistrations(stateDir).find(
379
+ (x) => x.harness === r.harness && x.scope === r.scope && x.name === r.name && x.url === r.url && x.keyFingerprint === fp
380
+ );
381
+ }
382
+
383
+ // src/claude-mcp.ts
355
384
  var MCP_SERVER_NAME = "tablation";
356
385
  var redact = (s, key) => key ? s.split(key).join("sk_***") : s;
357
386
  function mcpUrlFor(baseUrl) {
@@ -364,7 +393,7 @@ function runClaude(bin, args) {
364
393
  }
365
394
  function userScopeEntry(home) {
366
395
  try {
367
- const j = JSON.parse(readFileSync2(join2(home, ".claude.json"), "utf8"));
396
+ const j = JSON.parse(readFileSync3(join3(home, ".claude.json"), "utf8"));
368
397
  return j.mcpServers?.[MCP_SERVER_NAME];
369
398
  } catch {
370
399
  return void 0;
@@ -380,18 +409,32 @@ function inspectClaudeMcp(o) {
380
409
  }
381
410
  if (entry.url !== o.url) return { state: "different", detail: `it points at ${entry.url ?? "(no url)"}, not ${o.url}` };
382
411
  const auth = Object.entries(entry.headers ?? {}).find(([k]) => k.toLowerCase() === "authorization")?.[1];
383
- if (auth !== `Bearer ${o.key}`) return { state: "different", detail: `it is registered with a different key than this route's` };
412
+ if (auth !== `Bearer ${o.key}`) {
413
+ const bearer = auth?.startsWith("Bearer ") ? auth.slice("Bearer ".length) : void 0;
414
+ const mine = o.stateDir && bearer ? findMcpRegistration(o.stateDir, { harness: "claude", scope: "user", name: MCP_SERVER_NAME, url: o.url, key: bearer }) : void 0;
415
+ if (mine) return { state: "different", detail: `crew registered an earlier key here`, crewWrote: mine.at };
416
+ return { state: "different", detail: `it points at ${o.url} with a key that is not one crew issued` };
417
+ }
384
418
  return { state: "registered", url: o.url };
385
419
  }
386
420
  function ensureClaudeMcp(o) {
421
+ return ensureClaudeMcpResult(o).line;
422
+ }
423
+ function ensureClaudeMcpResult(o) {
424
+ const r = ensureInner(o);
425
+ return { ...r, url: o.url };
426
+ }
427
+ function ensureInner(o) {
387
428
  const manual = `claude mcp add --transport http ${MCP_SERVER_NAME} ${o.url} --header "Authorization: Bearer <key>" --scope user`;
388
429
  const st = inspectClaudeMcp(o);
389
- if (st.state === "no-claude") return `mcp: \`${o.bin}\` not found, so Claude Code was not configured. To do it by hand: ${manual}`;
390
- if (st.state === "registered") return `mcp: ${MCP_SERVER_NAME} already registered with Claude Code \u2192 ${st.url}`;
391
- if (st.state === "different" && !o.replace) {
392
- return `mcp: left Claude Code's "${MCP_SERVER_NAME}" server alone \u2014 ${st.detail}. Re-run with --mcp replace to overwrite it.`;
393
- }
394
- if (o.dryRun) return `(dry run) would register the ${MCP_SERVER_NAME} MCP server with Claude Code \u2192 ${o.url}`;
430
+ if (st.state === "no-claude") return { line: `mcp: \`${o.bin}\` not found, so Claude Code was not configured. To do it by hand: ${manual}` };
431
+ if (st.state === "registered") return { line: `mcp: ${MCP_SERVER_NAME} already registered with Claude Code \u2192 ${st.url}`, action: "registered" };
432
+ const crewWrote = st.state === "different" ? st.crewWrote : void 0;
433
+ const ours = crewWrote !== void 0;
434
+ if (st.state === "different" && !o.replace && !ours) {
435
+ return { line: `mcp: left Claude Code's "${MCP_SERVER_NAME}" server alone \u2014 ${st.detail}. Re-run with --mcp replace to overwrite it.`, action: "kept" };
436
+ }
437
+ if (o.dryRun) return { line: `(dry run) would register the ${MCP_SERVER_NAME} MCP server with Claude Code \u2192 ${o.url}` };
395
438
  if (st.state === "different") {
396
439
  runClaude(o.bin, ["mcp", "remove", MCP_SERVER_NAME, "--scope", "user"]);
397
440
  }
@@ -408,9 +451,21 @@ function ensureClaudeMcp(o) {
408
451
  "user"
409
452
  ]);
410
453
  if (add.status !== 0) {
411
- return `mcp: \`claude mcp add\` failed (${redact(add.stderr.trim(), o.key) || `exit ${add.status}`}). To do it by hand: ${manual}`;
454
+ return { line: `mcp: \`claude mcp add\` failed (${redact(add.stderr.trim(), o.key) || `exit ${add.status}`}). To do it by hand: ${manual}` };
455
+ }
456
+ if (o.stateDir) {
457
+ try {
458
+ recordMcpRegistration(o.stateDir, { harness: "claude", scope: "user", name: MCP_SERVER_NAME, url: o.url, keyFingerprint: keyFingerprint(o.key) });
459
+ } catch {
460
+ }
461
+ }
462
+ if (crewWrote !== void 0) {
463
+ return { line: `mcp: updated the key crew registered on ${crewWrote.slice(0, 10)} \u2192 ${o.url}`, action: "replaced" };
412
464
  }
413
- return `mcp: registered the ${MCP_SERVER_NAME} MCP server with Claude Code \u2192 ${o.url} (user scope, ~/.claude.json)`;
465
+ return {
466
+ line: `mcp: ${st.state === "different" ? "replaced" : "registered"} the ${MCP_SERVER_NAME} MCP server with Claude Code \u2192 ${o.url} (user scope, ~/.claude.json)`,
467
+ action: st.state === "different" ? "replaced" : "registered"
468
+ };
414
469
  }
415
470
  function removeClaudeMcp(o) {
416
471
  const got = runClaude(o.bin, ["mcp", "get", MCP_SERVER_NAME]);
@@ -422,8 +477,8 @@ function removeClaudeMcp(o) {
422
477
  }
423
478
 
424
479
  // src/launcher.ts
425
- import { basename, join as join3 } from "path";
426
- import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync } from "fs";
480
+ import { basename, join as join4 } from "path";
481
+ import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync } from "fs";
427
482
  import { homedir as homedir2 } from "os";
428
483
  function isOriLauncher(spec) {
429
484
  return !!spec.launcher && basename(spec.launcher) === "ori";
@@ -432,7 +487,7 @@ function isOmlxLauncher(spec) {
432
487
  return !!spec.launcher && basename(spec.launcher) === "omlx";
433
488
  }
434
489
  function defaultOmlxBin(home = homedir2()) {
435
- return join3(home, ".omlx", "bin", "omlx");
490
+ return join4(home, ".omlx", "bin", "omlx");
436
491
  }
437
492
  function launcherExecutable(spec, home) {
438
493
  if (!spec.launcher) return void 0;
@@ -485,8 +540,8 @@ function omlxModelPresent(model, dirs) {
485
540
  });
486
541
  }
487
542
  function readOmlxSettings(home = homedir2()) {
488
- const p = join3(home, ".omlx", "settings.json");
489
- return existsSync3(p) ? parseOmlxSettings(readFileSync3(p, "utf8")) : void 0;
543
+ const p = join4(home, ".omlx", "settings.json");
544
+ return existsSync3(p) ? parseOmlxSettings(readFileSync4(p, "utf8")) : void 0;
490
545
  }
491
546
 
492
547
  // src/capability.ts
@@ -613,17 +668,18 @@ var claudeAdapter = {
613
668
  const s = inspectClaudeMcp(o);
614
669
  return s.state === "no-claude" ? { state: "no-binary" } : s;
615
670
  },
616
- ensureMcp: ensureClaudeMcp
671
+ ensureMcp: ensureClaudeMcp,
672
+ ensureMcpResult: ensureClaudeMcpResult
617
673
  };
618
674
 
619
675
  // src/adapters/codex.ts
620
676
  import { spawnSync as spawnSync2 } from "child_process";
621
677
  import { homedir as homedir3 } from "os";
622
- import { join as join4 } from "path";
623
- import { rmSync as rmSync3, writeFileSync as writeFileSync2 } from "fs";
678
+ import { join as join5 } from "path";
679
+ import { rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
624
680
 
625
681
  // src/pair-files.ts
626
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync, rmSync as rmSync2 } from "fs";
682
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2, rmSync as rmSync2 } from "fs";
627
683
  import { dirname as dirname2 } from "path";
628
684
  var PAIR_BLOCK_START = "<!-- crew:pair start -->";
629
685
  var PAIR_BLOCK_END = "<!-- crew:pair end -->";
@@ -664,11 +720,11 @@ function removePairBlock(text) {
664
720
  return text.replace(new RegExp(`\\n*${BLOCK_RE.source}`), "\n").replace(/^\n+/, "").replace(/\n{3,}/g, "\n\n");
665
721
  }
666
722
  function readIfExists(path) {
667
- return existsSync4(path) ? readFileSync4(path, "utf8") : void 0;
723
+ return existsSync4(path) ? readFileSync5(path, "utf8") : void 0;
668
724
  }
669
725
  function writeFileMkdir(path, text) {
670
- mkdirSync2(dirname2(path), { recursive: true });
671
- writeFileSync(path, text);
726
+ mkdirSync3(dirname2(path), { recursive: true });
727
+ writeFileSync2(path, text);
672
728
  }
673
729
  function installBlockFile(path, harness, dryRun) {
674
730
  const before = readIfExists(path) ?? "";
@@ -684,7 +740,7 @@ function removeBlockFile(path, dryRun) {
684
740
  if (dryRun) return true;
685
741
  const after = removePairBlock(before);
686
742
  if (after.trim() === "") rmSync2(path);
687
- else writeFileSync(path, after);
743
+ else writeFileSync2(path, after);
688
744
  return true;
689
745
  }
690
746
  function blockVerdict(path, harness) {
@@ -811,8 +867,8 @@ function codexTable(url) {
811
867
  return [`[mcp_servers.${SERVER}]`, `url = ${tomlString(url)}`, `bearer_token_env_var = ${tomlString(PAIR_KEY_VAR)}`];
812
868
  }
813
869
  var codexPaths = (home) => ({
814
- instructions: join4(home, ".codex", "AGENTS.md"),
815
- config: join4(home, ".codex", "config.toml")
870
+ instructions: join5(home, ".codex", "AGENTS.md"),
871
+ config: join5(home, ".codex", "config.toml")
816
872
  });
817
873
  function codexMcpState(config, url) {
818
874
  const lines = config.split("\n");
@@ -863,7 +919,7 @@ var codexPair = {
863
919
  if (hadMcp && !o.dryRun) {
864
920
  const after = removeTomlTable(config, `mcp_servers.${SERVER}`);
865
921
  if (after === "") rmSync3(p.config);
866
- else writeFileSync2(p.config, after);
922
+ else writeFileSync3(p.config, after);
867
923
  }
868
924
  const what = hadBlock && hadMcp ? `the Pair block and ${SERVER} server` : hadBlock ? "the Pair block" : `the ${SERVER} server`;
869
925
  return hadBlock || hadMcp ? `pair(codex): ${o.dryRun ? "(dry run) would remove" : "removed"} ${what}${kept}` : `pair(codex): nothing to remove${kept}`;
@@ -959,8 +1015,8 @@ var codexAdapter = {
959
1015
 
960
1016
  // src/pair-install.ts
961
1017
  var geminiPaths = (home) => ({
962
- instructions: join5(home, ".gemini", "GEMINI.md"),
963
- config: join5(home, ".gemini", "settings.json")
1018
+ instructions: join6(home, ".gemini", "GEMINI.md"),
1019
+ config: join6(home, ".gemini", "settings.json")
964
1020
  });
965
1021
  function readGeminiSettings(path) {
966
1022
  const text = readIfExists(path);
@@ -1048,7 +1104,7 @@ var geminiPair = {
1048
1104
  if (Object.keys(rest).length > 0) next.mcpServers = rest;
1049
1105
  else delete next.mcpServers;
1050
1106
  if (Object.keys(next).length === 0) rmSync4(p.config);
1051
- else writeFileSync3(p.config, `${JSON.stringify(next, null, 2)}
1107
+ else writeFileSync4(p.config, `${JSON.stringify(next, null, 2)}
1052
1108
  `);
1053
1109
  }
1054
1110
  const what = hadBlock && hadMcp ? `the Pair block and ${SERVER} server` : hadBlock ? "the Pair block" : `the ${SERVER} server`;
@@ -1059,8 +1115,8 @@ var VSCODE_FLAVOURS = ["Code", "Code - Insiders"];
1059
1115
  function vscodeUserDirs(o) {
1060
1116
  const home = o.home ?? homedir4();
1061
1117
  const platform2 = o.platform ?? process.platform;
1062
- const root = platform2 === "darwin" ? join5(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join5(home, "AppData", "Roaming") : join5(home, ".config");
1063
- const all = VSCODE_FLAVOURS.map((f) => join5(root, f, "User"));
1118
+ const root = platform2 === "darwin" ? join6(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join6(home, "AppData", "Roaming") : join6(home, ".config");
1119
+ const all = VSCODE_FLAVOURS.map((f) => join6(root, f, "User"));
1064
1120
  const present = all.filter((d) => existsSync5(d));
1065
1121
  return present.length > 0 ? present : [all[0]];
1066
1122
  }
@@ -1099,7 +1155,7 @@ function removeCopilotInstructions(path, dryRun) {
1099
1155
  if (dryRun) return true;
1100
1156
  const after = removePairBlock(before);
1101
1157
  if (after.trim() === "" || after.trim() === COPILOT_FRONTMATTER.trim()) rmSync4(path);
1102
- else writeFileSync3(path, after);
1158
+ else writeFileSync4(path, after);
1103
1159
  return true;
1104
1160
  }
1105
1161
  var copilotPair = {
@@ -1108,8 +1164,8 @@ var copilotPair = {
1108
1164
  installPair(o) {
1109
1165
  const lines = [];
1110
1166
  for (const dir of vscodeUserDirs(o)) {
1111
- const mcpPath = join5(dir, "mcp.json");
1112
- const instrPath = join5(dir, "prompts", "crew-pair.instructions.md");
1167
+ const mcpPath = join6(dir, "mcp.json");
1168
+ const instrPath = join6(dir, "prompts", "crew-pair.instructions.md");
1113
1169
  const settings = readVscodeMcp(mcpPath);
1114
1170
  if (!settings) {
1115
1171
  lines.push(`pair(copilot): ${mcpPath} is not a plain JSON object (comments are not supported) \u2014 fix it by hand; nothing was written there`);
@@ -1137,8 +1193,8 @@ var copilotPair = {
1137
1193
  },
1138
1194
  verifyPair(o) {
1139
1195
  const verdicts = vscodeUserDirs(o).map((dir) => {
1140
- const instrPath = join5(dir, "prompts", "crew-pair.instructions.md");
1141
- const mcpPath = join5(dir, "mcp.json");
1196
+ const instrPath = join6(dir, "prompts", "crew-pair.instructions.md");
1197
+ const mcpPath = join6(dir, "mcp.json");
1142
1198
  const b = blockVerdict(instrPath, "copilot");
1143
1199
  if (!b.ok) return b;
1144
1200
  const settings = readVscodeMcp(mcpPath);
@@ -1153,8 +1209,8 @@ var copilotPair = {
1153
1209
  removePair(o) {
1154
1210
  const lines = [];
1155
1211
  for (const dir of vscodeUserDirs(o)) {
1156
- const mcpPath = join5(dir, "mcp.json");
1157
- const hadBlock = removeCopilotInstructions(join5(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1212
+ const mcpPath = join6(dir, "mcp.json");
1213
+ const hadBlock = removeCopilotInstructions(join6(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1158
1214
  const settings = readVscodeMcp(mcpPath);
1159
1215
  const st = settings ? copilotMcpState(settings, o.url) : { state: "missing" };
1160
1216
  const hadMcp = st.state === "registered";
@@ -1165,7 +1221,7 @@ var copilotPair = {
1165
1221
  if (Object.keys(rest).length > 0) next.servers = rest;
1166
1222
  else delete next.servers;
1167
1223
  if (Object.keys(next).length === 0) rmSync4(mcpPath);
1168
- else writeFileSync3(mcpPath, `${JSON.stringify(next, null, 2)}
1224
+ else writeFileSync4(mcpPath, `${JSON.stringify(next, null, 2)}
1169
1225
  `);
1170
1226
  }
1171
1227
  const what = hadBlock && hadMcp ? `the Pair instructions and ${SERVER} server` : hadBlock ? "the Pair instructions" : `the ${SERVER} server`;
@@ -1453,8 +1509,8 @@ function explain(ship, required) {
1453
1509
  }
1454
1510
 
1455
1511
  // src/repo-config.ts
1456
- import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
1457
- import { join as join6, basename as basename2 } from "path";
1512
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
1513
+ import { join as join7, basename as basename2 } from "path";
1458
1514
  import { parse } from "yaml";
1459
1515
  var SPEC_VERSION = 1;
1460
1516
  var REPO_CONFIG_FILENAMES = [".crew.yaml", ".crew.yml", ".crew.json"];
@@ -1537,7 +1593,7 @@ var MODES = ["local", "integrate", "ci_manual", "ci_auto", "external"];
1537
1593
  var PROVIDERS = ["github", "buildkite", "other", "none"];
1538
1594
  function findRepoConfig(dir) {
1539
1595
  for (const name of REPO_CONFIG_FILENAMES) {
1540
- const p = join6(dir, name);
1596
+ const p = join7(dir, name);
1541
1597
  if (existsSync6(p)) return p;
1542
1598
  }
1543
1599
  return null;
@@ -1776,7 +1832,7 @@ function parseRepoConfig(text, file) {
1776
1832
  function loadRepoConfig(dir) {
1777
1833
  const file = findRepoConfig(dir);
1778
1834
  if (!file) return null;
1779
- return parseRepoConfig(readFileSync5(file, "utf8"), file);
1835
+ return parseRepoConfig(readFileSync6(file, "utf8"), file);
1780
1836
  }
1781
1837
  function hookLabel(cfg, hook2) {
1782
1838
  return cfg.labels[hook2] ?? cfg.hooks[hook2] ?? hook2;
@@ -2230,11 +2286,11 @@ function expand(p, base) {
2230
2286
  return isAbsolute(out) ? out : resolve(base, out);
2231
2287
  }
2232
2288
  function resolvePromptsDir(promptSet, crewHome, base) {
2233
- if (!promptSet) return join7(crewHome, "prompts", "default");
2289
+ if (!promptSet) return join8(crewHome, "prompts", "default");
2234
2290
  if (promptSet.includes("/") || promptSet.startsWith(".") || promptSet.startsWith("~")) {
2235
2291
  return expand(promptSet, base);
2236
2292
  }
2237
- return join7(crewHome, "prompts", promptSet);
2293
+ return join8(crewHome, "prompts", promptSet);
2238
2294
  }
2239
2295
  var Missing = class {
2240
2296
  paths = [];
@@ -2258,7 +2314,7 @@ var Missing = class {
2258
2314
  };
2259
2315
  function crewVersion(crewHome) {
2260
2316
  try {
2261
- const pkg = JSON.parse(readFileSync6(join7(crewHome, "package.json"), "utf8"));
2317
+ const pkg = JSON.parse(readFileSync7(join8(crewHome, "package.json"), "utf8"));
2262
2318
  return pkg.version ?? "0.0.0";
2263
2319
  } catch {
2264
2320
  return "0.0.0";
@@ -2268,8 +2324,8 @@ function configSearchPath(crewHome) {
2268
2324
  if (process.env.CREW_CONFIG) return [resolve(process.env.CREW_CONFIG)];
2269
2325
  const xdg = process.env.XDG_CONFIG_HOME;
2270
2326
  return [
2271
- ...xdg ? [join7(xdg, "crew", "crew.yaml")] : [],
2272
- join7(homedir5(), ".config", "crew", "crew.yaml"),
2327
+ ...xdg ? [join8(xdg, "crew", "crew.yaml")] : [],
2328
+ join8(homedir5(), ".config", "crew", "crew.yaml"),
2273
2329
  resolve(crewHome, "crew.yaml")
2274
2330
  ];
2275
2331
  }
@@ -2277,17 +2333,17 @@ function findConfigFile(crewHome) {
2277
2333
  const candidates = configSearchPath(crewHome);
2278
2334
  return candidates.find((c) => existsSync7(c)) ?? candidates[candidates.length - 1];
2279
2335
  }
2280
- var defaultConfigPath = () => join7(process.env.XDG_CONFIG_HOME ?? join7(homedir5(), ".config"), "crew", "crew.yaml");
2336
+ var defaultConfigPath = () => join8(process.env.XDG_CONFIG_HOME ?? join8(homedir5(), ".config"), "crew", "crew.yaml");
2281
2337
  function routeSlug(route) {
2282
2338
  return route.replace(/\//g, "-");
2283
2339
  }
2284
2340
  function resolvedPathFor(stateDir, route) {
2285
2341
  const [workspace, project] = splitRoute(route);
2286
- return join7(stateDir, "resolved", workspace, `${project}.json`);
2342
+ return join8(stateDir, "resolved", workspace, `${project}.json`);
2287
2343
  }
2288
2344
  function apiKeyPathFor(stateDir, route) {
2289
2345
  const [workspace, project] = splitRoute(route);
2290
- return join7(stateDir, "keys", workspace, `${project}.env`);
2346
+ return join8(stateDir, "keys", workspace, `${project}.env`);
2291
2347
  }
2292
2348
  function splitRoute(route) {
2293
2349
  const parts = route.split("/");
@@ -2300,7 +2356,7 @@ function readResolvedFile(stateDir, route) {
2300
2356
  const path = resolvedPathFor(stateDir, route);
2301
2357
  if (!existsSync7(path)) return void 0;
2302
2358
  try {
2303
- return JSON.parse(readFileSync6(path, "utf8"));
2359
+ return JSON.parse(readFileSync7(path, "utf8"));
2304
2360
  } catch (e) {
2305
2361
  throw new ConfigError(`${path}: not valid JSON \u2014 ${e.message}`);
2306
2362
  }
@@ -2365,12 +2421,12 @@ function loadConfig(crewHome, configFile, opts = {}) {
2365
2421
  ` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
2366
2422
 
2367
2423
  Create the first of those:
2368
- mkdir -p ${dirname3(defaultConfigPath())} && cp ${join7(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
2424
+ mkdir -p ${dirname3(defaultConfigPath())} && cp ${join8(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
2369
2425
  );
2370
2426
  }
2371
2427
  let raw;
2372
2428
  try {
2373
- raw = parse2(opts.text ?? readFileSync6(file, "utf8"));
2429
+ raw = parse2(opts.text ?? readFileSync7(file, "utf8"));
2374
2430
  } catch (e) {
2375
2431
  throw new ConfigError(`${file}: not valid YAML \u2014 ${e.message}`);
2376
2432
  }
@@ -2448,7 +2504,7 @@ Create the first of those:
2448
2504
  useNvm: shipRaw.useNvm !== false,
2449
2505
  nvmSh: shipRaw.nvmSh,
2450
2506
  stateDir,
2451
- logFile: expand(shipRaw.logFile ?? join7(tmpdir(), "crew.log"), base),
2507
+ logFile: expand(shipRaw.logFile ?? join8(tmpdir(), "crew.log"), base),
2452
2508
  userAgent: shipRaw.userAgent ?? `Mozilla/5.0 CrewAgent/${crewVersion(crewHome)}`,
2453
2509
  maxConcurrentAgents,
2454
2510
  streamRetentionDays,
@@ -2589,7 +2645,7 @@ function parseOneRoute(c, where, file, base, stateDir, raw, missing, crewHome, t
2589
2645
  const hasRepos = Object.keys(repoDirs).length > 0;
2590
2646
  const reposBasePath = expand(String(c?.reposBasePath ?? raw.ship?.reposBasePath ?? DEFAULT_REPOS_BASE_PATH), base);
2591
2647
  const dirIsCloneBase = !c?.dir && !hasRepos && c?.repos && typeof c.repos === "object" && !Array.isArray(c.repos);
2592
- const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join7(reposBasePath, splitRoute(route)[0]) : missing.req(void 0, `${where}.dir (or ${where}.repos)`) ?? "";
2648
+ const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join8(reposBasePath, splitRoute(route)[0]) : missing.req(void 0, `${where}.dir (or ${where}.repos)`) ?? "";
2593
2649
  const weight = c?.weight === void 0 ? void 0 : Number(c.weight);
2594
2650
  if (weight !== void 0 && !(Number.isFinite(weight) && weight > 0)) {
2595
2651
  missing.add(`${where}.weight must be a positive number (got "${c?.weight}")`);
@@ -2673,7 +2729,7 @@ function repoIdForName(r, name) {
2673
2729
  function resolveApiKey(r) {
2674
2730
  if (r.apiKey) return r.apiKey;
2675
2731
  if (r.apiKeyFile && r.apiKeyVar && existsSync7(r.apiKeyFile)) {
2676
- for (const line of readFileSync6(r.apiKeyFile, "utf8").split("\n")) {
2732
+ for (const line of readFileSync7(r.apiKeyFile, "utf8").split("\n")) {
2677
2733
  const m = line.match(new RegExp(`^${r.apiKeyVar}=(.*)$`));
2678
2734
  if (m) return (m[1] ?? "").trim().replace(/^["']|["']$/g, "");
2679
2735
  }
@@ -2688,7 +2744,7 @@ function credentialSource(r) {
2688
2744
  if (r.apiKey) return KEYCHAIN_ELIGIBLE_ROUTES.has(r) ? "keychain" : "inline";
2689
2745
  if (r.apiKeyFile && r.apiKeyVar && existsSync7(r.apiKeyFile)) {
2690
2746
  const re = new RegExp(`^${r.apiKeyVar}=`);
2691
- if (readFileSync6(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
2747
+ if (readFileSync7(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
2692
2748
  }
2693
2749
  return process.env.CREW_API_KEY?.trim() ? "CREW_API_KEY" : "none";
2694
2750
  }
@@ -2744,7 +2800,7 @@ function configuredMembers(rt) {
2744
2800
  return out;
2745
2801
  }
2746
2802
  function defaultRepoDir(basePath, workspace, repoName) {
2747
- return join7(basePath, workspace, repoName);
2803
+ return join8(basePath, workspace, repoName);
2748
2804
  }
2749
2805
  function reposOf(r) {
2750
2806
  const named = Object.entries(r.repos);
@@ -2777,7 +2833,7 @@ function passengerRepoTargets(r) {
2777
2833
  return reposOf(r).filter((t) => r.repoOverrides[t.name]?.hostPassengers !== false);
2778
2834
  }
2779
2835
  function passengerCheckoutDir(basePath, workspace, repoName) {
2780
- return join7(basePath, workspace, ".passenger-checkouts", repoName);
2836
+ return join8(basePath, workspace, ".passenger-checkouts", repoName);
2781
2837
  }
2782
2838
  function passengerCheckoutTargets(r) {
2783
2839
  if (!r.hostPassengers) return [];
@@ -3125,8 +3181,8 @@ function renderEnvironment(i) {
3125
3181
  }
3126
3182
 
3127
3183
  // src/agent-log.ts
3128
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "fs";
3129
- import { join as join8 } from "path";
3184
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
3185
+ import { join as join9 } from "path";
3130
3186
 
3131
3187
  // src/pending-backoff.ts
3132
3188
  var sleepBackoff = (attempts) => Math.min(6e4 * 2 ** Math.max(0, attempts - 1), 30 * 6e4);
@@ -3193,20 +3249,20 @@ async function reportAgentRun(target, run2) {
3193
3249
  }
3194
3250
  return { agentLogId: entry.id };
3195
3251
  }
3196
- var queueDir = (stateDir) => join8(stateDir, "pending-agent-logs");
3252
+ var queueDir = (stateDir) => join9(stateDir, "pending-agent-logs");
3197
3253
  function queueAgentRun(stateDir, name, q) {
3198
- mkdirSync3(queueDir(stateDir), { recursive: true });
3199
- writeFileSync4(join8(queueDir(stateDir), `${name}.json`), JSON.stringify(q));
3254
+ mkdirSync4(queueDir(stateDir), { recursive: true });
3255
+ writeFileSync5(join9(queueDir(stateDir), `${name}.json`), JSON.stringify(q));
3200
3256
  }
3201
3257
  async function replayQueuedAgentRuns(stateDir, route, keyFor, log, now = /* @__PURE__ */ new Date()) {
3202
3258
  const dir = queueDir(stateDir);
3203
3259
  if (!existsSync8(dir)) return 0;
3204
3260
  let sent = 0;
3205
3261
  for (const f of readdirSync2(dir).filter((n) => n.endsWith(".json")).sort()) {
3206
- const path = join8(dir, f);
3262
+ const path = join9(dir, f);
3207
3263
  let q;
3208
3264
  try {
3209
- q = JSON.parse(readFileSync7(path, "utf8"));
3265
+ q = JSON.parse(readFileSync8(path, "utf8"));
3210
3266
  } catch {
3211
3267
  continue;
3212
3268
  }
@@ -3225,7 +3281,7 @@ async function replayQueuedAgentRuns(stateDir, route, keyFor, log, now = /* @__P
3225
3281
  continue;
3226
3282
  }
3227
3283
  const attempts = q.attempts + 1;
3228
- writeFileSync4(path, JSON.stringify({
3284
+ writeFileSync5(path, JSON.stringify({
3229
3285
  ...q,
3230
3286
  attempts,
3231
3287
  warned: true,
@@ -3406,25 +3462,25 @@ var HandoffTranscriptTracker = class {
3406
3462
  };
3407
3463
 
3408
3464
  // src/pending-handoff.ts
3409
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as readFileSync11, rmSync as rmSync8, writeFileSync as writeFileSync8 } from "fs";
3410
- import { join as join14 } from "path";
3465
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync5, readFileSync as readFileSync12, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
3466
+ import { join as join15 } from "path";
3411
3467
 
3412
3468
  // src/git.ts
3413
3469
  import { execFileSync as execFileSync2 } from "child_process";
3414
3470
  import {
3415
3471
  existsSync as existsSync9,
3416
- mkdirSync as mkdirSync4,
3472
+ mkdirSync as mkdirSync5,
3417
3473
  openSync as openSync2,
3418
3474
  closeSync as closeSync2,
3419
3475
  writeSync,
3420
3476
  statSync as statSync2,
3421
3477
  unlinkSync,
3422
- readFileSync as readFileSync8,
3423
- writeFileSync as writeFileSync5,
3478
+ readFileSync as readFileSync9,
3479
+ writeFileSync as writeFileSync6,
3424
3480
  readdirSync as readdirSync3,
3425
3481
  rmSync as rmSync6
3426
3482
  } from "fs";
3427
- import { dirname as dirname4, basename as basename5, join as join9 } from "path";
3483
+ import { dirname as dirname4, basename as basename5, join as join10 } from "path";
3428
3484
  var GitError = class extends Error {
3429
3485
  };
3430
3486
  function streamText(x) {
@@ -3808,11 +3864,11 @@ function sweepStaleReleaseRoots(tmp, now = Date.now(), maxAgeMs = 60 * 60 * 1e3)
3808
3864
  }
3809
3865
  for (const name of names) {
3810
3866
  if (!name.startsWith("crew-release-")) continue;
3811
- const root = join9(tmp, name);
3867
+ const root = join10(tmp, name);
3812
3868
  try {
3813
3869
  const st = statSync2(root);
3814
3870
  if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
3815
- if (existsSync9(join9(root, "checkout"))) continue;
3871
+ if (existsSync9(join10(root, "checkout"))) continue;
3816
3872
  rmSync6(root, { recursive: true, force: true });
3817
3873
  removed.push(name);
3818
3874
  } catch {
@@ -3861,7 +3917,7 @@ function ensureRepoCheckout(dir, remote, opts = {}) {
3861
3917
  `${dir} does not exist and this repo has no discovered remote to clone from \u2014 re-run \`crew connect\`, or check out the repository yourself and point \`repos:\` at it`
3862
3918
  );
3863
3919
  }
3864
- mkdirSync4(dirname4(dir), { recursive: true });
3920
+ mkdirSync5(dirname4(dir), { recursive: true });
3865
3921
  const url = cloneUrlFor(remote, opts);
3866
3922
  try {
3867
3923
  execFileSync2("git", ["clone", url, dir], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
@@ -3886,7 +3942,7 @@ function sleepSync(ms2) {
3886
3942
  }
3887
3943
  function acquireCheckoutLock(dir, timeoutMs = 3e4) {
3888
3944
  const lockPath = checkoutLockPath(dir);
3889
- mkdirSync4(dirname4(lockPath), { recursive: true });
3945
+ mkdirSync5(dirname4(lockPath), { recursive: true });
3890
3946
  const deadline = Date.now() + timeoutMs;
3891
3947
  for (; ; ) {
3892
3948
  try {
@@ -3926,14 +3982,14 @@ function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base, clone = {
3926
3982
  if (!existsSync9(`${dir}/.git`) && githubRemote) {
3927
3983
  const url = cloneUrlFor(githubRemote, clone);
3928
3984
  const marker = cloneFailureMarker(dir);
3929
- if (existsSync9(marker) && readFileSync8(marker, "utf8").split("\n")[0] === url) {
3930
- return { action: "clone-failed", detail: readFileSync8(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
3985
+ if (existsSync9(marker) && readFileSync9(marker, "utf8").split("\n")[0] === url) {
3986
+ return { action: "clone-failed", detail: readFileSync9(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
3931
3987
  }
3932
3988
  try {
3933
3989
  ensureRepoCheckout(dir, githubRemote, clone);
3934
3990
  } catch (e) {
3935
3991
  if (!(e instanceof GitError)) throw e;
3936
- writeFileSync5(marker, `${url}
3992
+ writeFileSync6(marker, `${url}
3937
3993
  ${e.message}`);
3938
3994
  return { action: "clone-failed", detail: e.message, repeat: false };
3939
3995
  }
@@ -4354,11 +4410,11 @@ function recoveredFailureHeadlines(o, route) {
4354
4410
  }
4355
4411
 
4356
4412
  // src/install.ts
4357
- import { dirname as dirname5, join as join10, extname } from "path";
4413
+ import { dirname as dirname5, join as join11, extname } from "path";
4358
4414
  import { homedir as homedir6 } from "os";
4359
- import { accessSync, constants, existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, realpathSync } from "fs";
4415
+ import { accessSync, constants, existsSync as existsSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync3, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2, realpathSync } from "fs";
4360
4416
  import { spawnSync as spawnSync4 } from "child_process";
4361
- import { createHash } from "crypto";
4417
+ import { createHash as createHash2 } from "crypto";
4362
4418
 
4363
4419
  // src/runtime-info.ts
4364
4420
  function isCompiledBinary(moduleUrl) {
@@ -4368,7 +4424,7 @@ function isCompiledBinary(moduleUrl) {
4368
4424
  // src/install.ts
4369
4425
  function crewInvocation(crewHome) {
4370
4426
  if (isCompiledBinary(import.meta.url)) return [process.execPath];
4371
- return [process.execPath, join10(crewHome, "bin", "crew")];
4427
+ return [process.execPath, join11(crewHome, "bin", "crew")];
4372
4428
  }
4373
4429
  var INTERVAL_SECONDS = 120;
4374
4430
  function labelFor(crewHome, job) {
@@ -4379,7 +4435,7 @@ function labelFor(crewHome, job) {
4379
4435
  return crewHome;
4380
4436
  }
4381
4437
  })();
4382
- const hash = createHash("sha1").update(real).digest("hex").slice(0, 8);
4438
+ const hash = createHash2("sha1").update(real).digest("hex").slice(0, 8);
4383
4439
  const stem = job === "release" ? "com.tablation.crew-release" : job === "passengers" ? "com.tablation.crew-passengers" : "com.tablation.crew";
4384
4440
  return `${stem}.${hash}`;
4385
4441
  }
@@ -4406,7 +4462,7 @@ function pathFor(ship) {
4406
4462
  return parts.join(":");
4407
4463
  }
4408
4464
  function findOnPath(bin, searchPath) {
4409
- const candidates = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join10(dir, bin));
4465
+ const candidates = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join11(dir, bin));
4410
4466
  for (const candidate of candidates) {
4411
4467
  try {
4412
4468
  accessSync(candidate, constants.X_OK);
@@ -4416,7 +4472,7 @@ function findOnPath(bin, searchPath) {
4416
4472
  }
4417
4473
  return void 0;
4418
4474
  }
4419
- var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join10(homedir6(), ".docker", "bin")].join(":");
4475
+ var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join11(homedir6(), ".docker", "bin")].join(":");
4420
4476
  function dockerPathProblem(ship, shellPath = process.env.PATH ?? "") {
4421
4477
  const schedulerPath = pathFor(ship);
4422
4478
  if (findOnPath("docker", schedulerPath)) return void 0;
@@ -4428,7 +4484,7 @@ function xmlEscape(s) {
4428
4484
  }
4429
4485
  function planLaunchd(ship, crewHome, job) {
4430
4486
  const label = labelFor(crewHome, job);
4431
- const unitPath = join10(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4487
+ const unitPath = join11(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4432
4488
  const crewLog = ship.logFile;
4433
4489
  const schedulerLog = schedulerLogFor(crewLog, job);
4434
4490
  const persistent = job === "run";
@@ -4482,8 +4538,8 @@ ${scheduleKeys}
4482
4538
  }
4483
4539
  function planSystemd(ship, crewHome, job) {
4484
4540
  const label = labelFor(crewHome, job);
4485
- const unitDir = join10(homedir6(), ".config", "systemd", "user");
4486
- const servicePath = join10(unitDir, `${label}.service`);
4541
+ const unitDir = join11(homedir6(), ".config", "systemd", "user");
4542
+ const servicePath = join11(unitDir, `${label}.service`);
4487
4543
  const crewLog = ship.logFile;
4488
4544
  const schedulerLog = schedulerLogFor(crewLog, job);
4489
4545
  const description = job === "release" ? "Tablation crew \u2014 release" : job === "passengers" ? "Tablation crew \u2014 Host Passengers sync" : "Tablation crew \u2014 persistent supervisor loop";
@@ -4523,7 +4579,7 @@ WantedBy=default.target
4523
4579
  schedulerLog
4524
4580
  };
4525
4581
  }
4526
- const timerPath = join10(unitDir, `${label}.timer`);
4582
+ const timerPath = join11(unitDir, `${label}.timer`);
4527
4583
  const service = `# Written by \`crew install\` \u2014 re-run it rather than hand-editing this file.
4528
4584
  [Unit]
4529
4585
  Description=${description}
@@ -4597,7 +4653,7 @@ function planInstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4597
4653
  function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4598
4654
  const label = labelFor(crewHome, job);
4599
4655
  if (hostShip === "macos") {
4600
- const unitPath = join10(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4656
+ const unitPath = join11(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4601
4657
  return {
4602
4658
  mechanism: "launchd",
4603
4659
  unitPaths: [unitPath],
@@ -4607,18 +4663,18 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4607
4663
  };
4608
4664
  }
4609
4665
  if (hostShip === "linux" && hasSystemd) {
4610
- const unitDir = join10(homedir6(), ".config", "systemd", "user");
4666
+ const unitDir = join11(homedir6(), ".config", "systemd", "user");
4611
4667
  if (job === "run") {
4612
4668
  return {
4613
4669
  mechanism: "systemd",
4614
- unitPaths: [join10(unitDir, `${label}.service`)],
4670
+ unitPaths: [join11(unitDir, `${label}.service`)],
4615
4671
  crontabLine: null,
4616
4672
  unloadCommands: [["systemctl", "--user", "disable", "--now", `${label}.service`]]
4617
4673
  };
4618
4674
  }
4619
4675
  return {
4620
4676
  mechanism: "systemd",
4621
- unitPaths: [join10(unitDir, `${label}.service`), join10(unitDir, `${label}.timer`)],
4677
+ unitPaths: [join11(unitDir, `${label}.service`), join11(unitDir, `${label}.timer`)],
4622
4678
  crontabLine: null,
4623
4679
  unloadCommands: [["systemctl", "--user", "disable", "--now", `${label}.timer`]]
4624
4680
  };
@@ -4636,7 +4692,7 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4636
4692
  function planDaemonControl(crewHome, hostShip, hasSystemd) {
4637
4693
  const label = labelFor(crewHome, "run");
4638
4694
  if (hostShip === "macos") {
4639
- const unitPath = join10(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4695
+ const unitPath = join11(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4640
4696
  return {
4641
4697
  mechanism: "launchd",
4642
4698
  startCommand: ["launchctl", "load", unitPath],
@@ -4676,8 +4732,8 @@ async function applyInstall(plan, crewHome, dryRun, log) {
4676
4732
  log.emit(`would write ${path}`);
4677
4733
  continue;
4678
4734
  }
4679
- mkdirSync5(dirname5(path), { recursive: true });
4680
- writeFileSync6(path, plan.unitContent[path]);
4735
+ mkdirSync6(dirname5(path), { recursive: true });
4736
+ writeFileSync7(path, plan.unitContent[path]);
4681
4737
  log.emit(`wrote ${path}`);
4682
4738
  }
4683
4739
  if (plan.crontabLine) {
@@ -4750,7 +4806,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
4750
4806
  }
4751
4807
  var CREW_LAUNCHD_PREFIX = "com.tablation.crew";
4752
4808
  function launchAgentsDir() {
4753
- return join10(homedir6(), "Library", "LaunchAgents");
4809
+ return join11(homedir6(), "Library", "LaunchAgents");
4754
4810
  }
4755
4811
  function xmlUnescape(s) {
4756
4812
  return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
@@ -4769,10 +4825,10 @@ function loadedCrewLabels() {
4769
4825
  function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
4770
4826
  const files = existsSync10(dir) ? readdirSync4(dir) : [];
4771
4827
  const units = files.filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
4772
- const plistPath = join10(dir, f);
4828
+ const plistPath = join11(dir, f);
4773
4829
  let program = null;
4774
4830
  try {
4775
- program = programFromPlist(readFileSync9(plistPath, "utf8"));
4831
+ program = programFromPlist(readFileSync10(plistPath, "utf8"));
4776
4832
  } catch {
4777
4833
  }
4778
4834
  return {
@@ -4785,7 +4841,7 @@ function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
4785
4841
  const seen = new Set(units.map((u) => u.label));
4786
4842
  for (const label of [...loaded].sort()) {
4787
4843
  if (!label.startsWith(CREW_LAUNCHD_PREFIX) || seen.has(label)) continue;
4788
- units.push({ label, plistPath: join10(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
4844
+ units.push({ label, plistPath: join11(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
4789
4845
  }
4790
4846
  return units;
4791
4847
  }
@@ -5131,17 +5187,17 @@ var SHELL_WORDS = /* @__PURE__ */ new Set([
5131
5187
  ]);
5132
5188
 
5133
5189
  // src/state.ts
5134
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync6, rmSync as rmSync7, statSync as statSync4 } from "fs";
5135
- import { join as join11 } from "path";
5190
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync7, rmSync as rmSync7, statSync as statSync4 } from "fs";
5191
+ import { join as join12 } from "path";
5136
5192
  var EPOCH = "1970-01-01T00:00:00.000Z";
5137
5193
  var State = class _State {
5138
5194
  dir;
5139
5195
  constructor(dir) {
5140
5196
  this.dir = dir;
5141
- mkdirSync6(dir, { recursive: true });
5197
+ mkdirSync7(dir, { recursive: true });
5142
5198
  }
5143
5199
  path(...parts) {
5144
- return join11(this.dir, ...parts);
5200
+ return join12(this.dir, ...parts);
5145
5201
  }
5146
5202
  /**
5147
5203
  * The timestamp every "new since last poll" check is measured against.
@@ -5152,14 +5208,14 @@ var State = class _State {
5152
5208
  */
5153
5209
  watermark() {
5154
5210
  try {
5155
- const v = readFileSync10(this.path(".poll-watermark"), "utf8").trim();
5211
+ const v = readFileSync11(this.path(".poll-watermark"), "utf8").trim();
5156
5212
  return v || EPOCH;
5157
5213
  } catch {
5158
5214
  return EPOCH;
5159
5215
  }
5160
5216
  }
5161
5217
  setWatermark(at) {
5162
- writeFileSync7(this.path(".poll-watermark"), `${at}
5218
+ writeFileSync8(this.path(".poll-watermark"), `${at}
5163
5219
  `);
5164
5220
  }
5165
5221
  /**
@@ -5186,7 +5242,7 @@ var State = class _State {
5186
5242
  return new Set(roles.filter((r) => this.isRolePaused(r)));
5187
5243
  }
5188
5244
  pause(role) {
5189
- writeFileSync7(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), "");
5245
+ writeFileSync8(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), "");
5190
5246
  }
5191
5247
  resume(role) {
5192
5248
  rmSync7(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), { force: true });
@@ -5200,7 +5256,7 @@ var State = class _State {
5200
5256
  return existsSync12(this.path(".release-paused"));
5201
5257
  }
5202
5258
  setReleasePaused(paused) {
5203
- if (paused) writeFileSync7(this.path(".release-paused"), "");
5259
+ if (paused) writeFileSync8(this.path(".release-paused"), "");
5204
5260
  else rmSync7(this.path(".release-paused"), { force: true });
5205
5261
  }
5206
5262
  /**
@@ -5238,7 +5294,7 @@ var State = class _State {
5238
5294
  const name = _State.safe(rawName);
5239
5295
  const file = this.path(`.${name}.lock`);
5240
5296
  try {
5241
- const existing = Number.parseInt(readFileSync10(file, "utf8").trim(), 10);
5297
+ const existing = Number.parseInt(readFileSync11(file, "utf8").trim(), 10);
5242
5298
  if (Number.isFinite(existing) && existing > 0) {
5243
5299
  try {
5244
5300
  process.kill(existing, 0);
@@ -5248,7 +5304,7 @@ var State = class _State {
5248
5304
  }
5249
5305
  } catch {
5250
5306
  }
5251
- writeFileSync7(file, `${process.pid}
5307
+ writeFileSync8(file, `${process.pid}
5252
5308
  `);
5253
5309
  return {
5254
5310
  ok: true,
@@ -5332,7 +5388,7 @@ var State = class _State {
5332
5388
  const heldReported = `.release-held-${suffix}`;
5333
5389
  const read = (f) => {
5334
5390
  try {
5335
- return readFileSync10(this.path(f), "utf8").trim() || null;
5391
+ return readFileSync11(this.path(f), "utf8").trim() || null;
5336
5392
  } catch {
5337
5393
  return null;
5338
5394
  }
@@ -5341,7 +5397,7 @@ var State = class _State {
5341
5397
  blockedCount: () => Number.parseInt(read(blocked) ?? "", 10) || 0,
5342
5398
  noteBlocked: () => {
5343
5399
  const n = (Number.parseInt(read(blocked) ?? "", 10) || 0) + 1;
5344
- writeFileSync7(this.path(blocked), `${n}
5400
+ writeFileSync8(this.path(blocked), `${n}
5345
5401
  `);
5346
5402
  return n;
5347
5403
  },
@@ -5358,7 +5414,7 @@ var State = class _State {
5358
5414
  */
5359
5415
  deployFailedSha: () => read(failed),
5360
5416
  noteDeployFailed: (sha) => {
5361
- writeFileSync7(this.path(failed), `${sha}
5417
+ writeFileSync8(this.path(failed), `${sha}
5362
5418
  `);
5363
5419
  },
5364
5420
  clearDeployFailed: () => {
@@ -5372,7 +5428,7 @@ var State = class _State {
5372
5428
  noteTestGateFailed: (sha) => {
5373
5429
  const [prev, n] = (read(gateFailed) ?? "").split(" ");
5374
5430
  const next = prev === sha ? (Number.parseInt(n ?? "", 10) || 0) + 1 : 1;
5375
- writeFileSync7(this.path(gateFailed), `${sha} ${next}
5431
+ writeFileSync8(this.path(gateFailed), `${sha} ${next}
5376
5432
  `);
5377
5433
  return next;
5378
5434
  },
@@ -5382,13 +5438,13 @@ var State = class _State {
5382
5438
  /** Whether the board was already told about this head's red gate. */
5383
5439
  heldReported: () => {
5384
5440
  try {
5385
- return readFileSync10(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
5441
+ return readFileSync11(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
5386
5442
  } catch {
5387
5443
  return null;
5388
5444
  }
5389
5445
  },
5390
5446
  noteHeldReported: (signature) => {
5391
- writeFileSync7(this.path(heldReported), `${signature}
5447
+ writeFileSync8(this.path(heldReported), `${signature}
5392
5448
  `);
5393
5449
  },
5394
5450
  clearHeldReported: () => {
@@ -5396,7 +5452,7 @@ var State = class _State {
5396
5452
  },
5397
5453
  testGateReported: (sha) => read(gateReported) === sha,
5398
5454
  noteTestGateReported: (sha) => {
5399
- writeFileSync7(this.path(gateReported), `${sha}
5455
+ writeFileSync8(this.path(gateReported), `${sha}
5400
5456
  `);
5401
5457
  }
5402
5458
  };
@@ -5419,7 +5475,7 @@ var State = class _State {
5419
5475
  const ticketStreakFile = `.fairness-ticket-streak-${suffix}`;
5420
5476
  const read = (f) => {
5421
5477
  try {
5422
- return readFileSync10(this.path(f), "utf8").trim() || null;
5478
+ return readFileSync11(this.path(f), "utf8").trim() || null;
5423
5479
  } catch {
5424
5480
  return null;
5425
5481
  }
@@ -5452,18 +5508,18 @@ var State = class _State {
5452
5508
  clear2();
5453
5509
  return;
5454
5510
  }
5455
- writeFileSync7(this.path(streakFile), `${this.readInt(streakFile) + 1}
5511
+ writeFileSync8(this.path(streakFile), `${this.readInt(streakFile) + 1}
5456
5512
  `);
5457
5513
  const prior = read(ticketFile);
5458
5514
  if (topTicket && topTicket === prior) {
5459
- writeFileSync7(this.path(ticketStreakFile), `${this.readInt(ticketStreakFile) + 1}
5515
+ writeFileSync8(this.path(ticketStreakFile), `${this.readInt(ticketStreakFile) + 1}
5460
5516
  `);
5461
5517
  } else if (topTicket) {
5462
- writeFileSync7(this.path(ticketFile), `${topTicket}
5518
+ writeFileSync8(this.path(ticketFile), `${topTicket}
5463
5519
  `);
5464
- writeFileSync7(this.path(sinceFile), `${(/* @__PURE__ */ new Date()).toISOString()}
5520
+ writeFileSync8(this.path(sinceFile), `${(/* @__PURE__ */ new Date()).toISOString()}
5465
5521
  `);
5466
- writeFileSync7(this.path(ticketStreakFile), "1\n");
5522
+ writeFileSync8(this.path(ticketStreakFile), "1\n");
5467
5523
  }
5468
5524
  }
5469
5525
  };
@@ -5484,14 +5540,14 @@ var State = class _State {
5484
5540
  return {
5485
5541
  previous: () => {
5486
5542
  try {
5487
- const parsed = JSON.parse(readFileSync10(this.path(file), "utf8"));
5543
+ const parsed = JSON.parse(readFileSync11(this.path(file), "utf8"));
5488
5544
  return parsed && typeof parsed === "object" ? parsed : {};
5489
5545
  } catch {
5490
5546
  return {};
5491
5547
  }
5492
5548
  },
5493
5549
  persist: (next) => {
5494
- writeFileSync7(this.path(file), JSON.stringify(next));
5550
+ writeFileSync8(this.path(file), JSON.stringify(next));
5495
5551
  }
5496
5552
  };
5497
5553
  }
@@ -5505,14 +5561,14 @@ var State = class _State {
5505
5561
  return {
5506
5562
  previous: () => {
5507
5563
  try {
5508
- const parsed = JSON.parse(readFileSync10(this.path(file), "utf8"));
5564
+ const parsed = JSON.parse(readFileSync11(this.path(file), "utf8"));
5509
5565
  return Array.isArray(parsed) ? parsed : [];
5510
5566
  } catch {
5511
5567
  return [];
5512
5568
  }
5513
5569
  },
5514
5570
  persist: (next) => {
5515
- writeFileSync7(this.path(file), JSON.stringify(next));
5571
+ writeFileSync8(this.path(file), JSON.stringify(next));
5516
5572
  }
5517
5573
  };
5518
5574
  }
@@ -5525,7 +5581,7 @@ var State = class _State {
5525
5581
  const file = ".release-last-run";
5526
5582
  return {
5527
5583
  touch: () => {
5528
- writeFileSync7(this.path(file), (/* @__PURE__ */ new Date()).toISOString());
5584
+ writeFileSync8(this.path(file), (/* @__PURE__ */ new Date()).toISOString());
5529
5585
  },
5530
5586
  at: () => {
5531
5587
  try {
@@ -5538,7 +5594,7 @@ var State = class _State {
5538
5594
  }
5539
5595
  readInt(file) {
5540
5596
  try {
5541
- return Number.parseInt(readFileSync10(this.path(file), "utf8").trim(), 10) || 0;
5597
+ return Number.parseInt(readFileSync11(this.path(file), "utf8").trim(), 10) || 0;
5542
5598
  } catch {
5543
5599
  return 0;
5544
5600
  }
@@ -6540,12 +6596,12 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
6540
6596
 
6541
6597
  // src/run-worktree.ts
6542
6598
  import { existsSync as existsSync14 } from "fs";
6543
- import { dirname as dirname6, join as join13 } from "path";
6599
+ import { dirname as dirname6, join as join14 } from "path";
6544
6600
 
6545
6601
  // src/ports.ts
6546
6602
  import { execFileSync as execFileSync3 } from "child_process";
6547
6603
  import { existsSync as existsSync13 } from "fs";
6548
- import { join as join12 } from "path";
6604
+ import { join as join13 } from "path";
6549
6605
  var run = (cmd, args) => {
6550
6606
  try {
6551
6607
  return execFileSync3(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
@@ -6590,7 +6646,7 @@ function ticketForPort(port, s = DEFAULT_PORTS) {
6590
6646
  }
6591
6647
  function worktreeExists(parent, prefix, n) {
6592
6648
  for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
6593
- if (existsSync13(join12(parent, name, ".git"))) return true;
6649
+ if (existsSync13(join13(parent, name, ".git"))) return true;
6594
6650
  }
6595
6651
  return false;
6596
6652
  }
@@ -6682,7 +6738,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
6682
6738
  if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
6683
6739
  return { action: "recut", path: existing.path, branch, base: baseRef };
6684
6740
  }
6685
- const path = join13(dirname6(dir), worktreeForTicket(dir, cfg, t, role));
6741
+ const path = join14(dirname6(dir), worktreeForTicket(dir, cfg, t, role));
6686
6742
  if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
6687
6743
  return { action: "cut-remote", path, branch: located.branch };
6688
6744
  }
@@ -7026,12 +7082,12 @@ async function guardRunStrays(route, ship, tracker, role, workingId, since2, log
7026
7082
 
7027
7083
  // src/pending-handoff.ts
7028
7084
  var PENDING_MAX_AGE_MS = 48 * 36e5;
7029
- var pendingDirFor = (stateDir) => join14(stateDir, "pending-handoffs");
7030
- var stateDirOfStream = (streamPath) => join14(streamPath, "..", "..");
7085
+ var pendingDirFor = (stateDir) => join15(stateDir, "pending-handoffs");
7086
+ var stateDirOfStream = (streamPath) => join15(streamPath, "..", "..");
7031
7087
  function writePendingHandoff(stateDir, rec) {
7032
7088
  const dir = pendingDirFor(stateDir);
7033
- mkdirSync7(dir, { recursive: true });
7034
- writeFileSync8(join14(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
7089
+ mkdirSync8(dir, { recursive: true });
7090
+ writeFileSync9(join15(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
7035
7091
  }
7036
7092
  function listPendingHandoffs(stateDir, route) {
7037
7093
  const dir = pendingDirFor(stateDir);
@@ -7039,7 +7095,7 @@ function listPendingHandoffs(stateDir, route) {
7039
7095
  const out = [];
7040
7096
  for (const f of readdirSync5(dir).filter((n) => n.endsWith(".json")).sort()) {
7041
7097
  try {
7042
- const rec = JSON.parse(readFileSync11(join14(dir, f), "utf8"));
7098
+ const rec = JSON.parse(readFileSync12(join15(dir, f), "utf8"));
7043
7099
  if (rec.version === 1 && rec.ticketRecordId && (!route || rec.route === route)) out.push(rec);
7044
7100
  } catch {
7045
7101
  }
@@ -7047,7 +7103,7 @@ function listPendingHandoffs(stateDir, route) {
7047
7103
  return out;
7048
7104
  }
7049
7105
  function removePendingHandoff(stateDir, ticketRecordId) {
7050
- rmSync8(join14(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
7106
+ rmSync8(join15(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
7051
7107
  }
7052
7108
  function pendingHandoffFor(stateDir, ticketRecordId) {
7053
7109
  return listPendingHandoffs(stateDir).find((r) => r.ticketRecordId === ticketRecordId);
@@ -7157,8 +7213,8 @@ function expandSkillMarkers(text, lookup, opts = {}) {
7157
7213
  }
7158
7214
 
7159
7215
  // src/skills.ts
7160
- import { readFileSync as readFileSync13, readdirSync as readdirSync6, existsSync as existsSync17 } from "fs";
7161
- import { join as join16 } from "path";
7216
+ import { readFileSync as readFileSync14, readdirSync as readdirSync6, existsSync as existsSync17 } from "fs";
7217
+ import { join as join17 } from "path";
7162
7218
  import { parse as parseYaml } from "yaml";
7163
7219
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
7164
7220
 
@@ -7174,8 +7230,8 @@ ${additions}`;
7174
7230
  }
7175
7231
 
7176
7232
  // src/agents.ts
7177
- import { readFileSync as readFileSync12, existsSync as existsSync16 } from "fs";
7178
- import { join as join15 } from "path";
7233
+ import { readFileSync as readFileSync13, existsSync as existsSync16 } from "fs";
7234
+ import { join as join16 } from "path";
7179
7235
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
7180
7236
  var PERSONA_NAME = {
7181
7237
  dev: "Developer",
@@ -7189,12 +7245,12 @@ var CREW_AGENT_COLUMN = "agent_id";
7189
7245
  var AgentsSyncError = class extends Error {
7190
7246
  };
7191
7247
  function personaDefaultPrompt(promptsDir, role) {
7192
- const briefPath = join15(promptsDir, "personas", `lane-${role}.md`);
7248
+ const briefPath = join16(promptsDir, "personas", `lane-${role}.md`);
7193
7249
  if (!existsSync16(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
7194
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync12(briefPath, "utf8");
7195
- const commonPath = join15(promptsDir, "personas", "common.md");
7196
- if (!existsSync16(commonPath)) return readFileSync12(briefPath, "utf8");
7197
- return `${readFileSync12(commonPath, "utf8")}${readFileSync12(briefPath, "utf8")}`;
7250
+ if (SELF_CONTAINED_ROLES.has(role)) return readFileSync13(briefPath, "utf8");
7251
+ const commonPath = join16(promptsDir, "personas", "common.md");
7252
+ if (!existsSync16(commonPath)) return readFileSync13(briefPath, "utf8");
7253
+ return `${readFileSync13(commonPath, "utf8")}${readFileSync13(briefPath, "utf8")}`;
7198
7254
  }
7199
7255
  async function resolveAgentId(route, role, opts) {
7200
7256
  const cached = route.resolved?.agentPersonas?.[role]?.agentId;
@@ -7641,7 +7697,7 @@ function describeCrewLink(o) {
7641
7697
  var SkillSyncError = class extends Error {
7642
7698
  };
7643
7699
  function parseSkillFile(path) {
7644
- const text = readFileSync13(path, "utf8");
7700
+ const text = readFileSync14(path, "utf8");
7645
7701
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text);
7646
7702
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
7647
7703
  const frontmatter = match[1];
@@ -7654,9 +7710,9 @@ function parseSkillFile(path) {
7654
7710
  return { name: meta.name, description: meta.description, prompt: body, file: path };
7655
7711
  }
7656
7712
  function listLocalSkills(promptsDir) {
7657
- const dir = join16(promptsDir, "skills");
7713
+ const dir = join17(promptsDir, "skills");
7658
7714
  if (!existsSync17(dir)) return [];
7659
- return readdirSync6(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join16(dir, f)));
7715
+ return readdirSync6(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join17(dir, f)));
7660
7716
  }
7661
7717
  async function syncSkills(route, opts) {
7662
7718
  if (!route.resolved) {
@@ -7859,23 +7915,23 @@ var AgentError = class extends Error {
7859
7915
  var AgentSpawnError = class extends AgentError {
7860
7916
  };
7861
7917
  function planAgentRun(o) {
7862
- const briefPath = join17(o.route.promptsDir, "personas", `lane-${o.role}.md`);
7918
+ const briefPath = join18(o.route.promptsDir, "personas", `lane-${o.role}.md`);
7863
7919
  if (!existsSync18(briefPath)) {
7864
7920
  throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
7865
7921
  }
7866
- const commonPath = join17(o.route.promptsDir, "personas", "common.md");
7922
+ const commonPath = join18(o.route.promptsDir, "personas", "common.md");
7867
7923
  const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync18(commonPath);
7868
7924
  const now = o.now ?? (() => Date.now());
7869
7925
  const routeLabel = routeSlug(o.route.route);
7870
- const digestPath2 = join17(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
7926
+ const digestPath2 = join18(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
7871
7927
  let digest;
7872
7928
  let ageSeconds;
7873
7929
  if (existsSync18(digestPath2)) {
7874
7930
  ageSeconds = Math.floor((now() - statSync5(digestPath2).mtimeMs) / 1e3);
7875
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync14(digestPath2, "utf8");
7931
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync15(digestPath2, "utf8");
7876
7932
  }
7877
- const common = usesCommon ? readFileSync14(commonPath, "utf8") : "";
7878
- const brief = readFileSync14(briefPath, "utf8");
7933
+ const common = usesCommon ? readFileSync15(commonPath, "utf8") : "";
7934
+ const brief = readFileSync15(briefPath, "utf8");
7879
7935
  const effectiveCommon = o.divergedPrompt ? "" : common;
7880
7936
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
7881
7937
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -7896,8 +7952,8 @@ function planAgentRun(o) {
7896
7952
  brief: expanded.text,
7897
7953
  digest
7898
7954
  });
7899
- const promptSha = createHash2("sha256").update(expanded.text).digest("hex");
7900
- const streamsDir = join17(o.stateDir, "streams");
7955
+ const promptSha = createHash3("sha256").update(expanded.text).digest("hex");
7956
+ const streamsDir = join18(o.stateDir, "streams");
7901
7957
  const base = `${routeLabel}-${o.role}-${o.cycle}`;
7902
7958
  const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
7903
7959
  const { toolchain, model } = seat;
@@ -7953,8 +8009,8 @@ function planAgentRun(o) {
7953
8009
  digestAttached: digest !== void 0,
7954
8010
  digestAgeSeconds: ageSeconds,
7955
8011
  model,
7956
- streamPath: join17(streamsDir, `${base}.jsonl`),
7957
- eventsPath: join17(streamsDir, `${base}.events.jsonl`),
8012
+ streamPath: join18(streamsDir, `${base}.jsonl`),
8013
+ eventsPath: join18(streamsDir, `${base}.events.jsonl`),
7958
8014
  ticket: o.ticket,
7959
8015
  ticketRecordId: o.ticketRecordId ?? null,
7960
8016
  agentLog,
@@ -7983,7 +8039,7 @@ function describePlan(p) {
7983
8039
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
7984
8040
  function readRunSidecar(streamPath) {
7985
8041
  try {
7986
- const sc = JSON.parse(readFileSync14(pidPathFor(streamPath), "utf8"));
8042
+ const sc = JSON.parse(readFileSync15(pidPathFor(streamPath), "utf8"));
7987
8043
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
7988
8044
  } catch {
7989
8045
  return null;
@@ -8019,7 +8075,7 @@ function identityOf(plan, adapterHarness) {
8019
8075
  var withoutKey = ({ apiKey: _apiKey, ...rest }) => rest;
8020
8076
  function openSink(path) {
8021
8077
  try {
8022
- mkdirSync8(dirname7(path), { recursive: true });
8078
+ mkdirSync9(dirname7(path), { recursive: true });
8023
8079
  return createWriteStream(path, { flags: "a" });
8024
8080
  } catch {
8025
8081
  return void 0;
@@ -8198,7 +8254,7 @@ var RunObserver = class {
8198
8254
  };
8199
8255
  function writeRunMeta(id, startedAt, liveTicket, extra) {
8200
8256
  try {
8201
- writeFileSync9(`${id.streamPath}.meta.json`, JSON.stringify({
8257
+ writeFileSync10(`${id.streamPath}.meta.json`, JSON.stringify({
8202
8258
  route: id.route,
8203
8259
  role: id.role,
8204
8260
  hint: id.ticket,
@@ -8317,7 +8373,7 @@ function spawnAgent(plan, emit) {
8317
8373
  return new Promise((resolve6, reject) => {
8318
8374
  let streamFd;
8319
8375
  try {
8320
- mkdirSync8(dirname7(plan.streamPath), { recursive: true });
8376
+ mkdirSync9(dirname7(plan.streamPath), { recursive: true });
8321
8377
  streamFd = openSync3(plan.streamPath, "a");
8322
8378
  } catch (e) {
8323
8379
  void endSink(eventsSink);
@@ -8350,7 +8406,7 @@ function spawnAgent(plan, emit) {
8350
8406
  promptSha: plan.promptSha
8351
8407
  };
8352
8408
  try {
8353
- writeFileSync9(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
8409
+ writeFileSync10(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
8354
8410
  } catch {
8355
8411
  }
8356
8412
  }
@@ -8567,7 +8623,7 @@ async function runSelftest(i, deps = realDeps) {
8567
8623
  ...adapter.runEnv(spec),
8568
8624
  [KEY_VAR]: i.apiKey
8569
8625
  };
8570
- const cwd = mkdtempSync(join18(tmpdir2(), "crew-selftest-"));
8626
+ const cwd = mkdtempSync(join19(tmpdir2(), "crew-selftest-"));
8571
8627
  try {
8572
8628
  const run2 = await deps.run({
8573
8629
  bin: invocation.bin,
@@ -8599,11 +8655,11 @@ function formatResult(r) {
8599
8655
  const secs = `${(r.ms / 1e3).toFixed(1)}s`;
8600
8656
  return r.ok ? `${r.toolchain}: PASS ${r.detail} (${secs}${tok})` : `${r.toolchain}: FAIL ${r.failure} \u2014 ${r.detail} (${secs})`;
8601
8657
  }
8602
- var fileFor = (stateDir) => join18(stateDir, "selftest.json");
8658
+ var fileFor2 = (stateDir) => join19(stateDir, "selftest.json");
8603
8659
  function readSelftest(stateDir) {
8604
8660
  try {
8605
- const p = fileFor(stateDir);
8606
- return existsSync19(p) ? JSON.parse(readFileSync15(p, "utf8")) : {};
8661
+ const p = fileFor2(stateDir);
8662
+ return existsSync19(p) ? JSON.parse(readFileSync16(p, "utf8")) : {};
8607
8663
  } catch {
8608
8664
  return {};
8609
8665
  }
@@ -8611,8 +8667,8 @@ function readSelftest(stateDir) {
8611
8667
  function recordSelftest(stateDir, results) {
8612
8668
  const all = readSelftest(stateDir);
8613
8669
  for (const r of results) all[r.toolchain] = r;
8614
- mkdirSync9(stateDir, { recursive: true });
8615
- writeFileSync10(fileFor(stateDir), `${JSON.stringify(all, null, 2)}
8670
+ mkdirSync10(stateDir, { recursive: true });
8671
+ writeFileSync11(fileFor2(stateDir), `${JSON.stringify(all, null, 2)}
8616
8672
  `);
8617
8673
  }
8618
8674
  function doctorSelftestLines(stored, toolchains) {
@@ -8624,7 +8680,7 @@ function doctorSelftestLines(stored, toolchains) {
8624
8680
  }
8625
8681
 
8626
8682
  // src/cli.ts
8627
- import { resolve as resolve5, dirname as dirname20, join as join35, basename as basename11 } from "path";
8683
+ import { resolve as resolve5, dirname as dirname20, join as join36, basename as basename11 } from "path";
8628
8684
  import { fileURLToPath } from "url";
8629
8685
  import { hostname as hostname2, homedir as homedir9 } from "os";
8630
8686
  import { spawnSync as spawnSync6 } from "child_process";
@@ -8803,14 +8859,14 @@ function rosterMarkdown(roster, meId) {
8803
8859
  }
8804
8860
 
8805
8861
  // src/connect-resume.ts
8806
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync16, rmSync as rmSync10, writeFileSync as writeFileSync11 } from "fs";
8807
- import { dirname as dirname8, join as join19 } from "path";
8862
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync17, rmSync as rmSync10, writeFileSync as writeFileSync12 } from "fs";
8863
+ import { dirname as dirname8, join as join20 } from "path";
8808
8864
  function lastSessionFile(stateDir) {
8809
- return join19(stateDir, "connect-session.json");
8865
+ return join20(stateDir, "connect-session.json");
8810
8866
  }
8811
8867
  function readPointers(stateDir) {
8812
8868
  try {
8813
- const parsed = JSON.parse(readFileSync16(lastSessionFile(stateDir), "utf8"));
8869
+ const parsed = JSON.parse(readFileSync17(lastSessionFile(stateDir), "utf8"));
8814
8870
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
8815
8871
  const out = {};
8816
8872
  for (const [host, v] of Object.entries(parsed)) {
@@ -8824,8 +8880,8 @@ function readPointers(stateDir) {
8824
8880
  }
8825
8881
  function writePointers(stateDir, pointers) {
8826
8882
  const file = lastSessionFile(stateDir);
8827
- mkdirSync10(dirname8(file), { recursive: true });
8828
- writeFileSync11(file, `${JSON.stringify(pointers, null, 2)}
8883
+ mkdirSync11(dirname8(file), { recursive: true });
8884
+ writeFileSync12(file, `${JSON.stringify(pointers, null, 2)}
8829
8885
  `, { mode: 384 });
8830
8886
  }
8831
8887
  function rememberSession(stateDir, host, workspaceSlug) {
@@ -8881,15 +8937,15 @@ async function signOutSessions(o) {
8881
8937
  }
8882
8938
 
8883
8939
  // src/reset.ts
8884
- import { existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync17, readdirSync as readdirSync7, rmSync as rmSync11, statSync as statSync6, writeFileSync as writeFileSync12 } from "fs";
8885
- import { dirname as dirname9, join as join20 } from "path";
8940
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync18, readdirSync as readdirSync7, rmSync as rmSync11, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
8941
+ import { dirname as dirname9, join as join21 } from "path";
8886
8942
  import { hostFromUrl as hostFromUrl2 } from "@tablation/client";
8887
8943
  function clonedCheckoutsFile(stateDir) {
8888
- return join20(stateDir, "cloned-checkouts.json");
8944
+ return join21(stateDir, "cloned-checkouts.json");
8889
8945
  }
8890
8946
  function readClonedCheckouts(stateDir) {
8891
8947
  try {
8892
- const parsed = JSON.parse(readFileSync17(clonedCheckoutsFile(stateDir), "utf8"));
8948
+ const parsed = JSON.parse(readFileSync18(clonedCheckoutsFile(stateDir), "utf8"));
8893
8949
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
8894
8950
  } catch {
8895
8951
  return [];
@@ -8899,8 +8955,8 @@ function recordClonedCheckout(stateDir, dir) {
8899
8955
  const known = readClonedCheckouts(stateDir);
8900
8956
  if (known.includes(dir)) return;
8901
8957
  const file = clonedCheckoutsFile(stateDir);
8902
- mkdirSync11(dirname9(file), { recursive: true });
8903
- writeFileSync12(file, `${JSON.stringify([...known, dir], null, 2)}
8958
+ mkdirSync12(dirname9(file), { recursive: true });
8959
+ writeFileSync13(file, `${JSON.stringify([...known, dir], null, 2)}
8904
8960
  `);
8905
8961
  }
8906
8962
  var mark = (dryRun) => dryRun ? "would-remove" : "removed";
@@ -8974,10 +9030,10 @@ function removeCheckout(dir, dryRun, fx) {
8974
9030
  }
8975
9031
  function removeState(stateDir, dryRun) {
8976
9032
  const out = [];
8977
- const targets = [join20(stateDir, "resolved"), join20(stateDir, "keys"), clonedCheckoutsFile(stateDir), join20(stateDir, "connect-session.json"), join20(stateDir, "reload-requested"), join20(stateDir, "daemon.started")];
9033
+ const targets = [join21(stateDir, "resolved"), join21(stateDir, "keys"), clonedCheckoutsFile(stateDir), join21(stateDir, "connect-session.json"), join21(stateDir, "reload-requested"), join21(stateDir, "daemon.started")];
8978
9034
  try {
8979
9035
  for (const name of readdirSync7(stateDir)) {
8980
- if (name.startsWith(".") && statSync6(join20(stateDir, name)).isFile()) targets.push(join20(stateDir, name));
9036
+ if (name.startsWith(".") && statSync6(join21(stateDir, name)).isFile()) targets.push(join21(stateDir, name));
8981
9037
  }
8982
9038
  } catch {
8983
9039
  }
@@ -9762,8 +9818,8 @@ ${waiting.map((t) => `| ${displayKey2(t)} | ${t.status} | ${blockers(t, i)} |`).
9762
9818
  }
9763
9819
 
9764
9820
  // src/poll.ts
9765
- import { writeFileSync as writeFileSync13 } from "fs";
9766
- import { join as join21 } from "path";
9821
+ import { writeFileSync as writeFileSync14 } from "fs";
9822
+ import { join as join22 } from "path";
9767
9823
 
9768
9824
  // src/stalled.ts
9769
9825
  var STALLED_AFTER_CYCLES = 30;
@@ -10054,7 +10110,7 @@ async function decideCycle(o) {
10054
10110
  }
10055
10111
  return decision;
10056
10112
  }
10057
- var digestPath = (stateDir, route, role) => join21(stateDir, `digest-${route}-${role}.md`);
10113
+ var digestPath = (stateDir, route, role) => join22(stateDir, `digest-${route}-${role}.md`);
10058
10114
  function writeDigest(o, d, role, stateDir) {
10059
10115
  const me = o.route.resolved?.seats[role];
10060
10116
  if (!me) return false;
@@ -10122,7 +10178,7 @@ function writeDigest(o, d, role, stateDir) {
10122
10178
  };
10123
10179
  const text = role === "qa" ? qaDigest(input) : buildingDigest(input);
10124
10180
  const path = digestPath(stateDir, routeSlug(o.route.route), role);
10125
- writeFileSync13(path, text);
10181
+ writeFileSync14(path, text);
10126
10182
  o.emit.emit(`queue digest written for ${role} (${Buffer.byteLength(text)} bytes)`, {
10127
10183
  data: { role, bytes: Buffer.byteLength(text) }
10128
10184
  });
@@ -10528,8 +10584,8 @@ async function applyStrandedVerified(writer, s, contract, operatorId, memberId,
10528
10584
  }
10529
10585
 
10530
10586
  // src/connect-wizard.ts
10531
- import { delimiter, dirname as dirname10, join as join22 } from "path";
10532
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync21, mkdirSync as mkdirSync12, writeFileSync as writeFileSync14 } from "fs";
10587
+ import { delimiter, dirname as dirname10, join as join23 } from "path";
10588
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync21, mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
10533
10589
  import { stringify } from "yaml";
10534
10590
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
10535
10591
  function shouldRunWizard(opts) {
@@ -10551,7 +10607,7 @@ function defaultIsExecutable(p) {
10551
10607
  function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
10552
10608
  for (const dir of pathEnv.split(delimiter)) {
10553
10609
  if (!dir) continue;
10554
- const candidate = join22(dir, name);
10610
+ const candidate = join23(dir, name);
10555
10611
  if (isExecutable(candidate)) return candidate;
10556
10612
  }
10557
10613
  return void 0;
@@ -10612,9 +10668,9 @@ routes:
10612
10668
  ${routeBlock}`;
10613
10669
  }
10614
10670
  function writeNewConfig(path, text) {
10615
- mkdirSync12(dirname10(path), { recursive: true });
10671
+ mkdirSync13(dirname10(path), { recursive: true });
10616
10672
  try {
10617
- writeFileSync14(path, text, { mode: 384, flag: "wx" });
10673
+ writeFileSync15(path, text, { mode: 384, flag: "wx" });
10618
10674
  return true;
10619
10675
  } catch (e) {
10620
10676
  if (e.code === "EEXIST") return false;
@@ -10648,7 +10704,7 @@ Wrote ${o.configPath}.
10648
10704
  // src/connect-json.ts
10649
10705
  import { accessSync as accessSync3, constants as constants3 } from "fs";
10650
10706
  import { homedir as homedir7 } from "os";
10651
- import { dirname as dirname11, join as join23 } from "path";
10707
+ import { dirname as dirname11, join as join24 } from "path";
10652
10708
  import { delimiter as delimiter2 } from "path";
10653
10709
  var CHOICE_EXIT_CODE = 3;
10654
10710
  function createEmitter(json, write) {
@@ -10687,12 +10743,12 @@ function agentBinSearchDirs(home, pathEnv, extraPath) {
10687
10743
  const dirs = [
10688
10744
  ...pathEnv.split(delimiter2),
10689
10745
  ...(extraPath ?? "").split(delimiter2),
10690
- join23(home, ".local/bin"),
10691
- join23(home, ".claude/local"),
10746
+ join24(home, ".local/bin"),
10747
+ join24(home, ".claude/local"),
10692
10748
  "/opt/homebrew/bin",
10693
10749
  "/usr/local/bin",
10694
- join23(home, ".npm-global/bin"),
10695
- join23(home, ".volta/bin")
10750
+ join24(home, ".npm-global/bin"),
10751
+ join24(home, ".volta/bin")
10696
10752
  ];
10697
10753
  return [...new Set(dirs.filter(Boolean))];
10698
10754
  }
@@ -10728,8 +10784,8 @@ function flagWizardAnswers(f, env) {
10728
10784
  }
10729
10785
 
10730
10786
  // src/release-run.ts
10731
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync15, existsSync as existsSync22, copyFileSync, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, rmSync as rmSync12 } from "fs";
10732
- import { dirname as dirname12, join as join24 } from "path";
10787
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync16, existsSync as existsSync22, copyFileSync, mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync12 } from "fs";
10788
+ import { dirname as dirname12, join as join25 } from "path";
10733
10789
  import { tmpdir as tmpdir3 } from "os";
10734
10790
 
10735
10791
  // src/release.ts
@@ -11447,13 +11503,13 @@ async function bump(o, size) {
11447
11503
  }
11448
11504
  return printed;
11449
11505
  }
11450
- const files = o.repo.release.versionFiles.map((f) => join24(o.cwd, f));
11506
+ const files = o.repo.release.versionFiles.map((f) => join25(o.cwd, f));
11451
11507
  const first = files[0];
11452
11508
  if (!existsSync22(first)) {
11453
11509
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
11454
11510
  return null;
11455
11511
  }
11456
- const current = JSON.parse(readFileSync18(first, "utf8")).version;
11512
+ const current = JSON.parse(readFileSync19(first, "utf8")).version;
11457
11513
  if (!current) {
11458
11514
  o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
11459
11515
  return null;
@@ -11466,27 +11522,27 @@ async function bump(o, size) {
11466
11522
  return version;
11467
11523
  }
11468
11524
  for (const f of files) {
11469
- const src = readFileSync18(f, "utf8");
11525
+ const src = readFileSync19(f, "utf8");
11470
11526
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
11471
11527
  if (out === src) {
11472
11528
  o.emit.error(`no version field in ${f}`);
11473
11529
  return null;
11474
11530
  }
11475
- writeFileSync15(f, out);
11531
+ writeFileSync16(f, out);
11476
11532
  }
11477
11533
  return version;
11478
11534
  }
11479
11535
  function writeChangelog(o, version, entries) {
11480
11536
  const file = o.repo.release.changelog;
11481
11537
  if (!file || entries.length === 0) return;
11482
- const path = join24(o.cwd, file);
11538
+ const path = join25(o.cwd, file);
11483
11539
  const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
11484
11540
  if (o.dryRun) {
11485
11541
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
11486
11542
  return;
11487
11543
  }
11488
- const existing = existsSync22(path) ? readFileSync18(path, "utf8") : "# Changelog\n";
11489
- writeFileSync15(path, insertChangelogSection(existing, section));
11544
+ const existing = existsSync22(path) ? readFileSync19(path, "utf8") : "# Changelog\n";
11545
+ writeFileSync16(path, insertChangelogSection(existing, section));
11490
11546
  }
11491
11547
  async function confirm(o, expected) {
11492
11548
  if (!o.repo.hooks.released) return void 0;
@@ -11579,8 +11635,8 @@ function closeReleaseCheckout(cwd, root, dir, branch, run2 = gitOk) {
11579
11635
  function openReleaseCheckout(o) {
11580
11636
  const { remote, base } = o.repo.branch;
11581
11637
  const branch = `crew/release-${Date.now()}`;
11582
- const root = mkdtempSync2(join24(tmpdir3(), "crew-release-"));
11583
- const dir = join24(root, "checkout");
11638
+ const root = mkdtempSync2(join25(tmpdir3(), "crew-release-"));
11639
+ const dir = join25(root, "checkout");
11584
11640
  o.emit.emit(`release checkout: ${dir}`, { step: "release" });
11585
11641
  const close = () => closeReleaseCheckout(o.cwd, root, dir, branch);
11586
11642
  try {
@@ -11590,10 +11646,10 @@ function openReleaseCheckout(o) {
11590
11646
  throw e;
11591
11647
  }
11592
11648
  for (const f of o.repo.worktrees.copy) {
11593
- const src = join24(o.cwd, f);
11649
+ const src = join25(o.cwd, f);
11594
11650
  if (!existsSync22(src)) continue;
11595
- mkdirSync13(dirname12(join24(dir, f)), { recursive: true });
11596
- copyFileSync(src, join24(dir, f));
11651
+ mkdirSync14(dirname12(join25(dir, f)), { recursive: true });
11652
+ copyFileSync(src, join25(dir, f));
11597
11653
  }
11598
11654
  return { dir, branch, close };
11599
11655
  }
@@ -12664,19 +12720,19 @@ function nextRoles(decision, ran) {
12664
12720
  }
12665
12721
 
12666
12722
  // src/ship-name.ts
12667
- import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync20, renameSync as renameSync2, writeFileSync as writeFileSync17 } from "fs";
12723
+ import { existsSync as existsSync25, mkdirSync as mkdirSync16, readFileSync as readFileSync21, renameSync as renameSync2, writeFileSync as writeFileSync18 } from "fs";
12668
12724
  import { dirname as dirname14 } from "path";
12669
12725
  import { parseDocument } from "yaml";
12670
12726
 
12671
12727
  // src/machine-id.ts
12672
12728
  import { execFileSync as execFileSync4 } from "child_process";
12673
- import { createHash as createHash3 } from "crypto";
12674
- import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync19, writeFileSync as writeFileSync16 } from "fs";
12675
- import { dirname as dirname13, join as join25 } from "path";
12729
+ import { createHash as createHash4 } from "crypto";
12730
+ import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "fs";
12731
+ import { dirname as dirname13, join as join26 } from "path";
12676
12732
  function machineIdPathFor(stateDir) {
12677
- return join25(stateDir, "machine-id");
12733
+ return join26(stateDir, "machine-id");
12678
12734
  }
12679
- var sha256 = (s) => createHash3("sha256").update(s).digest("hex");
12735
+ var sha256 = (s) => createHash4("sha256").update(s).digest("hex");
12680
12736
  function readRawMachineId(platform2 = process.platform) {
12681
12737
  try {
12682
12738
  if (platform2 === "darwin") {
@@ -12688,7 +12744,7 @@ function readRawMachineId(platform2 = process.platform) {
12688
12744
  }
12689
12745
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
12690
12746
  if (existsSync24(p)) {
12691
- const id = readFileSync19(p, "utf8").trim();
12747
+ const id = readFileSync20(p, "utf8").trim();
12692
12748
  if (id) return id;
12693
12749
  }
12694
12750
  }
@@ -12700,14 +12756,14 @@ function ensureMachineId(stateDir, opts = {}) {
12700
12756
  if (opts.override?.trim()) return sha256(opts.override.trim());
12701
12757
  const path = machineIdPathFor(stateDir);
12702
12758
  if (existsSync24(path)) {
12703
- const cached = readFileSync19(path, "utf8").trim();
12759
+ const cached = readFileSync20(path, "utf8").trim();
12704
12760
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
12705
12761
  }
12706
12762
  const raw = (opts.read ?? readRawMachineId)();
12707
12763
  if (!raw) return void 0;
12708
12764
  const hashed = sha256(raw);
12709
- mkdirSync14(dirname13(path), { recursive: true });
12710
- writeFileSync16(path, `${hashed}
12765
+ mkdirSync15(dirname13(path), { recursive: true });
12766
+ writeFileSync17(path, `${hashed}
12711
12767
  `, { mode: 384 });
12712
12768
  return hashed;
12713
12769
  }
@@ -12780,7 +12836,7 @@ async function syncRouteName(i) {
12780
12836
  function readSyncedName(resolvedFile) {
12781
12837
  if (!existsSync25(resolvedFile)) return void 0;
12782
12838
  try {
12783
- const v = JSON.parse(readFileSync20(resolvedFile, "utf8")).shipName;
12839
+ const v = JSON.parse(readFileSync21(resolvedFile, "utf8")).shipName;
12784
12840
  return typeof v === "string" ? v : void 0;
12785
12841
  } catch {
12786
12842
  return void 0;
@@ -12790,22 +12846,22 @@ function writeSyncedName(resolvedFile, name) {
12790
12846
  if (!existsSync25(resolvedFile)) return;
12791
12847
  let raw;
12792
12848
  try {
12793
- raw = JSON.parse(readFileSync20(resolvedFile, "utf8"));
12849
+ raw = JSON.parse(readFileSync21(resolvedFile, "utf8"));
12794
12850
  } catch {
12795
12851
  return;
12796
12852
  }
12797
12853
  if (raw.shipName === name) return;
12798
12854
  raw.shipName = name;
12799
- mkdirSync15(dirname14(resolvedFile), { recursive: true });
12855
+ mkdirSync16(dirname14(resolvedFile), { recursive: true });
12800
12856
  const tmp = `${resolvedFile}.tmp${process.pid}`;
12801
- writeFileSync17(tmp, `${JSON.stringify(raw, null, 2)}
12857
+ writeFileSync18(tmp, `${JSON.stringify(raw, null, 2)}
12802
12858
  `);
12803
12859
  renameSync2(tmp, resolvedFile);
12804
12860
  }
12805
12861
  async function reconcileShipName(i) {
12806
12862
  let local = i.ship.name;
12807
12863
  try {
12808
- local = shipNameOfYaml(readFileSync20(i.configFile, "utf8")) ?? local;
12864
+ local = shipNameOfYaml(readFileSync21(i.configFile, "utf8")) ?? local;
12809
12865
  } catch {
12810
12866
  }
12811
12867
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -12829,8 +12885,8 @@ async function reconcileShipName(i) {
12829
12885
  }
12830
12886
  if (result.status !== "ok") return result;
12831
12887
  if (result.action === "adopt" || result.action === "row-wins") {
12832
- const text = readFileSync20(i.configFile, "utf8");
12833
- writeFileSync17(i.configFile, setShipNameInYaml(text, result.name));
12888
+ const text = readFileSync21(i.configFile, "utf8");
12889
+ writeFileSync18(i.configFile, setShipNameInYaml(text, result.name));
12834
12890
  i.ship.name = result.name;
12835
12891
  if (result.action === "row-wins") {
12836
12892
  i.emit?.warn(
@@ -13738,7 +13794,7 @@ function describeCapability(i) {
13738
13794
  }
13739
13795
 
13740
13796
  // src/agents-sync-run.ts
13741
- import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync21, writeFileSync as writeFileSync18 } from "fs";
13797
+ import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
13742
13798
  import { dirname as dirname15 } from "path";
13743
13799
  async function runAgentsSync(target, o) {
13744
13800
  const dryRun = o.dryRun ?? false;
@@ -13772,12 +13828,12 @@ async function runAgentsSync(target, o) {
13772
13828
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
13773
13829
  if (!dryRun) {
13774
13830
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
13775
- const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync21(resolvedPath, "utf8")) : {};
13831
+ const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync22(resolvedPath, "utf8")) : {};
13776
13832
  raw.agentPersonas = personaResult.agentPersonas;
13777
13833
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
13778
13834
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
13779
- mkdirSync16(dirname15(resolvedPath), { recursive: true });
13780
- writeFileSync18(resolvedPath, `${JSON.stringify(raw, null, 2)}
13835
+ mkdirSync17(dirname15(resolvedPath), { recursive: true });
13836
+ writeFileSync19(resolvedPath, `${JSON.stringify(raw, null, 2)}
13781
13837
  `);
13782
13838
  if (target.resolved) {
13783
13839
  target.resolved.agentPersonas = personaResult.agentPersonas;
@@ -13792,7 +13848,7 @@ async function runAgentsSync(target, o) {
13792
13848
  }
13793
13849
 
13794
13850
  // src/logbook.ts
13795
- import { createHash as createHash4 } from "crypto";
13851
+ import { createHash as createHash5 } from "crypto";
13796
13852
  import { TablationClient as TablationClient4 } from "@tablation/client";
13797
13853
  var LogbookError = class extends Error {
13798
13854
  };
@@ -13865,7 +13921,7 @@ async function showLogEntry(route, entryId, opts = {}) {
13865
13921
  };
13866
13922
  }
13867
13923
  const prompt = appendAdditions(match.prompt ?? "", match.prompt_additions);
13868
- const actualSha = createHash4("sha256").update(prompt).digest("hex");
13924
+ const actualSha = createHash5("sha256").update(prompt).digest("hex");
13869
13925
  const exact = !!entry.prompt_sha && actualSha === entry.prompt_sha;
13870
13926
  return {
13871
13927
  entry,
@@ -13884,7 +13940,7 @@ import { isMap as isMap2, parseDocument as parseDocument3, YAMLMap } from "yaml"
13884
13940
 
13885
13941
  // src/repos-cmd.ts
13886
13942
  import { homedir as homedir8 } from "os";
13887
- import { basename as basename8, join as join26, resolve as resolve3 } from "path";
13943
+ import { basename as basename8, join as join27, resolve as resolve3 } from "path";
13888
13944
  import { isMap, isSeq, parseDocument as parseDocument2, stringify as stringify2 } from "yaml";
13889
13945
  var ReposError = class extends Error {
13890
13946
  };
@@ -14162,7 +14218,7 @@ function planRepoClone(i) {
14162
14218
  }
14163
14219
  const ship = doc.get("ship", true);
14164
14220
  const basePath = String(m.get("reposBasePath") ?? (isMap(ship) ? ship.get("reposBasePath") : void 0) ?? "~/Crew");
14165
- const target = i.dir ? expandDir(i.dir, i.base) : join26(expandDir(basePath, i.base), i.route.split("/")[0], name);
14221
+ const target = i.dir ? expandDir(i.dir, i.base) : join27(expandDir(basePath, i.base), i.route.split("/")[0], name);
14166
14222
  const add = planRepoAdd({ text: i.text, base: i.base, route: i.route, path: target, origin: remote, resolved: i.resolved, name, isCheckout: i.isCheckout });
14167
14223
  return { ...add, remote, target };
14168
14224
  }
@@ -14542,7 +14598,7 @@ ${run2.err}` });
14542
14598
  }
14543
14599
 
14544
14600
  // src/sample-ticket.ts
14545
- import { basename as basename9, dirname as dirname17, join as join27 } from "path";
14601
+ import { basename as basename9, dirname as dirname17, join as join28 } from "path";
14546
14602
  var SAMPLE_REPO = "crew-sample";
14547
14603
  var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
14548
14604
  var SAMPLE_MARKER = "sample: true";
@@ -14565,8 +14621,8 @@ has something safe to work on: its test and build hooks always pass, it has no
14565
14621
  remote, and \`crew sample-ticket --remove\` deletes it.
14566
14622
  `;
14567
14623
  function sampleDir(existing, reposBase, workspace) {
14568
- if (existing.length > 0) return join27(dirname17(existing[0]), SAMPLE_REPO);
14569
- return join27(reposBase, workspace, SAMPLE_REPO);
14624
+ if (existing.length > 0) return join28(dirname17(existing[0]), SAMPLE_REPO);
14625
+ return join28(reposBase, workspace, SAMPLE_REPO);
14570
14626
  }
14571
14627
  var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
14572
14628
  function sampleDescription() {
@@ -14647,8 +14703,8 @@ function schedulerInstalled(plan, exists, crontab) {
14647
14703
  }
14648
14704
 
14649
14705
  // src/worktree-sweep.ts
14650
- import { existsSync as existsSync27, readFileSync as readFileSync22, readdirSync as readdirSync8 } from "fs";
14651
- import { basename as basename10, dirname as dirname18, join as join28, resolve as resolve4 } from "path";
14706
+ import { existsSync as existsSync27, readFileSync as readFileSync23, readdirSync as readdirSync8 } from "fs";
14707
+ import { basename as basename10, dirname as dirname18, join as join29, resolve as resolve4 } from "path";
14652
14708
  function planWorktreeSweep(target, tickets, contract) {
14653
14709
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
14654
14710
  const verified = contract.statuses.verified;
@@ -14715,7 +14771,7 @@ function siblingWorktreeDirs(target) {
14715
14771
  for (const name of names) {
14716
14772
  if (!name.startsWith(stem)) continue;
14717
14773
  const m = DIR_NUMBER_SUFFIXED.exec(name);
14718
- const path = join28(parent, name);
14774
+ const path = join29(parent, name);
14719
14775
  if (m && !registered.has(resolve4(path))) out.push({ path, number: m[1] });
14720
14776
  }
14721
14777
  return out;
@@ -14818,7 +14874,7 @@ function isHandBackMerge(path, base) {
14818
14874
  if (!msgPath) return false;
14819
14875
  let first;
14820
14876
  try {
14821
- first = readFileSync22(resolve4(path, msgPath), "utf8").split("\n")[0] ?? "";
14877
+ first = readFileSync23(resolve4(path, msgPath), "utf8").split("\n")[0] ?? "";
14822
14878
  } catch {
14823
14879
  return false;
14824
14880
  }
@@ -14924,13 +14980,13 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
14924
14980
 
14925
14981
  // src/stream-sweep.ts
14926
14982
  import { existsSync as existsSync28, readdirSync as readdirSync9, rmSync as rmSync13, statSync as statSync8 } from "fs";
14927
- import { join as join29 } from "path";
14983
+ import { join as join30 } from "path";
14928
14984
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
14929
14985
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
14930
- const dir = join29(stateDir, "streams");
14986
+ const dir = join30(stateDir, "streams");
14931
14987
  if (!existsSync28(dir)) return [];
14932
14988
  const cutoff = now - retentionDays * DAY_MS2;
14933
- return readdirSync9(dir).map((name) => join29(dir, name)).filter((path) => {
14989
+ return readdirSync9(dir).map((name) => join30(dir, name)).filter((path) => {
14934
14990
  try {
14935
14991
  return statSync8(path).mtimeMs < cutoff;
14936
14992
  } catch {
@@ -14958,7 +15014,7 @@ function applyStreamSweep(paths, dryRun, log) {
14958
15014
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
14959
15015
  if (!existsSync28(stateDir)) return [];
14960
15016
  const cutoff = now - retentionDays * DAY_MS2;
14961
- return readdirSync9(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join29(stateDir, name)).filter((path) => {
15017
+ return readdirSync9(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join30(stateDir, name)).filter((path) => {
14962
15018
  try {
14963
15019
  return statSync8(path).mtimeMs < cutoff;
14964
15020
  } catch {
@@ -14968,24 +15024,24 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
14968
15024
  }
14969
15025
 
14970
15026
  // src/cli.ts
14971
- import { realpathSync as realpathSync3, readFileSync as readFileSync28, existsSync as existsSync34, mkdirSync as mkdirSync21, writeFileSync as writeFileSync22, rmSync as rmSync15, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync10, readdirSync as readdirSync14 } from "fs";
15027
+ import { realpathSync as realpathSync3, readFileSync as readFileSync29, existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync23, rmSync as rmSync15, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync10, readdirSync as readdirSync14 } from "fs";
14972
15028
  import { createInterface } from "readline/promises";
14973
15029
  import { dirname as dirOf, resolve as resolvePath } from "path";
14974
15030
 
14975
15031
  // src/ssh-keys.ts
14976
15032
  import { execFileSync as execFileSync6 } from "child_process";
14977
- import { existsSync as existsSync29, mkdirSync as mkdirSync17, readFileSync as readFileSync23, chmodSync } from "fs";
14978
- import { dirname as dirname19, join as join30 } from "path";
15033
+ import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync24, chmodSync } from "fs";
15034
+ import { dirname as dirname19, join as join31 } from "path";
14979
15035
  var SshKeyError = class extends Error {
14980
15036
  };
14981
15037
  function shipSshKeyPathFor(stateDir) {
14982
- return join30(stateDir, "ssh", "id_ed25519");
15038
+ return join31(stateDir, "ssh", "id_ed25519");
14983
15039
  }
14984
15040
  function ensureShipSshKeypair(stateDir) {
14985
15041
  const privateKeyPath = shipSshKeyPathFor(stateDir);
14986
15042
  const publicKeyPath = `${privateKeyPath}.pub`;
14987
15043
  if (!existsSync29(privateKeyPath)) {
14988
- mkdirSync17(dirname19(privateKeyPath), { recursive: true });
15044
+ mkdirSync18(dirname19(privateKeyPath), { recursive: true });
14989
15045
  try {
14990
15046
  execFileSync6("ssh-keygen", [
14991
15047
  "-t",
@@ -15008,7 +15064,7 @@ function ensureShipSshKeypair(stateDir) {
15008
15064
  if (!existsSync29(publicKeyPath)) {
15009
15065
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
15010
15066
  }
15011
- const publicKey = readFileSync23(publicKeyPath, "utf8").trim();
15067
+ const publicKey = readFileSync24(publicKeyPath, "utf8").trim();
15012
15068
  return { privateKeyPath, publicKeyPath, publicKey };
15013
15069
  }
15014
15070
  function sshKeygenAvailable() {
@@ -15022,7 +15078,7 @@ function sshKeygenAvailable() {
15022
15078
 
15023
15079
  // src/passenger-containers.ts
15024
15080
  import { execFileSync as execFileSync7 } from "child_process";
15025
- import { createHash as createHash5 } from "crypto";
15081
+ import { createHash as createHash6 } from "crypto";
15026
15082
  var realExec = (cmd, args) => {
15027
15083
  try {
15028
15084
  return execFileSync7(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
@@ -15059,12 +15115,12 @@ function containerNameFor(workspaceId) {
15059
15115
  return `crew-passenger-${workspaceId}`;
15060
15116
  }
15061
15117
  function portFor(workspaceId, base = 28765, span = 1e3) {
15062
- const digest = createHash5("sha256").update(workspaceId).digest();
15118
+ const digest = createHash6("sha256").update(workspaceId).digest();
15063
15119
  return base + digest.readUInt32BE(0) % span;
15064
15120
  }
15065
15121
  function mountsDigest(mounts, image) {
15066
15122
  const sorted2 = [...mounts].sort((a, b) => a.containerPath.localeCompare(b.containerPath));
15067
- return createHash5("sha256").update(JSON.stringify({ mounts: sorted2, image })).digest("hex").slice(0, 16);
15123
+ return createHash6("sha256").update(JSON.stringify({ mounts: sorted2, image })).digest("hex").slice(0, 16);
15068
15124
  }
15069
15125
  function planContainers(cfg) {
15070
15126
  const byWorkspace = /* @__PURE__ */ new Map();
@@ -15224,9 +15280,9 @@ function syncAllPassengerCheckouts(cfg) {
15224
15280
 
15225
15281
  // src/tunnel.ts
15226
15282
  import { spawn as spawn5 } from "child_process";
15227
- import { existsSync as existsSync30, mkdirSync as mkdirSync18, readFileSync as readFileSync24, writeFileSync as writeFileSync19, unlinkSync as unlinkSync3, readdirSync as readdirSync10 } from "fs";
15283
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync20, unlinkSync as unlinkSync3, readdirSync as readdirSync10 } from "fs";
15228
15284
  import { randomInt } from "crypto";
15229
- import { join as join31 } from "path";
15285
+ import { join as join32 } from "path";
15230
15286
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
15231
15287
  var defaultSpawn = (cmd, args) => spawn5(cmd, args, DEFAULT_SPAWN_OPTIONS);
15232
15288
  var SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -15293,21 +15349,21 @@ function startTunnel(opts) {
15293
15349
  };
15294
15350
  }
15295
15351
  function tunnelStatePath(stateDir, workspaceId) {
15296
- return join31(stateDir, "passengers", `${workspaceId}.json`);
15352
+ return join32(stateDir, "passengers", `${workspaceId}.json`);
15297
15353
  }
15298
15354
  function readPersistedTunnel(stateDir, workspaceId) {
15299
15355
  const path = tunnelStatePath(stateDir, workspaceId);
15300
15356
  if (!existsSync30(path)) return void 0;
15301
15357
  try {
15302
- return JSON.parse(readFileSync24(path, "utf8"));
15358
+ return JSON.parse(readFileSync25(path, "utf8"));
15303
15359
  } catch {
15304
15360
  return void 0;
15305
15361
  }
15306
15362
  }
15307
15363
  function writePersistedTunnel(stateDir, state) {
15308
15364
  const path = tunnelStatePath(stateDir, state.workspaceId);
15309
- mkdirSync18(join31(stateDir, "passengers"), { recursive: true });
15310
- writeFileSync19(path, `${JSON.stringify(state, null, 2)}
15365
+ mkdirSync19(join32(stateDir, "passengers"), { recursive: true });
15366
+ writeFileSync20(path, `${JSON.stringify(state, null, 2)}
15311
15367
  `);
15312
15368
  }
15313
15369
  function updatePersistedTunnel(stateDir, workspaceId, patch2) {
@@ -15404,13 +15460,13 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
15404
15460
  }
15405
15461
  }
15406
15462
  function readAllPersistedWorkspaceIds(stateDir) {
15407
- const dir = join31(stateDir, "passengers");
15463
+ const dir = join32(stateDir, "passengers");
15408
15464
  if (!existsSync30(dir)) return [];
15409
15465
  return readdirSync10(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
15410
15466
  }
15411
15467
 
15412
15468
  // src/mcp-endpoint-registry.ts
15413
- import { createHash as createHash6 } from "crypto";
15469
+ import { createHash as createHash7 } from "crypto";
15414
15470
  function endpointsUrl(api, workspaceId, id) {
15415
15471
  const base = `${api.baseUrl.replace(/\/+$/, "")}/api/workspaces/${workspaceId}/mcp-endpoints`;
15416
15472
  return id ? `${base}/${id}` : base;
@@ -15478,7 +15534,7 @@ async function probeEndpointAccess(api, workspaceId) {
15478
15534
  }
15479
15535
  var REGISTER_MCP_ENDPOINTS_HINT = "give this key's role the REGISTER_MCP_ENDPOINTS capability, e.g. via the group that holds the Crew project role";
15480
15536
  function registrationKey(url, mcpSecret) {
15481
- return createHash6("sha256").update(`${url}
15537
+ return createHash7("sha256").update(`${url}
15482
15538
  ${mcpSecret ?? ""}`).digest("hex").slice(0, 16);
15483
15539
  }
15484
15540
  async function reconcileEndpoint(workspaceId, d) {
@@ -15526,8 +15582,8 @@ async function unregisterEndpoint(workspaceId, info, d) {
15526
15582
  import { createServer as createHttpServer } from "http";
15527
15583
  import { spawn as spawn6 } from "child_process";
15528
15584
  import { randomBytes } from "crypto";
15529
- import { existsSync as existsSync31, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync20, unlinkSync as unlinkSync4, readdirSync as readdirSync11 } from "fs";
15530
- import { join as join32 } from "path";
15585
+ import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as readFileSync26, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readdirSync as readdirSync11 } from "fs";
15586
+ import { join as join33 } from "path";
15531
15587
 
15532
15588
  // src/secret-compare.ts
15533
15589
  import { timingSafeEqual } from "crypto";
@@ -15616,21 +15672,21 @@ var defaultSpawn2 = (cmd, args, env) => {
15616
15672
  return child;
15617
15673
  };
15618
15674
  function syncDaemonStatePath(stateDir, workspaceId) {
15619
- return join32(stateDir, "passengers", `${workspaceId}-sync.json`);
15675
+ return join33(stateDir, "passengers", `${workspaceId}-sync.json`);
15620
15676
  }
15621
15677
  function readPersistedSyncDaemon(stateDir, workspaceId) {
15622
15678
  const path = syncDaemonStatePath(stateDir, workspaceId);
15623
15679
  if (!existsSync31(path)) return void 0;
15624
15680
  try {
15625
- return JSON.parse(readFileSync25(path, "utf8"));
15681
+ return JSON.parse(readFileSync26(path, "utf8"));
15626
15682
  } catch {
15627
15683
  return void 0;
15628
15684
  }
15629
15685
  }
15630
15686
  function writePersistedSyncDaemon(stateDir, state) {
15631
15687
  const path = syncDaemonStatePath(stateDir, state.workspaceId);
15632
- mkdirSync19(join32(stateDir, "passengers"), { recursive: true });
15633
- writeFileSync20(path, `${JSON.stringify(state, null, 2)}
15688
+ mkdirSync20(join33(stateDir, "passengers"), { recursive: true });
15689
+ writeFileSync21(path, `${JSON.stringify(state, null, 2)}
15634
15690
  `);
15635
15691
  }
15636
15692
  function removePersistedSyncDaemon(stateDir, workspaceId) {
@@ -15650,7 +15706,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
15650
15706
  }
15651
15707
  }
15652
15708
  function readAllPersistedSyncWorkspaceIds(stateDir) {
15653
- const dir = join32(stateDir, "passengers");
15709
+ const dir = join33(stateDir, "passengers");
15654
15710
  if (!existsSync31(dir)) return [];
15655
15711
  const suffix = "-sync.json";
15656
15712
  return readdirSync11(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
@@ -15739,12 +15795,12 @@ function describeCause(err) {
15739
15795
  }
15740
15796
 
15741
15797
  // src/daemon.ts
15742
- import { existsSync as existsSync33, mkdirSync as mkdirSync20, readFileSync as readFileSync27, readdirSync as readdirSync13, rmSync as rmSync14, statSync as statSync9, writeFileSync as writeFileSync21 } from "fs";
15743
- import { join as join34 } from "path";
15798
+ import { existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as readFileSync28, readdirSync as readdirSync13, rmSync as rmSync14, statSync as statSync9, writeFileSync as writeFileSync22 } from "fs";
15799
+ import { join as join35 } from "path";
15744
15800
 
15745
15801
  // src/interrupted.ts
15746
- import { existsSync as existsSync32, readdirSync as readdirSync12, readFileSync as readFileSync26 } from "fs";
15747
- import { join as join33 } from "path";
15802
+ import { existsSync as existsSync32, readdirSync as readdirSync12, readFileSync as readFileSync27 } from "fs";
15803
+ import { join as join34 } from "path";
15748
15804
  var INTERRUPTED_MARKER = calloutHeader("NOTE", "Interrupted");
15749
15805
  function interruptedComment(b) {
15750
15806
  const tree = b.dirty === null ? "The worktree could not be checked." : b.dirty ? "The worktree has uncommitted changes \u2014 they are this seat's own, from that run." : "The worktree is clean.";
@@ -15765,19 +15821,19 @@ function runFinished(streamPath) {
15765
15821
  const path = `${streamPath}.meta.json`;
15766
15822
  if (!existsSync32(path)) return false;
15767
15823
  try {
15768
- return JSON.parse(readFileSync26(path, "utf8")).running !== true;
15824
+ return JSON.parse(readFileSync27(path, "utf8")).running !== true;
15769
15825
  } catch {
15770
15826
  return true;
15771
15827
  }
15772
15828
  }
15773
15829
  function orphanedStreams(o) {
15774
- const dir = join33(o.stateDir, "streams");
15830
+ const dir = join34(o.stateDir, "streams");
15775
15831
  if (!existsSync32(dir)) return [];
15776
15832
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
15777
15833
  const out = [];
15778
15834
  for (const name of readdirSync12(dir)) {
15779
15835
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
15780
- const streamPath = join33(dir, name);
15836
+ const streamPath = join34(dir, name);
15781
15837
  if (runFinished(streamPath)) continue;
15782
15838
  const sidecar = readRunSidecar(streamPath);
15783
15839
  if (!sidecar || sidecar.route !== o.routeLabel) continue;
@@ -15897,7 +15953,7 @@ function latestMtime(path) {
15897
15953
  if (!stat.isDirectory()) return 0;
15898
15954
  let latest = stat.mtimeMs;
15899
15955
  for (const entry of readdirSync13(path, { withFileTypes: true })) {
15900
- latest = Math.max(latest, latestMtime(join34(path, entry.name)));
15956
+ latest = Math.max(latest, latestMtime(join35(path, entry.name)));
15901
15957
  }
15902
15958
  return latest;
15903
15959
  }
@@ -15909,20 +15965,20 @@ function makeStaleChecker(paths, opts = {}) {
15909
15965
  return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync33(opts.reloadSentinel);
15910
15966
  }
15911
15967
  function reloadSentinelFor(stateDir) {
15912
- return join34(stateDir, "reload-requested");
15968
+ return join35(stateDir, "reload-requested");
15913
15969
  }
15914
15970
  function daemonStampFor(stateDir) {
15915
- return join34(stateDir, "daemon.started");
15971
+ return join35(stateDir, "daemon.started");
15916
15972
  }
15917
15973
  function noteDaemonStarted(stateDir, now = Date.now()) {
15918
- mkdirSync20(stateDir, { recursive: true });
15974
+ mkdirSync21(stateDir, { recursive: true });
15919
15975
  rmSync14(reloadSentinelFor(stateDir), { force: true });
15920
- writeFileSync21(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
15976
+ writeFileSync22(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
15921
15977
  `);
15922
15978
  }
15923
15979
  function readDaemonStarted(stateDir) {
15924
15980
  try {
15925
- const v = JSON.parse(readFileSync27(daemonStampFor(stateDir), "utf8")).startedAt;
15981
+ const v = JSON.parse(readFileSync28(daemonStampFor(stateDir), "utf8")).startedAt;
15926
15982
  return typeof v === "number" ? v : null;
15927
15983
  } catch {
15928
15984
  return null;
@@ -16943,7 +16999,7 @@ if (command === "passenger-sync-daemon") {
16943
16999
  }
16944
17000
  if (host === "linux" && hasSystemd) {
16945
17001
  const label = labelFor(CREW_HOME, "run");
16946
- const staleTimer = join35(homedir9(), ".config", "systemd", "user", `${label}.timer`);
17002
+ const staleTimer = join36(homedir9(), ".config", "systemd", "user", `${label}.timer`);
16947
17003
  if (existsSync34(staleTimer)) {
16948
17004
  process.stdout.write(
16949
17005
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
@@ -17005,7 +17061,7 @@ if (command === "passenger-sync-daemon") {
17005
17061
  for (const a of plan.actions) {
17006
17062
  if (a.kind === "enable") {
17007
17063
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
17008
- writeFileSync22(cfg.configFile, planEnable(readFileSync28(cfg.configFile, "utf8"), a.route));
17064
+ writeFileSync23(cfg.configFile, planEnable(readFileSync29(cfg.configFile, "utf8"), a.route));
17009
17065
  enabledSound.add(a.route);
17010
17066
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
17011
17067
  } else {
@@ -18014,7 +18070,7 @@ ${changed2.map((c) => ` - ${c}
18014
18070
  process.exit(1);
18015
18071
  }
18016
18072
  requireArmed("run the daemon loop");
18017
- const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join35(CREW_HOME, "src"), join35(CREW_HOME, "dist"), join35(CREW_HOME, "bin")];
18073
+ const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join36(CREW_HOME, "src"), join36(CREW_HOME, "dist"), join36(CREW_HOME, "bin")];
18018
18074
  const configPaths = daemonConfigPaths(cfg);
18019
18075
  watchPaths.push(...configPaths);
18020
18076
  noteDaemonStarted(cfg.ship.stateDir);
@@ -18233,10 +18289,10 @@ ${changed2.map((c) => ` - ${c}
18233
18289
  `);
18234
18290
  if (!dryRun) {
18235
18291
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
18236
- const raw = existsSync34(resolvedPath) ? JSON.parse(readFileSync28(resolvedPath, "utf8")) : {};
18292
+ const raw = existsSync34(resolvedPath) ? JSON.parse(readFileSync29(resolvedPath, "utf8")) : {};
18237
18293
  raw.agentSkills = result.agentSkills;
18238
- mkdirSync21(dirOf(resolvedPath), { recursive: true });
18239
- writeFileSync22(resolvedPath, `${JSON.stringify(raw, null, 2)}
18294
+ mkdirSync22(dirOf(resolvedPath), { recursive: true });
18295
+ writeFileSync23(resolvedPath, `${JSON.stringify(raw, null, 2)}
18240
18296
  `);
18241
18297
  }
18242
18298
  if (result.outcomes.some((o) => o.action === "diverged")) process.exitCode = 1;
@@ -18259,7 +18315,7 @@ ${changed2.map((c) => ` - ${c}
18259
18315
  throw e;
18260
18316
  }
18261
18317
  const oldName = cfg.ship.name;
18262
- writeFileSync22(cfg.configFile, setShipNameInYaml(readFileSync28(cfg.configFile, "utf8"), newName));
18318
+ writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync29(cfg.configFile, "utf8"), newName));
18263
18319
  cfg.ship.name = newName;
18264
18320
  let renameMachineId;
18265
18321
  try {
@@ -18306,7 +18362,7 @@ ${changed2.map((c) => ` - ${c}
18306
18362
  process.exit(2);
18307
18363
  };
18308
18364
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks") usage2();
18309
- const configText = readFileSync28(cfg.configFile, "utf8");
18365
+ const configText = readFileSync29(cfg.configFile, "utf8");
18310
18366
  if (sub === "hooks") {
18311
18367
  try {
18312
18368
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
@@ -18320,7 +18376,7 @@ ${changed2.map((c) => ` - ${c}
18320
18376
  }
18321
18377
  }, read: (r) => {
18322
18378
  try {
18323
- return readFileSync28(resolvePath(dir, r), "utf8");
18379
+ return readFileSync29(resolvePath(dir, r), "utf8");
18324
18380
  } catch {
18325
18381
  return void 0;
18326
18382
  }
@@ -18358,13 +18414,13 @@ ${changed2.map((c) => ` - ${c}
18358
18414
  if (write && suggestion.source === null && recipeIds.length === 0 && hookSpecs.length === 0) throw new ReposError(`nothing to write: ${suggestion.notes[0]}`);
18359
18415
  if (write === "route") {
18360
18416
  const plan = planRouteHooks(configText, a.route, a.name, picked.hooks);
18361
- writeFileSync22(cfg.configFile, plan.text);
18417
+ writeFileSync23(cfg.configFile, plan.text);
18362
18418
  written = { target: "route", file: cfg.configFile, hooks: plan.written, kept: plan.kept };
18363
18419
  } else if (write === "repo") {
18364
18420
  const have = existingRepoConfig(files);
18365
18421
  if (have) throw new ReposError(`${resolvePath(dir, have)} already exists; not overwriting it`);
18366
18422
  const file = resolvePath(dir, ".crew.yaml");
18367
- writeFileSync22(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
18423
+ writeFileSync23(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
18368
18424
  written = { target: "repo", file, hooks: Object.keys(picked.hooks), kept: [] };
18369
18425
  }
18370
18426
  if (flag("json")) {
@@ -18447,7 +18503,7 @@ ${changed2.map((c) => ` - ${c}
18447
18503
  let resolvedRemotes;
18448
18504
  if (existsSync34(resolvedFile)) {
18449
18505
  try {
18450
- resolvedRemotes = JSON.parse(readFileSync28(resolvedFile, "utf8"));
18506
+ resolvedRemotes = JSON.parse(readFileSync29(resolvedFile, "utf8"));
18451
18507
  } catch {
18452
18508
  }
18453
18509
  }
@@ -18468,7 +18524,7 @@ ${changed2.map((c) => ` - ${c}
18468
18524
  isCheckout,
18469
18525
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
18470
18526
  try {
18471
- return Object.keys(parseRepoConfig(readFileSync28(resolvePath(d, f), "utf8"), f).hooks).length > 0;
18527
+ return Object.keys(parseRepoConfig(readFileSync29(resolvePath(d, f), "utf8"), f).hooks).length > 0;
18472
18528
  } catch {
18473
18529
  return false;
18474
18530
  }
@@ -18517,7 +18573,7 @@ ${changed2.map((c) => ` - ${c}
18517
18573
  throw new ReposError(`${e.message}${authHint}
18518
18574
  crew.yaml was not changed`);
18519
18575
  }
18520
- writeFileSync22(cfg.configFile, plan2.text);
18576
+ writeFileSync23(cfg.configFile, plan2.text);
18521
18577
  if (plan2.droppedDir) process.stderr.write(`crew repos: dropped dir: ${plan2.droppedDir} from route ${routeName} \u2014 it is not a checkout (the clone base); checkouts now live under repos:
18522
18578
  `);
18523
18579
  say("attached", { route: routeName, name: plan2.name, dir: plan2.target, mode: plan2.mode }, `cloned and attached ${plan2.target} to ${routeName} as ${plan2.mode === "dir" ? "dir" : `repos.${plan2.name}`} in ${cfg.configFile}`);
@@ -18577,14 +18633,14 @@ ${changed2.map((c) => ` - ${c}
18577
18633
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
18578
18634
  `);
18579
18635
  if (existsSync34(resolvedFile)) {
18580
- const raw = JSON.parse(readFileSync28(resolvedFile, "utf8"));
18636
+ const raw = JSON.parse(readFileSync29(resolvedFile, "utf8"));
18581
18637
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
18582
18638
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
18583
- writeFileSync22(resolvedFile, `${JSON.stringify(raw, null, 2)}
18639
+ writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
18584
18640
  `);
18585
18641
  }
18586
18642
  }
18587
- writeFileSync22(cfg.configFile, plan.text);
18643
+ writeFileSync23(cfg.configFile, plan.text);
18588
18644
  process.stdout.write(`added ${top} to ${routeName} as ${plan.mode === "dir" ? "dir" : `repos.${plan.name}`} in ${cfg.configFile}
18589
18645
  `);
18590
18646
  } catch (e) {
@@ -18935,7 +18991,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
18935
18991
  process.exit(2);
18936
18992
  }
18937
18993
  if (found.shipNameAdopted && !dryRun) {
18938
- writeFileSync22(cfg.configFile, setShipNameInYaml(readFileSync28(cfg.configFile, "utf8"), found.shipNameAdopted));
18994
+ writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync29(cfg.configFile, "utf8"), found.shipNameAdopted));
18939
18995
  cfg.ship.name = found.shipNameAdopted;
18940
18996
  }
18941
18997
  if (found.shipRow) {
@@ -18981,7 +19037,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
18981
19037
  const hadPreviousFile = existsSync34(resolvedPath);
18982
19038
  if (hadPreviousFile) {
18983
19039
  try {
18984
- previous = JSON.parse(readFileSync28(resolvedPath, "utf8"));
19040
+ previous = JSON.parse(readFileSync29(resolvedPath, "utf8"));
18985
19041
  } catch {
18986
19042
  }
18987
19043
  }
@@ -19101,10 +19157,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19101
19157
  process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
19102
19158
  `);
19103
19159
  } else {
19104
- mkdirSync21(dirname20(resolvedPath), { recursive: true });
19160
+ mkdirSync22(dirname20(resolvedPath), { recursive: true });
19105
19161
  if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
19106
19162
  const tmpPath = `${resolvedPath}.tmp${process.pid}`;
19107
- writeFileSync22(tmpPath, `${JSON.stringify(resolved, null, 2)}
19163
+ writeFileSync23(tmpPath, `${JSON.stringify(resolved, null, 2)}
19108
19164
  `);
19109
19165
  renameSync3(tmpPath, resolvedPath);
19110
19166
  process.stderr.write(`Resolved ids written to ${resolvedPath}
@@ -19146,8 +19202,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19146
19202
  process.stderr.write(`(dry run) would write the minted API key to ${apiKeyFile}
19147
19203
  `);
19148
19204
  } else {
19149
- mkdirSync21(dirname20(apiKeyFile), { recursive: true });
19150
- writeFileSync22(apiKeyFile, `${apiKeyVar}=${apiKey}
19205
+ mkdirSync22(dirname20(apiKeyFile), { recursive: true });
19206
+ writeFileSync23(apiKeyFile, `${apiKeyVar}=${apiKey}
19151
19207
  `, { mode: 384 });
19152
19208
  process.stderr.write(`API key written to ${apiKeyFile}
19153
19209
  `);
@@ -19183,8 +19239,11 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19183
19239
  const id = `${tc.harness}\0${tc.bin}`;
19184
19240
  if (seen.has(id)) continue;
19185
19241
  seen.add(id);
19186
- const line = adapterFor(tc.harness).ensureMcp({ bin: tc.bin, url: mcpUrlFor(baseUrl), key: apiKey, replace, dryRun });
19187
- connectEvents.emit("mcp_registered", line, { harness: tc.harness, bin: tc.bin });
19242
+ const adapter = adapterFor(tc.harness);
19243
+ const mcpUrl = mcpUrlFor(baseUrl);
19244
+ const mcpOpts = { bin: tc.bin, url: mcpUrl, key: apiKey, replace, dryRun, stateDir: cfg.ship.stateDir };
19245
+ const { line, action } = adapter.ensureMcpResult ? adapter.ensureMcpResult(mcpOpts) : { line: adapter.ensureMcp(mcpOpts), action: void 0 };
19246
+ connectEvents.emit("mcp_registered", line, { harness: tc.harness, bin: tc.bin, url: mcpUrl, ...action ? { action } : {} });
19188
19247
  process.stderr.write(`${line}
19189
19248
  `);
19190
19249
  }
@@ -19835,7 +19894,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19835
19894
  };
19836
19895
  const droppedRoutes = [];
19837
19896
  try {
19838
- const rawCfg = parseYaml2(readFileSync28(cfg.configFile, "utf8"));
19897
+ const rawCfg = parseYaml2(readFileSync29(cfg.configFile, "utf8"));
19839
19898
  for (const r of rawCfg?.routes ?? []) {
19840
19899
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
19841
19900
  try {
@@ -19877,8 +19936,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19877
19936
  process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
19878
19937
  break;
19879
19938
  }
19880
- mkdirSync21(cfg.ship.stateDir, { recursive: true });
19881
- writeFileSync22(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
19939
+ mkdirSync22(cfg.ship.stateDir, { recursive: true });
19940
+ writeFileSync23(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
19882
19941
  `);
19883
19942
  process.stdout.write("crew reload: requested \u2014 the daemon finishes its in-flight pass, then relaunches with the new config\n");
19884
19943
  if (flag("now") && control?.stopCommand && control.startCommand) {
@@ -19929,7 +19988,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19929
19988
  }
19930
19989
  case "log": {
19931
19990
  try {
19932
- const text = readFileSync28(cfg.ship.logFile, "utf8").split("\n");
19991
+ const text = readFileSync29(cfg.ship.logFile, "utf8").split("\n");
19933
19992
  process.stdout.write(`${text.slice(-(Number(value("tail")) || 40)).join("\n")}
19934
19993
  `);
19935
19994
  } catch {
@@ -19952,7 +20011,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19952
20011
  };
19953
20012
  if (!route.resolved) fail(`route ${route.route} is not connected \u2014 run \`crew connect\` first`);
19954
20013
  const tracker = new Tracker(route, cfg.ship);
19955
- const configText = readFileSync28(cfg.configFile, "utf8");
20014
+ const configText = readFileSync29(cfg.configFile, "utf8");
19956
20015
  const cfgBase = dirname20(cfg.configFile);
19957
20016
  const configured = configuredRepos(configText, route.route, cfgBase);
19958
20017
  const dir = configured.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured.map((r) => r.dir), route.reposBasePath, route.route.split("/")[0]);
@@ -19981,7 +20040,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19981
20040
  }
19982
20041
  if (configured.some((r) => r.dir === dir)) {
19983
20042
  try {
19984
- writeFileSync22(cfg.configFile, planRepoRemove(configText, cfgBase, route.route, dir).text);
20043
+ writeFileSync23(cfg.configFile, planRepoRemove(configText, cfgBase, route.route, dir).text);
19985
20044
  } catch (e) {
19986
20045
  if (e instanceof ReposError) fail(e.message);
19987
20046
  throw e;
@@ -19990,28 +20049,28 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19990
20049
  if (row) {
19991
20050
  await tracker.deleteRepoRow(row.id);
19992
20051
  if (existsSync34(resolvedFile)) {
19993
- const raw = JSON.parse(readFileSync28(resolvedFile, "utf8"));
20052
+ const raw = JSON.parse(readFileSync29(resolvedFile, "utf8"));
19994
20053
  if (raw.repoNames) delete raw.repoNames[row.id];
19995
20054
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
19996
- writeFileSync22(resolvedFile, `${JSON.stringify(raw, null, 2)}
20055
+ writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
19997
20056
  `);
19998
20057
  }
19999
20058
  }
20000
- if (existsSync34(join35(dir, ".git")) && basename11(dir) === SAMPLE_REPO) rmSync15(dir, { recursive: true, force: true });
20059
+ if (existsSync34(join36(dir, ".git")) && basename11(dir) === SAMPLE_REPO) rmSync15(dir, { recursive: true, force: true });
20001
20060
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
20002
20061
  break;
20003
20062
  }
20004
20063
  const projectId = route.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
20005
- const fresh = !existsSync34(join35(dir, ".git"));
20064
+ const fresh = !existsSync34(join36(dir, ".git"));
20006
20065
  if (existsSync34(dir) && fresh && readdirSync14(dir).length > 0) fail(`${dir} exists and is not a git checkout \u2014 move it aside`);
20007
20066
  if (dryRun) {
20008
20067
  say("dry-run", { dir, newCheckout: fresh, repoRow: row?.id ?? null }, `would ${fresh ? "create" : "reuse"} ${dir}${row ? "" : ", create the Repos row"}, and file one ${tracker.contract.statuses.approved} ticket`);
20009
20068
  break;
20010
20069
  }
20011
20070
  if (fresh) {
20012
- mkdirSync21(dir, { recursive: true });
20013
- writeFileSync22(join35(dir, "README.md"), SAMPLE_README);
20014
- writeFileSync22(join35(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
20071
+ mkdirSync22(dir, { recursive: true });
20072
+ writeFileSync23(join36(dir, "README.md"), SAMPLE_README);
20073
+ writeFileSync23(join36(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
20015
20074
  const g = (...a) => {
20016
20075
  const r = spawnSync6("git", a, { cwd: dir, encoding: "utf8" });
20017
20076
  if (r.status !== 0) fail(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
@@ -20030,16 +20089,16 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20030
20089
  });
20031
20090
  repoId = (await tracker.createRepoRow(payload)).id;
20032
20091
  if (existsSync34(resolvedFile)) {
20033
- const raw = JSON.parse(readFileSync28(resolvedFile, "utf8"));
20092
+ const raw = JSON.parse(readFileSync29(resolvedFile, "utf8"));
20034
20093
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
20035
20094
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
20036
- writeFileSync22(resolvedFile, `${JSON.stringify(raw, null, 2)}
20095
+ writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
20037
20096
  `);
20038
20097
  }
20039
20098
  }
20040
20099
  if (!configured.some((r) => r.dir === dir)) {
20041
20100
  try {
20042
- writeFileSync22(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route.route, path: dir, name: SAMPLE_REPO }).text);
20101
+ writeFileSync23(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route.route, path: dir, name: SAMPLE_REPO }).text);
20043
20102
  } catch (e) {
20044
20103
  if (e instanceof ReposError) fail(e.message);
20045
20104
  throw e;
@@ -20393,7 +20452,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
20393
20452
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run");
20394
20453
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
20395
20454
  try {
20396
- return readFileSync28(p, "utf8");
20455
+ return readFileSync29(p, "utf8");
20397
20456
  } catch {
20398
20457
  return null;
20399
20458
  }