@tablation/crew 0.120.0 → 0.121.0

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 +691 -617
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -183,10 +183,12 @@ function dropFileMtime(worktree) {
183
183
 
184
184
  // src/intel-register.ts
185
185
  import { execFileSync as execFileSync2 } from "child_process";
186
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
187
- import { dirname as dirname2, join as join2 } from "path";
186
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
187
+ import { dirname as dirname2, join as join3 } from "path";
188
188
 
189
189
  // src/intel.ts
190
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
191
+ import { join as join2 } from "path";
190
192
  var INTEL_FLAVORS = {
191
193
  serena: {
192
194
  summary: "crew runs a Serena symbol service per worktree, seeded from the main checkout's index",
@@ -197,11 +199,65 @@ var INTEL_FLAVORS = {
197
199
  implemented: true
198
200
  },
199
201
  "typescript-lsp": {
200
- summary: "crew runs a TypeScript language-server symbol service per worktree (CREW-2916)",
202
+ summary: "the Claude harness's own LSP tool serves the repository through a crew-generated language-server plugin (Claude only)",
203
+ // Checked against the main checkout's node_modules/.bin, then PATH, by `lspBinaryFound`.
201
204
  prerequisites: [],
202
- implemented: false
205
+ implemented: true
206
+ }
207
+ };
208
+ var LSP_SERVERS = {
209
+ "typescript-lsp": {
210
+ bin: "typescript-language-server",
211
+ args: ["--stdio"],
212
+ install: "npm install -D typescript-language-server typescript",
213
+ extensionToLanguage: {
214
+ ".ts": "typescript",
215
+ ".tsx": "typescriptreact",
216
+ ".js": "javascript",
217
+ ".jsx": "javascriptreact",
218
+ ".mts": "typescript",
219
+ ".cts": "typescript",
220
+ ".mjs": "javascript",
221
+ ".cjs": "javascript"
222
+ }
203
223
  }
204
224
  };
225
+ function lspFlavor(script) {
226
+ const f = intelFlavor(script);
227
+ return f && Object.prototype.hasOwnProperty.call(LSP_SERVERS, f) ? f : void 0;
228
+ }
229
+ var lspLocalBin = (mainCheckout, flavor) => join2(mainCheckout, "node_modules", ".bin", LSP_SERVERS[flavor].bin);
230
+ function lspBinaryFound(flavor, mainCheckout, onPath2) {
231
+ return existsSync3(lspLocalBin(mainCheckout, flavor)) || onPath2(LSP_SERVERS[flavor].bin);
232
+ }
233
+ function ensureLspPlugin(stateDir, flavor, mainCheckout) {
234
+ const def = LSP_SERVERS[flavor];
235
+ const dir = join2(stateDir, "intel", "plugins", flavor);
236
+ const command2 = existsSync3(lspLocalBin(mainCheckout, flavor)) ? `\${CLAUDE_PROJECT_DIR}/node_modules/.bin/${def.bin}` : def.bin;
237
+ const files = [
238
+ [join2(".claude-plugin", "plugin.json"), { name: `crew-${flavor}`, version: "1.0.0", description: `Language server for the ${flavor} intel flavor, generated by crew` }],
239
+ [".lsp.json", { [def.bin]: { command: command2, args: [...def.args], extensionToLanguage: def.extensionToLanguage } }]
240
+ ];
241
+ for (const [rel, doc] of files) {
242
+ const path = join2(dir, rel);
243
+ const text2 = `${JSON.stringify(doc, null, 2)}
244
+ `;
245
+ let old;
246
+ try {
247
+ old = readFileSync2(path, "utf8");
248
+ } catch {
249
+ }
250
+ if (old === text2) continue;
251
+ mkdirSync2(join2(path, ".."), { recursive: true });
252
+ writeFileSync2(path, text2);
253
+ }
254
+ return dir;
255
+ }
256
+ function lspToolsParagraph(harness, omlx = false) {
257
+ if (harness !== "claude") return "No symbol service is available for this harness: this repository's `intel` flavor serves only the Claude Code harness's own LSP tool.";
258
+ if (omlx) return "The harness's LSP tool is off for this launcher, so no symbol service is available for this run.";
259
+ return "The harness's `LSP` tool is active for this repository's TypeScript and JavaScript files (.ts .tsx .js .jsx .mts .cts .mjs .cjs). It offers definition, references, hover, file symbols, workspace symbol search, implementations and call hierarchy, read-only; diagnostics arrive after each edit.";
260
+ }
205
261
  function intelFlavor(script) {
206
262
  const w = script?.trim();
207
263
  return w && Object.prototype.hasOwnProperty.call(INTEL_FLAVORS, w) ? w : void 0;
@@ -330,11 +386,11 @@ function registerCodeServer(worktree, url, server = INTEL_SERVER_NAME) {
330
386
  [".gemini/settings.json", (old) => mergeJsonServer(old, { httpUrl: url }, server)]
331
387
  ];
332
388
  for (const [rel, merge] of plans) {
333
- const file = join2(worktree, rel);
389
+ const file = join3(worktree, rel);
334
390
  try {
335
- const next = merge(existsSync3(file) ? readFileSync2(file, "utf8") : void 0);
336
- mkdirSync2(dirname2(file), { recursive: true });
337
- writeFileSync2(file, next);
391
+ const next = merge(existsSync4(file) ? readFileSync3(file, "utf8") : void 0);
392
+ mkdirSync3(dirname2(file), { recursive: true });
393
+ writeFileSync3(file, next);
338
394
  if (tracked(worktree, rel)) execFileSync2("git", ["update-index", "--skip-worktree", "--", rel], { cwd: worktree, stdio: "ignore" });
339
395
  else excludeFromGit(worktree, rel);
340
396
  out.written.push(rel);
@@ -347,13 +403,13 @@ function registerCodeServer(worktree, url, server = INTEL_SERVER_NAME) {
347
403
 
348
404
  // src/intel-serena.ts
349
405
  import { spawn, spawnSync } from "child_process";
350
- import { closeSync, cpSync, existsSync as existsSync5, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
351
- import { dirname as dirname3, join as join4 } from "path";
406
+ import { closeSync, cpSync, existsSync as existsSync6, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync4 } from "fs";
407
+ import { dirname as dirname3, join as join5 } from "path";
352
408
 
353
409
  // src/ports.ts
354
410
  import { execFileSync as execFileSync3 } from "child_process";
355
- import { existsSync as existsSync4 } from "fs";
356
- import { join as join3 } from "path";
411
+ import { existsSync as existsSync5 } from "fs";
412
+ import { join as join4 } from "path";
357
413
  var run = (cmd, args) => {
358
414
  try {
359
415
  return execFileSync3(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
@@ -402,7 +458,7 @@ var intelPortFor = (n, s = DEFAULT_PORTS) => (s.intelBase ?? DEFAULT_PORTS.intel
402
458
  var proxyPortFor = (n, s = DEFAULT_PORTS) => (s.proxyBase ?? DEFAULT_PORTS.proxyBase) + n;
403
459
  function worktreeExists(parent, prefix, n) {
404
460
  for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
405
- if (existsSync4(join3(parent, name, ".git"))) return true;
461
+ if (existsSync5(join4(parent, name, ".git"))) return true;
406
462
  }
407
463
  return false;
408
464
  }
@@ -446,10 +502,10 @@ function pidsInWorktree(path, n, s = DEFAULT_PORTS) {
446
502
 
447
503
  // src/intel-serena.ts
448
504
  var serenaPaths = (stateDir, route2, ticket) => ({
449
- home: join4(stateDir, "intel", route2, ticket, "home"),
450
- mainHome: join4(stateDir, "intel", route2, "main", "home"),
451
- mainProject: join4(stateDir, "intel", route2, "main"),
452
- project: join4(stateDir, "intel", route2, ticket)
505
+ home: join5(stateDir, "intel", route2, ticket, "home"),
506
+ mainHome: join5(stateDir, "intel", route2, "main", "home"),
507
+ mainProject: join5(stateDir, "intel", route2, "main"),
508
+ project: join5(stateDir, "intel", route2, ticket)
453
509
  });
454
510
  var serenaFolderFor = (stateDir, route2, ticket) => serenaPaths(stateDir, route2, ticket).project;
455
511
  function serenaConfigYaml(projectFolder) {
@@ -493,20 +549,20 @@ function missingPrerequisites(flavor, onPath2) {
493
549
  function prepareFolder(d, home, folder, checkout) {
494
550
  d.mkdir(home);
495
551
  d.mkdir(folder);
496
- d.write(join4(home, "serena_config.yml"), serenaConfigYaml(folder));
497
- d.write(join4(folder, "project.yml"), serenaProjectYaml(d.read(join4(checkout, ".serena", "project.yml"))));
552
+ d.write(join5(home, "serena_config.yml"), serenaConfigYaml(folder));
553
+ d.write(join5(folder, "project.yml"), serenaProjectYaml(d.read(join5(checkout, ".serena", "project.yml"))));
498
554
  }
499
555
  function ensureMainIndex(d, s) {
500
556
  const p = serenaPaths(s.stateDir, s.route, String(s.ticket));
501
557
  const head = d.headOf(s.mainCheckout);
502
558
  if (!head) return "unavailable";
503
- const stamp = join4(p.mainProject, "indexed-commit");
559
+ const stamp = join5(p.mainProject, "indexed-commit");
504
560
  if (d.read(stamp)?.trim() === head) return "fresh";
505
- const pidFile = join4(p.mainProject, "indexing.pid");
561
+ const pidFile = join5(p.mainProject, "indexing.pid");
506
562
  const running = Number(d.read(pidFile));
507
563
  if (running > 0 && d.alive(running)) return "running";
508
564
  prepareFolder(d, p.mainHome, p.mainProject, s.mainCheckout);
509
- const pid = d.spawnIndex(serenaIndexArgs(s.mainCheckout), { SERENA_HOME: p.mainHome }, join4(p.mainProject, "index.log"), stamp, head);
565
+ const pid = d.spawnIndex(serenaIndexArgs(s.mainCheckout), { SERENA_HOME: p.mainHome }, join5(p.mainProject, "index.log"), stamp, head);
510
566
  d.write(pidFile, String(pid));
511
567
  return "started";
512
568
  }
@@ -516,15 +572,15 @@ function startSerena(d, s) {
516
572
  const p = serenaPaths(s.stateDir, s.route, String(s.ticket));
517
573
  try {
518
574
  prepareFolder(d, p.home, p.project, s.worktree);
519
- const mainCache = join4(p.mainProject, "cache");
520
- const cache = join4(p.project, "cache");
575
+ const mainCache = join5(p.mainProject, "cache");
576
+ const cache = join5(p.project, "cache");
521
577
  if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
522
578
  try {
523
579
  ensureMainIndex(d, s);
524
580
  } catch {
525
581
  }
526
582
  const port = intelPortFor(s.ticket);
527
- d.spawnDetached("serena", serenaServeArgs(port, s.worktree), { SERENA_HOME: p.home }, join4(p.project, "serena.log"));
583
+ d.spawnDetached("serena", serenaServeArgs(port, s.worktree), { SERENA_HOME: p.home }, join5(p.project, "serena.log"));
528
584
  return { code: 0, output: `http://127.0.0.1:${port}/mcp
529
585
  ` };
530
586
  } catch (e) {
@@ -533,7 +589,7 @@ function startSerena(d, s) {
533
589
  }
534
590
  function realSerenaDeps(onPath2, extraEnv = process.env) {
535
591
  const detached = (bin, args, env, log) => {
536
- mkdirSync3(dirname3(log), { recursive: true });
592
+ mkdirSync4(dirname3(log), { recursive: true });
537
593
  const fd = openSync(log, "a");
538
594
  const child = spawn(bin, args, { detached: true, stdio: ["ignore", fd, fd], env: { ...extraEnv, ...env } });
539
595
  child.unref();
@@ -543,20 +599,20 @@ function realSerenaDeps(onPath2, extraEnv = process.env) {
543
599
  };
544
600
  return {
545
601
  onPath: onPath2,
546
- exists: existsSync5,
602
+ exists: existsSync6,
547
603
  mkdir: (p) => {
548
- mkdirSync3(p, { recursive: true });
604
+ mkdirSync4(p, { recursive: true });
549
605
  },
550
606
  read: (p) => {
551
607
  try {
552
- return readFileSync3(p, "utf8");
608
+ return readFileSync4(p, "utf8");
553
609
  } catch {
554
610
  return void 0;
555
611
  }
556
612
  },
557
613
  write: (p, text2) => {
558
- mkdirSync3(dirname3(p), { recursive: true });
559
- writeFileSync3(p, text2);
614
+ mkdirSync4(dirname3(p), { recursive: true });
615
+ writeFileSync4(p, text2);
560
616
  },
561
617
  copyDir: (from, to) => cpSync(from, to, { recursive: true }),
562
618
  headOf: (checkout) => {
@@ -582,14 +638,14 @@ function dropSerenaFolder(stateDir, route2, ticket) {
582
638
  }
583
639
 
584
640
  // src/periscope-state.ts
585
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
641
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
586
642
  import { connect } from "net";
587
- import { join as join5 } from "path";
588
- var dirOf = (stateDir) => join5(stateDir, "periscopes");
589
- var fileOf = (stateDir, ticketRecordId) => join5(dirOf(stateDir), `${ticketRecordId}.json`);
643
+ import { join as join6 } from "path";
644
+ var dirOf = (stateDir) => join6(stateDir, "periscopes");
645
+ var fileOf = (stateDir, ticketRecordId) => join6(dirOf(stateDir), `${ticketRecordId}.json`);
590
646
  function writePeriscopeSidecar(stateDir, sc) {
591
- mkdirSync4(dirOf(stateDir), { recursive: true });
592
- writeFileSync4(fileOf(stateDir, sc.ticketRecordId), `${JSON.stringify(sc, null, 2)}
647
+ mkdirSync5(dirOf(stateDir), { recursive: true });
648
+ writeFileSync5(fileOf(stateDir, sc.ticketRecordId), `${JSON.stringify(sc, null, 2)}
593
649
  `);
594
650
  }
595
651
  function removePeriscopeSidecar(stateDir, ticketRecordId) {
@@ -597,12 +653,12 @@ function removePeriscopeSidecar(stateDir, ticketRecordId) {
597
653
  }
598
654
  function readPeriscopeSidecars(stateDir) {
599
655
  const dir = dirOf(stateDir);
600
- if (!existsSync6(dir)) return [];
656
+ if (!existsSync7(dir)) return [];
601
657
  const out = [];
602
658
  for (const name of readdirSync(dir)) {
603
659
  if (!name.endsWith(".json")) continue;
604
660
  try {
605
- const sc = JSON.parse(readFileSync4(join5(dir, name), "utf8"));
661
+ const sc = JSON.parse(readFileSync5(join6(dir, name), "utf8"));
606
662
  if (sc && typeof sc.ticketRecordId === "string" && typeof sc.url === "string") out.push(sc);
607
663
  } catch {
608
664
  }
@@ -720,23 +776,23 @@ function shellInitScript(shell) {
720
776
  }
721
777
 
722
778
  // src/config.ts
723
- import { readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
724
- import { resolve as resolve2, dirname as dirname7, isAbsolute as isAbsolute2, join as join16, basename as basename4 } from "path";
779
+ import { readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
780
+ import { resolve as resolve2, dirname as dirname7, isAbsolute as isAbsolute2, join as join17, basename as basename4 } from "path";
725
781
  import { homedir as homedir6, tmpdir } from "os";
726
782
  import { parse as parse2 } from "yaml";
727
783
 
728
784
  // src/events.ts
729
785
  import {
730
786
  appendFileSync as appendFileSync2,
731
- mkdirSync as mkdirSync5,
732
- existsSync as existsSync7,
787
+ mkdirSync as mkdirSync6,
788
+ existsSync as existsSync8,
733
789
  statSync as statSync2,
734
790
  renameSync,
735
791
  openSync as openSync2,
736
792
  closeSync as closeSync2,
737
793
  rmSync as rmSync3
738
794
  } from "fs";
739
- import { join as join6, dirname as dirname4 } from "path";
795
+ import { join as join7, dirname as dirname4 } from "path";
740
796
  function render(e) {
741
797
  const scope = e.role ? `${e.step}[${e.role}]` : e.step;
742
798
  const ticket = e.ticket ? ` ${e.ticket}` : "";
@@ -748,7 +804,7 @@ var ROTATED_EVENTS_RE = /^events\.\d{8}T\d{6}Z(?:-\d+)?\.jsonl$/;
748
804
  var ROTATE_LOCK_STALE_MS = 6e4;
749
805
  function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
750
806
  try {
751
- if (!existsSync7(file) || statSync2(file).size <= maxBytes) return void 0;
807
+ if (!existsSync8(file) || statSync2(file).size <= maxBytes) return void 0;
752
808
  } catch {
753
809
  return void 0;
754
810
  }
@@ -768,8 +824,8 @@ function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
768
824
  try {
769
825
  if (statSync2(file).size <= maxBytes) return void 0;
770
826
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
771
- let dest = join6(dirname4(file), `events.${stamp}.jsonl`);
772
- for (let n = 1; existsSync7(dest); n++) dest = join6(dirname4(file), `events.${stamp}-${n}.jsonl`);
827
+ let dest = join7(dirname4(file), `events.${stamp}.jsonl`);
828
+ for (let n = 1; existsSync8(dest); n++) dest = join7(dirname4(file), `events.${stamp}-${n}.jsonl`);
773
829
  renameSync(file, dest);
774
830
  return dest;
775
831
  } finally {
@@ -792,7 +848,7 @@ var Emitter = class _Emitter {
792
848
  for (const f of [opts.eventFile, opts.logFile]) {
793
849
  if (!f) continue;
794
850
  try {
795
- mkdirSync5(dirname4(f), { recursive: true });
851
+ mkdirSync6(dirname4(f), { recursive: true });
796
852
  } catch {
797
853
  }
798
854
  }
@@ -868,18 +924,18 @@ var Emitter = class _Emitter {
868
924
  }
869
925
  }
870
926
  };
871
- var eventFileFor = (stateDir) => join6(stateDir, "events.jsonl");
927
+ var eventFileFor = (stateDir) => join7(stateDir, "events.jsonl");
872
928
 
873
929
  // src/pair-install.ts
874
- import { existsSync as existsSync10, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
930
+ import { existsSync as existsSync11, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "fs";
875
931
  import { spawnSync as spawnSync4 } from "child_process";
876
932
  import { homedir as homedir5 } from "os";
877
- import { join as join12 } from "path";
933
+ import { join as join13 } from "path";
878
934
 
879
935
  // src/adapters/claude.ts
880
- import { readFileSync as readFileSync9 } from "fs";
936
+ import { readFileSync as readFileSync10 } from "fs";
881
937
  import { homedir as homedir3 } from "os";
882
- import { join as join10 } from "path";
938
+ import { join as join11 } from "path";
883
939
 
884
940
  // src/stream.ts
885
941
  function toolTarget(name, input) {
@@ -938,19 +994,19 @@ function extractResult(line) {
938
994
 
939
995
  // src/claude-mcp.ts
940
996
  import { spawnSync as spawnSync2 } from "child_process";
941
- import { readFileSync as readFileSync7 } from "fs";
997
+ import { readFileSync as readFileSync8 } from "fs";
942
998
  import { homedir } from "os";
943
- import { join as join8 } from "path";
999
+ import { join as join9 } from "path";
944
1000
 
945
1001
  // src/mcp-registrations.ts
946
1002
  import { createHash } from "crypto";
947
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
948
- import { join as join7 } from "path";
949
- var fileFor = (stateDir) => join7(stateDir, "mcp-registrations.json");
1003
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
1004
+ import { join as join8 } from "path";
1005
+ var fileFor = (stateDir) => join8(stateDir, "mcp-registrations.json");
950
1006
  var keyFingerprint = (key) => createHash("sha256").update(key).digest("hex").slice(0, 8);
951
1007
  function readMcpRegistrations(stateDir) {
952
1008
  try {
953
- const j = JSON.parse(readFileSync6(fileFor(stateDir), "utf8"));
1009
+ const j = JSON.parse(readFileSync7(fileFor(stateDir), "utf8"));
954
1010
  return Array.isArray(j) ? j : [];
955
1011
  } catch {
956
1012
  return [];
@@ -958,8 +1014,8 @@ function readMcpRegistrations(stateDir) {
958
1014
  }
959
1015
  function recordMcpRegistration(stateDir, r, now = /* @__PURE__ */ new Date()) {
960
1016
  const rest = readMcpRegistrations(stateDir).filter((x) => !(x.harness === r.harness && x.scope === r.scope && x.name === r.name));
961
- mkdirSync6(stateDir, { recursive: true });
962
- writeFileSync5(fileFor(stateDir), `${JSON.stringify([...rest, { ...r, at: now.toISOString() }], null, 2)}
1017
+ mkdirSync7(stateDir, { recursive: true });
1018
+ writeFileSync6(fileFor(stateDir), `${JSON.stringify([...rest, { ...r, at: now.toISOString() }], null, 2)}
963
1019
  `, { mode: 384 });
964
1020
  }
965
1021
  function findMcpRegistration(stateDir, r) {
@@ -982,7 +1038,7 @@ function runClaude(bin, args) {
982
1038
  }
983
1039
  function userScopeEntry(home) {
984
1040
  try {
985
- const j = JSON.parse(readFileSync7(join8(home, ".claude.json"), "utf8"));
1041
+ const j = JSON.parse(readFileSync8(join9(home, ".claude.json"), "utf8"));
986
1042
  return j.mcpServers?.[MCP_SERVER_NAME];
987
1043
  } catch {
988
1044
  return void 0;
@@ -1066,8 +1122,8 @@ function removeClaudeMcp(o) {
1066
1122
  }
1067
1123
 
1068
1124
  // src/launcher.ts
1069
- import { basename, join as join9 } from "path";
1070
- import { existsSync as existsSync8, readFileSync as readFileSync8, readdirSync as readdirSync2 } from "fs";
1125
+ import { basename, join as join10 } from "path";
1126
+ import { existsSync as existsSync9, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
1071
1127
  import { homedir as homedir2 } from "os";
1072
1128
  function isOriLauncher(spec) {
1073
1129
  return !!spec.launcher && basename(spec.launcher) === "ori";
@@ -1076,7 +1132,7 @@ function isOmlxLauncher(spec) {
1076
1132
  return !!spec.launcher && basename(spec.launcher) === "omlx";
1077
1133
  }
1078
1134
  function defaultOmlxBin(home = homedir2()) {
1079
- return join9(home, ".omlx", "bin", "omlx");
1135
+ return join10(home, ".omlx", "bin", "omlx");
1080
1136
  }
1081
1137
  function launcherExecutable(spec, home) {
1082
1138
  if (!spec.launcher) return void 0;
@@ -1129,8 +1185,8 @@ function omlxModelPresent(model, dirs) {
1129
1185
  });
1130
1186
  }
1131
1187
  function readOmlxSettings(home = homedir2()) {
1132
- const p = join9(home, ".omlx", "settings.json");
1133
- return existsSync8(p) ? parseOmlxSettings(readFileSync8(p, "utf8")) : void 0;
1188
+ const p = join10(home, ".omlx", "settings.json");
1189
+ return existsSync9(p) ? parseOmlxSettings(readFileSync9(p, "utf8")) : void 0;
1134
1190
  }
1135
1191
 
1136
1192
  // src/capability.ts
@@ -1194,7 +1250,7 @@ function describeClaudeAuth(spec, env = process.env, home = homedir3()) {
1194
1250
  if (spec.auth === "workspace") return `workspace${spec.inference ? ` (${new URL(spec.inference.baseUrl).host})` : " (endpoint resolved per route at run time)"}`;
1195
1251
  let fromSettings;
1196
1252
  try {
1197
- const parsed = JSON.parse(readFileSync9(join10(home, ".claude", "settings.json"), "utf8"));
1253
+ const parsed = JSON.parse(readFileSync10(join11(home, ".claude", "settings.json"), "utf8"));
1198
1254
  const e = parsed.env ?? {};
1199
1255
  const on = (k) => e[k] !== void 0 && e[k] !== "" && e[k] !== "0";
1200
1256
  if (on("CLAUDE_CODE_USE_VERTEX")) fromSettings = "vertex from ~/.claude/settings.json";
@@ -1262,6 +1318,9 @@ var claudeAdapter = {
1262
1318
  }
1263
1319
  }
1264
1320
  })] : [],
1321
+ // A crew-generated language-server plugin (the `typescript-lsp` intel flavor). Not under oMLX,
1322
+ // which denies the LSP tool above.
1323
+ ...isOmlxLauncher(spec) ? [] : (o.pluginDirs ?? []).flatMap((d) => ["--plugin-dir", d]),
1265
1324
  ...spec.auth === "subscription" ? ["--settings", subscriptionSettings()] : [],
1266
1325
  ...spec.auth === "workspace" && spec.inference ? ["--settings", workspaceSettings(spec.inference.baseUrl)] : [],
1267
1326
  ...spec.args
@@ -1298,11 +1357,11 @@ var claudeAdapter = {
1298
1357
  // src/adapters/codex.ts
1299
1358
  import { spawnSync as spawnSync3 } from "child_process";
1300
1359
  import { homedir as homedir4 } from "os";
1301
- import { join as join11 } from "path";
1302
- import { rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
1360
+ import { join as join12 } from "path";
1361
+ import { rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
1303
1362
 
1304
1363
  // src/pair-files.ts
1305
- import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6, rmSync as rmSync4 } from "fs";
1364
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync7, rmSync as rmSync4 } from "fs";
1306
1365
  import { dirname as dirname5 } from "path";
1307
1366
  var PAIR_BLOCK_START = "<!-- crew:pair start -->";
1308
1367
  var PAIR_BLOCK_END = "<!-- crew:pair end -->";
@@ -1343,11 +1402,11 @@ function removePairBlock(text2) {
1343
1402
  return text2.replace(new RegExp(`\\n*${BLOCK_RE.source}`), "\n").replace(/^\n+/, "").replace(/\n{3,}/g, "\n\n");
1344
1403
  }
1345
1404
  function readIfExists(path) {
1346
- return existsSync9(path) ? readFileSync10(path, "utf8") : void 0;
1405
+ return existsSync10(path) ? readFileSync11(path, "utf8") : void 0;
1347
1406
  }
1348
1407
  function writeFileMkdir(path, text2) {
1349
- mkdirSync7(dirname5(path), { recursive: true });
1350
- writeFileSync6(path, text2);
1408
+ mkdirSync8(dirname5(path), { recursive: true });
1409
+ writeFileSync7(path, text2);
1351
1410
  }
1352
1411
  function installBlockFile(path, harness, dryRun) {
1353
1412
  const before = readIfExists(path) ?? "";
@@ -1363,7 +1422,7 @@ function removeBlockFile(path, dryRun) {
1363
1422
  if (dryRun) return true;
1364
1423
  const after = removePairBlock(before);
1365
1424
  if (after.trim() === "") rmSync4(path);
1366
- else writeFileSync6(path, after);
1425
+ else writeFileSync7(path, after);
1367
1426
  return true;
1368
1427
  }
1369
1428
  function blockVerdict(path, harness) {
@@ -1490,8 +1549,8 @@ function codexTable(url) {
1490
1549
  return [`[mcp_servers.${SERVER}]`, `url = ${tomlString(url)}`, `bearer_token_env_var = ${tomlString(PAIR_KEY_VAR)}`];
1491
1550
  }
1492
1551
  var codexPaths = (home) => ({
1493
- instructions: join11(home, ".codex", "AGENTS.md"),
1494
- config: join11(home, ".codex", "config.toml")
1552
+ instructions: join12(home, ".codex", "AGENTS.md"),
1553
+ config: join12(home, ".codex", "config.toml")
1495
1554
  });
1496
1555
  function codexMcpState(config, url) {
1497
1556
  const lines2 = config.split("\n");
@@ -1542,7 +1601,7 @@ var codexPair = {
1542
1601
  if (hadMcp && !o.dryRun) {
1543
1602
  const after = removeTomlTable(config, `mcp_servers.${SERVER}`);
1544
1603
  if (after === "") rmSync5(p.config);
1545
- else writeFileSync7(p.config, after);
1604
+ else writeFileSync8(p.config, after);
1546
1605
  }
1547
1606
  const what = hadBlock && hadMcp ? `the Pair block and ${SERVER} server` : hadBlock ? "the Pair block" : `the ${SERVER} server`;
1548
1607
  return hadBlock || hadMcp ? `pair(codex): ${o.dryRun ? "(dry run) would remove" : "removed"} ${what}${kept}` : `pair(codex): nothing to remove${kept}`;
@@ -1638,8 +1697,8 @@ var codexAdapter = {
1638
1697
 
1639
1698
  // src/pair-install.ts
1640
1699
  var geminiPaths = (home) => ({
1641
- instructions: join12(home, ".gemini", "GEMINI.md"),
1642
- config: join12(home, ".gemini", "settings.json")
1700
+ instructions: join13(home, ".gemini", "GEMINI.md"),
1701
+ config: join13(home, ".gemini", "settings.json")
1643
1702
  });
1644
1703
  function readGeminiSettings(path) {
1645
1704
  const text2 = readIfExists(path);
@@ -1727,7 +1786,7 @@ var geminiPair = {
1727
1786
  if (Object.keys(rest).length > 0) next.mcpServers = rest;
1728
1787
  else delete next.mcpServers;
1729
1788
  if (Object.keys(next).length === 0) rmSync6(p.config);
1730
- else writeFileSync8(p.config, `${JSON.stringify(next, null, 2)}
1789
+ else writeFileSync9(p.config, `${JSON.stringify(next, null, 2)}
1731
1790
  `);
1732
1791
  }
1733
1792
  const what = hadBlock && hadMcp ? `the Pair block and ${SERVER} server` : hadBlock ? "the Pair block" : `the ${SERVER} server`;
@@ -1738,9 +1797,9 @@ var VSCODE_FLAVOURS = ["Code", "Code - Insiders"];
1738
1797
  function vscodeUserDirs(o) {
1739
1798
  const home = o.home ?? homedir5();
1740
1799
  const platform2 = o.platform ?? process.platform;
1741
- const root = platform2 === "darwin" ? join12(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join12(home, "AppData", "Roaming") : join12(home, ".config");
1742
- const all = VSCODE_FLAVOURS.map((f) => join12(root, f, "User"));
1743
- const present = all.filter((d) => existsSync10(d));
1800
+ const root = platform2 === "darwin" ? join13(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join13(home, "AppData", "Roaming") : join13(home, ".config");
1801
+ const all = VSCODE_FLAVOURS.map((f) => join13(root, f, "User"));
1802
+ const present = all.filter((d) => existsSync11(d));
1744
1803
  return present.length > 0 ? present : [all[0]];
1745
1804
  }
1746
1805
  var COPILOT_FRONTMATTER = '---\napplyTo: "**"\n---\n\n';
@@ -1778,7 +1837,7 @@ function removeCopilotInstructions(path, dryRun) {
1778
1837
  if (dryRun) return true;
1779
1838
  const after = removePairBlock(before);
1780
1839
  if (after.trim() === "" || after.trim() === COPILOT_FRONTMATTER.trim()) rmSync6(path);
1781
- else writeFileSync8(path, after);
1840
+ else writeFileSync9(path, after);
1782
1841
  return true;
1783
1842
  }
1784
1843
  var copilotPair = {
@@ -1787,8 +1846,8 @@ var copilotPair = {
1787
1846
  installPair(o) {
1788
1847
  const lines2 = [];
1789
1848
  for (const dir of vscodeUserDirs(o)) {
1790
- const mcpPath = join12(dir, "mcp.json");
1791
- const instrPath = join12(dir, "prompts", "crew-pair.instructions.md");
1849
+ const mcpPath = join13(dir, "mcp.json");
1850
+ const instrPath = join13(dir, "prompts", "crew-pair.instructions.md");
1792
1851
  const settings = readVscodeMcp(mcpPath);
1793
1852
  if (!settings) {
1794
1853
  lines2.push(`pair(copilot): ${mcpPath} is not a plain JSON object (comments are not supported) \u2014 fix it by hand; nothing was written there`);
@@ -1816,8 +1875,8 @@ var copilotPair = {
1816
1875
  },
1817
1876
  verifyPair(o) {
1818
1877
  const verdicts = vscodeUserDirs(o).map((dir) => {
1819
- const instrPath = join12(dir, "prompts", "crew-pair.instructions.md");
1820
- const mcpPath = join12(dir, "mcp.json");
1878
+ const instrPath = join13(dir, "prompts", "crew-pair.instructions.md");
1879
+ const mcpPath = join13(dir, "mcp.json");
1821
1880
  const b = blockVerdict(instrPath, "copilot");
1822
1881
  if (!b.ok) return b;
1823
1882
  const settings = readVscodeMcp(mcpPath);
@@ -1832,8 +1891,8 @@ var copilotPair = {
1832
1891
  removePair(o) {
1833
1892
  const lines2 = [];
1834
1893
  for (const dir of vscodeUserDirs(o)) {
1835
- const mcpPath = join12(dir, "mcp.json");
1836
- const hadBlock = removeCopilotInstructions(join12(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1894
+ const mcpPath = join13(dir, "mcp.json");
1895
+ const hadBlock = removeCopilotInstructions(join13(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1837
1896
  const settings = readVscodeMcp(mcpPath);
1838
1897
  const st = settings ? copilotMcpState(settings, o.url) : { state: "missing" };
1839
1898
  const hadMcp = st.state === "registered";
@@ -1844,7 +1903,7 @@ var copilotPair = {
1844
1903
  if (Object.keys(rest).length > 0) next.servers = rest;
1845
1904
  else delete next.servers;
1846
1905
  if (Object.keys(next).length === 0) rmSync6(mcpPath);
1847
- else writeFileSync8(mcpPath, `${JSON.stringify(next, null, 2)}
1906
+ else writeFileSync9(mcpPath, `${JSON.stringify(next, null, 2)}
1848
1907
  `);
1849
1908
  }
1850
1909
  const what = hadBlock && hadMcp ? `the Pair instructions and ${SERVER} server` : hadBlock ? "the Pair instructions" : `the ${SERVER} server`;
@@ -2094,8 +2153,8 @@ function resolveSeat(ship, route2, role, persona = {}, repo) {
2094
2153
 
2095
2154
  // src/forges.ts
2096
2155
  import { execFileSync as execFileSync4 } from "child_process";
2097
- import { chmodSync, existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync11, rmSync as rmSync7, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
2098
- import { dirname as dirname6, join as join13 } from "path";
2156
+ import { chmodSync, existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync12, rmSync as rmSync7, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
2157
+ import { dirname as dirname6, join as join14 } from "path";
2099
2158
  var ForgeError = class extends Error {
2100
2159
  };
2101
2160
  var DEFAULT_FORGE_USERNAME = "x-access-token";
@@ -2123,7 +2182,7 @@ function forgeDescriptorFor(host) {
2123
2182
  }
2124
2183
  var DEFAULT_KEYCHAIN_SERVICE = "crew-forge";
2125
2184
  function defaultTokenFilePath(stateDir, host) {
2126
- return join13(stateDir, "forges", `${host.replace(/[^a-z0-9.-]/gi, "_")}.token`);
2185
+ return join14(stateDir, "forges", `${host.replace(/[^a-z0-9.-]/gi, "_")}.token`);
2127
2186
  }
2128
2187
  var envSource = {
2129
2188
  read: (s, c) => c.env[s.var]?.trim() || null,
@@ -2132,19 +2191,19 @@ var envSource = {
2132
2191
  var fileSource = {
2133
2192
  read(s, c) {
2134
2193
  const path = s.path ?? defaultTokenFilePath(c.stateDir, c.host);
2135
- if (!existsSync11(path)) return null;
2194
+ if (!existsSync12(path)) return null;
2136
2195
  const mode = statSync3(path).mode & 511;
2137
2196
  if (mode & 63) {
2138
2197
  throw new ForgeError(
2139
2198
  `${path} is mode ${mode.toString(8).padStart(3, "0")}; a forge token file must be owner-only (chmod 600 ${path})`
2140
2199
  );
2141
2200
  }
2142
- return readFileSync11(path, "utf8").trim() || null;
2201
+ return readFileSync12(path, "utf8").trim() || null;
2143
2202
  },
2144
2203
  write(s, token, c) {
2145
2204
  const path = s.path ?? defaultTokenFilePath(c.stateDir, c.host);
2146
- mkdirSync8(dirname6(path), { recursive: true, mode: 448 });
2147
- writeFileSync9(path, `${token}
2205
+ mkdirSync9(dirname6(path), { recursive: true, mode: 448 });
2206
+ writeFileSync10(path, `${token}
2148
2207
  `, { mode: 384 });
2149
2208
  chmodSync(path, 384);
2150
2209
  },
@@ -2278,10 +2337,10 @@ function answerCredential(action, input, forges, stateDir, env = process.env) {
2278
2337
  password=${token}
2279
2338
  `;
2280
2339
  }
2281
- var missesPath = (stateDir) => join13(stateDir, "forge-misses.json");
2340
+ var missesPath = (stateDir) => join14(stateDir, "forge-misses.json");
2282
2341
  function readForgeMisses(stateDir) {
2283
2342
  try {
2284
- const raw = JSON.parse(readFileSync11(missesPath(stateDir), "utf8"));
2343
+ const raw = JSON.parse(readFileSync12(missesPath(stateDir), "utf8"));
2285
2344
  return Object.entries(raw).filter(([, t]) => typeof t === "string").map(([host, lastMissAt]) => ({ host, lastMissAt })).sort((a, b) => a.host.localeCompare(b.host));
2286
2345
  } catch {
2287
2346
  return [];
@@ -2296,8 +2355,8 @@ function noteForgeMiss(stateDir, host, at) {
2296
2355
  if (!(bare in cur)) return;
2297
2356
  delete cur[bare];
2298
2357
  } else cur[bare] = at;
2299
- mkdirSync8(stateDir, { recursive: true });
2300
- writeFileSync9(missesPath(stateDir), JSON.stringify(cur), { mode: 384 });
2358
+ mkdirSync9(stateDir, { recursive: true });
2359
+ writeFileSync10(missesPath(stateDir), JSON.stringify(cur), { mode: 384 });
2301
2360
  } catch {
2302
2361
  }
2303
2362
  }
@@ -2399,8 +2458,8 @@ import { hostFromUrl } from "@tablation/client";
2399
2458
 
2400
2459
  // src/secret-store.ts
2401
2460
  import { createHash as createHash2 } from "crypto";
2402
- import { accessSync, constants, existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync2, rmSync as rmSync8, statSync as statSync4, writeFileSync as writeFileSync10, chmodSync as chmodSync2 } from "fs";
2403
- import { join as join14 } from "path";
2461
+ import { accessSync, constants, existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync2, rmSync as rmSync8, statSync as statSync4, writeFileSync as writeFileSync11, chmodSync as chmodSync2 } from "fs";
2462
+ import { join as join15 } from "path";
2404
2463
  import { getSessionStore as getOsSessionStore } from "@tablation/client";
2405
2464
  var SECRET_STORE_MODES = ["auto", "os", "file"];
2406
2465
  function isSecretStoreMode(v) {
@@ -2411,7 +2470,7 @@ function configureSecretStore(c) {
2411
2470
  configured = c;
2412
2471
  }
2413
2472
  function keysDir(stateDir) {
2414
- return join14(stateDir, "keys");
2473
+ return join15(stateDir, "keys");
2415
2474
  }
2416
2475
  function entryName(host, slug2) {
2417
2476
  return `${createHash2("sha256").update(`${host}\0${slug2}`).digest("hex")}.json`;
@@ -2422,34 +2481,34 @@ var FileSessionStore = class {
2422
2481
  this.dir = dir;
2423
2482
  }
2424
2483
  ensureDir() {
2425
- mkdirSync9(this.dir, { recursive: true, mode: 448 });
2484
+ mkdirSync10(this.dir, { recursive: true, mode: 448 });
2426
2485
  if ((statSync4(this.dir).mode & 63) !== 0) chmodSync2(this.dir, 448);
2427
2486
  }
2428
2487
  async get(host, slug2) {
2429
- const file = join14(this.dir, entryName(host, slug2));
2430
- if (!existsSync12(this.dir)) return void 0;
2488
+ const file = join15(this.dir, entryName(host, slug2));
2489
+ if (!existsSync13(this.dir)) return void 0;
2431
2490
  accessSync(this.dir, constants.R_OK | constants.X_OK);
2432
- if (!existsSync12(file)) return void 0;
2433
- return JSON.parse(readFileSync12(file, "utf8"));
2491
+ if (!existsSync13(file)) return void 0;
2492
+ return JSON.parse(readFileSync13(file, "utf8"));
2434
2493
  }
2435
2494
  async set(host, slug2, session) {
2436
2495
  this.ensureDir();
2437
- const file = join14(this.dir, entryName(host, slug2));
2496
+ const file = join15(this.dir, entryName(host, slug2));
2438
2497
  const tmp = `${file}.${process.pid}.tmp`;
2439
- writeFileSync10(tmp, JSON.stringify(session), { mode: 384 });
2498
+ writeFileSync11(tmp, JSON.stringify(session), { mode: 384 });
2440
2499
  renameSync2(tmp, file);
2441
2500
  }
2442
2501
  async clear(host, slug2) {
2443
- if (!existsSync12(this.dir)) return;
2444
- rmSync8(join14(this.dir, entryName(host, slug2)), { force: true });
2502
+ if (!existsSync13(this.dir)) return;
2503
+ rmSync8(join15(this.dir, entryName(host, slug2)), { force: true });
2445
2504
  }
2446
2505
  /** Whether a key could be written here right now, without leaving anything behind. */
2447
2506
  writable() {
2448
2507
  try {
2449
2508
  this.ensureDir();
2450
2509
  accessSync(this.dir, constants.R_OK | constants.W_OK | constants.X_OK);
2451
- const probe = join14(this.dir, `.probe.${process.pid}`);
2452
- writeFileSync10(probe, "", { mode: 384 });
2510
+ const probe = join15(this.dir, `.probe.${process.pid}`);
2511
+ writeFileSync11(probe, "", { mode: 384 });
2453
2512
  rmSync8(probe, { force: true });
2454
2513
  return { ok: true };
2455
2514
  } catch (e) {
@@ -2500,11 +2559,11 @@ function osStoreReachable(env = process.env, platform2 = process.platform, hasBi
2500
2559
  if (platform2 !== "linux") return true;
2501
2560
  if (!hasBinary("secret-tool")) return false;
2502
2561
  if (env.DBUS_SESSION_BUS_ADDRESS) return true;
2503
- return !!env.XDG_RUNTIME_DIR && existsSync12(join14(env.XDG_RUNTIME_DIR, "bus"));
2562
+ return !!env.XDG_RUNTIME_DIR && existsSync13(join15(env.XDG_RUNTIME_DIR, "bus"));
2504
2563
  }
2505
2564
  function onPath(name) {
2506
2565
  for (const dir of (process.env.PATH ?? "").split(":")) {
2507
- if (dir && existsSync12(join14(dir, name))) return true;
2566
+ if (dir && existsSync13(join15(dir, name))) return true;
2508
2567
  }
2509
2568
  return false;
2510
2569
  }
@@ -2571,8 +2630,8 @@ function explain(ship, required) {
2571
2630
  }
2572
2631
 
2573
2632
  // src/repo-config.ts
2574
- import { readFileSync as readFileSync13, existsSync as existsSync13 } from "fs";
2575
- import { join as join15, basename as basename3 } from "path";
2633
+ import { readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
2634
+ import { join as join16, basename as basename3 } from "path";
2576
2635
  import { parse } from "yaml";
2577
2636
 
2578
2637
  // src/sandbox-policy.ts
@@ -2856,8 +2915,8 @@ var MODES = ["local", "integrate", "ci_manual", "ci_auto", "external"];
2856
2915
  var PROVIDERS = ["github", "buildkite", "other", "none"];
2857
2916
  function findRepoConfig(dir) {
2858
2917
  for (const name of REPO_CONFIG_FILENAMES) {
2859
- const p = join15(dir, name);
2860
- if (existsSync13(p)) return p;
2918
+ const p = join16(dir, name);
2919
+ if (existsSync14(p)) return p;
2861
2920
  }
2862
2921
  return null;
2863
2922
  }
@@ -3143,7 +3202,7 @@ function parseRepoConfig(text2, file) {
3143
3202
  function loadRepoConfig(dir) {
3144
3203
  const file = findRepoConfig(dir);
3145
3204
  if (!file) return null;
3146
- return parseRepoConfig(readFileSync13(file, "utf8"), file);
3205
+ return parseRepoConfig(readFileSync14(file, "utf8"), file);
3147
3206
  }
3148
3207
  function hookLabel(cfg, hook2) {
3149
3208
  return cfg.labels[hook2] ?? cfg.hooks[hook2] ?? hook2;
@@ -3672,11 +3731,11 @@ function expand(p, base) {
3672
3731
  return isAbsolute2(out) ? out : resolve2(base, out);
3673
3732
  }
3674
3733
  function resolvePromptsDir(promptSet, crewHome, base) {
3675
- if (!promptSet) return join16(crewHome, "prompts", "default");
3734
+ if (!promptSet) return join17(crewHome, "prompts", "default");
3676
3735
  if (promptSet.includes("/") || promptSet.startsWith(".") || promptSet.startsWith("~")) {
3677
3736
  return expand(promptSet, base);
3678
3737
  }
3679
- return join16(crewHome, "prompts", promptSet);
3738
+ return join17(crewHome, "prompts", promptSet);
3680
3739
  }
3681
3740
  var Missing = class {
3682
3741
  paths = [];
@@ -3700,7 +3759,7 @@ var Missing = class {
3700
3759
  };
3701
3760
  function crewVersion(crewHome) {
3702
3761
  try {
3703
- const pkg = JSON.parse(readFileSync14(join16(crewHome, "package.json"), "utf8"));
3762
+ const pkg = JSON.parse(readFileSync15(join17(crewHome, "package.json"), "utf8"));
3704
3763
  return pkg.version ?? "0.0.0";
3705
3764
  } catch {
3706
3765
  return "0.0.0";
@@ -3710,26 +3769,26 @@ function configSearchPath(crewHome) {
3710
3769
  if (process.env.CREW_CONFIG) return [resolve2(process.env.CREW_CONFIG)];
3711
3770
  const xdg = process.env.XDG_CONFIG_HOME;
3712
3771
  return [
3713
- ...xdg ? [join16(xdg, "crew", "crew.yaml")] : [],
3714
- join16(homedir6(), ".config", "crew", "crew.yaml"),
3772
+ ...xdg ? [join17(xdg, "crew", "crew.yaml")] : [],
3773
+ join17(homedir6(), ".config", "crew", "crew.yaml"),
3715
3774
  resolve2(crewHome, "crew.yaml")
3716
3775
  ];
3717
3776
  }
3718
3777
  function findConfigFile(crewHome) {
3719
3778
  const candidates2 = configSearchPath(crewHome);
3720
- return candidates2.find((c) => existsSync14(c)) ?? candidates2[candidates2.length - 1];
3779
+ return candidates2.find((c) => existsSync15(c)) ?? candidates2[candidates2.length - 1];
3721
3780
  }
3722
- var defaultConfigPath = () => join16(process.env.XDG_CONFIG_HOME ?? join16(homedir6(), ".config"), "crew", "crew.yaml");
3781
+ var defaultConfigPath = () => join17(process.env.XDG_CONFIG_HOME ?? join17(homedir6(), ".config"), "crew", "crew.yaml");
3723
3782
  function routeSlug(route2) {
3724
3783
  return route2.replace(/\//g, "-");
3725
3784
  }
3726
3785
  function resolvedPathFor(stateDir, route2) {
3727
3786
  const [workspace, project] = splitRoute(route2);
3728
- return join16(stateDir, "resolved", workspace, `${project}.json`);
3787
+ return join17(stateDir, "resolved", workspace, `${project}.json`);
3729
3788
  }
3730
3789
  function apiKeyPathFor(stateDir, route2) {
3731
3790
  const [workspace, project] = splitRoute(route2);
3732
- return join16(stateDir, "keys", workspace, `${project}.env`);
3791
+ return join17(stateDir, "keys", workspace, `${project}.env`);
3733
3792
  }
3734
3793
  function splitRoute(route2) {
3735
3794
  const parts = route2.split("/");
@@ -3740,9 +3799,9 @@ function splitRoute(route2) {
3740
3799
  }
3741
3800
  function readResolvedFile(stateDir, route2) {
3742
3801
  const path = resolvedPathFor(stateDir, route2);
3743
- if (!existsSync14(path)) return void 0;
3802
+ if (!existsSync15(path)) return void 0;
3744
3803
  try {
3745
- return JSON.parse(readFileSync14(path, "utf8"));
3804
+ return JSON.parse(readFileSync15(path, "utf8"));
3746
3805
  } catch (e) {
3747
3806
  throw new ConfigError(`${path}: not valid JSON \u2014 ${e.message}`);
3748
3807
  }
@@ -3803,18 +3862,18 @@ function parseResolved(raw, m, where) {
3803
3862
  }
3804
3863
  function loadConfig(crewHome, configFile, opts = {}) {
3805
3864
  const file = configFile ?? findConfigFile(crewHome);
3806
- if (opts.text === void 0 && !existsSync14(file)) {
3865
+ if (opts.text === void 0 && !existsSync15(file)) {
3807
3866
  throw new ConfigError(
3808
3867
  `no config found. Looked in:
3809
3868
  ` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
3810
3869
 
3811
3870
  Create the first of those:
3812
- mkdir -p ${dirname7(defaultConfigPath())} && cp ${join16(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
3871
+ mkdir -p ${dirname7(defaultConfigPath())} && cp ${join17(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
3813
3872
  );
3814
3873
  }
3815
3874
  let raw;
3816
3875
  try {
3817
- raw = parse2(opts.text ?? readFileSync14(file, "utf8"));
3876
+ raw = parse2(opts.text ?? readFileSync15(file, "utf8"));
3818
3877
  } catch (e) {
3819
3878
  throw new ConfigError(`${file}: not valid YAML \u2014 ${e.message}`);
3820
3879
  }
@@ -3941,7 +4000,7 @@ Create the first of those:
3941
4000
  useNvm: shipRaw.useNvm !== false,
3942
4001
  nvmSh: shipRaw.nvmSh,
3943
4002
  stateDir,
3944
- logFile: expand(shipRaw.logFile ?? join16(tmpdir(), "crew.log"), base),
4003
+ logFile: expand(shipRaw.logFile ?? join17(tmpdir(), "crew.log"), base),
3945
4004
  userAgent: shipRaw.userAgent ?? `Mozilla/5.0 CrewAgent/${crewVersion(crewHome)}`,
3946
4005
  maxConcurrentAgents,
3947
4006
  maxConcurrentAgentsSet,
@@ -4123,7 +4182,7 @@ function parseOneRoute(c, where, file, base, stateDir, raw, missing, crewHome, t
4123
4182
  const hasRepos = Object.keys(repoDirs).length > 0;
4124
4183
  const reposBasePath = expand(String(c?.reposBasePath ?? raw.ship?.reposBasePath ?? DEFAULT_REPOS_BASE_PATH), base);
4125
4184
  const dirIsCloneBase = !c?.dir && !hasRepos && !Array.isArray(c?.repos);
4126
- const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join16(reposBasePath, splitRoute(route2)[0]) : "";
4185
+ const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join17(reposBasePath, splitRoute(route2)[0]) : "";
4127
4186
  const weight = c?.weight === void 0 ? void 0 : Number(c.weight);
4128
4187
  if (weight !== void 0 && !(Number.isFinite(weight) && weight > 0)) {
4129
4188
  missing.add(`${where}.weight must be a positive number (got "${c?.weight}")`);
@@ -4221,8 +4280,8 @@ function repoIdForName(r, name) {
4221
4280
  }
4222
4281
  function resolveApiKey(r) {
4223
4282
  if (r.apiKey) return r.apiKey;
4224
- if (r.apiKeyFile && r.apiKeyVar && existsSync14(r.apiKeyFile)) {
4225
- for (const line of readFileSync14(r.apiKeyFile, "utf8").split("\n")) {
4283
+ if (r.apiKeyFile && r.apiKeyVar && existsSync15(r.apiKeyFile)) {
4284
+ for (const line of readFileSync15(r.apiKeyFile, "utf8").split("\n")) {
4226
4285
  const m = line.match(new RegExp(`^${r.apiKeyVar}=(.*)$`));
4227
4286
  if (m) return (m[1] ?? "").trim().replace(/^["']|["']$/g, "");
4228
4287
  }
@@ -4235,9 +4294,9 @@ function resolveApiKey(r) {
4235
4294
  }
4236
4295
  function credentialSource(r) {
4237
4296
  if (r.apiKey) return KEYCHAIN_ELIGIBLE_ROUTES.has(r) ? "keychain" : "inline";
4238
- if (r.apiKeyFile && r.apiKeyVar && existsSync14(r.apiKeyFile)) {
4297
+ if (r.apiKeyFile && r.apiKeyVar && existsSync15(r.apiKeyFile)) {
4239
4298
  const re = new RegExp(`^${r.apiKeyVar}=`);
4240
- if (readFileSync14(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
4299
+ if (readFileSync15(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
4241
4300
  }
4242
4301
  return process.env.CREW_API_KEY?.trim() ? "CREW_API_KEY" : "none";
4243
4302
  }
@@ -4293,7 +4352,7 @@ function configuredMembers(rt) {
4293
4352
  return out;
4294
4353
  }
4295
4354
  function defaultRepoDir(basePath, workspace, repoName) {
4296
- return join16(basePath, workspace, repoName);
4355
+ return join17(basePath, workspace, repoName);
4297
4356
  }
4298
4357
  function reposOf(r) {
4299
4358
  const named = Object.entries(r.repos);
@@ -4332,10 +4391,10 @@ function passengerRepoTargets(r) {
4332
4391
  return reposOf(r).filter((t) => r.repoOverrides[t.name]?.hostPassengers !== false);
4333
4392
  }
4334
4393
  function passengerCheckoutDir(basePath, workspace, repoName) {
4335
- return join16(basePath, workspace, ".passenger-checkouts", repoName);
4394
+ return join17(basePath, workspace, ".passenger-checkouts", repoName);
4336
4395
  }
4337
4396
  function passengerMirrorArea(ship) {
4338
- return ship.runner === "sandbox" ? join16(ship.stateDir, "passenger-sources") : void 0;
4397
+ return ship.runner === "sandbox" ? join17(ship.stateDir, "passenger-sources") : void 0;
4339
4398
  }
4340
4399
  function passengerCheckoutTargets(r, mirrorArea) {
4341
4400
  if (!r.hostPassengers) return [];
@@ -4346,7 +4405,7 @@ function passengerCheckoutTargets(r, mirrorArea) {
4346
4405
  const forgeHost = t.forgeHost ?? (id ? r.resolved?.repoForgeHosts?.[id] : void 0);
4347
4406
  return {
4348
4407
  name: t.name,
4349
- dir: mirrorArea ? join16(mirrorArea, workspace, t.name) : passengerCheckoutDir(r.reposBasePath, workspace, t.name),
4408
+ dir: mirrorArea ? join17(mirrorArea, workspace, t.name) : passengerCheckoutDir(r.reposBasePath, workspace, t.name),
4350
4409
  remote: t.remote ?? (id ? remotes[id] : void 0),
4351
4410
  ...forgeHost ? { forgeHost } : {}
4352
4411
  };
@@ -4628,9 +4687,9 @@ function recoveredFailureHeadlines(o, route2) {
4628
4687
  }
4629
4688
 
4630
4689
  // src/install.ts
4631
- import { dirname as dirname8, join as join17, extname } from "path";
4690
+ import { dirname as dirname8, join as join18, extname } from "path";
4632
4691
  import { homedir as homedir7, userInfo } from "os";
4633
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync15, mkdirSync as mkdirSync10, readdirSync as readdirSync3, readFileSync as readFileSync15, statSync as statSync5, writeFileSync as writeFileSync11, unlinkSync, realpathSync } from "fs";
4692
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync16, mkdirSync as mkdirSync11, readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync5, writeFileSync as writeFileSync12, unlinkSync, realpathSync } from "fs";
4634
4693
  import { spawnSync as spawnSync5 } from "child_process";
4635
4694
  import { createHash as createHash3 } from "crypto";
4636
4695
 
@@ -4642,7 +4701,7 @@ function isCompiledBinary(moduleUrl) {
4642
4701
  // src/install.ts
4643
4702
  function crewInvocation(crewHome) {
4644
4703
  if (isCompiledBinary(import.meta.url)) return [process.execPath];
4645
- return [process.execPath, join17(crewHome, "bin", "crew")];
4704
+ return [process.execPath, join18(crewHome, "bin", "crew")];
4646
4705
  }
4647
4706
  var INTERVAL_SECONDS = 120;
4648
4707
  function labelFor(crewHome, job) {
@@ -4680,7 +4739,7 @@ function pathFor(ship) {
4680
4739
  return parts.join(":");
4681
4740
  }
4682
4741
  function findOnPath(bin, searchPath) {
4683
- const candidates2 = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join17(dir, bin));
4742
+ const candidates2 = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join18(dir, bin));
4684
4743
  for (const candidate of candidates2) {
4685
4744
  try {
4686
4745
  accessSync2(candidate, constants2.X_OK);
@@ -4690,7 +4749,7 @@ function findOnPath(bin, searchPath) {
4690
4749
  }
4691
4750
  return void 0;
4692
4751
  }
4693
- var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join17(homedir7(), ".docker", "bin")].join(":");
4752
+ var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join18(homedir7(), ".docker", "bin")].join(":");
4694
4753
  function dockerPathProblem(ship, shellPath = process.env.PATH ?? "") {
4695
4754
  const schedulerPath = pathFor(ship);
4696
4755
  if (findOnPath("docker", schedulerPath)) return void 0;
@@ -4705,7 +4764,7 @@ function installContext(env = process.env) {
4705
4764
  }
4706
4765
  var SYSTEM_UNIT_DIR = "/etc/systemd/system";
4707
4766
  function systemUnitPathFor(label, suffix = "service") {
4708
- return join17(SYSTEM_UNIT_DIR, `${label}.${suffix}`);
4767
+ return join18(SYSTEM_UNIT_DIR, `${label}.${suffix}`);
4709
4768
  }
4710
4769
  function unitEnvironment(ship) {
4711
4770
  return [["PATH", pathFor(ship)], ...Object.entries(ship.env ?? {})];
@@ -4726,7 +4785,7 @@ function xmlEscape(s) {
4726
4785
  }
4727
4786
  function planLaunchd(ship, crewHome, job) {
4728
4787
  const label = labelFor(crewHome, job);
4729
- const unitPath = join17(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4788
+ const unitPath = join18(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4730
4789
  const crewLog = ship.logFile;
4731
4790
  const schedulerLog = schedulerLogFor(crewLog, job);
4732
4791
  const persistent = job === "run";
@@ -4780,7 +4839,7 @@ ${scheduleKeys}
4780
4839
  }
4781
4840
  function planSystemd(ship, crewHome, job, ctx) {
4782
4841
  const label = labelFor(crewHome, job);
4783
- const unitDir = ctx.root ? SYSTEM_UNIT_DIR : join17(homedir7(), ".config", "systemd", "user");
4842
+ const unitDir = ctx.root ? SYSTEM_UNIT_DIR : join18(homedir7(), ".config", "systemd", "user");
4784
4843
  const ctl = ctx.root ? ["systemctl"] : ["systemctl", "--user"];
4785
4844
  const userLine = ctx.root && ctx.user && ctx.user !== "root" ? `User=${ctx.user}
4786
4845
  ` : "";
@@ -4789,7 +4848,7 @@ function planSystemd(ship, crewHome, job, ctx) {
4789
4848
  lingerCommand: ["loginctl", "enable-linger", ctx.user],
4790
4849
  lingerHint: `the daemon will stop when ${ctx.user} logs out; run \`sudo loginctl enable-linger ${ctx.user}\` once, or run \`crew install\` as root for a system unit`
4791
4850
  };
4792
- const servicePath = join17(unitDir, `${label}.service`);
4851
+ const servicePath = join18(unitDir, `${label}.service`);
4793
4852
  const crewLog = ship.logFile;
4794
4853
  const schedulerLog = schedulerLogFor(crewLog, job);
4795
4854
  const description = job === "release" ? "Tablation crew \u2014 release" : job === "passengers" ? "Tablation crew \u2014 Host Passengers sync" : "Tablation crew \u2014 persistent supervisor loop";
@@ -4830,7 +4889,7 @@ WantedBy=${wantedBy}
4830
4889
  ...linger
4831
4890
  };
4832
4891
  }
4833
- const timerPath = join17(unitDir, `${label}.timer`);
4892
+ const timerPath = join18(unitDir, `${label}.timer`);
4834
4893
  const service = `# Written by \`crew install\` \u2014 re-run it rather than hand-editing this file.
4835
4894
  [Unit]
4836
4895
  Description=${description}
@@ -4905,7 +4964,7 @@ function planInstall(ship, crewHome, hostShip, hasSystemd, job = "run", ctx = US
4905
4964
  function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4906
4965
  const label = labelFor(crewHome, job);
4907
4966
  if (hostShip === "macos") {
4908
- const unitPath = join17(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4967
+ const unitPath = join18(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4909
4968
  return {
4910
4969
  mechanism: "launchd",
4911
4970
  unitPaths: [unitPath],
@@ -4915,20 +4974,20 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4915
4974
  };
4916
4975
  }
4917
4976
  if (hostShip === "linux" && hasSystemd) {
4918
- const system = existsSync15(systemUnitPathFor(label));
4919
- const unitDir = system ? SYSTEM_UNIT_DIR : join17(homedir7(), ".config", "systemd", "user");
4977
+ const system = existsSync16(systemUnitPathFor(label));
4978
+ const unitDir = system ? SYSTEM_UNIT_DIR : join18(homedir7(), ".config", "systemd", "user");
4920
4979
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
4921
4980
  if (job === "run") {
4922
4981
  return {
4923
4982
  mechanism: "systemd",
4924
- unitPaths: [join17(unitDir, `${label}.service`)],
4983
+ unitPaths: [join18(unitDir, `${label}.service`)],
4925
4984
  crontabLine: null,
4926
4985
  unloadCommands: [[...ctl, "disable", "--now", `${label}.service`]]
4927
4986
  };
4928
4987
  }
4929
4988
  return {
4930
4989
  mechanism: "systemd",
4931
- unitPaths: [join17(unitDir, `${label}.service`), join17(unitDir, `${label}.timer`)],
4990
+ unitPaths: [join18(unitDir, `${label}.service`), join18(unitDir, `${label}.timer`)],
4932
4991
  crontabLine: null,
4933
4992
  unloadCommands: [[...ctl, "disable", "--now", `${label}.timer`]]
4934
4993
  };
@@ -4946,7 +5005,7 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4946
5005
  function planDaemonControl(crewHome, hostShip, hasSystemd) {
4947
5006
  const label = labelFor(crewHome, "run");
4948
5007
  if (hostShip === "macos") {
4949
- const unitPath = join17(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
5008
+ const unitPath = join18(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4950
5009
  return {
4951
5010
  mechanism: "launchd",
4952
5011
  startCommand: ["launchctl", "load", unitPath],
@@ -4956,7 +5015,7 @@ function planDaemonControl(crewHome, hostShip, hasSystemd) {
4956
5015
  }
4957
5016
  if (hostShip === "linux" && hasSystemd) {
4958
5017
  const unit = `${label}.service`;
4959
- const ctl = existsSync15(systemUnitPathFor(label)) ? ["systemctl"] : ["systemctl", "--user"];
5018
+ const ctl = existsSync16(systemUnitPathFor(label)) ? ["systemctl"] : ["systemctl", "--user"];
4960
5019
  return {
4961
5020
  mechanism: "systemd",
4962
5021
  startCommand: [...ctl, "start", unit],
@@ -4988,10 +5047,10 @@ async function applyInstall(plan, crewHome, dryRun, log) {
4988
5047
  log.emit(`would write ${path}`);
4989
5048
  continue;
4990
5049
  }
4991
- const before = existsSync15(path) ? readFileSync15(path, "utf8") : null;
5050
+ const before = existsSync16(path) ? readFileSync16(path, "utf8") : null;
4992
5051
  if (before !== plan.unitContent[path]) changed2 = true;
4993
- mkdirSync10(dirname8(path), { recursive: true });
4994
- writeFileSync11(path, plan.unitContent[path]);
5052
+ mkdirSync11(dirname8(path), { recursive: true });
5053
+ writeFileSync12(path, plan.unitContent[path]);
4995
5054
  log.emit(`wrote ${path}`);
4996
5055
  }
4997
5056
  if (plan.crontabLine) {
@@ -5055,7 +5114,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
5055
5114
  }
5056
5115
  }
5057
5116
  for (const path of plan.unitPaths) {
5058
- if (!existsSync15(path)) continue;
5117
+ if (!existsSync16(path)) continue;
5059
5118
  if (dryRun) {
5060
5119
  log.emit(`would remove ${path}`);
5061
5120
  continue;
@@ -5074,7 +5133,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
5074
5133
  }
5075
5134
  var CREW_LAUNCHD_PREFIX = "com.tablation.crew";
5076
5135
  function launchAgentsDir() {
5077
- return join17(homedir7(), "Library", "LaunchAgents");
5136
+ return join18(homedir7(), "Library", "LaunchAgents");
5078
5137
  }
5079
5138
  function xmlUnescape(s) {
5080
5139
  return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
@@ -5091,25 +5150,25 @@ function loadedCrewLabels() {
5091
5150
  return [...parseLaunchctlList(list.stdout ?? "").keys()].filter((l) => l.startsWith(CREW_LAUNCHD_PREFIX));
5092
5151
  }
5093
5152
  function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
5094
- const files = existsSync15(dir) ? readdirSync3(dir) : [];
5153
+ const files = existsSync16(dir) ? readdirSync3(dir) : [];
5095
5154
  const units = files.filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
5096
- const plistPath = join17(dir, f);
5155
+ const plistPath = join18(dir, f);
5097
5156
  let program = null;
5098
5157
  try {
5099
- program = programFromPlist(readFileSync15(plistPath, "utf8"));
5158
+ program = programFromPlist(readFileSync16(plistPath, "utf8"));
5100
5159
  } catch {
5101
5160
  }
5102
5161
  return {
5103
5162
  label: f.slice(0, -".plist".length),
5104
5163
  plistPath,
5105
5164
  program,
5106
- programExists: program !== null && existsSync15(program)
5165
+ programExists: program !== null && existsSync16(program)
5107
5166
  };
5108
5167
  });
5109
5168
  const seen = new Set(units.map((u) => u.label));
5110
5169
  for (const label of [...loaded].sort()) {
5111
5170
  if (!label.startsWith(CREW_LAUNCHD_PREFIX) || seen.has(label)) continue;
5112
- units.push({ label, plistPath: join17(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
5171
+ units.push({ label, plistPath: join18(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
5113
5172
  }
5114
5173
  return units;
5115
5174
  }
@@ -5122,9 +5181,9 @@ function removeLaunchdUnit(unit, dryRun, log) {
5122
5181
  log.emit(`would unload and remove ${unit.label}`);
5123
5182
  return;
5124
5183
  }
5125
- if (existsSync15(unit.plistPath)) spawnSync5("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
5184
+ if (existsSync16(unit.plistPath)) spawnSync5("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
5126
5185
  spawnSync5("launchctl", ["remove", unit.label], { encoding: "utf8" });
5127
- if (existsSync15(unit.plistPath)) unlinkSync(unit.plistPath);
5186
+ if (existsSync16(unit.plistPath)) unlinkSync(unit.plistPath);
5128
5187
  log.emit(`removed ${unit.label} (${unit.plistPath})`);
5129
5188
  }
5130
5189
  function clearForeignLaunchdUnits(crewHome, replace, dryRun, log, dir, loaded) {
@@ -5202,24 +5261,24 @@ function detectSystemd() {
5202
5261
  }
5203
5262
 
5204
5263
  // src/ship-attention.ts
5205
- import { existsSync as existsSync17, statfsSync } from "fs";
5264
+ import { existsSync as existsSync18, statfsSync } from "fs";
5206
5265
 
5207
5266
  // src/git.ts
5208
5267
  import { execFileSync as execFileSync5 } from "child_process";
5209
5268
  import {
5210
- existsSync as existsSync16,
5211
- mkdirSync as mkdirSync11,
5269
+ existsSync as existsSync17,
5270
+ mkdirSync as mkdirSync12,
5212
5271
  openSync as openSync3,
5213
5272
  closeSync as closeSync3,
5214
5273
  writeSync,
5215
5274
  statSync as statSync6,
5216
5275
  unlinkSync as unlinkSync2,
5217
- readFileSync as readFileSync16,
5218
- writeFileSync as writeFileSync12,
5276
+ readFileSync as readFileSync17,
5277
+ writeFileSync as writeFileSync13,
5219
5278
  readdirSync as readdirSync4,
5220
5279
  rmSync as rmSync9
5221
5280
  } from "fs";
5222
- import { dirname as dirname9, basename as basename5, join as join18 } from "path";
5281
+ import { dirname as dirname9, basename as basename5, join as join19 } from "path";
5223
5282
  var GitError = class extends Error {
5224
5283
  };
5225
5284
  function streamText(x) {
@@ -5617,11 +5676,11 @@ function sweepStaleReleaseRoots(tmp, now = Date.now(), maxAgeMs = 60 * 60 * 1e3)
5617
5676
  }
5618
5677
  for (const name of names) {
5619
5678
  if (!name.startsWith("crew-release-")) continue;
5620
- const root = join18(tmp, name);
5679
+ const root = join19(tmp, name);
5621
5680
  try {
5622
5681
  const st = statSync6(root);
5623
5682
  if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
5624
- if (existsSync16(join18(root, "checkout"))) continue;
5683
+ if (existsSync17(join19(root, "checkout"))) continue;
5625
5684
  rmSync9(root, { recursive: true, force: true });
5626
5685
  removed.push(name);
5627
5686
  } catch {
@@ -5673,13 +5732,13 @@ function originUrlOf(dir, remoteName) {
5673
5732
  }
5674
5733
  }
5675
5734
  function ensureRepoCheckout(dir, remote, opts = {}) {
5676
- if (existsSync16(`${dir}/.git`)) return false;
5735
+ if (existsSync17(`${dir}/.git`)) return false;
5677
5736
  if (!remote) {
5678
5737
  throw new GitError(
5679
5738
  `${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`
5680
5739
  );
5681
5740
  }
5682
- mkdirSync11(dirname9(dir), { recursive: true });
5741
+ mkdirSync12(dirname9(dir), { recursive: true });
5683
5742
  const url = cloneUrlFor(remote, opts);
5684
5743
  try {
5685
5744
  execFileSync5("git", ["clone", url, dir], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
@@ -5704,7 +5763,7 @@ function sleepSync(ms3) {
5704
5763
  }
5705
5764
  function acquireCheckoutLock(dir, timeoutMs = 3e4) {
5706
5765
  const lockPath = checkoutLockPath(dir);
5707
- mkdirSync11(dirname9(lockPath), { recursive: true });
5766
+ mkdirSync12(dirname9(lockPath), { recursive: true });
5708
5767
  const deadline = Date.now() + timeoutMs;
5709
5768
  for (; ; ) {
5710
5769
  try {
@@ -5741,17 +5800,17 @@ function acquireCheckoutLock(dir, timeoutMs = 3e4) {
5741
5800
  function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base, clone = {}) {
5742
5801
  const release = acquireCheckoutLock(dir);
5743
5802
  try {
5744
- if (!existsSync16(`${dir}/.git`) && githubRemote) {
5803
+ if (!existsSync17(`${dir}/.git`) && githubRemote) {
5745
5804
  const url = cloneUrlFor(githubRemote, clone);
5746
5805
  const marker = cloneFailureMarker(dir);
5747
- if (existsSync16(marker) && readFileSync16(marker, "utf8").split("\n")[0] === url) {
5748
- return { action: "clone-failed", detail: readFileSync16(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
5806
+ if (existsSync17(marker) && readFileSync17(marker, "utf8").split("\n")[0] === url) {
5807
+ return { action: "clone-failed", detail: readFileSync17(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
5749
5808
  }
5750
5809
  try {
5751
5810
  ensureRepoCheckout(dir, githubRemote, clone);
5752
5811
  } catch (e) {
5753
5812
  if (!(e instanceof GitError)) throw e;
5754
- writeFileSync12(marker, `${url}
5813
+ writeFileSync13(marker, `${url}
5755
5814
  ${e.message}`);
5756
5815
  return { action: "clone-failed", detail: e.message, repeat: false };
5757
5816
  }
@@ -5977,7 +6036,7 @@ function noteHeldWorktree(state, h, nowMs = Date.now()) {
5977
6036
  function reconcileHeldWorktrees(state, repo, log, stillRefused) {
5978
6037
  for (const i of state.shipAttention().previous()) {
5979
6038
  if (i.kind !== "worktree_held" || !i.held || i.held.repo !== repo) continue;
5980
- const gone = !existsSync17(i.held.path);
6039
+ const gone = !existsSync18(i.held.path);
5981
6040
  if (!gone && !(stillRefused && !stillRefused.has(i.held.path))) continue;
5982
6041
  clearShipAttention(state, i.key);
5983
6042
  log.emit(
@@ -6130,8 +6189,8 @@ function attentionProblems(items, already) {
6130
6189
  import { createHash as createHash6 } from "crypto";
6131
6190
  import { spawn as spawn5 } from "child_process";
6132
6191
  import { execFileSync as execFileSync8 } from "child_process";
6133
- import { mkdirSync as mkdirSync17, readFileSync as readFileSync24, renameSync as renameSync3, writeFileSync as writeFileSync18, rmSync as rmSync14 } from "fs";
6134
- import { dirname as dirname13, join as join29 } from "path";
6192
+ import { mkdirSync as mkdirSync18, readFileSync as readFileSync25, renameSync as renameSync3, writeFileSync as writeFileSync19, rmSync as rmSync14 } from "fs";
6193
+ import { dirname as dirname13, join as join30 } from "path";
6135
6194
  import { tmpdir as tmpdir3 } from "os";
6136
6195
 
6137
6196
  // src/passenger-containers.ts
@@ -6144,32 +6203,32 @@ import { promisify as promisify2 } from "util";
6144
6203
 
6145
6204
  // src/deprovision-host.ts
6146
6205
  import { spawnSync as spawnSync7 } from "child_process";
6147
- import { existsSync as existsSync27, readdirSync as readdirSync11 } from "fs";
6206
+ import { existsSync as existsSync28, readdirSync as readdirSync11 } from "fs";
6148
6207
 
6149
6208
  // src/interrupted.ts
6150
- import { existsSync as existsSync25, readdirSync as readdirSync9, readFileSync as readFileSync23 } from "fs";
6151
- import { join as join27 } from "path";
6209
+ import { existsSync as existsSync26, readdirSync as readdirSync9, readFileSync as readFileSync24 } from "fs";
6210
+ import { join as join28 } from "path";
6152
6211
 
6153
6212
  // src/agent.ts
6154
6213
  import { spawnSync as spawnSync6 } from "child_process";
6155
- import { readFileSync as readFileSync22, existsSync as existsSync24, statSync as statSync8, mkdirSync as mkdirSync16, writeFileSync as writeFileSync17, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
6214
+ import { readFileSync as readFileSync23, existsSync as existsSync25, statSync as statSync8, mkdirSync as mkdirSync17, writeFileSync as writeFileSync18, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
6156
6215
  import { StringDecoder } from "string_decoder";
6157
6216
  import { finished } from "stream/promises";
6158
- import { join as join26, dirname as dirname12, basename as basename9 } from "path";
6217
+ import { join as join27, dirname as dirname12, basename as basename9 } from "path";
6159
6218
  import { createHash as createHash4 } from "crypto";
6160
6219
 
6161
6220
  // src/state.ts
6162
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync18, mkdirSync as mkdirSync12, rmSync as rmSync10, statSync as statSync7, readdirSync as readdirSync5 } from "fs";
6163
- import { join as join19 } from "path";
6221
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync19, mkdirSync as mkdirSync13, rmSync as rmSync10, statSync as statSync7, readdirSync as readdirSync5 } from "fs";
6222
+ import { join as join20 } from "path";
6164
6223
  var EPOCH = "1970-01-01T00:00:00.000Z";
6165
6224
  var State = class _State {
6166
6225
  dir;
6167
6226
  constructor(dir) {
6168
6227
  this.dir = dir;
6169
- mkdirSync12(dir, { recursive: true });
6228
+ mkdirSync13(dir, { recursive: true });
6170
6229
  }
6171
6230
  path(...parts) {
6172
- return join19(this.dir, ...parts);
6231
+ return join20(this.dir, ...parts);
6173
6232
  }
6174
6233
  /**
6175
6234
  * The timestamp every "new since last poll" check is measured against.
@@ -6180,14 +6239,14 @@ var State = class _State {
6180
6239
  */
6181
6240
  watermark() {
6182
6241
  try {
6183
- const v = readFileSync17(this.path(".poll-watermark"), "utf8").trim();
6242
+ const v = readFileSync18(this.path(".poll-watermark"), "utf8").trim();
6184
6243
  return v || EPOCH;
6185
6244
  } catch {
6186
6245
  return EPOCH;
6187
6246
  }
6188
6247
  }
6189
6248
  setWatermark(at) {
6190
- writeFileSync13(this.path(".poll-watermark"), `${at}
6249
+ writeFileSync14(this.path(".poll-watermark"), `${at}
6191
6250
  `);
6192
6251
  }
6193
6252
  /**
@@ -6205,16 +6264,16 @@ var State = class _State {
6205
6264
  }
6206
6265
  /** Whole-crew pause, and the per-role sentinels one level down. */
6207
6266
  isPaused() {
6208
- return existsSync18(this.path(".crew-paused"));
6267
+ return existsSync19(this.path(".crew-paused"));
6209
6268
  }
6210
6269
  isRolePaused(role) {
6211
- return existsSync18(this.path(`.role-paused-${role}`));
6270
+ return existsSync19(this.path(`.role-paused-${role}`));
6212
6271
  }
6213
6272
  pausedRoles(roles) {
6214
6273
  return new Set(roles.filter((r) => this.isRolePaused(r)));
6215
6274
  }
6216
6275
  pause(role) {
6217
- writeFileSync13(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), "");
6276
+ writeFileSync14(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), "");
6218
6277
  }
6219
6278
  resume(role) {
6220
6279
  rmSync10(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), { force: true });
@@ -6225,10 +6284,10 @@ var State = class _State {
6225
6284
  * while agents keep working.
6226
6285
  */
6227
6286
  isReleasePaused() {
6228
- return existsSync18(this.path(".release-paused"));
6287
+ return existsSync19(this.path(".release-paused"));
6229
6288
  }
6230
6289
  setReleasePaused(paused) {
6231
- if (paused) writeFileSync13(this.path(".release-paused"), "");
6290
+ if (paused) writeFileSync14(this.path(".release-paused"), "");
6232
6291
  else rmSync10(this.path(".release-paused"), { force: true });
6233
6292
  }
6234
6293
  /**
@@ -6266,7 +6325,7 @@ var State = class _State {
6266
6325
  const name = _State.safe(rawName);
6267
6326
  const file = this.path(`.${name}.lock`);
6268
6327
  try {
6269
- const existing = Number.parseInt(readFileSync17(file, "utf8").trim(), 10);
6328
+ const existing = Number.parseInt(readFileSync18(file, "utf8").trim(), 10);
6270
6329
  if (Number.isFinite(existing) && existing > 0) {
6271
6330
  try {
6272
6331
  process.kill(existing, 0);
@@ -6276,7 +6335,7 @@ var State = class _State {
6276
6335
  }
6277
6336
  } catch {
6278
6337
  }
6279
- writeFileSync13(file, `${process.pid}
6338
+ writeFileSync14(file, `${process.pid}
6280
6339
  `);
6281
6340
  return {
6282
6341
  ok: true,
@@ -6304,7 +6363,7 @@ var State = class _State {
6304
6363
  if (!/^\.release-.+\.lock$/.test(n)) continue;
6305
6364
  let pid = 0;
6306
6365
  try {
6307
- pid = Number.parseInt(readFileSync17(this.path(n), "utf8").trim(), 10);
6366
+ pid = Number.parseInt(readFileSync18(this.path(n), "utf8").trim(), 10);
6308
6367
  } catch {
6309
6368
  continue;
6310
6369
  }
@@ -6390,7 +6449,7 @@ var State = class _State {
6390
6449
  ticketInFlight(recordId) {
6391
6450
  const file = this.path(`.${_State.safe(`crew-ticket-${recordId}`)}.lock`);
6392
6451
  try {
6393
- const pid = Number.parseInt(readFileSync17(file, "utf8").trim(), 10);
6452
+ const pid = Number.parseInt(readFileSync18(file, "utf8").trim(), 10);
6394
6453
  if (!Number.isFinite(pid) || pid <= 0) return false;
6395
6454
  process.kill(pid, 0);
6396
6455
  return true;
@@ -6424,7 +6483,7 @@ var State = class _State {
6424
6483
  const heldReported = `.release-held-${suffix}`;
6425
6484
  const read = (f) => {
6426
6485
  try {
6427
- return readFileSync17(this.path(f), "utf8").trim() || null;
6486
+ return readFileSync18(this.path(f), "utf8").trim() || null;
6428
6487
  } catch {
6429
6488
  return null;
6430
6489
  }
@@ -6433,7 +6492,7 @@ var State = class _State {
6433
6492
  blockedCount: () => Number.parseInt(read(blocked) ?? "", 10) || 0,
6434
6493
  noteBlocked: () => {
6435
6494
  const n = (Number.parseInt(read(blocked) ?? "", 10) || 0) + 1;
6436
- writeFileSync13(this.path(blocked), `${n}
6495
+ writeFileSync14(this.path(blocked), `${n}
6437
6496
  `);
6438
6497
  return n;
6439
6498
  },
@@ -6450,7 +6509,7 @@ var State = class _State {
6450
6509
  */
6451
6510
  deployFailedSha: () => read(failed),
6452
6511
  noteDeployFailed: (sha) => {
6453
- writeFileSync13(this.path(failed), `${sha}
6512
+ writeFileSync14(this.path(failed), `${sha}
6454
6513
  `);
6455
6514
  },
6456
6515
  clearDeployFailed: () => {
@@ -6464,7 +6523,7 @@ var State = class _State {
6464
6523
  noteTestGateFailed: (sha) => {
6465
6524
  const [prev, n] = (read(gateFailed) ?? "").split(" ");
6466
6525
  const next = prev === sha ? (Number.parseInt(n ?? "", 10) || 0) + 1 : 1;
6467
- writeFileSync13(this.path(gateFailed), `${sha} ${next}
6526
+ writeFileSync14(this.path(gateFailed), `${sha} ${next}
6468
6527
  `);
6469
6528
  return next;
6470
6529
  },
@@ -6474,13 +6533,13 @@ var State = class _State {
6474
6533
  /** Whether the board was already told about this head's red gate. */
6475
6534
  heldReported: () => {
6476
6535
  try {
6477
- return readFileSync17(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
6536
+ return readFileSync18(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
6478
6537
  } catch {
6479
6538
  return null;
6480
6539
  }
6481
6540
  },
6482
6541
  noteHeldReported: (signature) => {
6483
- writeFileSync13(this.path(heldReported), `${signature}
6542
+ writeFileSync14(this.path(heldReported), `${signature}
6484
6543
  `);
6485
6544
  },
6486
6545
  clearHeldReported: () => {
@@ -6488,7 +6547,7 @@ var State = class _State {
6488
6547
  },
6489
6548
  testGateReported: (sha) => read(gateReported) === sha,
6490
6549
  noteTestGateReported: (sha) => {
6491
- writeFileSync13(this.path(gateReported), `${sha}
6550
+ writeFileSync14(this.path(gateReported), `${sha}
6492
6551
  `);
6493
6552
  }
6494
6553
  };
@@ -6511,7 +6570,7 @@ var State = class _State {
6511
6570
  const ticketStreakFile = `.fairness-ticket-streak-${suffix}`;
6512
6571
  const read = (f) => {
6513
6572
  try {
6514
- return readFileSync17(this.path(f), "utf8").trim() || null;
6573
+ return readFileSync18(this.path(f), "utf8").trim() || null;
6515
6574
  } catch {
6516
6575
  return null;
6517
6576
  }
@@ -6544,18 +6603,18 @@ var State = class _State {
6544
6603
  clear2();
6545
6604
  return;
6546
6605
  }
6547
- writeFileSync13(this.path(streakFile), `${this.readInt(streakFile) + 1}
6606
+ writeFileSync14(this.path(streakFile), `${this.readInt(streakFile) + 1}
6548
6607
  `);
6549
6608
  const prior = read(ticketFile);
6550
6609
  if (topTicket && topTicket === prior) {
6551
- writeFileSync13(this.path(ticketStreakFile), `${this.readInt(ticketStreakFile) + 1}
6610
+ writeFileSync14(this.path(ticketStreakFile), `${this.readInt(ticketStreakFile) + 1}
6552
6611
  `);
6553
6612
  } else if (topTicket) {
6554
- writeFileSync13(this.path(ticketFile), `${topTicket}
6613
+ writeFileSync14(this.path(ticketFile), `${topTicket}
6555
6614
  `);
6556
- writeFileSync13(this.path(sinceFile), `${(/* @__PURE__ */ new Date()).toISOString()}
6615
+ writeFileSync14(this.path(sinceFile), `${(/* @__PURE__ */ new Date()).toISOString()}
6557
6616
  `);
6558
- writeFileSync13(this.path(ticketStreakFile), "1\n");
6617
+ writeFileSync14(this.path(ticketStreakFile), "1\n");
6559
6618
  }
6560
6619
  }
6561
6620
  };
@@ -6576,14 +6635,14 @@ var State = class _State {
6576
6635
  return {
6577
6636
  previous: () => {
6578
6637
  try {
6579
- const parsed = JSON.parse(readFileSync17(this.path(file), "utf8"));
6638
+ const parsed = JSON.parse(readFileSync18(this.path(file), "utf8"));
6580
6639
  return parsed && typeof parsed === "object" ? parsed : {};
6581
6640
  } catch {
6582
6641
  return {};
6583
6642
  }
6584
6643
  },
6585
6644
  persist: (next) => {
6586
- writeFileSync13(this.path(file), JSON.stringify(next));
6645
+ writeFileSync14(this.path(file), JSON.stringify(next));
6587
6646
  }
6588
6647
  };
6589
6648
  }
@@ -6599,14 +6658,14 @@ var State = class _State {
6599
6658
  return {
6600
6659
  previous: () => {
6601
6660
  try {
6602
- const parsed = JSON.parse(readFileSync17(this.path(file), "utf8"));
6661
+ const parsed = JSON.parse(readFileSync18(this.path(file), "utf8"));
6603
6662
  return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : [];
6604
6663
  } catch {
6605
6664
  return [];
6606
6665
  }
6607
6666
  },
6608
6667
  persist: (next) => {
6609
- writeFileSync13(this.path(file), JSON.stringify(next));
6668
+ writeFileSync14(this.path(file), JSON.stringify(next));
6610
6669
  }
6611
6670
  };
6612
6671
  }
@@ -6620,14 +6679,14 @@ var State = class _State {
6620
6679
  return {
6621
6680
  previous: () => {
6622
6681
  try {
6623
- const parsed = JSON.parse(readFileSync17(this.path(file), "utf8"));
6682
+ const parsed = JSON.parse(readFileSync18(this.path(file), "utf8"));
6624
6683
  return Array.isArray(parsed) ? parsed : [];
6625
6684
  } catch {
6626
6685
  return [];
6627
6686
  }
6628
6687
  },
6629
6688
  persist: (next) => {
6630
- writeFileSync13(this.path(file), JSON.stringify(next));
6689
+ writeFileSync14(this.path(file), JSON.stringify(next));
6631
6690
  }
6632
6691
  };
6633
6692
  }
@@ -6640,7 +6699,7 @@ var State = class _State {
6640
6699
  const file = ".release-last-run";
6641
6700
  return {
6642
6701
  touch: () => {
6643
- writeFileSync13(this.path(file), (/* @__PURE__ */ new Date()).toISOString());
6702
+ writeFileSync14(this.path(file), (/* @__PURE__ */ new Date()).toISOString());
6644
6703
  },
6645
6704
  at: () => {
6646
6705
  try {
@@ -6653,7 +6712,7 @@ var State = class _State {
6653
6712
  }
6654
6713
  readInt(file) {
6655
6714
  try {
6656
- return Number.parseInt(readFileSync17(this.path(file), "utf8").trim(), 10) || 0;
6715
+ return Number.parseInt(readFileSync18(this.path(file), "utf8").trim(), 10) || 0;
6657
6716
  } catch {
6658
6717
  return 0;
6659
6718
  }
@@ -7270,8 +7329,8 @@ var BeforeFirstEditTracker = class {
7270
7329
  };
7271
7330
 
7272
7331
  // src/agent-log.ts
7273
- import { existsSync as existsSync19, mkdirSync as mkdirSync13, readdirSync as readdirSync6, readFileSync as readFileSync18, rmSync as rmSync11, writeFileSync as writeFileSync14 } from "fs";
7274
- import { join as join20 } from "path";
7332
+ import { existsSync as existsSync20, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync19, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7333
+ import { join as join21 } from "path";
7275
7334
 
7276
7335
  // src/pending-backoff.ts
7277
7336
  var sleepBackoff = (attempts) => Math.min(6e4 * 2 ** Math.max(0, attempts - 1), 30 * 6e4);
@@ -7474,20 +7533,20 @@ async function addRunToTicket(target, t, recordId, run3) {
7474
7533
  };
7475
7534
  }
7476
7535
  }
7477
- var queueDir = (stateDir) => join20(stateDir, "pending-agent-logs");
7536
+ var queueDir = (stateDir) => join21(stateDir, "pending-agent-logs");
7478
7537
  function queueAgentRun(stateDir, name, q2) {
7479
- mkdirSync13(queueDir(stateDir), { recursive: true });
7480
- writeFileSync14(join20(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
7538
+ mkdirSync14(queueDir(stateDir), { recursive: true });
7539
+ writeFileSync15(join21(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
7481
7540
  }
7482
7541
  async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
7483
7542
  const dir = queueDir(stateDir);
7484
- if (!existsSync19(dir)) return 0;
7543
+ if (!existsSync20(dir)) return 0;
7485
7544
  let sent = 0;
7486
7545
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
7487
- const path = join20(dir, f);
7546
+ const path = join21(dir, f);
7488
7547
  let q2;
7489
7548
  try {
7490
- q2 = JSON.parse(readFileSync18(path, "utf8"));
7549
+ q2 = JSON.parse(readFileSync19(path, "utf8"));
7491
7550
  } catch {
7492
7551
  continue;
7493
7552
  }
@@ -7516,7 +7575,7 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
7516
7575
  continue;
7517
7576
  }
7518
7577
  const attempts = q2.attempts + 1;
7519
- writeFileSync14(path, JSON.stringify({
7578
+ writeFileSync15(path, JSON.stringify({
7520
7579
  ...q2,
7521
7580
  attempts,
7522
7581
  warned: true,
@@ -7527,20 +7586,20 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
7527
7586
  }
7528
7587
  return sent;
7529
7588
  }
7530
- var countersDir = (stateDir) => join20(stateDir, "pending-ticket-counters");
7589
+ var countersDir = (stateDir) => join21(stateDir, "pending-ticket-counters");
7531
7590
  function queueTicketCounters(stateDir, name, q2) {
7532
- mkdirSync13(countersDir(stateDir), { recursive: true });
7533
- writeFileSync14(join20(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
7591
+ mkdirSync14(countersDir(stateDir), { recursive: true });
7592
+ writeFileSync15(join21(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
7534
7593
  }
7535
7594
  async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
7536
7595
  const dir = countersDir(stateDir);
7537
- if (!existsSync19(dir)) return 0;
7596
+ if (!existsSync20(dir)) return 0;
7538
7597
  let sent = 0;
7539
7598
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
7540
- const path = join20(dir, f);
7599
+ const path = join21(dir, f);
7541
7600
  let q2;
7542
7601
  try {
7543
- q2 = JSON.parse(readFileSync18(path, "utf8"));
7602
+ q2 = JSON.parse(readFileSync19(path, "utf8"));
7544
7603
  } catch {
7545
7604
  continue;
7546
7605
  }
@@ -7559,7 +7618,7 @@ async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /
7559
7618
  continue;
7560
7619
  }
7561
7620
  const attempts = q2.attempts + 1;
7562
- writeFileSync14(path, JSON.stringify({
7621
+ writeFileSync15(path, JSON.stringify({
7563
7622
  ...q2,
7564
7623
  attempts,
7565
7624
  warned: true,
@@ -7762,8 +7821,8 @@ var HandoffTranscriptTracker = class {
7762
7821
  };
7763
7822
 
7764
7823
  // src/pending-handoff.ts
7765
- import { existsSync as existsSync21, mkdirSync as mkdirSync14, readdirSync as readdirSync7, readFileSync as readFileSync19, rmSync as rmSync12, writeFileSync as writeFileSync15 } from "fs";
7766
- import { join as join22 } from "path";
7824
+ import { existsSync as existsSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync7, readFileSync as readFileSync20, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7825
+ import { join as join23 } from "path";
7767
7826
 
7768
7827
  // src/ticket-branch.ts
7769
7828
  import { basename as basename7 } from "path";
@@ -8929,8 +8988,8 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
8929
8988
  }
8930
8989
 
8931
8990
  // src/run-worktree.ts
8932
- import { existsSync as existsSync20 } from "fs";
8933
- import { dirname as dirname10, join as join21 } from "path";
8991
+ import { existsSync as existsSync21 } from "fs";
8992
+ import { dirname as dirname10, join as join22 } from "path";
8934
8993
  var describeRunWorktreePlan = (p) => {
8935
8994
  switch (p.action) {
8936
8995
  case "cut-remote":
@@ -8971,7 +9030,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
8971
9030
  const existing = worktreeForNumber(dir, num);
8972
9031
  const located = locateBranchForTicket(dir, cfg, t, role);
8973
9032
  const baseRef = hasRemote ? `${remote}/${base}` : base;
8974
- if (existing && !existing.prunable && existsSync20(existing.path)) {
9033
+ if (existing && !existing.prunable && existsSync21(existing.path)) {
8975
9034
  const branch = existing.branch;
8976
9035
  if (status(existing.path).length > 0) {
8977
9036
  return { action: opts.resumeInterrupted ? "resume-dirty" : "leave-dirty", path: existing.path, branch };
@@ -8987,7 +9046,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
8987
9046
  if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
8988
9047
  return { action: "recut", path: existing.path, branch, base: baseRef };
8989
9048
  }
8990
- const path = join21(dirname10(dir), worktreeForTicket(dir, cfg, t, role));
9049
+ const path = join22(dirname10(dir), worktreeForTicket(dir, cfg, t, role));
8991
9050
  if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
8992
9051
  return { action: "cut-remote", path, branch: located.branch };
8993
9052
  }
@@ -9041,7 +9100,7 @@ function dropEmptyRunBranch(dir, cfg, branch) {
9041
9100
  if (remoteConfigured(dir, remote) && remoteBranchExists(dir, remote, branch)) return false;
9042
9101
  if (countCommits(dir, `${baseRef}..${branch}`) !== 0) return false;
9043
9102
  const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
9044
- if (wt && existsSync20(wt.path)) {
9103
+ if (wt && existsSync21(wt.path)) {
9045
9104
  if (status(wt.path).length > 0) return false;
9046
9105
  if (gitOk(dir, ["worktree", "remove", wt.path]) === null) return false;
9047
9106
  }
@@ -9049,7 +9108,7 @@ function dropEmptyRunBranch(dir, cfg, branch) {
9049
9108
  }
9050
9109
  async function finishRunWorktree(dir, cfg, branch) {
9051
9110
  const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
9052
- if (!wt || !existsSync20(wt.path)) return { kind: "none" };
9111
+ if (!wt || !existsSync21(wt.path)) return { kind: "none" };
9053
9112
  if (cfg.hooks.handoff) return { kind: "kept-handoff", path: wt.path };
9054
9113
  if (status(wt.path).length > 0) return { kind: "kept-dirty", path: wt.path };
9055
9114
  if (remoteConfigured(dir, cfg.branch.remote) && worktreeHoldsUnpushed(wt.path, cfg.branch.remote)) {
@@ -9061,7 +9120,7 @@ async function finishRunWorktree(dir, cfg, branch) {
9061
9120
  if (pids.length) await killGently(pids);
9062
9121
  }
9063
9122
  gitOk(dir, ["worktree", "remove", wt.path]);
9064
- return existsSync20(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
9123
+ return existsSync21(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
9065
9124
  }
9066
9125
 
9067
9126
  // src/handoff-guard.ts
@@ -9444,20 +9503,20 @@ async function guardRunStrays(route2, ship, tracker, role, workingId, since2, lo
9444
9503
 
9445
9504
  // src/pending-handoff.ts
9446
9505
  var PENDING_MAX_AGE_MS = 48 * 36e5;
9447
- var pendingDirFor = (stateDir) => join22(stateDir, "pending-handoffs");
9448
- var stateDirOfStream = (streamPath) => join22(streamPath, "..", "..");
9506
+ var pendingDirFor = (stateDir) => join23(stateDir, "pending-handoffs");
9507
+ var stateDirOfStream = (streamPath) => join23(streamPath, "..", "..");
9449
9508
  function writePendingHandoff(stateDir, rec) {
9450
9509
  const dir = pendingDirFor(stateDir);
9451
- mkdirSync14(dir, { recursive: true });
9452
- writeFileSync15(join22(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
9510
+ mkdirSync15(dir, { recursive: true });
9511
+ writeFileSync16(join23(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
9453
9512
  }
9454
9513
  function listPendingHandoffs(stateDir, route2) {
9455
9514
  const dir = pendingDirFor(stateDir);
9456
- if (!existsSync21(dir)) return [];
9515
+ if (!existsSync22(dir)) return [];
9457
9516
  const out = [];
9458
9517
  for (const f of readdirSync7(dir).filter((n) => n.endsWith(".json")).sort()) {
9459
9518
  try {
9460
- const rec = JSON.parse(readFileSync19(join22(dir, f), "utf8"));
9519
+ const rec = JSON.parse(readFileSync20(join23(dir, f), "utf8"));
9461
9520
  if (rec.version === 1 && rec.ticketRecordId && (!route2 || rec.route === route2)) out.push(rec);
9462
9521
  } catch {
9463
9522
  }
@@ -9465,7 +9524,7 @@ function listPendingHandoffs(stateDir, route2) {
9465
9524
  return out;
9466
9525
  }
9467
9526
  function removePendingHandoff(stateDir, ticketRecordId) {
9468
- rmSync12(join22(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
9527
+ rmSync12(join23(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
9469
9528
  }
9470
9529
  function pendingHandoffFor(stateDir, ticketRecordId) {
9471
9530
  return listPendingHandoffs(stateDir).find((r) => r.ticketRecordId === ticketRecordId);
@@ -9575,8 +9634,8 @@ function expandSkillMarkers(text2, lookup2, opts = {}) {
9575
9634
  }
9576
9635
 
9577
9636
  // src/skills.ts
9578
- import { readFileSync as readFileSync21, readdirSync as readdirSync8, existsSync as existsSync23 } from "fs";
9579
- import { join as join24 } from "path";
9637
+ import { readFileSync as readFileSync22, readdirSync as readdirSync8, existsSync as existsSync24 } from "fs";
9638
+ import { join as join25 } from "path";
9580
9639
  import { parse as parseYaml } from "yaml";
9581
9640
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
9582
9641
 
@@ -9592,8 +9651,8 @@ ${additions}`;
9592
9651
  }
9593
9652
 
9594
9653
  // src/agents.ts
9595
- import { readFileSync as readFileSync20, existsSync as existsSync22 } from "fs";
9596
- import { join as join23 } from "path";
9654
+ import { readFileSync as readFileSync21, existsSync as existsSync23 } from "fs";
9655
+ import { join as join24 } from "path";
9597
9656
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
9598
9657
  var PERSONA_NAME = {
9599
9658
  dev: "Developer",
@@ -9607,12 +9666,12 @@ var CREW_AGENT_COLUMN = "agent_id";
9607
9666
  var AgentsSyncError = class extends Error {
9608
9667
  };
9609
9668
  function personaDefaultPrompt(promptsDir, role) {
9610
- const briefPath = join23(promptsDir, "personas", `lane-${role}.md`);
9611
- if (!existsSync22(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
9612
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync20(briefPath, "utf8");
9613
- const commonPath = join23(promptsDir, "personas", "common.md");
9614
- if (!existsSync22(commonPath)) return readFileSync20(briefPath, "utf8");
9615
- return `${readFileSync20(commonPath, "utf8")}${readFileSync20(briefPath, "utf8")}`;
9669
+ const briefPath = join24(promptsDir, "personas", `lane-${role}.md`);
9670
+ if (!existsSync23(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
9671
+ if (SELF_CONTAINED_ROLES.has(role)) return readFileSync21(briefPath, "utf8");
9672
+ const commonPath = join24(promptsDir, "personas", "common.md");
9673
+ if (!existsSync23(commonPath)) return readFileSync21(briefPath, "utf8");
9674
+ return `${readFileSync21(commonPath, "utf8")}${readFileSync21(briefPath, "utf8")}`;
9616
9675
  }
9617
9676
  async function resolveAgentId(route2, role, opts) {
9618
9677
  const cached = route2.resolved?.agentPersonas?.[role]?.agentId;
@@ -10059,7 +10118,7 @@ function describeCrewLink(o) {
10059
10118
  var SkillSyncError = class extends Error {
10060
10119
  };
10061
10120
  function parseSkillFile(path) {
10062
- const text2 = readFileSync21(path, "utf8");
10121
+ const text2 = readFileSync22(path, "utf8");
10063
10122
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text2);
10064
10123
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
10065
10124
  const frontmatter = match[1];
@@ -10072,9 +10131,9 @@ function parseSkillFile(path) {
10072
10131
  return { name: meta.name, description: meta.description, prompt: body, file: path };
10073
10132
  }
10074
10133
  function listLocalSkills(promptsDir) {
10075
- const dir = join24(promptsDir, "skills");
10076
- if (!existsSync23(dir)) return [];
10077
- return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join24(dir, f)));
10134
+ const dir = join25(promptsDir, "skills");
10135
+ if (!existsSync24(dir)) return [];
10136
+ return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join25(dir, f)));
10078
10137
  }
10079
10138
  async function syncSkills(route2, opts) {
10080
10139
  if (!route2.resolved) {
@@ -10259,9 +10318,9 @@ function skillMarkerWarnings(promptsDir, personaPrompts) {
10259
10318
 
10260
10319
  // src/runner.ts
10261
10320
  import { spawn as spawn3, execFile, execFileSync as execFileSync6 } from "child_process";
10262
- import { mkdtempSync, mkdirSync as mkdirSync15, writeFileSync as writeFileSync16, rmSync as rmSync13 } from "fs";
10321
+ import { mkdtempSync, mkdirSync as mkdirSync16, writeFileSync as writeFileSync17, rmSync as rmSync13 } from "fs";
10263
10322
  import { tmpdir as tmpdir2 } from "os";
10264
- import { basename as basename8, dirname as dirname11, join as join25 } from "path";
10323
+ import { basename as basename8, dirname as dirname11, join as join26 } from "path";
10265
10324
  import { promisify } from "util";
10266
10325
 
10267
10326
  // src/run-limit.ts
@@ -10390,8 +10449,8 @@ var SandboxRunner = class {
10390
10449
  throw new Error(`cannot generate the sandbox policy: ${e.message}`);
10391
10450
  }
10392
10451
  policyFile = policyPathFor(plan.streamPath);
10393
- mkdirSync15(dirname11(policyFile), { recursive: true });
10394
- writeFileSync16(policyFile, generated.yaml, { mode: 384 });
10452
+ mkdirSync16(dirname11(policyFile), { recursive: true });
10453
+ writeFileSync17(policyFile, generated.yaml, { mode: 384 });
10395
10454
  }
10396
10455
  const create2 = ["sandbox", "create", "--name", name, "--detach"];
10397
10456
  if (policyFile) create2.push("--policy", policyFile);
@@ -10434,10 +10493,10 @@ var SandboxRunner = class {
10434
10493
  const harness = cfg.agentBin ?? basename8(plan.bin);
10435
10494
  const viaUpload = Buffer.byteLength(plan.prompt) > SANDBOX_STDIN_LIMIT;
10436
10495
  if (viaUpload) {
10437
- const tmp = mkdtempSync(join25(tmpdir2(), "crew-prompt-"));
10496
+ const tmp = mkdtempSync(join26(tmpdir2(), "crew-prompt-"));
10438
10497
  try {
10439
- const file = join25(tmp, "prompt.txt");
10440
- writeFileSync16(file, plan.prompt, { mode: 384 });
10498
+ const file = join26(tmp, "prompt.txt");
10499
+ writeFileSync17(file, plan.prompt, { mode: 384 });
10441
10500
  await this.os(["sandbox", "upload", name, file, SANDBOX_PROMPT_PATH]);
10442
10501
  } finally {
10443
10502
  rmSync13(tmp, { recursive: true, force: true });
@@ -10571,6 +10630,15 @@ function sandboxPolicyInputFor(route2, toolchain, o) {
10571
10630
  ...o.sandbox?.workDir ? { workDir: o.sandbox.workDir } : {}
10572
10631
  };
10573
10632
  }
10633
+ function lspPluginDirsFor(route2, repo, stateDir) {
10634
+ try {
10635
+ const dir = reposOf(route2).find((r) => r.name === repo)?.dir ?? route2.dir;
10636
+ const flavor = lspFlavor(loadRepoConfig(dir)?.hooks.intel);
10637
+ return flavor ? [ensureLspPlugin(stateDir, flavor, dir)] : void 0;
10638
+ } catch {
10639
+ return void 0;
10640
+ }
10641
+ }
10574
10642
  var AgentSpawnError = class extends AgentError {
10575
10643
  };
10576
10644
  var WorkspaceAuthRefused = class extends AgentSpawnError {
@@ -10581,7 +10649,7 @@ var ALLOWANCE_REFUSAL_MARKER = "AI_ALLOWANCE_EXCEEDED";
10581
10649
  function streamShowsAllowanceRefusal(streamPath) {
10582
10650
  let raw;
10583
10651
  try {
10584
- raw = readFileSync22(streamPath, "utf8");
10652
+ raw = readFileSync23(streamPath, "utf8");
10585
10653
  } catch {
10586
10654
  return false;
10587
10655
  }
@@ -10604,26 +10672,26 @@ function streamShowsAllowanceRefusal(streamPath) {
10604
10672
  }
10605
10673
  return false;
10606
10674
  }
10607
- var digestPathFor = (stateDir, routeLabel, role, runId) => join26(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10675
+ var digestPathFor = (stateDir, routeLabel, role, runId) => join27(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10608
10676
  function planAgentRun(o) {
10609
- const briefPath = join26(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10610
- if (!existsSync24(briefPath)) {
10677
+ const briefPath = join27(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10678
+ if (!existsSync25(briefPath)) {
10611
10679
  throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
10612
10680
  }
10613
- const commonPath = join26(o.route.promptsDir, "personas", "common.md");
10614
- const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync24(commonPath);
10681
+ const commonPath = join27(o.route.promptsDir, "personas", "common.md");
10682
+ const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync25(commonPath);
10615
10683
  const now = o.now ?? (() => Date.now());
10616
10684
  const routeLabel = routeSlug(o.route.route);
10617
10685
  const ownDigest = o.runId ? digestPathFor(o.stateDir, routeLabel, o.role, o.runId) : void 0;
10618
- const digestPath2 = ownDigest && existsSync24(ownDigest) ? ownDigest : digestPathFor(o.stateDir, routeLabel, o.role);
10686
+ const digestPath2 = ownDigest && existsSync25(ownDigest) ? ownDigest : digestPathFor(o.stateDir, routeLabel, o.role);
10619
10687
  let digest;
10620
10688
  let ageSeconds;
10621
- if (existsSync24(digestPath2)) {
10689
+ if (existsSync25(digestPath2)) {
10622
10690
  ageSeconds = Math.floor((now() - statSync8(digestPath2).mtimeMs) / 1e3);
10623
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync22(digestPath2, "utf8");
10691
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync23(digestPath2, "utf8");
10624
10692
  }
10625
- const common = usesCommon ? readFileSync22(commonPath, "utf8") : "";
10626
- const brief = readFileSync22(briefPath, "utf8");
10693
+ const common = usesCommon ? readFileSync23(commonPath, "utf8") : "";
10694
+ const brief = readFileSync23(briefPath, "utf8");
10627
10695
  const effectiveCommon = o.divergedPrompt ? "" : common;
10628
10696
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
10629
10697
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -10646,7 +10714,7 @@ function planAgentRun(o) {
10646
10714
  digest
10647
10715
  });
10648
10716
  const promptSha = createHash4("sha256").update(expanded.text).digest("hex");
10649
- const streamsDir = join26(o.stateDir, "streams");
10717
+ const streamsDir = join27(o.stateDir, "streams");
10650
10718
  const base = `${routeLabel}-${o.role}-${o.cycle}${o.runId ? `-${o.runId}` : ""}`;
10651
10719
  const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
10652
10720
  const { model } = seat;
@@ -10664,6 +10732,7 @@ function planAgentRun(o) {
10664
10732
  const invocation = adapter.invocation(toolchain, {
10665
10733
  role: o.role,
10666
10734
  model,
10735
+ pluginDirs: adapter.harness === "claude" ? lspPluginDirsFor(o.route, o.repo, o.stateDir) : void 0,
10667
10736
  // Skipped when there's no key to send: a route `crew connect` hasn't
10668
10737
  // resolved yet has nothing to authenticate the MCP connection with either.
10669
10738
  mcp: o.apiKey ? { url: `${o.route.baseUrl}/api/mcp`, keyVar: API_KEY_VAR } : void 0
@@ -10736,8 +10805,8 @@ function planAgentRun(o) {
10736
10805
  digestAttached: digest !== void 0,
10737
10806
  digestAgeSeconds: ageSeconds,
10738
10807
  model,
10739
- streamPath: join26(streamsDir, `${base}.jsonl`),
10740
- eventsPath: join26(streamsDir, `${base}.events.jsonl`),
10808
+ streamPath: join27(streamsDir, `${base}.jsonl`),
10809
+ eventsPath: join27(streamsDir, `${base}.events.jsonl`),
10741
10810
  ticket: o.ticket,
10742
10811
  ticketRecordId: o.ticketRecordId ?? null,
10743
10812
  repo: o.repo ?? null,
@@ -10768,7 +10837,7 @@ function describePlan(p) {
10768
10837
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
10769
10838
  function readRunSidecar(streamPath) {
10770
10839
  try {
10771
- const sc = JSON.parse(readFileSync22(pidPathFor(streamPath), "utf8"));
10840
+ const sc = JSON.parse(readFileSync23(pidPathFor(streamPath), "utf8"));
10772
10841
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
10773
10842
  } catch {
10774
10843
  return null;
@@ -10815,7 +10884,7 @@ async function killForDeadline(kill, pid, emit, role, ticket, spawnedAt, limitMi
10815
10884
  var withoutKey = ({ apiKey: _apiKey, ...rest }) => rest;
10816
10885
  function openSink(path) {
10817
10886
  try {
10818
- mkdirSync16(dirname12(path), { recursive: true });
10887
+ mkdirSync17(dirname12(path), { recursive: true });
10819
10888
  return createWriteStream(path, { flags: "a" });
10820
10889
  } catch {
10821
10890
  return void 0;
@@ -11001,7 +11070,7 @@ var RunObserver = class {
11001
11070
  };
11002
11071
  function writeRunMeta(id, startedAt, liveTicket, extra) {
11003
11072
  try {
11004
- writeFileSync17(`${id.streamPath}.meta.json`, JSON.stringify({
11073
+ writeFileSync18(`${id.streamPath}.meta.json`, JSON.stringify({
11005
11074
  route: id.route,
11006
11075
  role: id.role,
11007
11076
  hint: id.ticket,
@@ -11167,7 +11236,7 @@ function spawnAgent(plan, emit, hintMoved) {
11167
11236
  const id = identityOf(plan, adapter.harness);
11168
11237
  let streamFd;
11169
11238
  try {
11170
- mkdirSync16(dirname12(plan.streamPath), { recursive: true });
11239
+ mkdirSync17(dirname12(plan.streamPath), { recursive: true });
11171
11240
  streamFd = openSync4(plan.streamPath, "a");
11172
11241
  } catch (e) {
11173
11242
  void endSink(eventsSink);
@@ -11225,7 +11294,7 @@ function spawnAgent(plan, emit, hintMoved) {
11225
11294
  promptSha: plan.promptSha
11226
11295
  };
11227
11296
  try {
11228
- writeFileSync17(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
11297
+ writeFileSync18(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
11229
11298
  } catch {
11230
11299
  }
11231
11300
  }
@@ -11339,21 +11408,21 @@ var pidAlive = (pid) => {
11339
11408
  };
11340
11409
  function runFinished(streamPath) {
11341
11410
  const path = `${streamPath}.meta.json`;
11342
- if (!existsSync25(path)) return false;
11411
+ if (!existsSync26(path)) return false;
11343
11412
  try {
11344
- return JSON.parse(readFileSync23(path, "utf8")).running !== true;
11413
+ return JSON.parse(readFileSync24(path, "utf8")).running !== true;
11345
11414
  } catch {
11346
11415
  return true;
11347
11416
  }
11348
11417
  }
11349
11418
  function orphanedStreams(o) {
11350
- const dir = join27(o.stateDir, "streams");
11351
- if (!existsSync25(dir)) return [];
11419
+ const dir = join28(o.stateDir, "streams");
11420
+ if (!existsSync26(dir)) return [];
11352
11421
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
11353
11422
  const out = [];
11354
11423
  for (const name of readdirSync9(dir)) {
11355
11424
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11356
- const streamPath = join27(dir, name);
11425
+ const streamPath = join28(dir, name);
11357
11426
  if (runFinished(streamPath)) continue;
11358
11427
  const sidecar = readRunSidecar(streamPath);
11359
11428
  if (!sidecar || sidecar.route !== o.routeLabel) continue;
@@ -11406,15 +11475,15 @@ async function reconcileRuns(o) {
11406
11475
  }
11407
11476
 
11408
11477
  // src/live-runs.ts
11409
- import { existsSync as existsSync26, readdirSync as readdirSync10, statSync as statSync9 } from "fs";
11410
- import { join as join28 } from "path";
11478
+ import { existsSync as existsSync27, readdirSync as readdirSync10, statSync as statSync9 } from "fs";
11479
+ import { join as join29 } from "path";
11411
11480
  function liveRuns(stateDir, isAlive = runIsAlive) {
11412
- const dir = join28(stateDir, "streams");
11413
- if (!existsSync26(dir)) return [];
11481
+ const dir = join29(stateDir, "streams");
11482
+ if (!existsSync27(dir)) return [];
11414
11483
  const out = [];
11415
11484
  for (const name of readdirSync10(dir)) {
11416
11485
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11417
- const streamPath = join28(dir, name);
11486
+ const streamPath = join29(dir, name);
11418
11487
  if (runFinished(streamPath)) continue;
11419
11488
  const sidecar = readRunSidecar(streamPath);
11420
11489
  if (!sidecar || !isAlive(sidecar)) continue;
@@ -11424,12 +11493,12 @@ function liveRuns(stateDir, isAlive = runIsAlive) {
11424
11493
  }
11425
11494
  function runActivity(stateDir, isAlive = runIsAlive) {
11426
11495
  const activeRuns = liveRuns(stateDir, isAlive).length;
11427
- const dir = join28(stateDir, "streams");
11496
+ const dir = join29(stateDir, "streams");
11428
11497
  let newest = 0;
11429
- if (existsSync26(dir)) {
11498
+ if (existsSync27(dir)) {
11430
11499
  for (const name of readdirSync10(dir)) {
11431
11500
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11432
- const streamPath = join28(dir, name);
11501
+ const streamPath = join29(dir, name);
11433
11502
  if (!runFinished(streamPath)) continue;
11434
11503
  try {
11435
11504
  newest = Math.max(newest, statSync9(`${streamPath}.meta.json`).mtimeMs);
@@ -11800,7 +11869,7 @@ function realDeprovisionDeps(cfg, emit, crewHome, getStore) {
11800
11869
  if (host === "windows") return [];
11801
11870
  const ctx = installContext();
11802
11871
  const systemd = detectSystemd();
11803
- return ["run", "release", "passengers"].flatMap((job) => planInstall(cfg.ship, crewHome, host, systemd, job, ctx).unitPaths).filter((p) => existsSync27(p));
11872
+ return ["run", "release", "passengers"].flatMap((job) => planInstall(cfg.ship, crewHome, host, systemd, job, ctx).unitPaths).filter((p) => existsSync28(p));
11804
11873
  };
11805
11874
  return {
11806
11875
  liveRuns: () => liveRuns(stateDir).map((r) => ({ role: r.role, route: r.route, pid: r.pid, ticketRecordId: r.ticketRecordId ?? void 0, streamPath: r.streamPath })),
@@ -11861,7 +11930,7 @@ function realDeprovisionDeps(cfg, emit, crewHome, getStore) {
11861
11930
  },
11862
11931
  remaining() {
11863
11932
  const left = [];
11864
- if (existsSync27(cfg.configFile)) left.push(`config ${cfg.configFile}`);
11933
+ if (existsSync28(cfg.configFile)) left.push(`config ${cfg.configFile}`);
11865
11934
  for (const h of forgeHosts()) if (hasForgeToken(cfg.ship.forges, h, stateDir)) left.push(`forge token for ${h}`);
11866
11935
  try {
11867
11936
  for (const k of readdirSync11(keysDir(stateDir))) left.push(`seat key ${k}`);
@@ -12469,19 +12538,19 @@ function planSandbox(repo, decl, warm = {}) {
12469
12538
  const layer = repoLayer(repo, decl, crew, warm);
12470
12539
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
12471
12540
  }
12472
- var sandboxStatePath = (stateDir) => join29(stateDir, "sandbox-images.json");
12541
+ var sandboxStatePath = (stateDir) => join30(stateDir, "sandbox-images.json");
12473
12542
  function readSandboxState(stateDir) {
12474
12543
  try {
12475
- const v = JSON.parse(readFileSync24(sandboxStatePath(stateDir), "utf8"));
12544
+ const v = JSON.parse(readFileSync25(sandboxStatePath(stateDir), "utf8"));
12476
12545
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
12477
12546
  } catch {
12478
12547
  return {};
12479
12548
  }
12480
12549
  }
12481
12550
  function writeSandboxState(stateDir, state) {
12482
- mkdirSync17(stateDir, { recursive: true });
12551
+ mkdirSync18(stateDir, { recursive: true });
12483
12552
  const file = sandboxStatePath(stateDir);
12484
- writeFileSync18(`${file}.tmp`, `${JSON.stringify(state, null, 2)}
12553
+ writeFileSync19(`${file}.tmp`, `${JSON.stringify(state, null, 2)}
12485
12554
  `);
12486
12555
  renameSync3(`${file}.tmp`, file);
12487
12556
  }
@@ -12501,7 +12570,7 @@ var spawnBuildRunner = (engine = "docker") => (args, logPath) => new Promise((re
12501
12570
  child.stdout.on("data", (d) => chunks.push(d));
12502
12571
  child.stderr.on("data", (d) => chunks.push(d));
12503
12572
  const done = (code, extra = "") => {
12504
- writeFileSync18(logPath, `$ ${engine} ${args.join(" ")}
12573
+ writeFileSync19(logPath, `$ ${engine} ${args.join(" ")}
12505
12574
  ${Buffer.concat(chunks).toString("utf8")}${extra}`, { flag: "a" });
12506
12575
  resolve8(code);
12507
12576
  };
@@ -12523,7 +12592,7 @@ var engineRemoveImage = (engine = "docker", exec = realExec2) => (tag) => {
12523
12592
  } catch {
12524
12593
  }
12525
12594
  };
12526
- var sandboxLogDir = (stateDir) => join29(stateDir, "sandbox-logs");
12595
+ var sandboxLogDir = (stateDir) => join30(stateDir, "sandbox-logs");
12527
12596
  async function buildSandboxImage(plan, deps) {
12528
12597
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
12529
12598
  const state = readSandboxState(deps.stateDir);
@@ -12532,20 +12601,20 @@ async function buildSandboxImage(plan, deps) {
12532
12601
  return { kind: "reused", tag: plan.tag };
12533
12602
  }
12534
12603
  const logDir = sandboxLogDir(deps.stateDir);
12535
- mkdirSync17(logDir, { recursive: true });
12536
- const logPath = join29(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
12604
+ mkdirSync18(logDir, { recursive: true });
12605
+ const logPath = join30(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
12537
12606
  const builtLayers = [];
12538
12607
  for (const layer of plan.layers) {
12539
12608
  if (deps.hasImage(layer.tag)) continue;
12540
- const ctx = join29(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
12541
- mkdirSync17(ctx, { recursive: true });
12609
+ const ctx = join30(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
12610
+ mkdirSync18(ctx, { recursive: true });
12542
12611
  try {
12543
- writeFileSync18(join29(ctx, "Dockerfile"), layer.dockerfile);
12612
+ writeFileSync19(join30(ctx, "Dockerfile"), layer.dockerfile);
12544
12613
  for (const [name, content] of Object.entries(layer.files)) {
12545
- mkdirSync17(dirname13(join29(ctx, name)), { recursive: true });
12546
- writeFileSync18(join29(ctx, name), content);
12614
+ mkdirSync18(dirname13(join30(ctx, name)), { recursive: true });
12615
+ writeFileSync19(join30(ctx, name), content);
12547
12616
  }
12548
- const code = await deps.run(["build", "--tag", layer.tag, "--file", join29(ctx, "Dockerfile"), ctx], logPath);
12617
+ const code = await deps.run(["build", "--tag", layer.tag, "--file", join30(ctx, "Dockerfile"), ctx], logPath);
12549
12618
  if (code !== 0) {
12550
12619
  state[plan.repo] = { ...entry, failure: { tag: plan.tag, at: now().toISOString(), logPath } };
12551
12620
  writeSandboxState(deps.stateDir, state);
@@ -12734,9 +12803,9 @@ function managedRuntimeWarnings(kind, ship) {
12734
12803
 
12735
12804
  // src/selftest.ts
12736
12805
  import { spawn as spawn6 } from "child_process";
12737
- import { existsSync as existsSync28, mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync2, readFileSync as readFileSync25, rmSync as rmSync15, writeFileSync as writeFileSync19 } from "fs";
12806
+ import { existsSync as existsSync29, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync2, readFileSync as readFileSync26, rmSync as rmSync15, writeFileSync as writeFileSync20 } from "fs";
12738
12807
  import { tmpdir as tmpdir4 } from "os";
12739
- import { join as join30 } from "path";
12808
+ import { join as join31 } from "path";
12740
12809
  var SELFTEST_TIMEOUT_MS = 12e4;
12741
12810
  var KEY_VAR = "CREW_API_KEY";
12742
12811
  var SELFTEST_PROMPT = "This is a read-only connectivity check. Use the tracker MCP tool to read this workspace (the workspaces find-one tool, using the workspace id below) and reply with the workspace's name alone, on one line. Do not write anything, do not create files, and do not use any other tool.\n\nWorkspace id: ";
@@ -12897,7 +12966,7 @@ async function runSelftest(i, deps = realDeps) {
12897
12966
  ...adapter.runEnv(spec),
12898
12967
  [KEY_VAR]: i.apiKey
12899
12968
  };
12900
- const cwd = mkdtempSync2(join30(tmpdir4(), "crew-selftest-"));
12969
+ const cwd = mkdtempSync2(join31(tmpdir4(), "crew-selftest-"));
12901
12970
  try {
12902
12971
  const run3 = await deps.run({
12903
12972
  bin: invocation.bin,
@@ -12930,11 +12999,11 @@ function formatResult(r) {
12930
12999
  const secs = `${(r.ms / 1e3).toFixed(1)}s`;
12931
13000
  return r.ok ? `${r.toolchain}: PASS ${r.detail} (${secs}${tok})` : `${r.toolchain}: FAIL ${r.failure} \u2014 ${r.detail} (${secs})`;
12932
13001
  }
12933
- var fileFor2 = (stateDir) => join30(stateDir, "selftest.json");
13002
+ var fileFor2 = (stateDir) => join31(stateDir, "selftest.json");
12934
13003
  function readSelftest(stateDir) {
12935
13004
  try {
12936
13005
  const p = fileFor2(stateDir);
12937
- return existsSync28(p) ? JSON.parse(readFileSync25(p, "utf8")) : {};
13006
+ return existsSync29(p) ? JSON.parse(readFileSync26(p, "utf8")) : {};
12938
13007
  } catch {
12939
13008
  return {};
12940
13009
  }
@@ -12942,8 +13011,8 @@ function readSelftest(stateDir) {
12942
13011
  function recordSelftest(stateDir, results) {
12943
13012
  const all = readSelftest(stateDir);
12944
13013
  for (const r of results) all[r.toolchain] = r;
12945
- mkdirSync18(stateDir, { recursive: true });
12946
- writeFileSync19(fileFor2(stateDir), `${JSON.stringify(all, null, 2)}
13014
+ mkdirSync19(stateDir, { recursive: true });
13015
+ writeFileSync20(fileFor2(stateDir), `${JSON.stringify(all, null, 2)}
12947
13016
  `);
12948
13017
  }
12949
13018
  function doctorSelftestLines(stored, toolchains) {
@@ -12955,7 +13024,7 @@ function doctorSelftestLines(stored, toolchains) {
12955
13024
  }
12956
13025
 
12957
13026
  // src/cli.ts
12958
- import { resolve as resolve7, dirname as dirname32, join as join52, basename as basename16 } from "path";
13027
+ import { resolve as resolve7, dirname as dirname32, join as join53, basename as basename16 } from "path";
12959
13028
  import { fileURLToPath } from "url";
12960
13029
  import { hostname as hostname4, homedir as homedir11 } from "os";
12961
13030
  import { spawnSync as spawnSync11 } from "child_process";
@@ -13152,14 +13221,14 @@ function rosterMarkdown(roster, meId) {
13152
13221
  }
13153
13222
 
13154
13223
  // src/connect-resume.ts
13155
- import { mkdirSync as mkdirSync19, readFileSync as readFileSync26, rmSync as rmSync16, writeFileSync as writeFileSync20 } from "fs";
13156
- import { dirname as dirname14, join as join31 } from "path";
13224
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync27, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13225
+ import { dirname as dirname14, join as join32 } from "path";
13157
13226
  function lastSessionFile(stateDir) {
13158
- return join31(stateDir, "connect-session.json");
13227
+ return join32(stateDir, "connect-session.json");
13159
13228
  }
13160
13229
  function readPointers(stateDir) {
13161
13230
  try {
13162
- const parsed = JSON.parse(readFileSync26(lastSessionFile(stateDir), "utf8"));
13231
+ const parsed = JSON.parse(readFileSync27(lastSessionFile(stateDir), "utf8"));
13163
13232
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
13164
13233
  const out = {};
13165
13234
  for (const [host, v] of Object.entries(parsed)) {
@@ -13173,8 +13242,8 @@ function readPointers(stateDir) {
13173
13242
  }
13174
13243
  function writePointers(stateDir, pointers) {
13175
13244
  const file = lastSessionFile(stateDir);
13176
- mkdirSync19(dirname14(file), { recursive: true });
13177
- writeFileSync20(file, `${JSON.stringify(pointers, null, 2)}
13245
+ mkdirSync20(dirname14(file), { recursive: true });
13246
+ writeFileSync21(file, `${JSON.stringify(pointers, null, 2)}
13178
13247
  `, { mode: 384 });
13179
13248
  }
13180
13249
  function rememberSession(stateDir, host, workspaceSlug) {
@@ -13259,15 +13328,15 @@ async function signOutSessions(o) {
13259
13328
  }
13260
13329
 
13261
13330
  // src/reset.ts
13262
- import { existsSync as existsSync29, mkdirSync as mkdirSync20, readFileSync as readFileSync27, readdirSync as readdirSync12, rmSync as rmSync17, statSync as statSync10, writeFileSync as writeFileSync21 } from "fs";
13263
- import { dirname as dirname15, join as join32 } from "path";
13331
+ import { existsSync as existsSync30, mkdirSync as mkdirSync21, readFileSync as readFileSync28, readdirSync as readdirSync12, rmSync as rmSync17, statSync as statSync10, writeFileSync as writeFileSync22 } from "fs";
13332
+ import { dirname as dirname15, join as join33 } from "path";
13264
13333
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13265
13334
  function clonedCheckoutsFile(stateDir) {
13266
- return join32(stateDir, "cloned-checkouts.json");
13335
+ return join33(stateDir, "cloned-checkouts.json");
13267
13336
  }
13268
13337
  function readClonedCheckouts(stateDir) {
13269
13338
  try {
13270
- const parsed = JSON.parse(readFileSync27(clonedCheckoutsFile(stateDir), "utf8"));
13339
+ const parsed = JSON.parse(readFileSync28(clonedCheckoutsFile(stateDir), "utf8"));
13271
13340
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
13272
13341
  } catch {
13273
13342
  return [];
@@ -13277,8 +13346,8 @@ function recordClonedCheckout(stateDir, dir) {
13277
13346
  const known = readClonedCheckouts(stateDir);
13278
13347
  if (known.includes(dir)) return;
13279
13348
  const file = clonedCheckoutsFile(stateDir);
13280
- mkdirSync20(dirname15(file), { recursive: true });
13281
- writeFileSync21(file, `${JSON.stringify([...known, dir], null, 2)}
13349
+ mkdirSync21(dirname15(file), { recursive: true });
13350
+ writeFileSync22(file, `${JSON.stringify([...known, dir], null, 2)}
13282
13351
  `);
13283
13352
  }
13284
13353
  var mark = (dryRun) => dryRun ? "would-remove" : "removed";
@@ -13332,7 +13401,7 @@ async function runReset(o, fx) {
13332
13401
  }
13333
13402
  if (o.beforeState) steps.push(...await o.beforeState());
13334
13403
  steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
13335
- if (existsSync29(o.configFile)) {
13404
+ if (existsSync30(o.configFile)) {
13336
13405
  if (!o.dryRun) rmSync17(o.configFile, { force: true });
13337
13406
  steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
13338
13407
  } else {
@@ -13341,7 +13410,7 @@ async function runReset(o, fx) {
13341
13410
  return steps;
13342
13411
  }
13343
13412
  function removeCheckout(dir, dryRun, fx) {
13344
- if (!existsSync29(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13413
+ if (!existsSync30(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13345
13414
  const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
13346
13415
  if (dirty.length) {
13347
13416
  return { step: "checkout", target: dir, action: "kept", detail: `has uncommitted changes (${dirty.length} path${dirty.length === 1 ? "" : "s"}: ${dirty.slice(0, 5).join(", ")}${dirty.length > 5 ? ", \u2026" : ""})` };
@@ -13354,15 +13423,15 @@ function removeCheckout(dir, dryRun, fx) {
13354
13423
  }
13355
13424
  function removeState(stateDir, dryRun) {
13356
13425
  const out = [];
13357
- const targets = [join32(stateDir, "resolved"), join32(stateDir, "keys"), clonedCheckoutsFile(stateDir), join32(stateDir, "connect-session.json"), join32(stateDir, "reload-requested"), join32(stateDir, "daemon.started")];
13426
+ const targets = [join33(stateDir, "resolved"), join33(stateDir, "keys"), clonedCheckoutsFile(stateDir), join33(stateDir, "connect-session.json"), join33(stateDir, "reload-requested"), join33(stateDir, "daemon.started")];
13358
13427
  try {
13359
13428
  for (const name of readdirSync12(stateDir)) {
13360
- if (name.startsWith(".") && statSync10(join32(stateDir, name)).isFile()) targets.push(join32(stateDir, name));
13429
+ if (name.startsWith(".") && statSync10(join33(stateDir, name)).isFile()) targets.push(join33(stateDir, name));
13361
13430
  }
13362
13431
  } catch {
13363
13432
  }
13364
13433
  for (const t of targets) {
13365
- if (!existsSync29(t)) continue;
13434
+ if (!existsSync30(t)) continue;
13366
13435
  if (!dryRun) rmSync17(t, { recursive: true, force: true });
13367
13436
  out.push({ step: "state", target: t, action: mark(dryRun) });
13368
13437
  }
@@ -13377,7 +13446,7 @@ function wipeStateDir(stateDir, dryRun) {
13377
13446
  return [{ step: "state", target: stateDir, action: "absent", detail: "no state directory" }];
13378
13447
  }
13379
13448
  if (!names.length) return [{ step: "state", target: stateDir, action: "absent", detail: "already empty" }];
13380
- if (!dryRun) for (const n of names) rmSync17(join32(stateDir, n), { recursive: true, force: true });
13449
+ if (!dryRun) for (const n of names) rmSync17(join33(stateDir, n), { recursive: true, force: true });
13381
13450
  return [{ step: "state", target: stateDir, action: mark(dryRun), detail: `${names.length} entr${names.length === 1 ? "y" : "ies"}` }];
13382
13451
  }
13383
13452
  function describeResetStep(s) {
@@ -13458,9 +13527,9 @@ async function runDeprovision(o, fx, deps) {
13458
13527
  }
13459
13528
 
13460
13529
  // src/pool-member.ts
13461
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync28, renameSync as renameSync4, writeFileSync as writeFileSync22 } from "fs";
13530
+ import { mkdirSync as mkdirSync22, readFileSync as readFileSync29, renameSync as renameSync4, writeFileSync as writeFileSync23 } from "fs";
13462
13531
  import { homedir as homedir8 } from "os";
13463
- import { dirname as dirname16, join as join33 } from "path";
13532
+ import { dirname as dirname16, join as join34 } from "path";
13464
13533
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
13465
13534
  function backoffMs(attempt) {
13466
13535
  return Math.min(6e4, 5e3 * 2 ** Math.max(0, attempt));
@@ -13691,22 +13760,22 @@ async function runPoolMember(deps) {
13691
13760
  }
13692
13761
  }
13693
13762
  function defaultMemberDir(env = process.env) {
13694
- return env.CREW_POOL_DIR ?? join33(env.XDG_STATE_HOME ?? join33(homedir8(), ".local", "state"), "crew-pool-member");
13763
+ return env.CREW_POOL_DIR ?? join34(env.XDG_STATE_HOME ?? join34(homedir8(), ".local", "state"), "crew-pool-member");
13695
13764
  }
13696
13765
  function fileMemberStore(dir) {
13697
- const file = join33(dir, "member.json");
13766
+ const file = join34(dir, "member.json");
13698
13767
  return {
13699
13768
  read() {
13700
13769
  try {
13701
- return JSON.parse(readFileSync28(file, "utf8"));
13770
+ return JSON.parse(readFileSync29(file, "utf8"));
13702
13771
  } catch {
13703
13772
  return {};
13704
13773
  }
13705
13774
  },
13706
13775
  write(f) {
13707
- mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
13776
+ mkdirSync22(dirname16(file), { recursive: true, mode: 448 });
13708
13777
  const tmp = `${file}.tmp`;
13709
- writeFileSync22(tmp, `${JSON.stringify(f)}
13778
+ writeFileSync23(tmp, `${JSON.stringify(f)}
13710
13779
  `, { mode: 384 });
13711
13780
  renameSync4(tmp, file);
13712
13781
  }
@@ -13758,16 +13827,16 @@ function hostArchitecture(arch = process.arch) {
13758
13827
 
13759
13828
  // src/pool-member-host.ts
13760
13829
  import "child_process";
13761
- import { existsSync as existsSync33 } from "fs";
13830
+ import { existsSync as existsSync34 } from "fs";
13762
13831
  import { hostname as hostname3 } from "os";
13763
13832
 
13764
13833
  // src/machine-id.ts
13765
13834
  import { execFileSync as execFileSync10 } from "child_process";
13766
13835
  import { createHash as createHash7 } from "crypto";
13767
- import { existsSync as existsSync30, mkdirSync as mkdirSync22, readFileSync as readFileSync29, writeFileSync as writeFileSync23 } from "fs";
13768
- import { dirname as dirname17, join as join34 } from "path";
13836
+ import { existsSync as existsSync31, mkdirSync as mkdirSync23, readFileSync as readFileSync30, writeFileSync as writeFileSync24 } from "fs";
13837
+ import { dirname as dirname17, join as join35 } from "path";
13769
13838
  function machineIdPathFor(stateDir) {
13770
- return join34(stateDir, "machine-id");
13839
+ return join35(stateDir, "machine-id");
13771
13840
  }
13772
13841
  var sha256 = (s) => createHash7("sha256").update(s).digest("hex");
13773
13842
  function readRawMachineId(platform2 = process.platform) {
@@ -13780,8 +13849,8 @@ function readRawMachineId(platform2 = process.platform) {
13780
13849
  return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
13781
13850
  }
13782
13851
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
13783
- if (existsSync30(p)) {
13784
- const id = readFileSync29(p, "utf8").trim();
13852
+ if (existsSync31(p)) {
13853
+ const id = readFileSync30(p, "utf8").trim();
13785
13854
  if (id) return id;
13786
13855
  }
13787
13856
  }
@@ -13792,15 +13861,15 @@ function readRawMachineId(platform2 = process.platform) {
13792
13861
  function ensureMachineId(stateDir, opts = {}) {
13793
13862
  if (opts.override?.trim()) return sha256(opts.override.trim());
13794
13863
  const path = machineIdPathFor(stateDir);
13795
- if (existsSync30(path)) {
13796
- const cached = readFileSync29(path, "utf8").trim();
13864
+ if (existsSync31(path)) {
13865
+ const cached = readFileSync30(path, "utf8").trim();
13797
13866
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
13798
13867
  }
13799
13868
  const raw = (opts.read ?? readRawMachineId)();
13800
13869
  if (!raw) return void 0;
13801
13870
  const hashed = sha256(raw);
13802
- mkdirSync22(dirname17(path), { recursive: true });
13803
- writeFileSync23(path, `${hashed}
13871
+ mkdirSync23(dirname17(path), { recursive: true });
13872
+ writeFileSync24(path, `${hashed}
13804
13873
  `, { mode: 384 });
13805
13874
  return hashed;
13806
13875
  }
@@ -13813,7 +13882,7 @@ function matchShipRows(ships, shipName2, machineId) {
13813
13882
 
13814
13883
  // src/provision.ts
13815
13884
  import { spawnSync as spawnSync8 } from "child_process";
13816
- import { existsSync as existsSync31, readFileSync as readFileSync30, writeFileSync as writeFileSync24 } from "fs";
13885
+ import { existsSync as existsSync32, readFileSync as readFileSync31, writeFileSync as writeFileSync25 } from "fs";
13817
13886
  import { hostname as hostname2 } from "os";
13818
13887
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
13819
13888
 
@@ -13822,7 +13891,7 @@ import { parseDocument as parseDocument2 } from "yaml";
13822
13891
 
13823
13892
  // src/repos-cmd.ts
13824
13893
  import { homedir as homedir9 } from "os";
13825
- import { basename as basename10, join as join35, resolve as resolve4 } from "path";
13894
+ import { basename as basename10, join as join36, resolve as resolve4 } from "path";
13826
13895
  import { isMap, isSeq, parseDocument, stringify as stringify2 } from "yaml";
13827
13896
  var ReposError = class extends Error {
13828
13897
  };
@@ -14100,7 +14169,7 @@ function planRepoClone(i) {
14100
14169
  }
14101
14170
  const ship = doc.get("ship", true);
14102
14171
  const basePath = String(m.get("reposBasePath") ?? (isMap(ship) ? ship.get("reposBasePath") : void 0) ?? "~/Crew");
14103
- const target = i.dir ? expandDir(i.dir, i.base) : join35(expandDir(basePath, i.base), i.route.split("/")[0], name);
14172
+ const target = i.dir ? expandDir(i.dir, i.base) : join36(expandDir(basePath, i.base), i.route.split("/")[0], name);
14104
14173
  const add = planRepoAdd({ text: i.text, base: i.base, route: i.route, path: target, origin: remote, resolved: i.resolved, name, isCheckout: i.isCheckout });
14105
14174
  const forgeHost = i.resolved?.repoForgeHosts?.[id];
14106
14175
  return { ...add, remote, target, ...forgeHost ? { forgeHost } : {} };
@@ -14407,7 +14476,7 @@ function realProvisionDeps(crewHome) {
14407
14476
  };
14408
14477
  const configFile = () => {
14409
14478
  const f = findConfigFile(crewHome);
14410
- return existsSync31(f) ? f : null;
14479
+ return existsSync32(f) ? f : null;
14411
14480
  };
14412
14481
  return {
14413
14482
  crew: spawnCrew,
@@ -14415,13 +14484,13 @@ function realProvisionDeps(crewHome) {
14415
14484
  enableRoute(route2) {
14416
14485
  const file = configFile();
14417
14486
  if (!file) return false;
14418
- const text2 = readFileSync30(file, "utf8");
14487
+ const text2 = readFileSync31(file, "utf8");
14419
14488
  const doc = parseDocument3(text2);
14420
14489
  const routes = doc.get("routes");
14421
14490
  const entry = routes?.items?.find((r) => r.get("route") === route2);
14422
14491
  if (!entry) throw new Error(`route ${route2} is not in ${file}`);
14423
14492
  if (entry.get("enabled") === true) return false;
14424
- writeFileSync24(file, planEnable(text2, route2));
14493
+ writeFileSync25(file, planEnable(text2, route2));
14425
14494
  return true;
14426
14495
  },
14427
14496
  installState() {
@@ -14441,14 +14510,14 @@ function realProvisionDeps(crewHome) {
14441
14510
  continue;
14442
14511
  }
14443
14512
  for (const p of plan.unitPaths) {
14444
- if (!existsSync31(p) || readFileSync30(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14513
+ if (!existsSync32(p) || readFileSync31(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14445
14514
  }
14446
14515
  }
14447
14516
  const label = labelFor(crewHome, "run");
14448
14517
  let running = false;
14449
14518
  let lingering = true;
14450
14519
  if (mechanism === "systemd") {
14451
- const system = ctx.root || existsSync31(systemUnitPathFor(label));
14520
+ const system = ctx.root || existsSync32(systemUnitPathFor(label));
14452
14521
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
14453
14522
  running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
14454
14523
  if (!system) {
@@ -14465,18 +14534,18 @@ function realProvisionDeps(crewHome) {
14465
14534
  setShipSetting(key, value2) {
14466
14535
  const file = configFile();
14467
14536
  if (!file) return false;
14468
- const doc = parseDocument3(readFileSync30(file, "utf8"));
14537
+ const doc = parseDocument3(readFileSync31(file, "utf8"));
14469
14538
  if (doc.getIn(["ship", key]) === value2) return false;
14470
14539
  doc.setIn(["ship", key], value2);
14471
- writeFileSync24(file, doc.toString());
14540
+ writeFileSync25(file, doc.toString());
14472
14541
  return true;
14473
14542
  },
14474
14543
  applyManagedDefaults() {
14475
14544
  const file = configFile();
14476
14545
  if (!file) return [];
14477
- const doc = parseDocument3(readFileSync30(file, "utf8"));
14546
+ const doc = parseDocument3(readFileSync31(file, "utf8"));
14478
14547
  const wrote = applyManagedDefaultsToDoc(doc);
14479
- if (wrote.length) writeFileSync24(file, doc.toString());
14548
+ if (wrote.length) writeFileSync25(file, doc.toString());
14480
14549
  return wrote;
14481
14550
  },
14482
14551
  storedToken(host) {
@@ -14493,9 +14562,9 @@ host=${host}
14493
14562
 
14494
14563
  // src/tunnel.ts
14495
14564
  import { spawn as spawn7 } from "child_process";
14496
- import { existsSync as existsSync32, mkdirSync as mkdirSync23, readFileSync as readFileSync31, writeFileSync as writeFileSync25, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14565
+ import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync26, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14497
14566
  import { randomInt } from "crypto";
14498
- import { join as join36 } from "path";
14567
+ import { join as join37 } from "path";
14499
14568
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
14500
14569
  var defaultSpawn = (cmd, args) => spawn7(cmd, args, DEFAULT_SPAWN_OPTIONS);
14501
14570
  var SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -14567,21 +14636,21 @@ function startTunnel(opts) {
14567
14636
  };
14568
14637
  }
14569
14638
  function tunnelStatePath(stateDir, workspaceId) {
14570
- return join36(stateDir, "passengers", `${workspaceId}.json`);
14639
+ return join37(stateDir, "passengers", `${workspaceId}.json`);
14571
14640
  }
14572
14641
  function readPersistedTunnel(stateDir, workspaceId) {
14573
14642
  const path = tunnelStatePath(stateDir, workspaceId);
14574
- if (!existsSync32(path)) return void 0;
14643
+ if (!existsSync33(path)) return void 0;
14575
14644
  try {
14576
- return JSON.parse(readFileSync31(path, "utf8"));
14645
+ return JSON.parse(readFileSync32(path, "utf8"));
14577
14646
  } catch {
14578
14647
  return void 0;
14579
14648
  }
14580
14649
  }
14581
14650
  function writePersistedTunnel(stateDir, state) {
14582
14651
  const path = tunnelStatePath(stateDir, state.workspaceId);
14583
- mkdirSync23(join36(stateDir, "passengers"), { recursive: true });
14584
- writeFileSync25(path, `${JSON.stringify(state, null, 2)}
14652
+ mkdirSync24(join37(stateDir, "passengers"), { recursive: true });
14653
+ writeFileSync26(path, `${JSON.stringify(state, null, 2)}
14585
14654
  `);
14586
14655
  }
14587
14656
  function updatePersistedTunnel(stateDir, workspaceId, patch2) {
@@ -14683,8 +14752,8 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
14683
14752
  }
14684
14753
  }
14685
14754
  function readAllPersistedWorkspaceIds(stateDir) {
14686
- const dir = join36(stateDir, "passengers");
14687
- if (!existsSync32(dir)) return [];
14755
+ const dir = join37(stateDir, "passengers");
14756
+ if (!existsSync33(dir)) return [];
14688
14757
  return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
14689
14758
  }
14690
14759
 
@@ -14709,7 +14778,7 @@ function realPoolMemberDeps(o) {
14709
14778
  crewVersion: crewVersion(o.crewHome),
14710
14779
  hostname: hostname3()
14711
14780
  },
14712
- hasShipConfig: () => existsSync33(findConfigFile(o.crewHome)),
14781
+ hasShipConfig: () => existsSync34(findConfigFile(o.crewHome)),
14713
14782
  async provision(lease, instanceClass, shipKind) {
14714
14783
  const hosts = lease.forges.map((f) => f.host.toLowerCase());
14715
14784
  const lines2 = [];
@@ -15585,8 +15654,8 @@ ${waiting.map((t) => `| ${displayKey2(t)} | ${t.status} | ${blockers(t, i)} |`).
15585
15654
  }
15586
15655
 
15587
15656
  // src/poll.ts
15588
- import { writeFileSync as writeFileSync26 } from "fs";
15589
- import { join as join37 } from "path";
15657
+ import { writeFileSync as writeFileSync27 } from "fs";
15658
+ import { join as join38 } from "path";
15590
15659
 
15591
15660
  // src/stalled.ts
15592
15661
  var STALLED_AFTER_CYCLES = 30;
@@ -15893,7 +15962,7 @@ async function decideCycle(o) {
15893
15962
  }
15894
15963
  return decision;
15895
15964
  }
15896
- var digestPath = (stateDir, route2, role, runId) => join37(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
15965
+ var digestPath = (stateDir, route2, role, runId) => join38(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
15897
15966
  function otherRunsRemoved(tickets, own) {
15898
15967
  if (!own) return tickets;
15899
15968
  return tickets.filter((t) => t.id === own.ticket.id || !own.inFlight(t.id));
@@ -15971,7 +16040,7 @@ function writeDigest(o, d, role, stateDir, runId, own) {
15971
16040
 
15972
16041
  ${body}` : body;
15973
16042
  const path = digestPath(stateDir, routeSlug(o.route.route), role, runId);
15974
- writeFileSync26(path, text2);
16043
+ writeFileSync27(path, text2);
15975
16044
  o.emit.emit(`queue digest written for ${role} (${Buffer.byteLength(text2)} bytes)`, {
15976
16045
  data: { role, bytes: Buffer.byteLength(text2) }
15977
16046
  });
@@ -16079,7 +16148,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16079
16148
  }
16080
16149
 
16081
16150
  // src/repo-refresh.ts
16082
- import { existsSync as existsSync34, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync27 } from "fs";
16151
+ import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync33, writeFileSync as writeFileSync28 } from "fs";
16083
16152
  import { dirname as dirname19 } from "path";
16084
16153
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16085
16154
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16109,11 +16178,11 @@ function applyRepoRows(resolved, rows) {
16109
16178
  }
16110
16179
  function persistRepoRows(stateDir, routeName, rows) {
16111
16180
  const path = resolvedPathFor(stateDir, routeName);
16112
- if (!existsSync34(path)) return;
16113
- const raw = JSON.parse(readFileSync32(path, "utf8"));
16181
+ if (!existsSync35(path)) return;
16182
+ const raw = JSON.parse(readFileSync33(path, "utf8"));
16114
16183
  applyRepoRows(raw, rows);
16115
- mkdirSync24(dirname19(path), { recursive: true });
16116
- writeFileSync27(path, `${JSON.stringify(raw, null, 2)}
16184
+ mkdirSync25(dirname19(path), { recursive: true });
16185
+ writeFileSync28(path, `${JSON.stringify(raw, null, 2)}
16117
16186
  `);
16118
16187
  }
16119
16188
  async function refreshUnknownRepos(o) {
@@ -16563,13 +16632,13 @@ Ship ${shipName2} found: ${problem}. The release refuses to squash a ref nobody
16563
16632
  var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.record_id === ticket.id && e.event_type === "local_ref_skip" && (e.message ?? "").includes(problem));
16564
16633
 
16565
16634
  // src/connect-wizard.ts
16566
- import { delimiter, dirname as dirname20, join as join38 } from "path";
16567
- import { accessSync as accessSync3, constants as constants3, existsSync as existsSync35, mkdirSync as mkdirSync25, writeFileSync as writeFileSync28 } from "fs";
16635
+ import { delimiter, dirname as dirname20, join as join39 } from "path";
16636
+ import { accessSync as accessSync3, constants as constants3, existsSync as existsSync36, mkdirSync as mkdirSync26, writeFileSync as writeFileSync29 } from "fs";
16568
16637
  import { stringify as stringify3 } from "yaml";
16569
16638
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
16570
16639
  function shouldRunWizard(opts) {
16571
16640
  if (!opts.isTTY) return false;
16572
- const exists = opts.configExists ?? existsSync35;
16641
+ const exists = opts.configExists ?? existsSync36;
16573
16642
  return !configSearchPath(opts.crewHome).some((p) => exists(p));
16574
16643
  }
16575
16644
  function firstRunConfigPath() {
@@ -16586,7 +16655,7 @@ function defaultIsExecutable(p) {
16586
16655
  function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
16587
16656
  for (const dir of pathEnv.split(delimiter)) {
16588
16657
  if (!dir) continue;
16589
- const candidate = join38(dir, name);
16658
+ const candidate = join39(dir, name);
16590
16659
  if (isExecutable(candidate)) return candidate;
16591
16660
  }
16592
16661
  return void 0;
@@ -16647,9 +16716,9 @@ routes:
16647
16716
  ${routeBlock}`;
16648
16717
  }
16649
16718
  function writeNewConfig(path, text2) {
16650
- mkdirSync25(dirname20(path), { recursive: true });
16719
+ mkdirSync26(dirname20(path), { recursive: true });
16651
16720
  try {
16652
- writeFileSync28(path, text2, { mode: 384, flag: "wx" });
16721
+ writeFileSync29(path, text2, { mode: 384, flag: "wx" });
16653
16722
  return true;
16654
16723
  } catch (e) {
16655
16724
  if (e.code === "EEXIST") return false;
@@ -16683,7 +16752,7 @@ Wrote ${o.configPath}.
16683
16752
  // src/connect-json.ts
16684
16753
  import { accessSync as accessSync4, constants as constants4 } from "fs";
16685
16754
  import { homedir as homedir10 } from "os";
16686
- import { dirname as dirname21, join as join39 } from "path";
16755
+ import { dirname as dirname21, join as join40 } from "path";
16687
16756
  import { delimiter as delimiter2 } from "path";
16688
16757
  var CHOICE_EXIT_CODE = 3;
16689
16758
  function createEmitter(json2, write) {
@@ -16722,12 +16791,12 @@ function agentBinSearchDirs(home, pathEnv, extraPath) {
16722
16791
  const dirs = [
16723
16792
  ...pathEnv.split(delimiter2),
16724
16793
  ...(extraPath ?? "").split(delimiter2),
16725
- join39(home, ".local/bin"),
16726
- join39(home, ".claude/local"),
16794
+ join40(home, ".local/bin"),
16795
+ join40(home, ".claude/local"),
16727
16796
  "/opt/homebrew/bin",
16728
16797
  "/usr/local/bin",
16729
- join39(home, ".npm-global/bin"),
16730
- join39(home, ".volta/bin")
16798
+ join40(home, ".npm-global/bin"),
16799
+ join40(home, ".volta/bin")
16731
16800
  ];
16732
16801
  return [...new Set(dirs.filter(Boolean))];
16733
16802
  }
@@ -16763,8 +16832,8 @@ function flagWizardAnswers(f, env) {
16763
16832
  }
16764
16833
 
16765
16834
  // src/release-run.ts
16766
- import { readFileSync as readFileSync33, writeFileSync as writeFileSync29, existsSync as existsSync36, copyFileSync, mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, rmSync as rmSync18 } from "fs";
16767
- import { dirname as dirname22, join as join40 } from "path";
16835
+ import { readFileSync as readFileSync34, writeFileSync as writeFileSync30, existsSync as existsSync37, copyFileSync, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync3, rmSync as rmSync18 } from "fs";
16836
+ import { dirname as dirname22, join as join41 } from "path";
16768
16837
  import { tmpdir as tmpdir5 } from "os";
16769
16838
 
16770
16839
  // src/release.ts
@@ -17505,13 +17574,13 @@ async function bump(o, size) {
17505
17574
  }
17506
17575
  return printed;
17507
17576
  }
17508
- const files = o.repo.release.versionFiles.map((f) => join40(o.cwd, f));
17577
+ const files = o.repo.release.versionFiles.map((f) => join41(o.cwd, f));
17509
17578
  const first = files[0];
17510
- if (!existsSync36(first)) {
17579
+ if (!existsSync37(first)) {
17511
17580
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
17512
17581
  return null;
17513
17582
  }
17514
- const current = JSON.parse(readFileSync33(first, "utf8")).version;
17583
+ const current = JSON.parse(readFileSync34(first, "utf8")).version;
17515
17584
  if (!current) {
17516
17585
  o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
17517
17586
  return null;
@@ -17524,27 +17593,27 @@ async function bump(o, size) {
17524
17593
  return version;
17525
17594
  }
17526
17595
  for (const f of files) {
17527
- const src = readFileSync33(f, "utf8");
17596
+ const src = readFileSync34(f, "utf8");
17528
17597
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
17529
17598
  if (out === src) {
17530
17599
  o.emit.error(`no version field in ${f}`);
17531
17600
  return null;
17532
17601
  }
17533
- writeFileSync29(f, out);
17602
+ writeFileSync30(f, out);
17534
17603
  }
17535
17604
  return version;
17536
17605
  }
17537
17606
  function writeChangelog(o, version, entries) {
17538
17607
  const file = o.repo.release.changelog;
17539
17608
  if (!file || entries.length === 0) return;
17540
- const path = join40(o.cwd, file);
17609
+ const path = join41(o.cwd, file);
17541
17610
  const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
17542
17611
  if (o.dryRun) {
17543
17612
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
17544
17613
  return;
17545
17614
  }
17546
- const existing = existsSync36(path) ? readFileSync33(path, "utf8") : "# Changelog\n";
17547
- writeFileSync29(path, insertChangelogSection(existing, section));
17615
+ const existing = existsSync37(path) ? readFileSync34(path, "utf8") : "# Changelog\n";
17616
+ writeFileSync30(path, insertChangelogSection(existing, section));
17548
17617
  }
17549
17618
  async function confirm(o, expected) {
17550
17619
  if (!o.repo.hooks.released) return void 0;
@@ -17637,8 +17706,8 @@ function closeReleaseCheckout(cwd, root, dir, branch, run3 = gitOk) {
17637
17706
  function openReleaseCheckout(o) {
17638
17707
  const { remote, base } = o.repo.branch;
17639
17708
  const branch = `crew/release-${Date.now()}`;
17640
- const root = mkdtempSync3(join40(tmpdir5(), "crew-release-"));
17641
- const dir = join40(root, "checkout");
17709
+ const root = mkdtempSync3(join41(tmpdir5(), "crew-release-"));
17710
+ const dir = join41(root, "checkout");
17642
17711
  o.emit.emit(`release checkout: ${dir}`, { step: "release" });
17643
17712
  const close = () => closeReleaseCheckout(o.cwd, root, dir, branch);
17644
17713
  try {
@@ -17648,10 +17717,10 @@ function openReleaseCheckout(o) {
17648
17717
  throw e;
17649
17718
  }
17650
17719
  for (const f of o.repo.worktrees.copy) {
17651
- const src = join40(o.cwd, f);
17652
- if (!existsSync36(src)) continue;
17653
- mkdirSync26(dirname22(join40(dir, f)), { recursive: true });
17654
- copyFileSync(src, join40(dir, f));
17720
+ const src = join41(o.cwd, f);
17721
+ if (!existsSync37(src)) continue;
17722
+ mkdirSync27(dirname22(join41(dir, f)), { recursive: true });
17723
+ copyFileSync(src, join41(dir, f));
17655
17724
  }
17656
17725
  return { dir, branch, close };
17657
17726
  }
@@ -18673,7 +18742,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
18673
18742
  }
18674
18743
 
18675
18744
  // src/watch.ts
18676
- import { watch as watchFile, existsSync as existsSync37, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
18745
+ import { watch as watchFile, existsSync as existsSync38, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
18677
18746
  var E = "\x1B[";
18678
18747
  var dim = (s) => `${E}2m${s}${E}0m`;
18679
18748
  var bold = (s) => `${E}1m${s}${E}0m`;
@@ -18774,7 +18843,7 @@ function renderHeaders(states, now = Date.now()) {
18774
18843
  return states.map((s) => renderHeader(s, now)).join("");
18775
18844
  }
18776
18845
  function readFrom(file, from) {
18777
- if (!existsSync37(file)) return { events: [], offset: from };
18846
+ if (!existsSync38(file)) return { events: [], offset: from };
18778
18847
  const size = statSync11(file).size;
18779
18848
  if (size < from) return readFrom(file, 0);
18780
18849
  if (size === from) return { events: [], offset: from };
@@ -19017,7 +19086,7 @@ function nextRoles(decision, ran) {
19017
19086
  }
19018
19087
 
19019
19088
  // src/ship-name.ts
19020
- import { existsSync as existsSync38, mkdirSync as mkdirSync27, readFileSync as readFileSync34, renameSync as renameSync5, writeFileSync as writeFileSync30 } from "fs";
19089
+ import { existsSync as existsSync39, mkdirSync as mkdirSync28, readFileSync as readFileSync35, renameSync as renameSync5, writeFileSync as writeFileSync31 } from "fs";
19021
19090
  import { dirname as dirname23 } from "path";
19022
19091
  import { parseDocument as parseDocument5 } from "yaml";
19023
19092
  var ShipNameError = class extends Error {
@@ -19079,34 +19148,34 @@ async function syncRouteName(i) {
19079
19148
  return { status: "ok", ...base, action, name: i.local };
19080
19149
  }
19081
19150
  function readSyncedName(resolvedFile) {
19082
- if (!existsSync38(resolvedFile)) return void 0;
19151
+ if (!existsSync39(resolvedFile)) return void 0;
19083
19152
  try {
19084
- const v = JSON.parse(readFileSync34(resolvedFile, "utf8")).shipName;
19153
+ const v = JSON.parse(readFileSync35(resolvedFile, "utf8")).shipName;
19085
19154
  return typeof v === "string" ? v : void 0;
19086
19155
  } catch {
19087
19156
  return void 0;
19088
19157
  }
19089
19158
  }
19090
19159
  function writeSyncedName(resolvedFile, name) {
19091
- if (!existsSync38(resolvedFile)) return;
19160
+ if (!existsSync39(resolvedFile)) return;
19092
19161
  let raw;
19093
19162
  try {
19094
- raw = JSON.parse(readFileSync34(resolvedFile, "utf8"));
19163
+ raw = JSON.parse(readFileSync35(resolvedFile, "utf8"));
19095
19164
  } catch {
19096
19165
  return;
19097
19166
  }
19098
19167
  if (raw.shipName === name) return;
19099
19168
  raw.shipName = name;
19100
- mkdirSync27(dirname23(resolvedFile), { recursive: true });
19169
+ mkdirSync28(dirname23(resolvedFile), { recursive: true });
19101
19170
  const tmp = `${resolvedFile}.tmp${process.pid}`;
19102
- writeFileSync30(tmp, `${JSON.stringify(raw, null, 2)}
19171
+ writeFileSync31(tmp, `${JSON.stringify(raw, null, 2)}
19103
19172
  `);
19104
19173
  renameSync5(tmp, resolvedFile);
19105
19174
  }
19106
19175
  async function reconcileShipName(i) {
19107
19176
  let local = i.ship.name;
19108
19177
  try {
19109
- local = shipNameOfYaml(readFileSync34(i.configFile, "utf8")) ?? local;
19178
+ local = shipNameOfYaml(readFileSync35(i.configFile, "utf8")) ?? local;
19110
19179
  } catch {
19111
19180
  }
19112
19181
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -19130,8 +19199,8 @@ async function reconcileShipName(i) {
19130
19199
  }
19131
19200
  if (result.status !== "ok") return result;
19132
19201
  if (result.action === "adopt" || result.action === "row-wins") {
19133
- const text2 = readFileSync34(i.configFile, "utf8");
19134
- writeFileSync30(i.configFile, setShipNameInYaml(text2, result.name));
19202
+ const text2 = readFileSync35(i.configFile, "utf8");
19203
+ writeFileSync31(i.configFile, setShipNameInYaml(text2, result.name));
19135
19204
  i.ship.name = result.name;
19136
19205
  if (result.action === "row-wins") {
19137
19206
  i.emit?.warn(
@@ -19790,7 +19859,7 @@ function describeCapability(i) {
19790
19859
  }
19791
19860
 
19792
19861
  // src/agents-sync-run.ts
19793
- import { existsSync as existsSync39, mkdirSync as mkdirSync28, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
19862
+ import { existsSync as existsSync40, mkdirSync as mkdirSync29, readFileSync as readFileSync36, writeFileSync as writeFileSync32 } from "fs";
19794
19863
  import { dirname as dirname24 } from "path";
19795
19864
  async function runAgentsSync(target, o) {
19796
19865
  const dryRun = o.dryRun ?? false;
@@ -19824,12 +19893,12 @@ async function runAgentsSync(target, o) {
19824
19893
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
19825
19894
  if (!dryRun) {
19826
19895
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
19827
- const raw = existsSync39(resolvedPath) ? JSON.parse(readFileSync35(resolvedPath, "utf8")) : {};
19896
+ const raw = existsSync40(resolvedPath) ? JSON.parse(readFileSync36(resolvedPath, "utf8")) : {};
19828
19897
  raw.agentPersonas = personaResult.agentPersonas;
19829
19898
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
19830
19899
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
19831
- mkdirSync28(dirname24(resolvedPath), { recursive: true });
19832
- writeFileSync31(resolvedPath, `${JSON.stringify(raw, null, 2)}
19900
+ mkdirSync29(dirname24(resolvedPath), { recursive: true });
19901
+ writeFileSync32(resolvedPath, `${JSON.stringify(raw, null, 2)}
19833
19902
  `);
19834
19903
  if (target.resolved) {
19835
19904
  target.resolved.agentPersonas = personaResult.agentPersonas;
@@ -19946,7 +20015,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
19946
20015
 
19947
20016
  // src/repos-check.ts
19948
20017
  import { spawnSync as spawnSync10 } from "child_process";
19949
- import { readFileSync as readFileSync36 } from "fs";
20018
+ import { readFileSync as readFileSync37 } from "fs";
19950
20019
  import { basename as basename11 } from "path";
19951
20020
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
19952
20021
  function checkHookSyntax(script, shell) {
@@ -19987,7 +20056,7 @@ function checkRepoSpec(dir, opts = {}) {
19987
20056
  }
19988
20057
  let parsed;
19989
20058
  try {
19990
- parsed = parseRepoConfig(readFileSync36(file, "utf8"), file);
20059
+ parsed = parseRepoConfig(readFileSync37(file, "utf8"), file);
19991
20060
  } catch (e) {
19992
20061
  if (!(e instanceof RepoConfigError)) throw e;
19993
20062
  return { ...empty, file, error: e.message, exitCode: 1 };
@@ -20444,7 +20513,7 @@ ${run3.err}` });
20444
20513
  }
20445
20514
 
20446
20515
  // src/sample-ticket.ts
20447
- import { basename as basename12, dirname as dirname25, join as join41 } from "path";
20516
+ import { basename as basename12, dirname as dirname25, join as join42 } from "path";
20448
20517
  var SAMPLE_REPO = "crew-sample";
20449
20518
  var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
20450
20519
  var SAMPLE_MARKER = "sample: true";
@@ -20467,8 +20536,8 @@ has something safe to work on: its test and build hooks always pass, it has no
20467
20536
  remote, and \`crew sample-ticket --remove\` deletes it.
20468
20537
  `;
20469
20538
  function sampleDir(existing, reposBase, workspace) {
20470
- if (existing.length > 0) return join41(dirname25(existing[0]), SAMPLE_REPO);
20471
- return join41(reposBase, workspace, SAMPLE_REPO);
20539
+ if (existing.length > 0) return join42(dirname25(existing[0]), SAMPLE_REPO);
20540
+ return join42(reposBase, workspace, SAMPLE_REPO);
20472
20541
  }
20473
20542
  var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
20474
20543
  function sampleDescription() {
@@ -20512,8 +20581,8 @@ function planSampleTicket(i) {
20512
20581
  }
20513
20582
 
20514
20583
  // src/worktree-sweep.ts
20515
- import { existsSync as existsSync40, readFileSync as readFileSync37, readdirSync as readdirSync14 } from "fs";
20516
- import { basename as basename13, dirname as dirname26, join as join42, resolve as resolve5 } from "path";
20584
+ import { existsSync as existsSync41, readFileSync as readFileSync38, readdirSync as readdirSync14 } from "fs";
20585
+ import { basename as basename13, dirname as dirname26, join as join43, resolve as resolve5 } from "path";
20517
20586
  function planWorktreeSweep(target, tickets, contract) {
20518
20587
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
20519
20588
  const verified = contract.statuses.verified;
@@ -20590,7 +20659,7 @@ function siblingWorktreeDirs(target) {
20590
20659
  for (const name of names) {
20591
20660
  if (!name.startsWith(stem)) continue;
20592
20661
  const m = DIR_NUMBER_SUFFIXED.exec(name);
20593
- const path = join42(parent, name);
20662
+ const path = join43(parent, name);
20594
20663
  if (m && !registered.has(resolve5(path))) out.push({ path, number: m[1] });
20595
20664
  }
20596
20665
  return out;
@@ -20625,7 +20694,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20625
20694
  let removed = 0;
20626
20695
  let keptBranches = 0;
20627
20696
  for (const a of actions) {
20628
- if (!dryRun && base && !a.keepBranch && existsSync40(a.path) && isHandBackMerge(a.path, base)) {
20697
+ if (!dryRun && base && !a.keepBranch && existsSync41(a.path) && isHandBackMerge(a.path, base)) {
20629
20698
  if (gitOk(a.path, ["merge", "--abort"]) !== null) {
20630
20699
  log.emit(
20631
20700
  `abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
@@ -20633,7 +20702,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20633
20702
  );
20634
20703
  }
20635
20704
  }
20636
- if (!dryRun && existsSync40(a.path)) {
20705
+ if (!dryRun && existsSync41(a.path)) {
20637
20706
  let dirty = false;
20638
20707
  try {
20639
20708
  dirty = status(a.path).length > 0;
@@ -20644,7 +20713,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20644
20713
  continue;
20645
20714
  }
20646
20715
  }
20647
- if (!dryRun && a.requirePushed && a.branch && existsSync40(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20716
+ if (!dryRun && a.requirePushed && a.branch && existsSync41(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20648
20717
  refuse2(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
20649
20718
  continue;
20650
20719
  }
@@ -20693,7 +20762,7 @@ function isHandBackMerge(path, base) {
20693
20762
  if (!msgPath) return false;
20694
20763
  let first;
20695
20764
  try {
20696
- first = readFileSync37(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
20765
+ first = readFileSync38(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
20697
20766
  } catch {
20698
20767
  return false;
20699
20768
  }
@@ -20798,14 +20867,14 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
20798
20867
  }
20799
20868
 
20800
20869
  // src/stream-sweep.ts
20801
- import { existsSync as existsSync41, readdirSync as readdirSync15, rmSync as rmSync19, statSync as statSync12 } from "fs";
20802
- import { join as join43 } from "path";
20870
+ import { existsSync as existsSync42, readdirSync as readdirSync15, rmSync as rmSync19, statSync as statSync12 } from "fs";
20871
+ import { join as join44 } from "path";
20803
20872
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
20804
20873
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
20805
- const dir = join43(stateDir, "streams");
20806
- if (!existsSync41(dir)) return [];
20874
+ const dir = join44(stateDir, "streams");
20875
+ if (!existsSync42(dir)) return [];
20807
20876
  const cutoff = now - retentionDays * DAY_MS2;
20808
- return readdirSync15(dir).map((name) => join43(dir, name)).filter((path) => {
20877
+ return readdirSync15(dir).map((name) => join44(dir, name)).filter((path) => {
20809
20878
  try {
20810
20879
  return statSync12(path).mtimeMs < cutoff;
20811
20880
  } catch {
@@ -20831,9 +20900,9 @@ function applyStreamSweep(paths, dryRun, log) {
20831
20900
  return removed;
20832
20901
  }
20833
20902
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
20834
- if (!existsSync41(stateDir)) return [];
20903
+ if (!existsSync42(stateDir)) return [];
20835
20904
  const cutoff = now - retentionDays * DAY_MS2;
20836
- return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join43(stateDir, name)).filter((path) => {
20905
+ return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join44(stateDir, name)).filter((path) => {
20837
20906
  try {
20838
20907
  return statSync12(path).mtimeMs < cutoff;
20839
20908
  } catch {
@@ -20843,12 +20912,12 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
20843
20912
  }
20844
20913
 
20845
20914
  // src/cli.ts
20846
- import { realpathSync as realpathSync3, readFileSync as readFileSync43, existsSync as existsSync48, mkdirSync as mkdirSync36, writeFileSync as writeFileSync36, rmSync as rmSync24, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
20915
+ import { realpathSync as realpathSync3, readFileSync as readFileSync44, existsSync as existsSync49, mkdirSync as mkdirSync37, writeFileSync as writeFileSync37, rmSync as rmSync24, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
20847
20916
  import { createInterface } from "readline/promises";
20848
20917
 
20849
20918
  // src/periscope-cmd.ts
20850
- import { existsSync as existsSync42, mkdirSync as mkdirSync29, readFileSync as readFileSync38, rmSync as rmSync20, writeFileSync as writeFileSync32 } from "fs";
20851
- import { join as join44 } from "path";
20919
+ import { existsSync as existsSync43, mkdirSync as mkdirSync30, readFileSync as readFileSync39, rmSync as rmSync20, writeFileSync as writeFileSync33 } from "fs";
20920
+ import { join as join45 } from "path";
20852
20921
  var PeriscopeError = class extends Error {
20853
20922
  };
20854
20923
  var LivePeriscopeError = class extends PeriscopeError {
@@ -20858,22 +20927,22 @@ var LivePeriscopeError = class extends PeriscopeError {
20858
20927
  this.data = { refused: "live_periscope_exists", ...live };
20859
20928
  }
20860
20929
  };
20861
- var priorDir = (stateDir) => join44(stateDir, "periscope-priors");
20862
- var priorPath = (stateDir, ticketRecordId) => join44(priorDir(stateDir), `${ticketRecordId}.json`);
20930
+ var priorDir = (stateDir) => join45(stateDir, "periscope-priors");
20931
+ var priorPath = (stateDir, ticketRecordId) => join45(priorDir(stateDir), `${ticketRecordId}.json`);
20863
20932
  function removePeriscopePrior(stateDir, ticketRecordId) {
20864
20933
  rmSync20(priorPath(stateDir, ticketRecordId), { force: true });
20865
20934
  }
20866
20935
  function writePeriscopePrior(stateDir, ticketRecordId, prior) {
20867
20936
  try {
20868
- mkdirSync29(priorDir(stateDir), { recursive: true });
20869
- writeFileSync32(priorPath(stateDir, ticketRecordId), JSON.stringify(prior));
20937
+ mkdirSync30(priorDir(stateDir), { recursive: true });
20938
+ writeFileSync33(priorPath(stateDir, ticketRecordId), JSON.stringify(prior));
20870
20939
  } catch {
20871
20940
  }
20872
20941
  }
20873
20942
  function readPeriscopePrior(stateDir, ticketRecordId) {
20874
20943
  try {
20875
20944
  const p = priorPath(stateDir, ticketRecordId);
20876
- return existsSync42(p) ? JSON.parse(readFileSync38(p, "utf8")) : void 0;
20945
+ return existsSync43(p) ? JSON.parse(readFileSync39(p, "utf8")) : void 0;
20877
20946
  } catch {
20878
20947
  return void 0;
20879
20948
  }
@@ -21062,7 +21131,7 @@ ${tail(preview.output)}`);
21062
21131
  d.say("proxy", `proxy at ${url} in front of ${previewUrl}`, { url, previewUrl });
21063
21132
  }
21064
21133
  d.say("preview", `preview at ${url}`, { url });
21065
- const intel = await probeIntel(
21134
+ const intel = d.lspPluginDir ? { state: "absent" } : await probeIntel(
21066
21135
  { runHook: (e) => d.runHook("intel", wt.path, e) },
21067
21136
  env,
21068
21137
  d.intelStartupSeconds ?? DEFAULT_INTEL_STARTUP_SECONDS2
@@ -21078,7 +21147,7 @@ ${tail(preview.output)}`);
21078
21147
  d.say("warning", `symbol service not registered: ${e.message}`);
21079
21148
  }
21080
21149
  }
21081
- const codeTools = codeToolsParagraph(codeOutcome);
21150
+ const codeTools = d.lspPluginDir ? `${lspToolsParagraph("claude")} Start the Claude session with \`--plugin-dir ${d.lspPluginDir}\` to load it.` : codeToolsParagraph(codeOutcome);
21082
21151
  const now = d.now();
21083
21152
  let row;
21084
21153
  try {
@@ -21327,7 +21396,7 @@ ${tail2(preview.output)}`);
21327
21396
  // src/periscope-idle.ts
21328
21397
  import { execFileSync as execFileSync12 } from "child_process";
21329
21398
  import { statSync as statSync13 } from "fs";
21330
- import { join as join45 } from "path";
21399
+ import { join as join46 } from "path";
21331
21400
  var MAX_SCANNED_FILES = 2e4;
21332
21401
  function newestWorktreeMtime(root) {
21333
21402
  const a = newestTrackedMtime(root);
@@ -21352,7 +21421,7 @@ function newestTrackedMtime(root) {
21352
21421
  if (!rel) continue;
21353
21422
  if (++scanned > MAX_SCANNED_FILES) break;
21354
21423
  try {
21355
- const m = statSync13(join45(root, rel)).mtimeMs;
21424
+ const m = statSync13(join46(root, rel)).mtimeMs;
21356
21425
  if (newest === null || m > newest) newest = m;
21357
21426
  } catch {
21358
21427
  }
@@ -21428,18 +21497,18 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
21428
21497
 
21429
21498
  // src/ssh-keys.ts
21430
21499
  import { execFileSync as execFileSync13 } from "child_process";
21431
- import { existsSync as existsSync43, mkdirSync as mkdirSync30, readFileSync as readFileSync39, chmodSync as chmodSync3 } from "fs";
21432
- import { dirname as dirname27, join as join46 } from "path";
21500
+ import { existsSync as existsSync44, mkdirSync as mkdirSync31, readFileSync as readFileSync40, chmodSync as chmodSync3 } from "fs";
21501
+ import { dirname as dirname27, join as join47 } from "path";
21433
21502
  var SshKeyError = class extends Error {
21434
21503
  };
21435
21504
  function shipSshKeyPathFor(stateDir) {
21436
- return join46(stateDir, "ssh", "id_ed25519");
21505
+ return join47(stateDir, "ssh", "id_ed25519");
21437
21506
  }
21438
21507
  function ensureShipSshKeypair(stateDir) {
21439
21508
  const privateKeyPath = shipSshKeyPathFor(stateDir);
21440
21509
  const publicKeyPath = `${privateKeyPath}.pub`;
21441
- if (!existsSync43(privateKeyPath)) {
21442
- mkdirSync30(dirname27(privateKeyPath), { recursive: true });
21510
+ if (!existsSync44(privateKeyPath)) {
21511
+ mkdirSync31(dirname27(privateKeyPath), { recursive: true });
21443
21512
  try {
21444
21513
  execFileSync13("ssh-keygen", [
21445
21514
  "-t",
@@ -21459,10 +21528,10 @@ function ensureShipSshKeypair(stateDir) {
21459
21528
  }
21460
21529
  chmodSync3(privateKeyPath, 384);
21461
21530
  }
21462
- if (!existsSync43(publicKeyPath)) {
21531
+ if (!existsSync44(publicKeyPath)) {
21463
21532
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
21464
21533
  }
21465
- const publicKey = readFileSync39(publicKeyPath, "utf8").trim();
21534
+ const publicKey = readFileSync40(publicKeyPath, "utf8").trim();
21466
21535
  return { privateKeyPath, publicKeyPath, publicKey };
21467
21536
  }
21468
21537
  function sshKeygenAvailable() {
@@ -21626,11 +21695,11 @@ async function unregisterEndpoint(workspaceId, info, d) {
21626
21695
  import { createServer as createHttpServer } from "http";
21627
21696
  import { spawn as spawn9 } from "child_process";
21628
21697
  import { randomBytes } from "crypto";
21629
- import { existsSync as existsSync44, mkdirSync as mkdirSync32, readFileSync as readFileSync40, writeFileSync as writeFileSync33, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21630
- import { join as join47 } from "path";
21698
+ import { existsSync as existsSync45, mkdirSync as mkdirSync33, readFileSync as readFileSync41, writeFileSync as writeFileSync34, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21699
+ import { join as join48 } from "path";
21631
21700
 
21632
21701
  // src/passenger-mirror.ts
21633
- import { mkdirSync as mkdirSync31 } from "fs";
21702
+ import { mkdirSync as mkdirSync32 } from "fs";
21634
21703
  import { dirname as dirname28 } from "path";
21635
21704
  function mirrorRepoOf(spec) {
21636
21705
  const fromOrigin = spec.originUrl ? parseRemote(spec.originUrl) : null;
@@ -21650,8 +21719,8 @@ async function materializeMirrorCheckouts(specs, cacheFor = (s) => new SandboxCa
21650
21719
  }
21651
21720
  try {
21652
21721
  const parent = dirname28(spec.dir);
21653
- mkdirSync31(parent, { recursive: true });
21654
- mkdirSync31(spec.dir, { recursive: true });
21722
+ mkdirSync32(parent, { recursive: true });
21723
+ mkdirSync32(spec.dir, { recursive: true });
21655
21724
  const r = await cacheFor(spec).materialize(repo, spec.base, parent, spec.name);
21656
21725
  out.push({ ...base, action: r.changed ? "updated" : "unchanged", detail: r.tip });
21657
21726
  } catch (e) {
@@ -21757,21 +21826,21 @@ var defaultSpawn2 = (cmd, args, env) => {
21757
21826
  return child;
21758
21827
  };
21759
21828
  function syncDaemonStatePath(stateDir, workspaceId) {
21760
- return join47(stateDir, "passengers", `${workspaceId}-sync.json`);
21829
+ return join48(stateDir, "passengers", `${workspaceId}-sync.json`);
21761
21830
  }
21762
21831
  function readPersistedSyncDaemon(stateDir, workspaceId) {
21763
21832
  const path = syncDaemonStatePath(stateDir, workspaceId);
21764
- if (!existsSync44(path)) return void 0;
21833
+ if (!existsSync45(path)) return void 0;
21765
21834
  try {
21766
- return JSON.parse(readFileSync40(path, "utf8"));
21835
+ return JSON.parse(readFileSync41(path, "utf8"));
21767
21836
  } catch {
21768
21837
  return void 0;
21769
21838
  }
21770
21839
  }
21771
21840
  function writePersistedSyncDaemon(stateDir, state) {
21772
21841
  const path = syncDaemonStatePath(stateDir, state.workspaceId);
21773
- mkdirSync32(join47(stateDir, "passengers"), { recursive: true });
21774
- writeFileSync33(path, `${JSON.stringify(state, null, 2)}
21842
+ mkdirSync33(join48(stateDir, "passengers"), { recursive: true });
21843
+ writeFileSync34(path, `${JSON.stringify(state, null, 2)}
21775
21844
  `);
21776
21845
  }
21777
21846
  function removePersistedSyncDaemon(stateDir, workspaceId) {
@@ -21791,8 +21860,8 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
21791
21860
  }
21792
21861
  }
21793
21862
  function readAllPersistedSyncWorkspaceIds(stateDir) {
21794
- const dir = join47(stateDir, "passengers");
21795
- if (!existsSync44(dir)) return [];
21863
+ const dir = join48(stateDir, "passengers");
21864
+ if (!existsSync45(dir)) return [];
21796
21865
  const suffix = "-sync.json";
21797
21866
  return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
21798
21867
  }
@@ -21850,7 +21919,7 @@ function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
21850
21919
 
21851
21920
  // src/periscope-proxy.ts
21852
21921
  import { spawn as spawn10 } from "child_process";
21853
- import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync33 } from "fs";
21922
+ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync34 } from "fs";
21854
21923
  import { createServer, request as httpRequest } from "http";
21855
21924
  import { request as httpsRequest } from "https";
21856
21925
  import { connect as netConnect } from "net";
@@ -21969,7 +22038,7 @@ function parsePointedBody(raw) {
21969
22038
  return entry;
21970
22039
  }
21971
22040
  function appendPointed(file, entry, now = /* @__PURE__ */ new Date()) {
21972
- mkdirSync33(dirname29(file), { recursive: true });
22041
+ mkdirSync34(dirname29(file), { recursive: true });
21973
22042
  appendFileSync3(file, `${JSON.stringify({ ...entry, at: now.toISOString() })}
21974
22043
  `);
21975
22044
  }
@@ -22262,8 +22331,8 @@ function describeCause(err3) {
22262
22331
  }
22263
22332
 
22264
22333
  // src/daemon.ts
22265
- import { existsSync as existsSync45, mkdirSync as mkdirSync34, readFileSync as readFileSync41, readdirSync as readdirSync17, rmSync as rmSync21, statSync as statSync14, writeFileSync as writeFileSync34 } from "fs";
22266
- import { join as join48 } from "path";
22334
+ import { existsSync as existsSync46, mkdirSync as mkdirSync35, readFileSync as readFileSync42, readdirSync as readdirSync17, rmSync as rmSync21, statSync as statSync14, writeFileSync as writeFileSync35 } from "fs";
22335
+ import { join as join49 } from "path";
22267
22336
  import { randomBytes as randomBytes2 } from "crypto";
22268
22337
  var DEFAULT_FLOOR_MS = 5e3;
22269
22338
  var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
@@ -22351,7 +22420,7 @@ function latestMtime(path) {
22351
22420
  if (!stat2.isDirectory()) return 0;
22352
22421
  let latest = stat2.mtimeMs;
22353
22422
  for (const entry of readdirSync17(path, { withFileTypes: true })) {
22354
- latest = Math.max(latest, latestMtime(join48(path, entry.name)));
22423
+ latest = Math.max(latest, latestMtime(join49(path, entry.name)));
22355
22424
  }
22356
22425
  return latest;
22357
22426
  }
@@ -22360,10 +22429,10 @@ function computeTreeSignature(paths) {
22360
22429
  }
22361
22430
  function makeStaleChecker(paths, opts = {}) {
22362
22431
  const initial = computeTreeSignature(paths);
22363
- return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync45(opts.reloadSentinel);
22432
+ return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync46(opts.reloadSentinel);
22364
22433
  }
22365
22434
  function reloadSentinelFor(stateDir) {
22366
- return join48(stateDir, "reload-requested");
22435
+ return join49(stateDir, "reload-requested");
22367
22436
  }
22368
22437
  function readReloadRequestedAt(stateDir) {
22369
22438
  try {
@@ -22373,17 +22442,17 @@ function readReloadRequestedAt(stateDir) {
22373
22442
  }
22374
22443
  }
22375
22444
  function daemonStampFor(stateDir) {
22376
- return join48(stateDir, "daemon.started");
22445
+ return join49(stateDir, "daemon.started");
22377
22446
  }
22378
22447
  function noteDaemonStarted(stateDir, now = Date.now()) {
22379
- mkdirSync34(stateDir, { recursive: true });
22448
+ mkdirSync35(stateDir, { recursive: true });
22380
22449
  rmSync21(reloadSentinelFor(stateDir), { force: true });
22381
- writeFileSync34(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
22450
+ writeFileSync35(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
22382
22451
  `);
22383
22452
  }
22384
22453
  function readDaemonStarted(stateDir) {
22385
22454
  try {
22386
- const v = JSON.parse(readFileSync41(daemonStampFor(stateDir), "utf8")).startedAt;
22455
+ const v = JSON.parse(readFileSync42(daemonStampFor(stateDir), "utf8")).startedAt;
22387
22456
  return typeof v === "number" ? v : null;
22388
22457
  } catch {
22389
22458
  return null;
@@ -22949,7 +23018,7 @@ import { createHash as createHash10 } from "crypto";
22949
23018
  import {
22950
23019
  chmodSync as chmodSync4,
22951
23020
  closeSync as closeSync6,
22952
- existsSync as existsSync46,
23021
+ existsSync as existsSync47,
22953
23022
  fstatSync,
22954
23023
  openSync as openSync6,
22955
23024
  readSync as readSync3,
@@ -22960,7 +23029,7 @@ import {
22960
23029
  } from "fs";
22961
23030
  import { createConnection } from "net";
22962
23031
  import { createServer as createServer2 } from "http";
22963
- import { basename as basename14, dirname as dirname30, join as join49 } from "path";
23032
+ import { basename as basename14, dirname as dirname30, join as join50 } from "path";
22964
23033
  import { execFile as execFile3, spawn as spawn12 } from "child_process";
22965
23034
 
22966
23035
  // src/periscope-control.ts
@@ -23189,7 +23258,7 @@ function runCrew(args, onLine, timeoutMs = 20 * 6e4) {
23189
23258
  // src/control-api.ts
23190
23259
  var PAUSABLE = ["dev", "design", "qa"];
23191
23260
  var CONTROL_SOCKET_NAME = "control.sock";
23192
- var controlSocketFor = (stateDir) => join49(stateDir, CONTROL_SOCKET_NAME);
23261
+ var controlSocketFor = (stateDir) => join50(stateDir, CONTROL_SOCKET_NAME);
23193
23262
  var MAX_SOCKET_PATH = 100;
23194
23263
  var MAX_TAIL_BYTES = 1024 * 1024;
23195
23264
  var MAX_WAIT_SECONDS = 30;
@@ -23276,7 +23345,7 @@ function candidates(live, rotated) {
23276
23345
  const out = [live];
23277
23346
  if (!rotated) return out;
23278
23347
  try {
23279
- for (const n of readdirSync18(dirname30(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join49(dirname30(live), n));
23348
+ for (const n of readdirSync18(dirname30(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join50(dirname30(live), n));
23280
23349
  } catch {
23281
23350
  }
23282
23351
  return out;
@@ -23320,7 +23389,7 @@ function lastLines2(path, n) {
23320
23389
  }
23321
23390
  function readTail(live, opts) {
23322
23391
  const max = opts.maxBytes ?? MAX_TAIL_BYTES;
23323
- if (!existsSync46(live)) {
23392
+ if (!existsSync47(live)) {
23324
23393
  return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
23325
23394
  }
23326
23395
  const cur = opts.after ? decodeCursor(opts.after) : null;
@@ -23476,8 +23545,8 @@ async function route(deps, method, url, req, abort) {
23476
23545
  if (p.startsWith("/streams/") && get2) {
23477
23546
  const run3 = decodeURIComponent(p.slice("/streams/".length));
23478
23547
  if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
23479
- const file = join49(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23480
- if (!existsSync46(file)) return err2(404, "no such run");
23548
+ const file = join50(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23549
+ if (!existsSync47(file)) return err2(404, "no such run");
23481
23550
  return tailReply(file, url.searchParams, false, abort);
23482
23551
  }
23483
23552
  if ((p === "/pause" || p === "/resume") && post3) {
@@ -23521,7 +23590,7 @@ function send(res, r) {
23521
23590
  res.end(body);
23522
23591
  }
23523
23592
  async function socketIsStale(path) {
23524
- if (!existsSync46(path)) return false;
23593
+ if (!existsSync47(path)) return false;
23525
23594
  return new Promise((resolve8) => {
23526
23595
  const c = createConnection(path);
23527
23596
  c.once("connect", () => {
@@ -23540,7 +23609,7 @@ async function startControlServer(deps) {
23540
23609
  return null;
23541
23610
  }
23542
23611
  if (await socketIsStale(path)) rmSync22(path, { force: true });
23543
- else if (existsSync46(path)) {
23612
+ else if (existsSync47(path)) {
23544
23613
  log(`control API not started: ${path} is in use by another process`);
23545
23614
  return null;
23546
23615
  }
@@ -23626,31 +23695,31 @@ function crewJson(args, timeoutMs = 6e4) {
23626
23695
 
23627
23696
  // src/periscope-mcp.ts
23628
23697
  import { lstat, mkdir, readdir, readFile, readlink, realpath, rm, stat, writeFile } from "fs/promises";
23629
- import { basename as basename15, dirname as dirname31, isAbsolute as isAbsolute5, join as join51, relative as relative2, resolve as resolve6, sep } from "path";
23698
+ import { basename as basename15, dirname as dirname31, isAbsolute as isAbsolute5, join as join52, relative as relative2, resolve as resolve6, sep } from "path";
23630
23699
  import { z } from "zod";
23631
23700
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23632
23701
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
23633
23702
 
23634
23703
  // src/periscope-endpoints.ts
23635
23704
  import { randomBytes as randomBytes3 } from "crypto";
23636
- import { chmodSync as chmodSync5, existsSync as existsSync47, mkdirSync as mkdirSync35, readdirSync as readdirSync19, readFileSync as readFileSync42, rmSync as rmSync23, writeFileSync as writeFileSync35 } from "fs";
23637
- import { join as join50 } from "path";
23638
- var secretDir = (stateDir) => join50(stateDir, "periscope-mcp");
23639
- var secretFile = (stateDir, id) => join50(secretDir(stateDir), `${id}.json`);
23640
- var endpointDir = (stateDir) => join50(stateDir, "periscope-endpoints");
23641
- var endpointFile = (stateDir, id) => join50(endpointDir(stateDir), `${id}.json`);
23705
+ import { chmodSync as chmodSync5, existsSync as existsSync48, mkdirSync as mkdirSync36, readdirSync as readdirSync19, readFileSync as readFileSync43, rmSync as rmSync23, writeFileSync as writeFileSync36 } from "fs";
23706
+ import { join as join51 } from "path";
23707
+ var secretDir = (stateDir) => join51(stateDir, "periscope-mcp");
23708
+ var secretFile = (stateDir, id) => join51(secretDir(stateDir), `${id}.json`);
23709
+ var endpointDir = (stateDir) => join51(stateDir, "periscope-endpoints");
23710
+ var endpointFile = (stateDir, id) => join51(endpointDir(stateDir), `${id}.json`);
23642
23711
  function mintPeriscopeSecret(stateDir, ticketRecordId) {
23643
- mkdirSync35(secretDir(stateDir), { recursive: true, mode: 448 });
23712
+ mkdirSync36(secretDir(stateDir), { recursive: true, mode: 448 });
23644
23713
  const secret = randomBytes3(32).toString("base64url");
23645
23714
  const file = secretFile(stateDir, ticketRecordId);
23646
- writeFileSync35(file, `${JSON.stringify({ secret })}
23715
+ writeFileSync36(file, `${JSON.stringify({ secret })}
23647
23716
  `, { mode: 384 });
23648
23717
  chmodSync5(file, 384);
23649
23718
  return secret;
23650
23719
  }
23651
23720
  function readPeriscopeSecret(stateDir, ticketRecordId) {
23652
23721
  try {
23653
- const v = JSON.parse(readFileSync42(secretFile(stateDir, ticketRecordId), "utf8"));
23722
+ const v = JSON.parse(readFileSync43(secretFile(stateDir, ticketRecordId), "utf8"));
23654
23723
  return typeof v.secret === "string" && v.secret ? v.secret : void 0;
23655
23724
  } catch {
23656
23725
  return void 0;
@@ -23661,14 +23730,14 @@ function removePeriscopeSecret(stateDir, ticketRecordId) {
23661
23730
  }
23662
23731
  function readPeriscopeEndpoint(stateDir, ticketRecordId) {
23663
23732
  try {
23664
- return JSON.parse(readFileSync42(endpointFile(stateDir, ticketRecordId), "utf8"));
23733
+ return JSON.parse(readFileSync43(endpointFile(stateDir, ticketRecordId), "utf8"));
23665
23734
  } catch {
23666
23735
  return void 0;
23667
23736
  }
23668
23737
  }
23669
23738
  function writePeriscopeEndpoint(stateDir, rec) {
23670
- mkdirSync35(endpointDir(stateDir), { recursive: true });
23671
- writeFileSync35(endpointFile(stateDir, rec.ticketRecordId), `${JSON.stringify(rec, null, 2)}
23739
+ mkdirSync36(endpointDir(stateDir), { recursive: true });
23740
+ writeFileSync36(endpointFile(stateDir, rec.ticketRecordId), `${JSON.stringify(rec, null, 2)}
23672
23741
  `);
23673
23742
  }
23674
23743
  function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
@@ -23676,7 +23745,7 @@ function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
23676
23745
  }
23677
23746
  function listPeriscopeEndpoints(stateDir) {
23678
23747
  const dir = endpointDir(stateDir);
23679
- if (!existsSync47(dir)) return [];
23748
+ if (!existsSync48(dir)) return [];
23680
23749
  const out = [];
23681
23750
  for (const name of readdirSync19(dir)) {
23682
23751
  if (!name.endsWith(".json")) continue;
@@ -23779,13 +23848,13 @@ async function resolveInWorktree(root, requested) {
23779
23848
  let abs;
23780
23849
  if (isAbsolute5(requested)) {
23781
23850
  abs = resolve6(requested);
23782
- if (isInside(root, abs)) abs = join51(realRoot, relative2(root, abs));
23851
+ if (isInside(root, abs)) abs = join52(realRoot, relative2(root, abs));
23783
23852
  } else {
23784
23853
  abs = resolve6(realRoot, requested);
23785
23854
  }
23786
23855
  if (!isInside(realRoot, abs)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
23787
23856
  const { real, rest } = await realAncestor(abs);
23788
- const final = rest ? join51(real, rest) : real;
23857
+ const final = rest ? join52(real, rest) : real;
23789
23858
  if (!isInside(realRoot, final)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
23790
23859
  if (relative2(realRoot, final).split(sep).includes(".git")) throw new PathRefusal(".git is not reachable from a periscope");
23791
23860
  return final;
@@ -23853,7 +23922,7 @@ function createPeriscopeMcpServer(d, sc) {
23853
23922
  const entries = (await readdir(abs, { withFileTypes: true })).filter((e) => e.name !== ".git");
23854
23923
  const lines2 = await Promise.all(entries.map(async (e) => {
23855
23924
  if (e.isDirectory()) return `[DIR] ${e.name}`;
23856
- const size = (await lstat(join51(abs, e.name))).size;
23925
+ const size = (await lstat(join52(abs, e.name))).size;
23857
23926
  return `[FILE] ${e.name} (${size} bytes)`;
23858
23927
  }));
23859
23928
  return text(lines2.join("\n") || "(empty)");
@@ -23919,7 +23988,7 @@ function createPeriscopeMcpServer(d, sc) {
23919
23988
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
23920
23989
  }, async ({ path }) => {
23921
23990
  const parent = await inTree(dirname31(path) === "" ? "." : dirname31(path));
23922
- const abs = join51(parent, basename15(path));
23991
+ const abs = join52(parent, basename15(path));
23923
23992
  if (basename15(path) === ".git" || abs === parent) return text("path must name a file", true);
23924
23993
  const st = await lstat(abs).catch(() => void 0);
23925
23994
  if (!st) return text(`${path} does not exist`, true);
@@ -24409,7 +24478,7 @@ if (command === "provision") {
24409
24478
  process.exit(outcome.code);
24410
24479
  }
24411
24480
  if (command === "pool-member") {
24412
- const pool = poolConfigFrom(argv, process.env, () => readFileSync43(0, "utf8"));
24481
+ const pool = poolConfigFrom(argv, process.env, () => readFileSync44(0, "utf8"));
24413
24482
  if (typeof pool === "string") {
24414
24483
  process.stderr.write(`crew pool-member: ${pool}
24415
24484
  `);
@@ -24644,7 +24713,7 @@ if (command === "passenger-sync-daemon") {
24644
24713
  } else {
24645
24714
  const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
24646
24715
  const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
24647
- if (configless && !existsSync48(findConfigFile(CREW_HOME))) {
24716
+ if (configless && !existsSync49(findConfigFile(CREW_HOME))) {
24648
24717
  cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
24649
24718
  } else {
24650
24719
  cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
@@ -24659,7 +24728,7 @@ if (command === "passenger-sync-daemon") {
24659
24728
  throw e;
24660
24729
  }
24661
24730
  if (command === "credential") {
24662
- const r = runCredential(positional[1], readFileSync43(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
24731
+ const r = runCredential(positional[1], readFileSync44(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
24663
24732
  process.stdout.write(r.out);
24664
24733
  process.stderr.write(r.err);
24665
24734
  process.exit(r.code);
@@ -24671,9 +24740,9 @@ if (command === "passenger-sync-daemon") {
24671
24740
  forges: cfg.ship.forges,
24672
24741
  stateDir: cfg.ship.stateDir,
24673
24742
  configFile: cfg.configFile,
24674
- readFile: (p) => readFileSync43(p, "utf8"),
24675
- writeFile: (p, t) => writeFileSync36(p, t),
24676
- stdin: () => readFileSync43(0, "utf8"),
24743
+ readFile: (p) => readFileSync44(p, "utf8"),
24744
+ writeFile: (p, t) => writeFileSync37(p, t),
24745
+ stdin: () => readFileSync44(0, "utf8"),
24677
24746
  env: process.env
24678
24747
  }
24679
24748
  );
@@ -24966,8 +25035,8 @@ if (command === "passenger-sync-daemon") {
24966
25035
  }
24967
25036
  if (host === "linux" && hasSystemd) {
24968
25037
  const label = labelFor(CREW_HOME, "run");
24969
- const staleTimer = join52(homedir11(), ".config", "systemd", "user", `${label}.timer`);
24970
- if (existsSync48(staleTimer)) {
25038
+ const staleTimer = join53(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25039
+ if (existsSync49(staleTimer)) {
24971
25040
  process.stdout.write(
24972
25041
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
24973
25042
  `
@@ -24994,7 +25063,7 @@ if (command === "passenger-sync-daemon") {
24994
25063
  const tracker = new Tracker(c, cfg.ship);
24995
25064
  problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
24996
25065
  for (const r of await resolvedRepos(c)) {
24997
- if (!existsSync48(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
25066
+ if (!existsSync49(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
24998
25067
  if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
24999
25068
  readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
25000
25069
  }
@@ -25021,7 +25090,7 @@ if (command === "passenger-sync-daemon") {
25021
25090
  }
25022
25091
  async function doctorFix(o) {
25023
25092
  const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
25024
- const unitExists = (p) => existsSync48(p);
25093
+ const unitExists = (p) => existsSync49(p);
25025
25094
  const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25026
25095
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
25027
25096
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
@@ -25038,7 +25107,7 @@ if (command === "passenger-sync-daemon") {
25038
25107
  for (const a of plan.actions) {
25039
25108
  if (a.kind === "enable") {
25040
25109
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
25041
- writeFileSync36(cfg.configFile, planEnable(readFileSync43(cfg.configFile, "utf8"), a.route));
25110
+ writeFileSync37(cfg.configFile, planEnable(readFileSync44(cfg.configFile, "utf8"), a.route));
25042
25111
  enabledSound.add(a.route);
25043
25112
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
25044
25113
  } else {
@@ -26177,7 +26246,7 @@ ${cu.tail.trimEnd()}
26177
26246
  process.exit(1);
26178
26247
  }
26179
26248
  requireArmed("run the daemon loop");
26180
- const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join52(CREW_HOME, "src"), join52(CREW_HOME, "dist"), join52(CREW_HOME, "bin")];
26249
+ const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join53(CREW_HOME, "src"), join53(CREW_HOME, "dist"), join53(CREW_HOME, "bin")];
26181
26250
  const configPaths = daemonConfigPaths(cfg);
26182
26251
  watchPaths.push(...configPaths);
26183
26252
  noteDaemonStarted(cfg.ship.stateDir);
@@ -26482,10 +26551,10 @@ ${cu.tail.trimEnd()}
26482
26551
  `);
26483
26552
  if (!dryRun) {
26484
26553
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
26485
- const raw = existsSync48(resolvedPath) ? JSON.parse(readFileSync43(resolvedPath, "utf8")) : {};
26554
+ const raw = existsSync49(resolvedPath) ? JSON.parse(readFileSync44(resolvedPath, "utf8")) : {};
26486
26555
  raw.agentSkills = result.agentSkills;
26487
- mkdirSync36(dirOf2(resolvedPath), { recursive: true });
26488
- writeFileSync36(resolvedPath, `${JSON.stringify(raw, null, 2)}
26556
+ mkdirSync37(dirOf2(resolvedPath), { recursive: true });
26557
+ writeFileSync37(resolvedPath, `${JSON.stringify(raw, null, 2)}
26489
26558
  `);
26490
26559
  }
26491
26560
  if (result.outcomes.some((o) => o.action === "diverged")) process.exitCode = 1;
@@ -26508,7 +26577,7 @@ ${cu.tail.trimEnd()}
26508
26577
  throw e;
26509
26578
  }
26510
26579
  const oldName = cfg.ship.name;
26511
- writeFileSync36(cfg.configFile, setShipNameInYaml(readFileSync43(cfg.configFile, "utf8"), newName));
26580
+ writeFileSync37(cfg.configFile, setShipNameInYaml(readFileSync44(cfg.configFile, "utf8"), newName));
26512
26581
  cfg.ship.name = newName;
26513
26582
  let renameMachineId;
26514
26583
  try {
@@ -26555,7 +26624,7 @@ ${cu.tail.trimEnd()}
26555
26624
  process.exit(2);
26556
26625
  };
26557
26626
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
26558
- const configText = readFileSync43(cfg.configFile, "utf8");
26627
+ const configText = readFileSync44(cfg.configFile, "utf8");
26559
26628
  if (sub === "check") {
26560
26629
  try {
26561
26630
  const routeArg = value("route") ?? positional[2];
@@ -26586,8 +26655,8 @@ ${cu.tail.trimEnd()}
26586
26655
  try {
26587
26656
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
26588
26657
  const dir = repoDirOf(configText, dirname32(cfg.configFile), a.route, a.name);
26589
- if (!existsSync48(dir)) throw new ReposError(`${dir} does not exist on disk`);
26590
- const files = { exists: (r) => existsSync48(resolvePath(dir, r)), list: (r) => {
26658
+ if (!existsSync49(dir)) throw new ReposError(`${dir} does not exist on disk`);
26659
+ const files = { exists: (r) => existsSync49(resolvePath(dir, r)), list: (r) => {
26591
26660
  try {
26592
26661
  return readdirSync20(resolvePath(dir, r));
26593
26662
  } catch {
@@ -26595,7 +26664,7 @@ ${cu.tail.trimEnd()}
26595
26664
  }
26596
26665
  }, read: (r) => {
26597
26666
  try {
26598
- return readFileSync43(resolvePath(dir, r), "utf8");
26667
+ return readFileSync44(resolvePath(dir, r), "utf8");
26599
26668
  } catch {
26600
26669
  return void 0;
26601
26670
  }
@@ -26637,19 +26706,19 @@ ${cu.tail.trimEnd()}
26637
26706
  let cleared;
26638
26707
  if (clear2) {
26639
26708
  const plan = clearRouteHooks(configText, a.route, a.name);
26640
- if (plan.removed.length > 0) writeFileSync36(cfg.configFile, plan.text);
26709
+ if (plan.removed.length > 0) writeFileSync37(cfg.configFile, plan.text);
26641
26710
  cleared = { file: cfg.configFile, removed: plan.removed };
26642
26711
  }
26643
26712
  if (write && suggestion.source === null && recipeIds.length === 0 && hookSpecs.length === 0) throw new ReposError(`nothing to write: ${suggestion.notes[0]}`);
26644
26713
  if (write === "route") {
26645
26714
  const plan = planRouteHooks(configText, a.route, a.name, picked.hooks, { replace: flag("replace") });
26646
- writeFileSync36(cfg.configFile, plan.text);
26715
+ writeFileSync37(cfg.configFile, plan.text);
26647
26716
  written = { target: "route", file: cfg.configFile, hooks: plan.written, kept: plan.kept, replaced: plan.replaced };
26648
26717
  } else if (write === "repo") {
26649
26718
  const have = existingRepoConfig(files);
26650
26719
  if (have) throw new ReposError(`${resolvePath(dir, have)} already exists; not overwriting it`);
26651
26720
  const file = resolvePath(dir, ".crew.yaml");
26652
- writeFileSync36(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
26721
+ writeFileSync37(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
26653
26722
  written = { target: "repo", file, hooks: Object.keys(picked.hooks), kept: [], replaced: [] };
26654
26723
  }
26655
26724
  if (flag("json")) {
@@ -26705,7 +26774,7 @@ ${cu.tail.trimEnd()}
26705
26774
  if (sub === "list" && !routeName) usage2();
26706
26775
  if (sub === "add") {
26707
26776
  try {
26708
- const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync48(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
26777
+ const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync49(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
26709
26778
  routeName = a.route;
26710
26779
  addPath = a.path;
26711
26780
  addName = a.name;
@@ -26735,7 +26804,7 @@ ${cu.tail.trimEnd()}
26735
26804
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
26736
26805
  if (sub === "list" || sub === "add") {
26737
26806
  const liveRoute = cfg.routes.find((r) => r.route === routeName);
26738
- if (liveRoute?.resolved?.reposModelId && existsSync48(resolvedFile) && !dryRun) {
26807
+ if (liveRoute?.resolved?.reposModelId && existsSync49(resolvedFile) && !dryRun) {
26739
26808
  try {
26740
26809
  await hydrateApiKeys([liveRoute]);
26741
26810
  persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
@@ -26744,16 +26813,16 @@ ${cu.tail.trimEnd()}
26744
26813
  }
26745
26814
  }
26746
26815
  let resolvedRemotes;
26747
- if (existsSync48(resolvedFile)) {
26816
+ if (existsSync49(resolvedFile)) {
26748
26817
  try {
26749
- resolvedRemotes = JSON.parse(readFileSync43(resolvedFile, "utf8"));
26818
+ resolvedRemotes = JSON.parse(readFileSync44(resolvedFile, "utf8"));
26750
26819
  } catch {
26751
26820
  }
26752
26821
  }
26753
26822
  const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
26754
26823
  const text2 = configText;
26755
26824
  const base = dirname32(cfg.configFile);
26756
- const isCheckout = (d) => existsSync48(`${d}/.git`);
26825
+ const isCheckout = (d) => existsSync49(`${d}/.git`);
26757
26826
  try {
26758
26827
  if (sub === "list") {
26759
26828
  if (flag("json")) {
@@ -26763,11 +26832,11 @@ ${cu.tail.trimEnd()}
26763
26832
  base,
26764
26833
  resolved: resolvedRemotes,
26765
26834
  originOf,
26766
- exists: (d) => existsSync48(d),
26835
+ exists: (d) => existsSync49(d),
26767
26836
  isCheckout,
26768
26837
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
26769
26838
  try {
26770
- return Object.keys(parseRepoConfig(readFileSync43(resolvePath(d, f), "utf8"), f).hooks).length > 0;
26839
+ return Object.keys(parseRepoConfig(readFileSync44(resolvePath(d, f), "utf8"), f).hooks).length > 0;
26771
26840
  } catch {
26772
26841
  return false;
26773
26842
  }
@@ -26784,7 +26853,7 @@ ${cu.tail.trimEnd()}
26784
26853
  for (const r of repos) {
26785
26854
  const origin2 = originOf(r.dir);
26786
26855
  const row = matchRepoName(origin2, resolvedRemotes);
26787
- const note = !existsSync48(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
26856
+ const note = !existsSync49(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
26788
26857
  process.stdout.write(`${r.name} ${r.dir} ${note}
26789
26858
  `);
26790
26859
  }
@@ -26796,9 +26865,9 @@ ${cu.tail.trimEnd()}
26796
26865
  ` : `${human}
26797
26866
  `);
26798
26867
  const plan2 = planRepoClone({ text: text2, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
26799
- if (existsSync48(plan2.target) && (existsSync48(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
26868
+ if (existsSync49(plan2.target) && (existsSync49(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
26800
26869
  throw new ReposError(
26801
- existsSync48(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
26870
+ existsSync49(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
26802
26871
  );
26803
26872
  }
26804
26873
  if (dryRun) {
@@ -26816,14 +26885,14 @@ ${cu.tail.trimEnd()}
26816
26885
  throw new ReposError(`${e.message}${authHint}
26817
26886
  crew.yaml was not changed`);
26818
26887
  }
26819
- writeFileSync36(cfg.configFile, plan2.text);
26888
+ writeFileSync37(cfg.configFile, plan2.text);
26820
26889
  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:
26821
26890
  `);
26822
26891
  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}`);
26823
26892
  break;
26824
26893
  }
26825
26894
  const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
26826
- const top = existsSync48(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
26895
+ const top = existsSync49(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
26827
26896
  if (!top) throw new ReposError(`${target} is not a git checkout`);
26828
26897
  const origin = originOf(top);
26829
26898
  const plan = planRepoAdd({
@@ -26875,15 +26944,15 @@ ${cu.tail.trimEnd()}
26875
26944
  const created = await rowTracker.createRepoRow(rowPayload);
26876
26945
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
26877
26946
  `);
26878
- if (existsSync48(resolvedFile)) {
26879
- const raw = JSON.parse(readFileSync43(resolvedFile, "utf8"));
26947
+ if (existsSync49(resolvedFile)) {
26948
+ const raw = JSON.parse(readFileSync44(resolvedFile, "utf8"));
26880
26949
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
26881
26950
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
26882
- writeFileSync36(resolvedFile, `${JSON.stringify(raw, null, 2)}
26951
+ writeFileSync37(resolvedFile, `${JSON.stringify(raw, null, 2)}
26883
26952
  `);
26884
26953
  }
26885
26954
  }
26886
- writeFileSync36(cfg.configFile, plan.text);
26955
+ writeFileSync37(cfg.configFile, plan.text);
26887
26956
  process.stdout.write(`added ${top} to ${routeName} as ${plan.mode === "dir" ? "dir" : `repos.${plan.name}`} in ${cfg.configFile}
26888
26957
  `);
26889
26958
  } catch (e) {
@@ -27254,7 +27323,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27254
27323
  process.exit(2);
27255
27324
  }
27256
27325
  if (found.shipNameAdopted && !dryRun) {
27257
- writeFileSync36(cfg.configFile, setShipNameInYaml(readFileSync43(cfg.configFile, "utf8"), found.shipNameAdopted));
27326
+ writeFileSync37(cfg.configFile, setShipNameInYaml(readFileSync44(cfg.configFile, "utf8"), found.shipNameAdopted));
27258
27327
  cfg.ship.name = found.shipNameAdopted;
27259
27328
  }
27260
27329
  if (found.shipRow) {
@@ -27297,10 +27366,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27297
27366
  }
27298
27367
  }
27299
27368
  let previous;
27300
- const hadPreviousFile = existsSync48(resolvedPath);
27369
+ const hadPreviousFile = existsSync49(resolvedPath);
27301
27370
  if (hadPreviousFile) {
27302
27371
  try {
27303
- previous = JSON.parse(readFileSync43(resolvedPath, "utf8"));
27372
+ previous = JSON.parse(readFileSync44(resolvedPath, "utf8"));
27304
27373
  } catch {
27305
27374
  }
27306
27375
  }
@@ -27430,10 +27499,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27430
27499
  process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
27431
27500
  `);
27432
27501
  } else {
27433
- mkdirSync36(dirname32(resolvedPath), { recursive: true });
27502
+ mkdirSync37(dirname32(resolvedPath), { recursive: true });
27434
27503
  if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
27435
27504
  const tmpPath = `${resolvedPath}.tmp${process.pid}`;
27436
- writeFileSync36(tmpPath, `${JSON.stringify(resolved, null, 2)}
27505
+ writeFileSync37(tmpPath, `${JSON.stringify(resolved, null, 2)}
27437
27506
  `);
27438
27507
  renameSync6(tmpPath, resolvedPath);
27439
27508
  process.stderr.write(`Resolved ids written to ${resolvedPath}
@@ -27479,8 +27548,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27479
27548
  process.stderr.write(`(dry run) would write the minted API key to ${apiKeyFile}
27480
27549
  `);
27481
27550
  } else {
27482
- mkdirSync36(dirname32(apiKeyFile), { recursive: true });
27483
- writeFileSync36(apiKeyFile, `${apiKeyVar}=${apiKey}
27551
+ mkdirSync37(dirname32(apiKeyFile), { recursive: true });
27552
+ writeFileSync37(apiKeyFile, `${apiKeyVar}=${apiKey}
27484
27553
  `, { mode: 384 });
27485
27554
  process.stderr.write(`API key written to ${apiKeyFile}
27486
27555
  `);
@@ -28361,7 +28430,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28361
28430
  const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
28362
28431
  const resumed = await runPeriscopeResume({
28363
28432
  find: async () => hit && { row: hit.row, tag: hit.tag },
28364
- worktreeExists: (p) => existsSync48(p),
28433
+ worktreeExists: (p) => existsSync49(p),
28365
28434
  prepareDropFile,
28366
28435
  startProxy: (wt, url, n) => launchProxy(crewInvocation(CREW_HOME), wt, url, Number(n), { host: hit.row.host || "localhost", inject: hit.repo.config.periscope?.inspector !== "repo" }),
28367
28436
  caps: periscopeCaps(cfg.ship),
@@ -28457,10 +28526,10 @@ ${where.join("\n")}`);
28457
28526
  },
28458
28527
  prepare: async (path) => {
28459
28528
  for (const f of repoP.config.worktrees.copy) {
28460
- const src = join52(repoP.dir, f);
28461
- if (!existsSync48(src)) continue;
28462
- mkdirSync36(dirOf2(join52(path, f)), { recursive: true });
28463
- copyFileSync2(src, join52(path, f));
28529
+ const src = join53(repoP.dir, f);
28530
+ if (!existsSync49(src)) continue;
28531
+ mkdirSync37(dirOf2(join53(path, f)), { recursive: true });
28532
+ copyFileSync2(src, join53(path, f));
28464
28533
  }
28465
28534
  const setup = repoP.config.hooks.setup;
28466
28535
  return setup ? runScript(setup, { cwd: path, shell: shellP, onLine: (l) => {
@@ -28478,7 +28547,7 @@ ${where.join("\n")}`);
28478
28547
  mainCheckout: repoP.dir
28479
28548
  });
28480
28549
  }
28481
- if (name === "intel" && intelFlavor(script)) return { code: 1, output: `intel: ${intelFlavor(script)} is not available yet` };
28550
+ if (name === "intel" && lspFlavor(script)) return null;
28482
28551
  return script ? runScript(script, { cwd, env, shell: shellP, timeoutMs: name === "preview" ? 18e4 : name === "intel" ? 6e4 : void 0 }) : null;
28483
28552
  },
28484
28553
  createRow: (fields) => trackerP.createPeriscope(fields),
@@ -28545,6 +28614,7 @@ ${where.join("\n")}`);
28545
28614
  now: () => /* @__PURE__ */ new Date(),
28546
28615
  host: a.host ?? "localhost",
28547
28616
  intelStartupSeconds: cfg.ship.intelStartupSeconds,
28617
+ lspPluginDir: lspFlavor(repoP.config.hooks.intel) ? ensureLspPlugin(cfg.ship.stateDir, lspFlavor(repoP.config.hooks.intel), repoP.dir) : void 0,
28548
28618
  say
28549
28619
  };
28550
28620
  if (dryRun) {
@@ -28769,7 +28839,7 @@ ${where.join("\n")}`);
28769
28839
  };
28770
28840
  const droppedRoutes = [];
28771
28841
  try {
28772
- const rawCfg = parseYaml2(readFileSync43(cfg.configFile, "utf8"));
28842
+ const rawCfg = parseYaml2(readFileSync44(cfg.configFile, "utf8"));
28773
28843
  for (const r of rawCfg?.routes ?? []) {
28774
28844
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
28775
28845
  try {
@@ -28822,8 +28892,8 @@ ${where.join("\n")}`);
28822
28892
  process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
28823
28893
  break;
28824
28894
  }
28825
- mkdirSync36(cfg.ship.stateDir, { recursive: true });
28826
- writeFileSync36(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
28895
+ mkdirSync37(cfg.ship.stateDir, { recursive: true });
28896
+ writeFileSync37(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
28827
28897
  `);
28828
28898
  process.stdout.write(
28829
28899
  "crew reload: requested \u2014 the daemon relaunches with the new config once its running agents end (it starts no new work meanwhile); if it is busy, `crew reload --now` forces the relaunch and interrupts them\n"
@@ -28888,7 +28958,7 @@ ${where.join("\n")}`);
28888
28958
  }
28889
28959
  case "log": {
28890
28960
  try {
28891
- const text2 = readFileSync43(cfg.ship.logFile, "utf8").split("\n");
28961
+ const text2 = readFileSync44(cfg.ship.logFile, "utf8").split("\n");
28892
28962
  process.stdout.write(`${text2.slice(-(Number(value("tail")) || 40)).join("\n")}
28893
28963
  `);
28894
28964
  } catch {
@@ -28911,7 +28981,7 @@ ${where.join("\n")}`);
28911
28981
  };
28912
28982
  if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
28913
28983
  const tracker = new Tracker(route2, cfg.ship);
28914
- const configText = readFileSync43(cfg.configFile, "utf8");
28984
+ const configText = readFileSync44(cfg.configFile, "utf8");
28915
28985
  const cfgBase = dirname32(cfg.configFile);
28916
28986
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
28917
28987
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
@@ -28940,7 +29010,7 @@ ${where.join("\n")}`);
28940
29010
  }
28941
29011
  if (configured2.some((r) => r.dir === dir)) {
28942
29012
  try {
28943
- writeFileSync36(cfg.configFile, planRepoRemove(configText, cfgBase, route2.route, dir).text);
29013
+ writeFileSync37(cfg.configFile, planRepoRemove(configText, cfgBase, route2.route, dir).text);
28944
29014
  } catch (e) {
28945
29015
  if (e instanceof ReposError) fail2(e.message);
28946
29016
  throw e;
@@ -28948,29 +29018,29 @@ ${where.join("\n")}`);
28948
29018
  }
28949
29019
  if (row) {
28950
29020
  await tracker.deleteRepoRow(row.id);
28951
- if (existsSync48(resolvedFile)) {
28952
- const raw = JSON.parse(readFileSync43(resolvedFile, "utf8"));
29021
+ if (existsSync49(resolvedFile)) {
29022
+ const raw = JSON.parse(readFileSync44(resolvedFile, "utf8"));
28953
29023
  if (raw.repoNames) delete raw.repoNames[row.id];
28954
29024
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
28955
- writeFileSync36(resolvedFile, `${JSON.stringify(raw, null, 2)}
29025
+ writeFileSync37(resolvedFile, `${JSON.stringify(raw, null, 2)}
28956
29026
  `);
28957
29027
  }
28958
29028
  }
28959
- if (existsSync48(join52(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync24(dir, { recursive: true, force: true });
29029
+ if (existsSync49(join53(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync24(dir, { recursive: true, force: true });
28960
29030
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
28961
29031
  break;
28962
29032
  }
28963
29033
  const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
28964
- const fresh = !existsSync48(join52(dir, ".git"));
28965
- if (existsSync48(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
29034
+ const fresh = !existsSync49(join53(dir, ".git"));
29035
+ if (existsSync49(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
28966
29036
  if (dryRun) {
28967
29037
  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`);
28968
29038
  break;
28969
29039
  }
28970
29040
  if (fresh) {
28971
- mkdirSync36(dir, { recursive: true });
28972
- writeFileSync36(join52(dir, "README.md"), SAMPLE_README);
28973
- writeFileSync36(join52(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
29041
+ mkdirSync37(dir, { recursive: true });
29042
+ writeFileSync37(join53(dir, "README.md"), SAMPLE_README);
29043
+ writeFileSync37(join53(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
28974
29044
  const g = (...a) => {
28975
29045
  const r = spawnSync11("git", a, { cwd: dir, encoding: "utf8" });
28976
29046
  if (r.status !== 0) fail2(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
@@ -28988,17 +29058,17 @@ ${where.join("\n")}`);
28988
29058
  spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
28989
29059
  });
28990
29060
  repoId = (await tracker.createRepoRow(payload)).id;
28991
- if (existsSync48(resolvedFile)) {
28992
- const raw = JSON.parse(readFileSync43(resolvedFile, "utf8"));
29061
+ if (existsSync49(resolvedFile)) {
29062
+ const raw = JSON.parse(readFileSync44(resolvedFile, "utf8"));
28993
29063
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
28994
29064
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
28995
- writeFileSync36(resolvedFile, `${JSON.stringify(raw, null, 2)}
29065
+ writeFileSync37(resolvedFile, `${JSON.stringify(raw, null, 2)}
28996
29066
  `);
28997
29067
  }
28998
29068
  }
28999
29069
  if (!configured2.some((r) => r.dir === dir)) {
29000
29070
  try {
29001
- writeFileSync36(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route2.route, path: dir, name: SAMPLE_REPO }).text);
29071
+ writeFileSync37(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route2.route, path: dir, name: SAMPLE_REPO }).text);
29002
29072
  } catch (e) {
29003
29073
  if (e instanceof ReposError) fail2(e.message);
29004
29074
  throw e;
@@ -29344,7 +29414,7 @@ watermark: ${state.watermark()}
29344
29414
  }
29345
29415
  for (const r of repos) {
29346
29416
  const remote = r.config.branch.remote;
29347
- if (existsSync48(r.dir)) {
29417
+ if (existsSync49(r.dir)) {
29348
29418
  pushAuth.push({
29349
29419
  route: c.route,
29350
29420
  repo: r.name,
@@ -29384,7 +29454,11 @@ watermark: ${state.watermark()}
29384
29454
  hooks: r.config.hooks,
29385
29455
  onPath: (cmd) => !!findOnPath(cmd, doctorSchedulerPath),
29386
29456
  intelSyntax: r.config.hooks.intel && !intelFlavor(r.config.hooks.intel) ? checkHookSyntax(r.config.hooks.intel, r.config.shell) : void 0,
29387
- intelFlavor: intelFlavor(r.config.hooks.intel) ? { name: intelFlavor(r.config.hooks.intel), missing: missingPrerequisites(intelFlavor(r.config.hooks.intel), (bin) => !!findOnPath(bin, doctorSchedulerPath)) } : void 0
29457
+ intelFlavor: intelFlavor(r.config.hooks.intel) ? { name: intelFlavor(r.config.hooks.intel), missing: [
29458
+ ...missingPrerequisites(intelFlavor(r.config.hooks.intel), (bin) => !!findOnPath(bin, doctorSchedulerPath)),
29459
+ // CREW-2916: an `*-lsp` flavor needs the language server in the checkout or on PATH.
29460
+ ...lspFlavor(r.config.hooks.intel) && !lspBinaryFound(lspFlavor(r.config.hooks.intel), r.dir, (bin) => !!findOnPath(bin, doctorSchedulerPath)) ? [`${LSP_SERVERS[lspFlavor(r.config.hooks.intel)].bin} (${LSP_SERVERS[lspFlavor(r.config.hooks.intel)].install})`] : []
29461
+ ] } : void 0
29388
29462
  });
29389
29463
  problems.push(...f.problems);
29390
29464
  passed += f.passed;
@@ -29435,7 +29509,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29435
29509
  }
29436
29510
  try {
29437
29511
  const evFile = eventFileFor(cfg.ship.stateDir);
29438
- const evSize = existsSync48(evFile) ? statSync16(evFile).size : 0;
29512
+ const evSize = existsSync49(evFile) ? statSync16(evFile).size : 0;
29439
29513
  process.stdout.write(
29440
29514
  evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
29441
29515
  ` : "events log: ok\n"
@@ -29446,7 +29520,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29446
29520
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run", installContext());
29447
29521
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
29448
29522
  try {
29449
- return readFileSync43(p, "utf8");
29523
+ return readFileSync44(p, "utf8");
29450
29524
  } catch {
29451
29525
  return null;
29452
29526
  }