@tablation/crew 0.114.0 → 0.115.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 +825 -735
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -45,12 +45,12 @@ function periscopeSummary(cfg) {
45
45
  if (routes.length === 0) return `off (no enabled route sets hostPeriscopes: true; ${limits})`;
46
46
  return `ON for ${routes.join(", ")} (host ${cfg.ship.previewHost ?? "NOT SET (ship.previewHost)"}, ${limits})`;
47
47
  }
48
- async function previewHostProblem(cfg, resolve7 = (h) => lookup(h)) {
48
+ async function previewHostProblem(cfg, resolve8 = (h) => lookup(h)) {
49
49
  if (periscopeRoutes(cfg).length === 0) return void 0;
50
50
  const host = cfg.ship.previewHost;
51
51
  if (!host) return "ship.previewHost is not set \u2014 a route hosts periscopes, so Passengers have no host to be sent to";
52
52
  try {
53
- await resolve7(host);
53
+ await resolve8(host);
54
54
  } catch {
55
55
  return `ship.previewHost "${host}" does not resolve from this machine`;
56
56
  }
@@ -103,15 +103,92 @@ function duplicateLivePeriscopes(rows, tagOf, shipName2, route2) {
103
103
  return out;
104
104
  }
105
105
 
106
+ // src/periscope-pointed.ts
107
+ import { execFileSync } from "child_process";
108
+ import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
109
+ import { dirname, isAbsolute, join, resolve } from "path";
110
+ var POINTED_AT_DIR = ".crew";
111
+ var POINTED_AT_REL = `${POINTED_AT_DIR}/pointed-at.jsonl`;
112
+ var pointedAtFile = (worktree) => join(worktree, POINTED_AT_REL);
113
+ function pointedAtEnv(worktree) {
114
+ return { CREW_PERISCOPE: "1", CREW_POINTED_AT_FILE: pointedAtFile(worktree) };
115
+ }
116
+ function excludeFile(worktree) {
117
+ try {
118
+ const p = execFileSync("git", ["rev-parse", "--git-path", "info/exclude"], {
119
+ cwd: worktree,
120
+ encoding: "utf8",
121
+ stdio: ["ignore", "pipe", "ignore"]
122
+ }).trim();
123
+ return p ? isAbsolute(p) ? p : resolve(worktree, p) : void 0;
124
+ } catch {
125
+ return void 0;
126
+ }
127
+ }
128
+ function prepareDropFile(worktree) {
129
+ const file = pointedAtFile(worktree);
130
+ mkdirSync(dirname(file), { recursive: true });
131
+ if (!existsSync2(file)) writeFileSync(file, "");
132
+ const ex = excludeFile(worktree);
133
+ if (ex) {
134
+ mkdirSync(dirname(ex), { recursive: true });
135
+ const have = existsSync2(ex) ? readFileSync(ex, "utf8") : "";
136
+ if (!have.split("\n").some((l) => l.trim() === `${POINTED_AT_DIR}/`)) {
137
+ appendFileSync(ex, `${have === "" || have.endsWith("\n") ? "" : "\n"}${POINTED_AT_DIR}/
138
+ `);
139
+ }
140
+ }
141
+ return file;
142
+ }
143
+ function parseLine(raw) {
144
+ let v;
145
+ try {
146
+ v = JSON.parse(raw);
147
+ } catch {
148
+ return void 0;
149
+ }
150
+ if (!v || typeof v !== "object") return void 0;
151
+ const o = v;
152
+ if (typeof o.path !== "string" || o.path === "" || typeof o.line !== "number" || !Number.isInteger(o.line) || o.line < 1) return void 0;
153
+ if (typeof o.at !== "string") return void 0;
154
+ const out = { path: o.path, line: o.line, at: o.at };
155
+ if (typeof o.column === "number") out.column = o.column;
156
+ if (typeof o.component === "string") out.component = o.component;
157
+ if (typeof o.url === "string") out.url = o.url;
158
+ return out;
159
+ }
160
+ function readPointedAt(worktree) {
161
+ let text2;
162
+ try {
163
+ text2 = readFileSync(pointedAtFile(worktree), "utf8");
164
+ } catch {
165
+ return null;
166
+ }
167
+ const lines2 = text2.split("\n");
168
+ for (let i = lines2.length - 1; i >= 0; i--) {
169
+ if (lines2[i].trim() === "") continue;
170
+ const p = parseLine(lines2[i]);
171
+ if (p) return p;
172
+ }
173
+ return null;
174
+ }
175
+ function dropFileMtime(worktree) {
176
+ try {
177
+ return statSync(pointedAtFile(worktree)).mtimeMs;
178
+ } catch {
179
+ return null;
180
+ }
181
+ }
182
+
106
183
  // src/periscope-state.ts
107
- import { existsSync as existsSync2, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
184
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
108
185
  import { connect } from "net";
109
- import { join } from "path";
110
- var dirOf = (stateDir) => join(stateDir, "periscopes");
111
- var fileOf = (stateDir, ticketRecordId) => join(dirOf(stateDir), `${ticketRecordId}.json`);
186
+ import { join as join2 } from "path";
187
+ var dirOf = (stateDir) => join2(stateDir, "periscopes");
188
+ var fileOf = (stateDir, ticketRecordId) => join2(dirOf(stateDir), `${ticketRecordId}.json`);
112
189
  function writePeriscopeSidecar(stateDir, sc) {
113
- mkdirSync(dirOf(stateDir), { recursive: true });
114
- writeFileSync(fileOf(stateDir, sc.ticketRecordId), `${JSON.stringify(sc, null, 2)}
190
+ mkdirSync2(dirOf(stateDir), { recursive: true });
191
+ writeFileSync2(fileOf(stateDir, sc.ticketRecordId), `${JSON.stringify(sc, null, 2)}
115
192
  `);
116
193
  }
117
194
  function removePeriscopeSidecar(stateDir, ticketRecordId) {
@@ -119,12 +196,12 @@ function removePeriscopeSidecar(stateDir, ticketRecordId) {
119
196
  }
120
197
  function readPeriscopeSidecars(stateDir) {
121
198
  const dir = dirOf(stateDir);
122
- if (!existsSync2(dir)) return [];
199
+ if (!existsSync3(dir)) return [];
123
200
  const out = [];
124
201
  for (const name of readdirSync(dir)) {
125
202
  if (!name.endsWith(".json")) continue;
126
203
  try {
127
- const sc = JSON.parse(readFileSync(join(dir, name), "utf8"));
204
+ const sc = JSON.parse(readFileSync2(join2(dir, name), "utf8"));
128
205
  if (sc && typeof sc.ticketRecordId === "string" && typeof sc.url === "string") out.push(sc);
129
206
  } catch {
130
207
  }
@@ -140,11 +217,11 @@ function urlPort(url) {
140
217
  }
141
218
  }
142
219
  function portListening(port, timeoutMs = 500) {
143
- return new Promise((resolve7) => {
220
+ return new Promise((resolve8) => {
144
221
  const s = connect({ port, host: "127.0.0.1" });
145
222
  const done = (ok) => {
146
223
  s.destroy();
147
- resolve7(ok);
224
+ resolve8(ok);
148
225
  };
149
226
  s.setTimeout(timeoutMs, () => done(false));
150
227
  s.once("connect", () => done(true));
@@ -154,7 +231,7 @@ function portListening(port, timeoutMs = 500) {
154
231
  async function periscopesForStatus(stateDir, probe = portListening) {
155
232
  return Promise.all(readPeriscopeSidecars(stateDir).map(async (sc) => {
156
233
  const port = urlPort(sc.url);
157
- return { ...sc, serving: port === void 0 ? false : await probe(port) };
234
+ return { ...sc, serving: port === void 0 ? false : await probe(port), pointedAt: readPointedAt(sc.worktree) };
158
235
  }));
159
236
  }
160
237
 
@@ -242,23 +319,23 @@ function shellInitScript(shell) {
242
319
  }
243
320
 
244
321
  // src/config.ts
245
- import { readFileSync as readFileSync11, existsSync as existsSync10 } from "fs";
246
- import { resolve, dirname as dirname4, isAbsolute, join as join12, basename as basename4 } from "path";
322
+ import { readFileSync as readFileSync12, existsSync as existsSync11 } from "fs";
323
+ import { resolve as resolve2, dirname as dirname5, isAbsolute as isAbsolute2, join as join13, basename as basename4 } from "path";
247
324
  import { homedir as homedir6, tmpdir } from "os";
248
325
  import { parse as parse2 } from "yaml";
249
326
 
250
327
  // src/events.ts
251
328
  import {
252
- appendFileSync,
253
- mkdirSync as mkdirSync2,
254
- existsSync as existsSync3,
255
- statSync,
329
+ appendFileSync as appendFileSync2,
330
+ mkdirSync as mkdirSync3,
331
+ existsSync as existsSync4,
332
+ statSync as statSync2,
256
333
  renameSync,
257
334
  openSync,
258
335
  closeSync,
259
336
  rmSync as rmSync2
260
337
  } from "fs";
261
- import { join as join2, dirname } from "path";
338
+ import { join as join3, dirname as dirname2 } from "path";
262
339
  function render(e) {
263
340
  const scope = e.role ? `${e.step}[${e.role}]` : e.step;
264
341
  const ticket = e.ticket ? ` ${e.ticket}` : "";
@@ -270,7 +347,7 @@ var ROTATED_EVENTS_RE = /^events\.\d{8}T\d{6}Z(?:-\d+)?\.jsonl$/;
270
347
  var ROTATE_LOCK_STALE_MS = 6e4;
271
348
  function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
272
349
  try {
273
- if (!existsSync3(file) || statSync(file).size <= maxBytes) return void 0;
350
+ if (!existsSync4(file) || statSync2(file).size <= maxBytes) return void 0;
274
351
  } catch {
275
352
  return void 0;
276
353
  }
@@ -280,7 +357,7 @@ function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
280
357
  closeSync(openSync(lock, "wx"));
281
358
  } catch {
282
359
  try {
283
- if (Date.now() - statSync(lock).mtimeMs < ROTATE_LOCK_STALE_MS) return void 0;
360
+ if (Date.now() - statSync2(lock).mtimeMs < ROTATE_LOCK_STALE_MS) return void 0;
284
361
  rmSync2(lock, { force: true });
285
362
  closeSync(openSync(lock, "wx"));
286
363
  } catch {
@@ -288,10 +365,10 @@ function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
288
365
  }
289
366
  }
290
367
  try {
291
- if (statSync(file).size <= maxBytes) return void 0;
368
+ if (statSync2(file).size <= maxBytes) return void 0;
292
369
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
293
- let dest = join2(dirname(file), `events.${stamp}.jsonl`);
294
- for (let n = 1; existsSync3(dest); n++) dest = join2(dirname(file), `events.${stamp}-${n}.jsonl`);
370
+ let dest = join3(dirname2(file), `events.${stamp}.jsonl`);
371
+ for (let n = 1; existsSync4(dest); n++) dest = join3(dirname2(file), `events.${stamp}-${n}.jsonl`);
295
372
  renameSync(file, dest);
296
373
  return dest;
297
374
  } finally {
@@ -314,7 +391,7 @@ var Emitter = class _Emitter {
314
391
  for (const f of [opts.eventFile, opts.logFile]) {
315
392
  if (!f) continue;
316
393
  try {
317
- mkdirSync2(dirname(f), { recursive: true });
394
+ mkdirSync3(dirname2(f), { recursive: true });
318
395
  } catch {
319
396
  }
320
397
  }
@@ -342,12 +419,12 @@ var Emitter = class _Emitter {
342
419
  } catch {
343
420
  }
344
421
  try {
345
- if (this.opts.eventFile) appendFileSync(this.opts.eventFile, `${JSON.stringify(e)}
422
+ if (this.opts.eventFile) appendFileSync2(this.opts.eventFile, `${JSON.stringify(e)}
346
423
  `);
347
424
  } catch {
348
425
  }
349
426
  try {
350
- if (this.opts.logFile) appendFileSync(this.opts.logFile, `${line}
427
+ if (this.opts.logFile) appendFileSync2(this.opts.logFile, `${line}
351
428
  `);
352
429
  } catch {
353
430
  }
@@ -390,18 +467,18 @@ var Emitter = class _Emitter {
390
467
  }
391
468
  }
392
469
  };
393
- var eventFileFor = (stateDir) => join2(stateDir, "events.jsonl");
470
+ var eventFileFor = (stateDir) => join3(stateDir, "events.jsonl");
394
471
 
395
472
  // src/pair-install.ts
396
- import { existsSync as existsSync6, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
473
+ import { existsSync as existsSync7, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
397
474
  import { spawnSync as spawnSync3 } from "child_process";
398
475
  import { homedir as homedir5 } from "os";
399
- import { join as join8 } from "path";
476
+ import { join as join9 } from "path";
400
477
 
401
478
  // src/adapters/claude.ts
402
- import { readFileSync as readFileSync6 } from "fs";
479
+ import { readFileSync as readFileSync7 } from "fs";
403
480
  import { homedir as homedir3 } from "os";
404
- import { join as join6 } from "path";
481
+ import { join as join7 } from "path";
405
482
 
406
483
  // src/stream.ts
407
484
  function toolTarget(name, input) {
@@ -460,19 +537,19 @@ function extractResult(line) {
460
537
 
461
538
  // src/claude-mcp.ts
462
539
  import { spawnSync } from "child_process";
463
- import { readFileSync as readFileSync4 } from "fs";
540
+ import { readFileSync as readFileSync5 } from "fs";
464
541
  import { homedir } from "os";
465
- import { join as join4 } from "path";
542
+ import { join as join5 } from "path";
466
543
 
467
544
  // src/mcp-registrations.ts
468
545
  import { createHash } from "crypto";
469
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
470
- import { join as join3 } from "path";
471
- var fileFor = (stateDir) => join3(stateDir, "mcp-registrations.json");
546
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
547
+ import { join as join4 } from "path";
548
+ var fileFor = (stateDir) => join4(stateDir, "mcp-registrations.json");
472
549
  var keyFingerprint = (key) => createHash("sha256").update(key).digest("hex").slice(0, 8);
473
550
  function readMcpRegistrations(stateDir) {
474
551
  try {
475
- const j = JSON.parse(readFileSync3(fileFor(stateDir), "utf8"));
552
+ const j = JSON.parse(readFileSync4(fileFor(stateDir), "utf8"));
476
553
  return Array.isArray(j) ? j : [];
477
554
  } catch {
478
555
  return [];
@@ -480,8 +557,8 @@ function readMcpRegistrations(stateDir) {
480
557
  }
481
558
  function recordMcpRegistration(stateDir, r, now = /* @__PURE__ */ new Date()) {
482
559
  const rest = readMcpRegistrations(stateDir).filter((x) => !(x.harness === r.harness && x.scope === r.scope && x.name === r.name));
483
- mkdirSync3(stateDir, { recursive: true });
484
- writeFileSync2(fileFor(stateDir), `${JSON.stringify([...rest, { ...r, at: now.toISOString() }], null, 2)}
560
+ mkdirSync4(stateDir, { recursive: true });
561
+ writeFileSync3(fileFor(stateDir), `${JSON.stringify([...rest, { ...r, at: now.toISOString() }], null, 2)}
485
562
  `, { mode: 384 });
486
563
  }
487
564
  function findMcpRegistration(stateDir, r) {
@@ -504,7 +581,7 @@ function runClaude(bin, args) {
504
581
  }
505
582
  function userScopeEntry(home) {
506
583
  try {
507
- const j = JSON.parse(readFileSync4(join4(home, ".claude.json"), "utf8"));
584
+ const j = JSON.parse(readFileSync5(join5(home, ".claude.json"), "utf8"));
508
585
  return j.mcpServers?.[MCP_SERVER_NAME];
509
586
  } catch {
510
587
  return void 0;
@@ -588,8 +665,8 @@ function removeClaudeMcp(o) {
588
665
  }
589
666
 
590
667
  // src/launcher.ts
591
- import { basename, join as join5 } from "path";
592
- import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
668
+ import { basename, join as join6 } from "path";
669
+ import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync as readdirSync2 } from "fs";
593
670
  import { homedir as homedir2 } from "os";
594
671
  function isOriLauncher(spec) {
595
672
  return !!spec.launcher && basename(spec.launcher) === "ori";
@@ -598,7 +675,7 @@ function isOmlxLauncher(spec) {
598
675
  return !!spec.launcher && basename(spec.launcher) === "omlx";
599
676
  }
600
677
  function defaultOmlxBin(home = homedir2()) {
601
- return join5(home, ".omlx", "bin", "omlx");
678
+ return join6(home, ".omlx", "bin", "omlx");
602
679
  }
603
680
  function launcherExecutable(spec, home) {
604
681
  if (!spec.launcher) return void 0;
@@ -651,8 +728,8 @@ function omlxModelPresent(model, dirs) {
651
728
  });
652
729
  }
653
730
  function readOmlxSettings(home = homedir2()) {
654
- const p = join5(home, ".omlx", "settings.json");
655
- return existsSync4(p) ? parseOmlxSettings(readFileSync5(p, "utf8")) : void 0;
731
+ const p = join6(home, ".omlx", "settings.json");
732
+ return existsSync5(p) ? parseOmlxSettings(readFileSync6(p, "utf8")) : void 0;
656
733
  }
657
734
 
658
735
  // src/capability.ts
@@ -716,7 +793,7 @@ function describeClaudeAuth(spec, env = process.env, home = homedir3()) {
716
793
  if (spec.auth === "workspace") return `workspace${spec.inference ? ` (${new URL(spec.inference.baseUrl).host})` : " (endpoint resolved per route at run time)"}`;
717
794
  let fromSettings;
718
795
  try {
719
- const parsed = JSON.parse(readFileSync6(join6(home, ".claude", "settings.json"), "utf8"));
796
+ const parsed = JSON.parse(readFileSync7(join7(home, ".claude", "settings.json"), "utf8"));
720
797
  const e = parsed.env ?? {};
721
798
  const on = (k) => e[k] !== void 0 && e[k] !== "" && e[k] !== "0";
722
799
  if (on("CLAUDE_CODE_USE_VERTEX")) fromSettings = "vertex from ~/.claude/settings.json";
@@ -820,12 +897,12 @@ var claudeAdapter = {
820
897
  // src/adapters/codex.ts
821
898
  import { spawnSync as spawnSync2 } from "child_process";
822
899
  import { homedir as homedir4 } from "os";
823
- import { join as join7 } from "path";
824
- import { rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
900
+ import { join as join8 } from "path";
901
+ import { rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
825
902
 
826
903
  // src/pair-files.ts
827
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, rmSync as rmSync3 } from "fs";
828
- import { dirname as dirname2 } from "path";
904
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4, rmSync as rmSync3 } from "fs";
905
+ import { dirname as dirname3 } from "path";
829
906
  var PAIR_BLOCK_START = "<!-- crew:pair start -->";
830
907
  var PAIR_BLOCK_END = "<!-- crew:pair end -->";
831
908
  var PAIR_KEY_VAR = "CREW_API_KEY";
@@ -865,11 +942,11 @@ function removePairBlock(text2) {
865
942
  return text2.replace(new RegExp(`\\n*${BLOCK_RE.source}`), "\n").replace(/^\n+/, "").replace(/\n{3,}/g, "\n\n");
866
943
  }
867
944
  function readIfExists(path) {
868
- return existsSync5(path) ? readFileSync7(path, "utf8") : void 0;
945
+ return existsSync6(path) ? readFileSync8(path, "utf8") : void 0;
869
946
  }
870
947
  function writeFileMkdir(path, text2) {
871
- mkdirSync4(dirname2(path), { recursive: true });
872
- writeFileSync3(path, text2);
948
+ mkdirSync5(dirname3(path), { recursive: true });
949
+ writeFileSync4(path, text2);
873
950
  }
874
951
  function installBlockFile(path, harness, dryRun) {
875
952
  const before = readIfExists(path) ?? "";
@@ -885,7 +962,7 @@ function removeBlockFile(path, dryRun) {
885
962
  if (dryRun) return true;
886
963
  const after = removePairBlock(before);
887
964
  if (after.trim() === "") rmSync3(path);
888
- else writeFileSync3(path, after);
965
+ else writeFileSync4(path, after);
889
966
  return true;
890
967
  }
891
968
  function blockVerdict(path, harness) {
@@ -1012,8 +1089,8 @@ function codexTable(url) {
1012
1089
  return [`[mcp_servers.${SERVER}]`, `url = ${tomlString(url)}`, `bearer_token_env_var = ${tomlString(PAIR_KEY_VAR)}`];
1013
1090
  }
1014
1091
  var codexPaths = (home) => ({
1015
- instructions: join7(home, ".codex", "AGENTS.md"),
1016
- config: join7(home, ".codex", "config.toml")
1092
+ instructions: join8(home, ".codex", "AGENTS.md"),
1093
+ config: join8(home, ".codex", "config.toml")
1017
1094
  });
1018
1095
  function codexMcpState(config, url) {
1019
1096
  const lines2 = config.split("\n");
@@ -1064,7 +1141,7 @@ var codexPair = {
1064
1141
  if (hadMcp && !o.dryRun) {
1065
1142
  const after = removeTomlTable(config, `mcp_servers.${SERVER}`);
1066
1143
  if (after === "") rmSync4(p.config);
1067
- else writeFileSync4(p.config, after);
1144
+ else writeFileSync5(p.config, after);
1068
1145
  }
1069
1146
  const what = hadBlock && hadMcp ? `the Pair block and ${SERVER} server` : hadBlock ? "the Pair block" : `the ${SERVER} server`;
1070
1147
  return hadBlock || hadMcp ? `pair(codex): ${o.dryRun ? "(dry run) would remove" : "removed"} ${what}${kept}` : `pair(codex): nothing to remove${kept}`;
@@ -1160,8 +1237,8 @@ var codexAdapter = {
1160
1237
 
1161
1238
  // src/pair-install.ts
1162
1239
  var geminiPaths = (home) => ({
1163
- instructions: join8(home, ".gemini", "GEMINI.md"),
1164
- config: join8(home, ".gemini", "settings.json")
1240
+ instructions: join9(home, ".gemini", "GEMINI.md"),
1241
+ config: join9(home, ".gemini", "settings.json")
1165
1242
  });
1166
1243
  function readGeminiSettings(path) {
1167
1244
  const text2 = readIfExists(path);
@@ -1249,7 +1326,7 @@ var geminiPair = {
1249
1326
  if (Object.keys(rest).length > 0) next.mcpServers = rest;
1250
1327
  else delete next.mcpServers;
1251
1328
  if (Object.keys(next).length === 0) rmSync5(p.config);
1252
- else writeFileSync5(p.config, `${JSON.stringify(next, null, 2)}
1329
+ else writeFileSync6(p.config, `${JSON.stringify(next, null, 2)}
1253
1330
  `);
1254
1331
  }
1255
1332
  const what = hadBlock && hadMcp ? `the Pair block and ${SERVER} server` : hadBlock ? "the Pair block" : `the ${SERVER} server`;
@@ -1260,9 +1337,9 @@ var VSCODE_FLAVOURS = ["Code", "Code - Insiders"];
1260
1337
  function vscodeUserDirs(o) {
1261
1338
  const home = o.home ?? homedir5();
1262
1339
  const platform2 = o.platform ?? process.platform;
1263
- const root = platform2 === "darwin" ? join8(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join8(home, "AppData", "Roaming") : join8(home, ".config");
1264
- const all = VSCODE_FLAVOURS.map((f) => join8(root, f, "User"));
1265
- const present = all.filter((d) => existsSync6(d));
1340
+ const root = platform2 === "darwin" ? join9(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join9(home, "AppData", "Roaming") : join9(home, ".config");
1341
+ const all = VSCODE_FLAVOURS.map((f) => join9(root, f, "User"));
1342
+ const present = all.filter((d) => existsSync7(d));
1266
1343
  return present.length > 0 ? present : [all[0]];
1267
1344
  }
1268
1345
  var COPILOT_FRONTMATTER = '---\napplyTo: "**"\n---\n\n';
@@ -1300,7 +1377,7 @@ function removeCopilotInstructions(path, dryRun) {
1300
1377
  if (dryRun) return true;
1301
1378
  const after = removePairBlock(before);
1302
1379
  if (after.trim() === "" || after.trim() === COPILOT_FRONTMATTER.trim()) rmSync5(path);
1303
- else writeFileSync5(path, after);
1380
+ else writeFileSync6(path, after);
1304
1381
  return true;
1305
1382
  }
1306
1383
  var copilotPair = {
@@ -1309,8 +1386,8 @@ var copilotPair = {
1309
1386
  installPair(o) {
1310
1387
  const lines2 = [];
1311
1388
  for (const dir of vscodeUserDirs(o)) {
1312
- const mcpPath = join8(dir, "mcp.json");
1313
- const instrPath = join8(dir, "prompts", "crew-pair.instructions.md");
1389
+ const mcpPath = join9(dir, "mcp.json");
1390
+ const instrPath = join9(dir, "prompts", "crew-pair.instructions.md");
1314
1391
  const settings = readVscodeMcp(mcpPath);
1315
1392
  if (!settings) {
1316
1393
  lines2.push(`pair(copilot): ${mcpPath} is not a plain JSON object (comments are not supported) \u2014 fix it by hand; nothing was written there`);
@@ -1338,8 +1415,8 @@ var copilotPair = {
1338
1415
  },
1339
1416
  verifyPair(o) {
1340
1417
  const verdicts = vscodeUserDirs(o).map((dir) => {
1341
- const instrPath = join8(dir, "prompts", "crew-pair.instructions.md");
1342
- const mcpPath = join8(dir, "mcp.json");
1418
+ const instrPath = join9(dir, "prompts", "crew-pair.instructions.md");
1419
+ const mcpPath = join9(dir, "mcp.json");
1343
1420
  const b = blockVerdict(instrPath, "copilot");
1344
1421
  if (!b.ok) return b;
1345
1422
  const settings = readVscodeMcp(mcpPath);
@@ -1354,8 +1431,8 @@ var copilotPair = {
1354
1431
  removePair(o) {
1355
1432
  const lines2 = [];
1356
1433
  for (const dir of vscodeUserDirs(o)) {
1357
- const mcpPath = join8(dir, "mcp.json");
1358
- const hadBlock = removeCopilotInstructions(join8(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1434
+ const mcpPath = join9(dir, "mcp.json");
1435
+ const hadBlock = removeCopilotInstructions(join9(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1359
1436
  const settings = readVscodeMcp(mcpPath);
1360
1437
  const st = settings ? copilotMcpState(settings, o.url) : { state: "missing" };
1361
1438
  const hadMcp = st.state === "registered";
@@ -1366,7 +1443,7 @@ var copilotPair = {
1366
1443
  if (Object.keys(rest).length > 0) next.servers = rest;
1367
1444
  else delete next.servers;
1368
1445
  if (Object.keys(next).length === 0) rmSync5(mcpPath);
1369
- else writeFileSync5(mcpPath, `${JSON.stringify(next, null, 2)}
1446
+ else writeFileSync6(mcpPath, `${JSON.stringify(next, null, 2)}
1370
1447
  `);
1371
1448
  }
1372
1449
  const what = hadBlock && hadMcp ? `the Pair instructions and ${SERVER} server` : hadBlock ? "the Pair instructions" : `the ${SERVER} server`;
@@ -1615,9 +1692,9 @@ function resolveSeat(ship, route2, role, persona = {}, repo) {
1615
1692
  }
1616
1693
 
1617
1694
  // src/forges.ts
1618
- import { execFileSync } from "child_process";
1619
- import { chmodSync, existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync8, rmSync as rmSync6, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
1620
- import { dirname as dirname3, join as join9 } from "path";
1695
+ import { execFileSync as execFileSync2 } from "child_process";
1696
+ import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync9, rmSync as rmSync6, statSync as statSync3, writeFileSync as writeFileSync7 } from "fs";
1697
+ import { dirname as dirname4, join as join10 } from "path";
1621
1698
  var ForgeError = class extends Error {
1622
1699
  };
1623
1700
  var DEFAULT_FORGE_USERNAME = "x-access-token";
@@ -1645,7 +1722,7 @@ function forgeDescriptorFor(host) {
1645
1722
  }
1646
1723
  var DEFAULT_KEYCHAIN_SERVICE = "crew-forge";
1647
1724
  function defaultTokenFilePath(stateDir, host) {
1648
- return join9(stateDir, "forges", `${host.replace(/[^a-z0-9.-]/gi, "_")}.token`);
1725
+ return join10(stateDir, "forges", `${host.replace(/[^a-z0-9.-]/gi, "_")}.token`);
1649
1726
  }
1650
1727
  var envSource = {
1651
1728
  read: (s, c) => c.env[s.var]?.trim() || null,
@@ -1654,19 +1731,19 @@ var envSource = {
1654
1731
  var fileSource = {
1655
1732
  read(s, c) {
1656
1733
  const path = s.path ?? defaultTokenFilePath(c.stateDir, c.host);
1657
- if (!existsSync7(path)) return null;
1658
- const mode = statSync2(path).mode & 511;
1734
+ if (!existsSync8(path)) return null;
1735
+ const mode = statSync3(path).mode & 511;
1659
1736
  if (mode & 63) {
1660
1737
  throw new ForgeError(
1661
1738
  `${path} is mode ${mode.toString(8).padStart(3, "0")}; a forge token file must be owner-only (chmod 600 ${path})`
1662
1739
  );
1663
1740
  }
1664
- return readFileSync8(path, "utf8").trim() || null;
1741
+ return readFileSync9(path, "utf8").trim() || null;
1665
1742
  },
1666
1743
  write(s, token, c) {
1667
1744
  const path = s.path ?? defaultTokenFilePath(c.stateDir, c.host);
1668
- mkdirSync5(dirname3(path), { recursive: true, mode: 448 });
1669
- writeFileSync6(path, `${token}
1745
+ mkdirSync6(dirname4(path), { recursive: true, mode: 448 });
1746
+ writeFileSync7(path, `${token}
1670
1747
  `, { mode: 384 });
1671
1748
  chmodSync(path, 384);
1672
1749
  },
@@ -1680,13 +1757,13 @@ var keychainSource = {
1680
1757
  read(s, c) {
1681
1758
  try {
1682
1759
  if (process.platform === "darwin") {
1683
- return execFileSync(
1760
+ return execFileSync2(
1684
1761
  "security",
1685
1762
  ["find-generic-password", "-s", keychainService(s), "-a", c.host, "-w"],
1686
1763
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
1687
1764
  ).trim() || null;
1688
1765
  }
1689
- return execFileSync(
1766
+ return execFileSync2(
1690
1767
  "secret-tool",
1691
1768
  ["lookup", "service", keychainService(s), "account", c.host],
1692
1769
  { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
@@ -1699,13 +1776,13 @@ var keychainSource = {
1699
1776
  try {
1700
1777
  if (process.platform === "darwin") {
1701
1778
  const q2 = (v) => `"${v.replace(/(["\\])/g, "\\$1")}"`;
1702
- execFileSync("security", ["-i"], {
1779
+ execFileSync2("security", ["-i"], {
1703
1780
  input: `add-generic-password -U -s ${q2(keychainService(s))} -a ${q2(c.host)} -w ${q2(token)}
1704
1781
  `,
1705
1782
  stdio: ["pipe", "ignore", "pipe"]
1706
1783
  });
1707
1784
  } else {
1708
- execFileSync(
1785
+ execFileSync2(
1709
1786
  "secret-tool",
1710
1787
  ["store", "--label", `crew ${c.host}`, "service", keychainService(s), "account", c.host],
1711
1788
  { input: token, stdio: ["pipe", "ignore", "pipe"] }
@@ -1718,9 +1795,9 @@ var keychainSource = {
1718
1795
  erase(s, c) {
1719
1796
  try {
1720
1797
  if (process.platform === "darwin") {
1721
- execFileSync("security", ["delete-generic-password", "-s", keychainService(s), "-a", c.host], { stdio: "ignore" });
1798
+ execFileSync2("security", ["delete-generic-password", "-s", keychainService(s), "-a", c.host], { stdio: "ignore" });
1722
1799
  } else {
1723
- execFileSync("secret-tool", ["clear", "service", keychainService(s), "account", c.host], { stdio: "ignore" });
1800
+ execFileSync2("secret-tool", ["clear", "service", keychainService(s), "account", c.host], { stdio: "ignore" });
1724
1801
  }
1725
1802
  } catch {
1726
1803
  }
@@ -1800,10 +1877,10 @@ function answerCredential(action, input, forges, stateDir, env = process.env) {
1800
1877
  password=${token}
1801
1878
  `;
1802
1879
  }
1803
- var missesPath = (stateDir) => join9(stateDir, "forge-misses.json");
1880
+ var missesPath = (stateDir) => join10(stateDir, "forge-misses.json");
1804
1881
  function readForgeMisses(stateDir) {
1805
1882
  try {
1806
- const raw = JSON.parse(readFileSync8(missesPath(stateDir), "utf8"));
1883
+ const raw = JSON.parse(readFileSync9(missesPath(stateDir), "utf8"));
1807
1884
  return Object.entries(raw).filter(([, t]) => typeof t === "string").map(([host, lastMissAt]) => ({ host, lastMissAt })).sort((a, b) => a.host.localeCompare(b.host));
1808
1885
  } catch {
1809
1886
  return [];
@@ -1818,8 +1895,8 @@ function noteForgeMiss(stateDir, host, at) {
1818
1895
  if (!(bare in cur)) return;
1819
1896
  delete cur[bare];
1820
1897
  } else cur[bare] = at;
1821
- mkdirSync5(stateDir, { recursive: true });
1822
- writeFileSync6(missesPath(stateDir), JSON.stringify(cur), { mode: 384 });
1898
+ mkdirSync6(stateDir, { recursive: true });
1899
+ writeFileSync7(missesPath(stateDir), JSON.stringify(cur), { mode: 384 });
1823
1900
  } catch {
1824
1901
  }
1825
1902
  }
@@ -1921,8 +1998,8 @@ import { hostFromUrl } from "@tablation/client";
1921
1998
 
1922
1999
  // src/secret-store.ts
1923
2000
  import { createHash as createHash2 } from "crypto";
1924
- import { accessSync, constants, existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync9, renameSync as renameSync2, rmSync as rmSync7, statSync as statSync3, writeFileSync as writeFileSync7, chmodSync as chmodSync2 } from "fs";
1925
- import { join as join10 } from "path";
2001
+ import { accessSync, constants, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync2, rmSync as rmSync7, statSync as statSync4, writeFileSync as writeFileSync8, chmodSync as chmodSync2 } from "fs";
2002
+ import { join as join11 } from "path";
1926
2003
  import { getSessionStore as getOsSessionStore } from "@tablation/client";
1927
2004
  var SECRET_STORE_MODES = ["auto", "os", "file"];
1928
2005
  function isSecretStoreMode(v) {
@@ -1933,7 +2010,7 @@ function configureSecretStore(c) {
1933
2010
  configured = c;
1934
2011
  }
1935
2012
  function keysDir(stateDir) {
1936
- return join10(stateDir, "keys");
2013
+ return join11(stateDir, "keys");
1937
2014
  }
1938
2015
  function entryName(host, slug2) {
1939
2016
  return `${createHash2("sha256").update(`${host}\0${slug2}`).digest("hex")}.json`;
@@ -1944,34 +2021,34 @@ var FileSessionStore = class {
1944
2021
  this.dir = dir;
1945
2022
  }
1946
2023
  ensureDir() {
1947
- mkdirSync6(this.dir, { recursive: true, mode: 448 });
1948
- if ((statSync3(this.dir).mode & 63) !== 0) chmodSync2(this.dir, 448);
2024
+ mkdirSync7(this.dir, { recursive: true, mode: 448 });
2025
+ if ((statSync4(this.dir).mode & 63) !== 0) chmodSync2(this.dir, 448);
1949
2026
  }
1950
2027
  async get(host, slug2) {
1951
- const file = join10(this.dir, entryName(host, slug2));
1952
- if (!existsSync8(this.dir)) return void 0;
2028
+ const file = join11(this.dir, entryName(host, slug2));
2029
+ if (!existsSync9(this.dir)) return void 0;
1953
2030
  accessSync(this.dir, constants.R_OK | constants.X_OK);
1954
- if (!existsSync8(file)) return void 0;
1955
- return JSON.parse(readFileSync9(file, "utf8"));
2031
+ if (!existsSync9(file)) return void 0;
2032
+ return JSON.parse(readFileSync10(file, "utf8"));
1956
2033
  }
1957
2034
  async set(host, slug2, session) {
1958
2035
  this.ensureDir();
1959
- const file = join10(this.dir, entryName(host, slug2));
2036
+ const file = join11(this.dir, entryName(host, slug2));
1960
2037
  const tmp = `${file}.${process.pid}.tmp`;
1961
- writeFileSync7(tmp, JSON.stringify(session), { mode: 384 });
2038
+ writeFileSync8(tmp, JSON.stringify(session), { mode: 384 });
1962
2039
  renameSync2(tmp, file);
1963
2040
  }
1964
2041
  async clear(host, slug2) {
1965
- if (!existsSync8(this.dir)) return;
1966
- rmSync7(join10(this.dir, entryName(host, slug2)), { force: true });
2042
+ if (!existsSync9(this.dir)) return;
2043
+ rmSync7(join11(this.dir, entryName(host, slug2)), { force: true });
1967
2044
  }
1968
2045
  /** Whether a key could be written here right now, without leaving anything behind. */
1969
2046
  writable() {
1970
2047
  try {
1971
2048
  this.ensureDir();
1972
2049
  accessSync(this.dir, constants.R_OK | constants.W_OK | constants.X_OK);
1973
- const probe = join10(this.dir, `.probe.${process.pid}`);
1974
- writeFileSync7(probe, "", { mode: 384 });
2050
+ const probe = join11(this.dir, `.probe.${process.pid}`);
2051
+ writeFileSync8(probe, "", { mode: 384 });
1975
2052
  rmSync7(probe, { force: true });
1976
2053
  return { ok: true };
1977
2054
  } catch (e) {
@@ -2022,11 +2099,11 @@ function osStoreReachable(env = process.env, platform2 = process.platform, hasBi
2022
2099
  if (platform2 !== "linux") return true;
2023
2100
  if (!hasBinary("secret-tool")) return false;
2024
2101
  if (env.DBUS_SESSION_BUS_ADDRESS) return true;
2025
- return !!env.XDG_RUNTIME_DIR && existsSync8(join10(env.XDG_RUNTIME_DIR, "bus"));
2102
+ return !!env.XDG_RUNTIME_DIR && existsSync9(join11(env.XDG_RUNTIME_DIR, "bus"));
2026
2103
  }
2027
2104
  function onPath(name) {
2028
2105
  for (const dir of (process.env.PATH ?? "").split(":")) {
2029
- if (dir && existsSync8(join10(dir, name))) return true;
2106
+ if (dir && existsSync9(join11(dir, name))) return true;
2030
2107
  }
2031
2108
  return false;
2032
2109
  }
@@ -2093,8 +2170,8 @@ function explain(ship, required) {
2093
2170
  }
2094
2171
 
2095
2172
  // src/repo-config.ts
2096
- import { readFileSync as readFileSync10, existsSync as existsSync9 } from "fs";
2097
- import { join as join11, basename as basename3 } from "path";
2173
+ import { readFileSync as readFileSync11, existsSync as existsSync10 } from "fs";
2174
+ import { join as join12, basename as basename3 } from "path";
2098
2175
  import { parse } from "yaml";
2099
2176
 
2100
2177
  // src/sandbox-policy.ts
@@ -2375,8 +2452,8 @@ var MODES = ["local", "integrate", "ci_manual", "ci_auto", "external"];
2375
2452
  var PROVIDERS = ["github", "buildkite", "other", "none"];
2376
2453
  function findRepoConfig(dir) {
2377
2454
  for (const name of REPO_CONFIG_FILENAMES) {
2378
- const p = join11(dir, name);
2379
- if (existsSync9(p)) return p;
2455
+ const p = join12(dir, name);
2456
+ if (existsSync10(p)) return p;
2380
2457
  }
2381
2458
  return null;
2382
2459
  }
@@ -2656,7 +2733,7 @@ function parseRepoConfig(text2, file) {
2656
2733
  function loadRepoConfig(dir) {
2657
2734
  const file = findRepoConfig(dir);
2658
2735
  if (!file) return null;
2659
- return parseRepoConfig(readFileSync10(file, "utf8"), file);
2736
+ return parseRepoConfig(readFileSync11(file, "utf8"), file);
2660
2737
  }
2661
2738
  function hookLabel(cfg, hook2) {
2662
2739
  return cfg.labels[hook2] ?? cfg.hooks[hook2] ?? hook2;
@@ -3178,14 +3255,14 @@ function mergeRouteRelease(route2, override) {
3178
3255
  function expand(p, base) {
3179
3256
  let out = p.startsWith("~") ? p.replace(/^~/, homedir6()) : p;
3180
3257
  out = out.replace(/\$\{?HOME\}?/g, homedir6());
3181
- return isAbsolute(out) ? out : resolve(base, out);
3258
+ return isAbsolute2(out) ? out : resolve2(base, out);
3182
3259
  }
3183
3260
  function resolvePromptsDir(promptSet, crewHome, base) {
3184
- if (!promptSet) return join12(crewHome, "prompts", "default");
3261
+ if (!promptSet) return join13(crewHome, "prompts", "default");
3185
3262
  if (promptSet.includes("/") || promptSet.startsWith(".") || promptSet.startsWith("~")) {
3186
3263
  return expand(promptSet, base);
3187
3264
  }
3188
- return join12(crewHome, "prompts", promptSet);
3265
+ return join13(crewHome, "prompts", promptSet);
3189
3266
  }
3190
3267
  var Missing = class {
3191
3268
  paths = [];
@@ -3209,36 +3286,36 @@ var Missing = class {
3209
3286
  };
3210
3287
  function crewVersion(crewHome) {
3211
3288
  try {
3212
- const pkg = JSON.parse(readFileSync11(join12(crewHome, "package.json"), "utf8"));
3289
+ const pkg = JSON.parse(readFileSync12(join13(crewHome, "package.json"), "utf8"));
3213
3290
  return pkg.version ?? "0.0.0";
3214
3291
  } catch {
3215
3292
  return "0.0.0";
3216
3293
  }
3217
3294
  }
3218
3295
  function configSearchPath(crewHome) {
3219
- if (process.env.CREW_CONFIG) return [resolve(process.env.CREW_CONFIG)];
3296
+ if (process.env.CREW_CONFIG) return [resolve2(process.env.CREW_CONFIG)];
3220
3297
  const xdg = process.env.XDG_CONFIG_HOME;
3221
3298
  return [
3222
- ...xdg ? [join12(xdg, "crew", "crew.yaml")] : [],
3223
- join12(homedir6(), ".config", "crew", "crew.yaml"),
3224
- resolve(crewHome, "crew.yaml")
3299
+ ...xdg ? [join13(xdg, "crew", "crew.yaml")] : [],
3300
+ join13(homedir6(), ".config", "crew", "crew.yaml"),
3301
+ resolve2(crewHome, "crew.yaml")
3225
3302
  ];
3226
3303
  }
3227
3304
  function findConfigFile(crewHome) {
3228
3305
  const candidates2 = configSearchPath(crewHome);
3229
- return candidates2.find((c) => existsSync10(c)) ?? candidates2[candidates2.length - 1];
3306
+ return candidates2.find((c) => existsSync11(c)) ?? candidates2[candidates2.length - 1];
3230
3307
  }
3231
- var defaultConfigPath = () => join12(process.env.XDG_CONFIG_HOME ?? join12(homedir6(), ".config"), "crew", "crew.yaml");
3308
+ var defaultConfigPath = () => join13(process.env.XDG_CONFIG_HOME ?? join13(homedir6(), ".config"), "crew", "crew.yaml");
3232
3309
  function routeSlug(route2) {
3233
3310
  return route2.replace(/\//g, "-");
3234
3311
  }
3235
3312
  function resolvedPathFor(stateDir, route2) {
3236
3313
  const [workspace, project] = splitRoute(route2);
3237
- return join12(stateDir, "resolved", workspace, `${project}.json`);
3314
+ return join13(stateDir, "resolved", workspace, `${project}.json`);
3238
3315
  }
3239
3316
  function apiKeyPathFor(stateDir, route2) {
3240
3317
  const [workspace, project] = splitRoute(route2);
3241
- return join12(stateDir, "keys", workspace, `${project}.env`);
3318
+ return join13(stateDir, "keys", workspace, `${project}.env`);
3242
3319
  }
3243
3320
  function splitRoute(route2) {
3244
3321
  const parts = route2.split("/");
@@ -3249,9 +3326,9 @@ function splitRoute(route2) {
3249
3326
  }
3250
3327
  function readResolvedFile(stateDir, route2) {
3251
3328
  const path = resolvedPathFor(stateDir, route2);
3252
- if (!existsSync10(path)) return void 0;
3329
+ if (!existsSync11(path)) return void 0;
3253
3330
  try {
3254
- return JSON.parse(readFileSync11(path, "utf8"));
3331
+ return JSON.parse(readFileSync12(path, "utf8"));
3255
3332
  } catch (e) {
3256
3333
  throw new ConfigError(`${path}: not valid JSON \u2014 ${e.message}`);
3257
3334
  }
@@ -3312,23 +3389,23 @@ function parseResolved(raw, m, where) {
3312
3389
  }
3313
3390
  function loadConfig(crewHome, configFile, opts = {}) {
3314
3391
  const file = configFile ?? findConfigFile(crewHome);
3315
- if (opts.text === void 0 && !existsSync10(file)) {
3392
+ if (opts.text === void 0 && !existsSync11(file)) {
3316
3393
  throw new ConfigError(
3317
3394
  `no config found. Looked in:
3318
3395
  ` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
3319
3396
 
3320
3397
  Create the first of those:
3321
- mkdir -p ${dirname4(defaultConfigPath())} && cp ${join12(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
3398
+ mkdir -p ${dirname5(defaultConfigPath())} && cp ${join13(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
3322
3399
  );
3323
3400
  }
3324
3401
  let raw;
3325
3402
  try {
3326
- raw = parse2(opts.text ?? readFileSync11(file, "utf8"));
3403
+ raw = parse2(opts.text ?? readFileSync12(file, "utf8"));
3327
3404
  } catch (e) {
3328
3405
  throw new ConfigError(`${file}: not valid YAML \u2014 ${e.message}`);
3329
3406
  }
3330
3407
  if (!raw || typeof raw !== "object") throw new ConfigError(`${file} is empty or not a mapping`);
3331
- const base = dirname4(file);
3408
+ const base = dirname5(file);
3332
3409
  const missing = new Missing();
3333
3410
  rejectUnknownKeys(raw, /* @__PURE__ */ new Set(["ship", "routes"]), "top-level", file);
3334
3411
  const shipRaw = raw.ship ?? {};
@@ -3442,7 +3519,7 @@ Create the first of those:
3442
3519
  useNvm: shipRaw.useNvm !== false,
3443
3520
  nvmSh: shipRaw.nvmSh,
3444
3521
  stateDir,
3445
- logFile: expand(shipRaw.logFile ?? join12(tmpdir(), "crew.log"), base),
3522
+ logFile: expand(shipRaw.logFile ?? join13(tmpdir(), "crew.log"), base),
3446
3523
  userAgent: shipRaw.userAgent ?? `Mozilla/5.0 CrewAgent/${crewVersion(crewHome)}`,
3447
3524
  maxConcurrentAgents,
3448
3525
  maxConcurrentAgentsSet,
@@ -3622,7 +3699,7 @@ function parseOneRoute(c, where, file, base, stateDir, raw, missing, crewHome, t
3622
3699
  const hasRepos = Object.keys(repoDirs).length > 0;
3623
3700
  const reposBasePath = expand(String(c?.reposBasePath ?? raw.ship?.reposBasePath ?? DEFAULT_REPOS_BASE_PATH), base);
3624
3701
  const dirIsCloneBase = !c?.dir && !hasRepos && !Array.isArray(c?.repos);
3625
- const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join12(reposBasePath, splitRoute(route2)[0]) : "";
3702
+ const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join13(reposBasePath, splitRoute(route2)[0]) : "";
3626
3703
  const weight = c?.weight === void 0 ? void 0 : Number(c.weight);
3627
3704
  if (weight !== void 0 && !(Number.isFinite(weight) && weight > 0)) {
3628
3705
  missing.add(`${where}.weight must be a positive number (got "${c?.weight}")`);
@@ -3671,21 +3748,21 @@ function routeForDir(cfg, dir) {
3671
3748
  }
3672
3749
  function locateDir(cfg, dir, commonDirOf) {
3673
3750
  const within = (base, target2) => {
3674
- const b = resolve(base);
3751
+ const b = resolve2(base);
3675
3752
  return target2 === b || target2.startsWith(`${b}/`);
3676
3753
  };
3677
3754
  const find = (target2) => {
3678
3755
  for (const route2 of cfg.routes) {
3679
3756
  const repo = reposOf(route2).find((x) => within(x.dir, target2));
3680
- if (repo) return { route: route2, repo, checkout: resolve(repo.dir) };
3757
+ if (repo) return { route: route2, repo, checkout: resolve2(repo.dir) };
3681
3758
  }
3682
3759
  return void 0;
3683
3760
  };
3684
- const target = resolve(dir);
3761
+ const target = resolve2(dir);
3685
3762
  const direct = find(target);
3686
3763
  if (direct) return direct;
3687
3764
  const common = commonDirOf?.(target);
3688
- return common ? find(dirname4(resolve(target, common))) : void 0;
3765
+ return common ? find(dirname5(resolve2(target, common))) : void 0;
3689
3766
  }
3690
3767
  function findRoute(cfg, route2) {
3691
3768
  if (!route2) {
@@ -3720,8 +3797,8 @@ function repoIdForName(r, name) {
3720
3797
  }
3721
3798
  function resolveApiKey(r) {
3722
3799
  if (r.apiKey) return r.apiKey;
3723
- if (r.apiKeyFile && r.apiKeyVar && existsSync10(r.apiKeyFile)) {
3724
- for (const line of readFileSync11(r.apiKeyFile, "utf8").split("\n")) {
3800
+ if (r.apiKeyFile && r.apiKeyVar && existsSync11(r.apiKeyFile)) {
3801
+ for (const line of readFileSync12(r.apiKeyFile, "utf8").split("\n")) {
3725
3802
  const m = line.match(new RegExp(`^${r.apiKeyVar}=(.*)$`));
3726
3803
  if (m) return (m[1] ?? "").trim().replace(/^["']|["']$/g, "");
3727
3804
  }
@@ -3734,9 +3811,9 @@ function resolveApiKey(r) {
3734
3811
  }
3735
3812
  function credentialSource(r) {
3736
3813
  if (r.apiKey) return KEYCHAIN_ELIGIBLE_ROUTES.has(r) ? "keychain" : "inline";
3737
- if (r.apiKeyFile && r.apiKeyVar && existsSync10(r.apiKeyFile)) {
3814
+ if (r.apiKeyFile && r.apiKeyVar && existsSync11(r.apiKeyFile)) {
3738
3815
  const re = new RegExp(`^${r.apiKeyVar}=`);
3739
- if (readFileSync11(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
3816
+ if (readFileSync12(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
3740
3817
  }
3741
3818
  return process.env.CREW_API_KEY?.trim() ? "CREW_API_KEY" : "none";
3742
3819
  }
@@ -3792,7 +3869,7 @@ function configuredMembers(rt) {
3792
3869
  return out;
3793
3870
  }
3794
3871
  function defaultRepoDir(basePath, workspace, repoName) {
3795
- return join12(basePath, workspace, repoName);
3872
+ return join13(basePath, workspace, repoName);
3796
3873
  }
3797
3874
  function reposOf(r) {
3798
3875
  const named = Object.entries(r.repos);
@@ -3831,10 +3908,10 @@ function passengerRepoTargets(r) {
3831
3908
  return reposOf(r).filter((t) => r.repoOverrides[t.name]?.hostPassengers !== false);
3832
3909
  }
3833
3910
  function passengerCheckoutDir(basePath, workspace, repoName) {
3834
- return join12(basePath, workspace, ".passenger-checkouts", repoName);
3911
+ return join13(basePath, workspace, ".passenger-checkouts", repoName);
3835
3912
  }
3836
3913
  function passengerMirrorArea(ship) {
3837
- return ship.runner === "sandbox" ? join12(ship.stateDir, "passenger-sources") : void 0;
3914
+ return ship.runner === "sandbox" ? join13(ship.stateDir, "passenger-sources") : void 0;
3838
3915
  }
3839
3916
  function passengerCheckoutTargets(r, mirrorArea) {
3840
3917
  if (!r.hostPassengers) return [];
@@ -3845,7 +3922,7 @@ function passengerCheckoutTargets(r, mirrorArea) {
3845
3922
  const forgeHost = t.forgeHost ?? (id ? r.resolved?.repoForgeHosts?.[id] : void 0);
3846
3923
  return {
3847
3924
  name: t.name,
3848
- dir: mirrorArea ? join12(mirrorArea, workspace, t.name) : passengerCheckoutDir(r.reposBasePath, workspace, t.name),
3925
+ dir: mirrorArea ? join13(mirrorArea, workspace, t.name) : passengerCheckoutDir(r.reposBasePath, workspace, t.name),
3849
3926
  remote: t.remote ?? (id ? remotes[id] : void 0),
3850
3927
  ...forgeHost ? { forgeHost } : {}
3851
3928
  };
@@ -4123,9 +4200,9 @@ function recoveredFailureHeadlines(o, route2) {
4123
4200
  }
4124
4201
 
4125
4202
  // src/install.ts
4126
- import { dirname as dirname5, join as join13, extname } from "path";
4203
+ import { dirname as dirname6, join as join14, extname } from "path";
4127
4204
  import { homedir as homedir7, userInfo } from "os";
4128
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync11, mkdirSync as mkdirSync7, readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync4, writeFileSync as writeFileSync8, unlinkSync, realpathSync } from "fs";
4205
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync12, mkdirSync as mkdirSync8, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync5, writeFileSync as writeFileSync9, unlinkSync, realpathSync } from "fs";
4129
4206
  import { spawnSync as spawnSync4 } from "child_process";
4130
4207
  import { createHash as createHash3 } from "crypto";
4131
4208
 
@@ -4137,7 +4214,7 @@ function isCompiledBinary(moduleUrl) {
4137
4214
  // src/install.ts
4138
4215
  function crewInvocation(crewHome) {
4139
4216
  if (isCompiledBinary(import.meta.url)) return [process.execPath];
4140
- return [process.execPath, join13(crewHome, "bin", "crew")];
4217
+ return [process.execPath, join14(crewHome, "bin", "crew")];
4141
4218
  }
4142
4219
  var INTERVAL_SECONDS = 120;
4143
4220
  function labelFor(crewHome, job) {
@@ -4168,29 +4245,29 @@ function schedulerLogFor(crewLog, job) {
4168
4245
  return `${stem}.${suffix}${ext || ".log"}`;
4169
4246
  }
4170
4247
  function pathFor(ship) {
4171
- const nodeBinDir = dirname5(process.execPath);
4248
+ const nodeBinDir = dirname6(process.execPath);
4172
4249
  const parts = [nodeBinDir];
4173
4250
  if (ship.extraPath) parts.push(ship.extraPath);
4174
4251
  parts.push("/usr/bin", "/bin", "/usr/sbin", "/sbin");
4175
4252
  return parts.join(":");
4176
4253
  }
4177
4254
  function findOnPath(bin, searchPath) {
4178
- const candidates2 = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join13(dir, bin));
4255
+ const candidates2 = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join14(dir, bin));
4179
4256
  for (const candidate of candidates2) {
4180
4257
  try {
4181
4258
  accessSync2(candidate, constants2.X_OK);
4182
- if (statSync4(candidate).isFile()) return candidate;
4259
+ if (statSync5(candidate).isFile()) return candidate;
4183
4260
  } catch {
4184
4261
  }
4185
4262
  }
4186
4263
  return void 0;
4187
4264
  }
4188
- var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join13(homedir7(), ".docker", "bin")].join(":");
4265
+ var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join14(homedir7(), ".docker", "bin")].join(":");
4189
4266
  function dockerPathProblem(ship, shellPath = process.env.PATH ?? "") {
4190
4267
  const schedulerPath = pathFor(ship);
4191
4268
  if (findOnPath("docker", schedulerPath)) return void 0;
4192
4269
  const inShell = findOnPath("docker", shellPath);
4193
- return inShell ? `\`docker\` is not on the scheduler PATH (${schedulerPath}) but is at ${inShell} in your shell; add ${dirname5(inShell)} to ship.extraPath in crew.yaml and run \`crew install\`` : `\`docker\` is not on the scheduler PATH (${schedulerPath}) or your shell PATH; install Docker, then add its directory to ship.extraPath in crew.yaml and run \`crew install\``;
4270
+ return inShell ? `\`docker\` is not on the scheduler PATH (${schedulerPath}) but is at ${inShell} in your shell; add ${dirname6(inShell)} to ship.extraPath in crew.yaml and run \`crew install\`` : `\`docker\` is not on the scheduler PATH (${schedulerPath}) or your shell PATH; install Docker, then add its directory to ship.extraPath in crew.yaml and run \`crew install\``;
4194
4271
  }
4195
4272
  var USER_INSTALL = { root: false, user: "" };
4196
4273
  function installContext(env = process.env) {
@@ -4200,7 +4277,7 @@ function installContext(env = process.env) {
4200
4277
  }
4201
4278
  var SYSTEM_UNIT_DIR = "/etc/systemd/system";
4202
4279
  function systemUnitPathFor(label, suffix = "service") {
4203
- return join13(SYSTEM_UNIT_DIR, `${label}.${suffix}`);
4280
+ return join14(SYSTEM_UNIT_DIR, `${label}.${suffix}`);
4204
4281
  }
4205
4282
  function unitEnvironment(ship) {
4206
4283
  return [["PATH", pathFor(ship)], ...Object.entries(ship.env ?? {})];
@@ -4221,7 +4298,7 @@ function xmlEscape(s) {
4221
4298
  }
4222
4299
  function planLaunchd(ship, crewHome, job) {
4223
4300
  const label = labelFor(crewHome, job);
4224
- const unitPath = join13(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4301
+ const unitPath = join14(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4225
4302
  const crewLog = ship.logFile;
4226
4303
  const schedulerLog = schedulerLogFor(crewLog, job);
4227
4304
  const persistent = job === "run";
@@ -4275,7 +4352,7 @@ ${scheduleKeys}
4275
4352
  }
4276
4353
  function planSystemd(ship, crewHome, job, ctx) {
4277
4354
  const label = labelFor(crewHome, job);
4278
- const unitDir = ctx.root ? SYSTEM_UNIT_DIR : join13(homedir7(), ".config", "systemd", "user");
4355
+ const unitDir = ctx.root ? SYSTEM_UNIT_DIR : join14(homedir7(), ".config", "systemd", "user");
4279
4356
  const ctl = ctx.root ? ["systemctl"] : ["systemctl", "--user"];
4280
4357
  const userLine = ctx.root && ctx.user && ctx.user !== "root" ? `User=${ctx.user}
4281
4358
  ` : "";
@@ -4284,7 +4361,7 @@ function planSystemd(ship, crewHome, job, ctx) {
4284
4361
  lingerCommand: ["loginctl", "enable-linger", ctx.user],
4285
4362
  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`
4286
4363
  };
4287
- const servicePath = join13(unitDir, `${label}.service`);
4364
+ const servicePath = join14(unitDir, `${label}.service`);
4288
4365
  const crewLog = ship.logFile;
4289
4366
  const schedulerLog = schedulerLogFor(crewLog, job);
4290
4367
  const description = job === "release" ? "Tablation crew \u2014 release" : job === "passengers" ? "Tablation crew \u2014 Host Passengers sync" : "Tablation crew \u2014 persistent supervisor loop";
@@ -4325,7 +4402,7 @@ WantedBy=${wantedBy}
4325
4402
  ...linger
4326
4403
  };
4327
4404
  }
4328
- const timerPath = join13(unitDir, `${label}.timer`);
4405
+ const timerPath = join14(unitDir, `${label}.timer`);
4329
4406
  const service = `# Written by \`crew install\` \u2014 re-run it rather than hand-editing this file.
4330
4407
  [Unit]
4331
4408
  Description=${description}
@@ -4400,7 +4477,7 @@ function planInstall(ship, crewHome, hostShip, hasSystemd, job = "run", ctx = US
4400
4477
  function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4401
4478
  const label = labelFor(crewHome, job);
4402
4479
  if (hostShip === "macos") {
4403
- const unitPath = join13(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4480
+ const unitPath = join14(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4404
4481
  return {
4405
4482
  mechanism: "launchd",
4406
4483
  unitPaths: [unitPath],
@@ -4410,20 +4487,20 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4410
4487
  };
4411
4488
  }
4412
4489
  if (hostShip === "linux" && hasSystemd) {
4413
- const system = existsSync11(systemUnitPathFor(label));
4414
- const unitDir = system ? SYSTEM_UNIT_DIR : join13(homedir7(), ".config", "systemd", "user");
4490
+ const system = existsSync12(systemUnitPathFor(label));
4491
+ const unitDir = system ? SYSTEM_UNIT_DIR : join14(homedir7(), ".config", "systemd", "user");
4415
4492
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
4416
4493
  if (job === "run") {
4417
4494
  return {
4418
4495
  mechanism: "systemd",
4419
- unitPaths: [join13(unitDir, `${label}.service`)],
4496
+ unitPaths: [join14(unitDir, `${label}.service`)],
4420
4497
  crontabLine: null,
4421
4498
  unloadCommands: [[...ctl, "disable", "--now", `${label}.service`]]
4422
4499
  };
4423
4500
  }
4424
4501
  return {
4425
4502
  mechanism: "systemd",
4426
- unitPaths: [join13(unitDir, `${label}.service`), join13(unitDir, `${label}.timer`)],
4503
+ unitPaths: [join14(unitDir, `${label}.service`), join14(unitDir, `${label}.timer`)],
4427
4504
  crontabLine: null,
4428
4505
  unloadCommands: [[...ctl, "disable", "--now", `${label}.timer`]]
4429
4506
  };
@@ -4441,7 +4518,7 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4441
4518
  function planDaemonControl(crewHome, hostShip, hasSystemd) {
4442
4519
  const label = labelFor(crewHome, "run");
4443
4520
  if (hostShip === "macos") {
4444
- const unitPath = join13(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4521
+ const unitPath = join14(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4445
4522
  return {
4446
4523
  mechanism: "launchd",
4447
4524
  startCommand: ["launchctl", "load", unitPath],
@@ -4451,7 +4528,7 @@ function planDaemonControl(crewHome, hostShip, hasSystemd) {
4451
4528
  }
4452
4529
  if (hostShip === "linux" && hasSystemd) {
4453
4530
  const unit = `${label}.service`;
4454
- const ctl = existsSync11(systemUnitPathFor(label)) ? ["systemctl"] : ["systemctl", "--user"];
4531
+ const ctl = existsSync12(systemUnitPathFor(label)) ? ["systemctl"] : ["systemctl", "--user"];
4455
4532
  return {
4456
4533
  mechanism: "systemd",
4457
4534
  startCommand: [...ctl, "start", unit],
@@ -4483,10 +4560,10 @@ async function applyInstall(plan, crewHome, dryRun, log) {
4483
4560
  log.emit(`would write ${path}`);
4484
4561
  continue;
4485
4562
  }
4486
- const before = existsSync11(path) ? readFileSync12(path, "utf8") : null;
4563
+ const before = existsSync12(path) ? readFileSync13(path, "utf8") : null;
4487
4564
  if (before !== plan.unitContent[path]) changed2 = true;
4488
- mkdirSync7(dirname5(path), { recursive: true });
4489
- writeFileSync8(path, plan.unitContent[path]);
4565
+ mkdirSync8(dirname6(path), { recursive: true });
4566
+ writeFileSync9(path, plan.unitContent[path]);
4490
4567
  log.emit(`wrote ${path}`);
4491
4568
  }
4492
4569
  if (plan.crontabLine) {
@@ -4550,7 +4627,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
4550
4627
  }
4551
4628
  }
4552
4629
  for (const path of plan.unitPaths) {
4553
- if (!existsSync11(path)) continue;
4630
+ if (!existsSync12(path)) continue;
4554
4631
  if (dryRun) {
4555
4632
  log.emit(`would remove ${path}`);
4556
4633
  continue;
@@ -4569,7 +4646,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
4569
4646
  }
4570
4647
  var CREW_LAUNCHD_PREFIX = "com.tablation.crew";
4571
4648
  function launchAgentsDir() {
4572
- return join13(homedir7(), "Library", "LaunchAgents");
4649
+ return join14(homedir7(), "Library", "LaunchAgents");
4573
4650
  }
4574
4651
  function xmlUnescape(s) {
4575
4652
  return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
@@ -4586,25 +4663,25 @@ function loadedCrewLabels() {
4586
4663
  return [...parseLaunchctlList(list.stdout ?? "").keys()].filter((l) => l.startsWith(CREW_LAUNCHD_PREFIX));
4587
4664
  }
4588
4665
  function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
4589
- const files = existsSync11(dir) ? readdirSync3(dir) : [];
4666
+ const files = existsSync12(dir) ? readdirSync3(dir) : [];
4590
4667
  const units = files.filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
4591
- const plistPath = join13(dir, f);
4668
+ const plistPath = join14(dir, f);
4592
4669
  let program = null;
4593
4670
  try {
4594
- program = programFromPlist(readFileSync12(plistPath, "utf8"));
4671
+ program = programFromPlist(readFileSync13(plistPath, "utf8"));
4595
4672
  } catch {
4596
4673
  }
4597
4674
  return {
4598
4675
  label: f.slice(0, -".plist".length),
4599
4676
  plistPath,
4600
4677
  program,
4601
- programExists: program !== null && existsSync11(program)
4678
+ programExists: program !== null && existsSync12(program)
4602
4679
  };
4603
4680
  });
4604
4681
  const seen = new Set(units.map((u) => u.label));
4605
4682
  for (const label of [...loaded].sort()) {
4606
4683
  if (!label.startsWith(CREW_LAUNCHD_PREFIX) || seen.has(label)) continue;
4607
- units.push({ label, plistPath: join13(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
4684
+ units.push({ label, plistPath: join14(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
4608
4685
  }
4609
4686
  return units;
4610
4687
  }
@@ -4617,9 +4694,9 @@ function removeLaunchdUnit(unit, dryRun, log) {
4617
4694
  log.emit(`would unload and remove ${unit.label}`);
4618
4695
  return;
4619
4696
  }
4620
- if (existsSync11(unit.plistPath)) spawnSync4("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
4697
+ if (existsSync12(unit.plistPath)) spawnSync4("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
4621
4698
  spawnSync4("launchctl", ["remove", unit.label], { encoding: "utf8" });
4622
- if (existsSync11(unit.plistPath)) unlinkSync(unit.plistPath);
4699
+ if (existsSync12(unit.plistPath)) unlinkSync(unit.plistPath);
4623
4700
  log.emit(`removed ${unit.label} (${unit.plistPath})`);
4624
4701
  }
4625
4702
  function clearForeignLaunchdUnits(crewHome, replace, dryRun, log, dir, loaded) {
@@ -4697,24 +4774,24 @@ function detectSystemd() {
4697
4774
  }
4698
4775
 
4699
4776
  // src/ship-attention.ts
4700
- import { existsSync as existsSync13, statfsSync } from "fs";
4777
+ import { existsSync as existsSync14, statfsSync } from "fs";
4701
4778
 
4702
4779
  // src/git.ts
4703
- import { execFileSync as execFileSync2 } from "child_process";
4780
+ import { execFileSync as execFileSync3 } from "child_process";
4704
4781
  import {
4705
- existsSync as existsSync12,
4706
- mkdirSync as mkdirSync8,
4782
+ existsSync as existsSync13,
4783
+ mkdirSync as mkdirSync9,
4707
4784
  openSync as openSync2,
4708
4785
  closeSync as closeSync2,
4709
4786
  writeSync,
4710
- statSync as statSync5,
4787
+ statSync as statSync6,
4711
4788
  unlinkSync as unlinkSync2,
4712
- readFileSync as readFileSync13,
4713
- writeFileSync as writeFileSync9,
4789
+ readFileSync as readFileSync14,
4790
+ writeFileSync as writeFileSync10,
4714
4791
  readdirSync as readdirSync4,
4715
4792
  rmSync as rmSync8
4716
4793
  } from "fs";
4717
- import { dirname as dirname6, basename as basename5, join as join14 } from "path";
4794
+ import { dirname as dirname7, basename as basename5, join as join15 } from "path";
4718
4795
  var GitError = class extends Error {
4719
4796
  };
4720
4797
  function streamText(x) {
@@ -4722,7 +4799,7 @@ function streamText(x) {
4722
4799
  }
4723
4800
  function gitRaw(cwd, args) {
4724
4801
  try {
4725
- return execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
4802
+ return execFileSync3("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
4726
4803
  } catch (e) {
4727
4804
  const err3 = e;
4728
4805
  const why = [err3.stderr, err3.stdout].map(streamText).filter(Boolean).join(" / ");
@@ -4731,7 +4808,7 @@ function gitRaw(cwd, args) {
4731
4808
  }
4732
4809
  function git(cwd, args) {
4733
4810
  try {
4734
- return execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
4811
+ return execFileSync3("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
4735
4812
  } catch (e) {
4736
4813
  const err3 = e;
4737
4814
  const why = [err3.stderr, err3.stdout].map(streamText).filter(Boolean).join(" / ");
@@ -4861,7 +4938,7 @@ function worktreeForNumber(cwd, num) {
4861
4938
  return worktrees(cwd).find((w) => re.test(basename5(w.path))) ?? null;
4862
4939
  }
4863
4940
  var commitBodies = (cwd, range) => gitOk(cwd, ["log", range, "--format=%B"]) ?? "";
4864
- var resolve2 = (cwd, ref) => gitOk(cwd, ["rev-parse", "-q", "--verify", `${ref}^{commit}`]);
4941
+ var resolve3 = (cwd, ref) => gitOk(cwd, ["rev-parse", "-q", "--verify", `${ref}^{commit}`]);
4865
4942
  var countCommits = (cwd, range) => Number.parseInt(gitOk(cwd, ["rev-list", "--count", range]) ?? "0", 10);
4866
4943
  function latestReleaseTag(cwd, pattern = "v*", ref = "HEAD") {
4867
4944
  return gitOk(cwd, ["describe", "--tags", "--abbrev=0", "--match", pattern, ref]);
@@ -4870,7 +4947,7 @@ function firstReleaseTagContaining(cwd, sha, pattern = "v*") {
4870
4947
  const out = gitOk(cwd, ["tag", "--contains", sha, "--list", pattern, "--sort=creatordate"]);
4871
4948
  return out?.split("\n").map((l) => l.trim()).find(Boolean) ?? null;
4872
4949
  }
4873
- var tagCommit = (cwd, tag) => resolve2(cwd, tag);
4950
+ var tagCommit = (cwd, tag) => resolve3(cwd, tag);
4874
4951
  function createReleaseTag(cwd, tag, sha, message) {
4875
4952
  git(cwd, ["tag", "-a", tag, sha, "-m", message]);
4876
4953
  }
@@ -4902,9 +4979,9 @@ function keyCommitsInRange(cwd, key, range) {
4902
4979
  }
4903
4980
  function patchIdOf(cwd, from, to) {
4904
4981
  try {
4905
- const diff = execFileSync2("git", ["diff", from, to], { cwd, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
4982
+ const diff = execFileSync3("git", ["diff", from, to], { cwd, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
4906
4983
  if (!diff) return null;
4907
- const out = execFileSync2("git", ["patch-id", "--stable"], { cwd, encoding: "utf8", input: diff });
4984
+ const out = execFileSync3("git", ["patch-id", "--stable"], { cwd, encoding: "utf8", input: diff });
4908
4985
  return out.split(" ")[0]?.trim() || null;
4909
4986
  } catch {
4910
4987
  return null;
@@ -5094,7 +5171,7 @@ function recoverStrandedReleaseCheckouts(cwd) {
5094
5171
  if (line.startsWith("worktree ")) path = line.slice("worktree ".length);
5095
5172
  else if (line.startsWith("branch refs/heads/crew/release-") && path) {
5096
5173
  gitOk(cwd, ["worktree", "remove", "--force", path]);
5097
- const root = dirname6(path);
5174
+ const root = dirname7(path);
5098
5175
  if (basename5(root).startsWith("crew-release-")) rmSync8(root, { recursive: true, force: true });
5099
5176
  }
5100
5177
  }
@@ -5112,11 +5189,11 @@ function sweepStaleReleaseRoots(tmp, now = Date.now(), maxAgeMs = 60 * 60 * 1e3)
5112
5189
  }
5113
5190
  for (const name of names) {
5114
5191
  if (!name.startsWith("crew-release-")) continue;
5115
- const root = join14(tmp, name);
5192
+ const root = join15(tmp, name);
5116
5193
  try {
5117
- const st = statSync5(root);
5194
+ const st = statSync6(root);
5118
5195
  if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
5119
- if (existsSync12(join14(root, "checkout"))) continue;
5196
+ if (existsSync13(join15(root, "checkout"))) continue;
5120
5197
  rmSync8(root, { recursive: true, force: true });
5121
5198
  removed.push(name);
5122
5199
  } catch {
@@ -5168,16 +5245,16 @@ function originUrlOf(dir, remoteName) {
5168
5245
  }
5169
5246
  }
5170
5247
  function ensureRepoCheckout(dir, remote, opts = {}) {
5171
- if (existsSync12(`${dir}/.git`)) return false;
5248
+ if (existsSync13(`${dir}/.git`)) return false;
5172
5249
  if (!remote) {
5173
5250
  throw new GitError(
5174
5251
  `${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`
5175
5252
  );
5176
5253
  }
5177
- mkdirSync8(dirname6(dir), { recursive: true });
5254
+ mkdirSync9(dirname7(dir), { recursive: true });
5178
5255
  const url = cloneUrlFor(remote, opts);
5179
5256
  try {
5180
- execFileSync2("git", ["clone", url, dir], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
5257
+ execFileSync3("git", ["clone", url, dir], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
5181
5258
  } catch (e) {
5182
5259
  const err3 = e;
5183
5260
  const why = [err3.stderr, err3.stdout].map(streamText).filter(Boolean).join(" / ");
@@ -5199,7 +5276,7 @@ function sleepSync(ms3) {
5199
5276
  }
5200
5277
  function acquireCheckoutLock(dir, timeoutMs = 3e4) {
5201
5278
  const lockPath = checkoutLockPath(dir);
5202
- mkdirSync8(dirname6(lockPath), { recursive: true });
5279
+ mkdirSync9(dirname7(lockPath), { recursive: true });
5203
5280
  const deadline = Date.now() + timeoutMs;
5204
5281
  for (; ; ) {
5205
5282
  try {
@@ -5217,7 +5294,7 @@ function acquireCheckoutLock(dir, timeoutMs = 3e4) {
5217
5294
  const err3 = e;
5218
5295
  if (err3.code !== "EEXIST") throw err3;
5219
5296
  try {
5220
- if (Date.now() - statSync5(lockPath).mtimeMs > CHECKOUT_LOCK_STALE_MS) {
5297
+ if (Date.now() - statSync6(lockPath).mtimeMs > CHECKOUT_LOCK_STALE_MS) {
5221
5298
  try {
5222
5299
  unlinkSync2(lockPath);
5223
5300
  } catch {
@@ -5236,17 +5313,17 @@ function acquireCheckoutLock(dir, timeoutMs = 3e4) {
5236
5313
  function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base, clone = {}) {
5237
5314
  const release = acquireCheckoutLock(dir);
5238
5315
  try {
5239
- if (!existsSync12(`${dir}/.git`) && githubRemote) {
5316
+ if (!existsSync13(`${dir}/.git`) && githubRemote) {
5240
5317
  const url = cloneUrlFor(githubRemote, clone);
5241
5318
  const marker = cloneFailureMarker(dir);
5242
- if (existsSync12(marker) && readFileSync13(marker, "utf8").split("\n")[0] === url) {
5243
- return { action: "clone-failed", detail: readFileSync13(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
5319
+ if (existsSync13(marker) && readFileSync14(marker, "utf8").split("\n")[0] === url) {
5320
+ return { action: "clone-failed", detail: readFileSync14(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
5244
5321
  }
5245
5322
  try {
5246
5323
  ensureRepoCheckout(dir, githubRemote, clone);
5247
5324
  } catch (e) {
5248
5325
  if (!(e instanceof GitError)) throw e;
5249
- writeFileSync9(marker, `${url}
5326
+ writeFileSync10(marker, `${url}
5250
5327
  ${e.message}`);
5251
5328
  return { action: "clone-failed", detail: e.message, repeat: false };
5252
5329
  }
@@ -5472,7 +5549,7 @@ function noteHeldWorktree(state, h, nowMs = Date.now()) {
5472
5549
  function reconcileHeldWorktrees(state, repo, log, stillRefused) {
5473
5550
  for (const i of state.shipAttention().previous()) {
5474
5551
  if (i.kind !== "worktree_held" || !i.held || i.held.repo !== repo) continue;
5475
- const gone = !existsSync13(i.held.path);
5552
+ const gone = !existsSync14(i.held.path);
5476
5553
  if (!gone && !(stillRefused && !stillRefused.has(i.held.path))) continue;
5477
5554
  clearShipAttention(state, i.key);
5478
5555
  log.emit(
@@ -5600,13 +5677,13 @@ function attentionProblems(items, already) {
5600
5677
  // src/sandbox-image.ts
5601
5678
  import { createHash as createHash6 } from "crypto";
5602
5679
  import { spawn as spawn4 } from "child_process";
5603
- import { execFileSync as execFileSync6 } from "child_process";
5604
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync21, renameSync as renameSync3, writeFileSync as writeFileSync15, rmSync as rmSync13 } from "fs";
5605
- import { dirname as dirname10, join as join26 } from "path";
5680
+ import { execFileSync as execFileSync7 } from "child_process";
5681
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync22, renameSync as renameSync3, writeFileSync as writeFileSync16, rmSync as rmSync13 } from "fs";
5682
+ import { dirname as dirname11, join as join27 } from "path";
5606
5683
  import { tmpdir as tmpdir3 } from "os";
5607
5684
 
5608
5685
  // src/passenger-containers.ts
5609
- import { execFileSync as execFileSync5 } from "child_process";
5686
+ import { execFileSync as execFileSync6 } from "child_process";
5610
5687
  import { createHash as createHash5 } from "crypto";
5611
5688
 
5612
5689
  // src/sandbox-cache.ts
@@ -5615,32 +5692,32 @@ import { promisify as promisify2 } from "util";
5615
5692
 
5616
5693
  // src/deprovision-host.ts
5617
5694
  import { spawnSync as spawnSync6 } from "child_process";
5618
- import { existsSync as existsSync24, readdirSync as readdirSync11 } from "fs";
5695
+ import { existsSync as existsSync25, readdirSync as readdirSync11 } from "fs";
5619
5696
 
5620
5697
  // src/interrupted.ts
5621
- import { existsSync as existsSync22, readdirSync as readdirSync9, readFileSync as readFileSync20 } from "fs";
5622
- import { join as join24 } from "path";
5698
+ import { existsSync as existsSync23, readdirSync as readdirSync9, readFileSync as readFileSync21 } from "fs";
5699
+ import { join as join25 } from "path";
5623
5700
 
5624
5701
  // src/agent.ts
5625
5702
  import { spawnSync as spawnSync5 } from "child_process";
5626
- import { readFileSync as readFileSync19, existsSync as existsSync21, statSync as statSync7, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
5703
+ import { readFileSync as readFileSync20, existsSync as existsSync22, statSync as statSync8, mkdirSync as mkdirSync14, writeFileSync as writeFileSync15, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
5627
5704
  import { StringDecoder } from "string_decoder";
5628
5705
  import { finished } from "stream/promises";
5629
- import { join as join23, dirname as dirname9, basename as basename9 } from "path";
5706
+ import { join as join24, dirname as dirname10, basename as basename9 } from "path";
5630
5707
  import { createHash as createHash4 } from "crypto";
5631
5708
 
5632
5709
  // src/state.ts
5633
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync14, mkdirSync as mkdirSync9, rmSync as rmSync9, statSync as statSync6, readdirSync as readdirSync5 } from "fs";
5634
- import { join as join15 } from "path";
5710
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, existsSync as existsSync15, mkdirSync as mkdirSync10, rmSync as rmSync9, statSync as statSync7, readdirSync as readdirSync5 } from "fs";
5711
+ import { join as join16 } from "path";
5635
5712
  var EPOCH = "1970-01-01T00:00:00.000Z";
5636
5713
  var State = class _State {
5637
5714
  dir;
5638
5715
  constructor(dir) {
5639
5716
  this.dir = dir;
5640
- mkdirSync9(dir, { recursive: true });
5717
+ mkdirSync10(dir, { recursive: true });
5641
5718
  }
5642
5719
  path(...parts) {
5643
- return join15(this.dir, ...parts);
5720
+ return join16(this.dir, ...parts);
5644
5721
  }
5645
5722
  /**
5646
5723
  * The timestamp every "new since last poll" check is measured against.
@@ -5651,14 +5728,14 @@ var State = class _State {
5651
5728
  */
5652
5729
  watermark() {
5653
5730
  try {
5654
- const v = readFileSync14(this.path(".poll-watermark"), "utf8").trim();
5731
+ const v = readFileSync15(this.path(".poll-watermark"), "utf8").trim();
5655
5732
  return v || EPOCH;
5656
5733
  } catch {
5657
5734
  return EPOCH;
5658
5735
  }
5659
5736
  }
5660
5737
  setWatermark(at) {
5661
- writeFileSync10(this.path(".poll-watermark"), `${at}
5738
+ writeFileSync11(this.path(".poll-watermark"), `${at}
5662
5739
  `);
5663
5740
  }
5664
5741
  /**
@@ -5676,16 +5753,16 @@ var State = class _State {
5676
5753
  }
5677
5754
  /** Whole-crew pause, and the per-role sentinels one level down. */
5678
5755
  isPaused() {
5679
- return existsSync14(this.path(".crew-paused"));
5756
+ return existsSync15(this.path(".crew-paused"));
5680
5757
  }
5681
5758
  isRolePaused(role) {
5682
- return existsSync14(this.path(`.role-paused-${role}`));
5759
+ return existsSync15(this.path(`.role-paused-${role}`));
5683
5760
  }
5684
5761
  pausedRoles(roles) {
5685
5762
  return new Set(roles.filter((r) => this.isRolePaused(r)));
5686
5763
  }
5687
5764
  pause(role) {
5688
- writeFileSync10(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), "");
5765
+ writeFileSync11(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), "");
5689
5766
  }
5690
5767
  resume(role) {
5691
5768
  rmSync9(role ? this.path(`.role-paused-${role}`) : this.path(".crew-paused"), { force: true });
@@ -5696,10 +5773,10 @@ var State = class _State {
5696
5773
  * while agents keep working.
5697
5774
  */
5698
5775
  isReleasePaused() {
5699
- return existsSync14(this.path(".release-paused"));
5776
+ return existsSync15(this.path(".release-paused"));
5700
5777
  }
5701
5778
  setReleasePaused(paused) {
5702
- if (paused) writeFileSync10(this.path(".release-paused"), "");
5779
+ if (paused) writeFileSync11(this.path(".release-paused"), "");
5703
5780
  else rmSync9(this.path(".release-paused"), { force: true });
5704
5781
  }
5705
5782
  /**
@@ -5737,7 +5814,7 @@ var State = class _State {
5737
5814
  const name = _State.safe(rawName);
5738
5815
  const file = this.path(`.${name}.lock`);
5739
5816
  try {
5740
- const existing = Number.parseInt(readFileSync14(file, "utf8").trim(), 10);
5817
+ const existing = Number.parseInt(readFileSync15(file, "utf8").trim(), 10);
5741
5818
  if (Number.isFinite(existing) && existing > 0) {
5742
5819
  try {
5743
5820
  process.kill(existing, 0);
@@ -5747,7 +5824,7 @@ var State = class _State {
5747
5824
  }
5748
5825
  } catch {
5749
5826
  }
5750
- writeFileSync10(file, `${process.pid}
5827
+ writeFileSync11(file, `${process.pid}
5751
5828
  `);
5752
5829
  return {
5753
5830
  ok: true,
@@ -5775,7 +5852,7 @@ var State = class _State {
5775
5852
  if (!/^\.release-.+\.lock$/.test(n)) continue;
5776
5853
  let pid = 0;
5777
5854
  try {
5778
- pid = Number.parseInt(readFileSync14(this.path(n), "utf8").trim(), 10);
5855
+ pid = Number.parseInt(readFileSync15(this.path(n), "utf8").trim(), 10);
5779
5856
  } catch {
5780
5857
  continue;
5781
5858
  }
@@ -5861,7 +5938,7 @@ var State = class _State {
5861
5938
  ticketInFlight(recordId) {
5862
5939
  const file = this.path(`.${_State.safe(`crew-ticket-${recordId}`)}.lock`);
5863
5940
  try {
5864
- const pid = Number.parseInt(readFileSync14(file, "utf8").trim(), 10);
5941
+ const pid = Number.parseInt(readFileSync15(file, "utf8").trim(), 10);
5865
5942
  if (!Number.isFinite(pid) || pid <= 0) return false;
5866
5943
  process.kill(pid, 0);
5867
5944
  return true;
@@ -5895,7 +5972,7 @@ var State = class _State {
5895
5972
  const heldReported = `.release-held-${suffix}`;
5896
5973
  const read = (f) => {
5897
5974
  try {
5898
- return readFileSync14(this.path(f), "utf8").trim() || null;
5975
+ return readFileSync15(this.path(f), "utf8").trim() || null;
5899
5976
  } catch {
5900
5977
  return null;
5901
5978
  }
@@ -5904,7 +5981,7 @@ var State = class _State {
5904
5981
  blockedCount: () => Number.parseInt(read(blocked) ?? "", 10) || 0,
5905
5982
  noteBlocked: () => {
5906
5983
  const n = (Number.parseInt(read(blocked) ?? "", 10) || 0) + 1;
5907
- writeFileSync10(this.path(blocked), `${n}
5984
+ writeFileSync11(this.path(blocked), `${n}
5908
5985
  `);
5909
5986
  return n;
5910
5987
  },
@@ -5921,7 +5998,7 @@ var State = class _State {
5921
5998
  */
5922
5999
  deployFailedSha: () => read(failed),
5923
6000
  noteDeployFailed: (sha) => {
5924
- writeFileSync10(this.path(failed), `${sha}
6001
+ writeFileSync11(this.path(failed), `${sha}
5925
6002
  `);
5926
6003
  },
5927
6004
  clearDeployFailed: () => {
@@ -5935,7 +6012,7 @@ var State = class _State {
5935
6012
  noteTestGateFailed: (sha) => {
5936
6013
  const [prev, n] = (read(gateFailed) ?? "").split(" ");
5937
6014
  const next = prev === sha ? (Number.parseInt(n ?? "", 10) || 0) + 1 : 1;
5938
- writeFileSync10(this.path(gateFailed), `${sha} ${next}
6015
+ writeFileSync11(this.path(gateFailed), `${sha} ${next}
5939
6016
  `);
5940
6017
  return next;
5941
6018
  },
@@ -5945,13 +6022,13 @@ var State = class _State {
5945
6022
  /** Whether the board was already told about this head's red gate. */
5946
6023
  heldReported: () => {
5947
6024
  try {
5948
- return readFileSync14(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
6025
+ return readFileSync15(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
5949
6026
  } catch {
5950
6027
  return null;
5951
6028
  }
5952
6029
  },
5953
6030
  noteHeldReported: (signature) => {
5954
- writeFileSync10(this.path(heldReported), `${signature}
6031
+ writeFileSync11(this.path(heldReported), `${signature}
5955
6032
  `);
5956
6033
  },
5957
6034
  clearHeldReported: () => {
@@ -5959,7 +6036,7 @@ var State = class _State {
5959
6036
  },
5960
6037
  testGateReported: (sha) => read(gateReported) === sha,
5961
6038
  noteTestGateReported: (sha) => {
5962
- writeFileSync10(this.path(gateReported), `${sha}
6039
+ writeFileSync11(this.path(gateReported), `${sha}
5963
6040
  `);
5964
6041
  }
5965
6042
  };
@@ -5982,7 +6059,7 @@ var State = class _State {
5982
6059
  const ticketStreakFile = `.fairness-ticket-streak-${suffix}`;
5983
6060
  const read = (f) => {
5984
6061
  try {
5985
- return readFileSync14(this.path(f), "utf8").trim() || null;
6062
+ return readFileSync15(this.path(f), "utf8").trim() || null;
5986
6063
  } catch {
5987
6064
  return null;
5988
6065
  }
@@ -6015,18 +6092,18 @@ var State = class _State {
6015
6092
  clear2();
6016
6093
  return;
6017
6094
  }
6018
- writeFileSync10(this.path(streakFile), `${this.readInt(streakFile) + 1}
6095
+ writeFileSync11(this.path(streakFile), `${this.readInt(streakFile) + 1}
6019
6096
  `);
6020
6097
  const prior = read(ticketFile);
6021
6098
  if (topTicket && topTicket === prior) {
6022
- writeFileSync10(this.path(ticketStreakFile), `${this.readInt(ticketStreakFile) + 1}
6099
+ writeFileSync11(this.path(ticketStreakFile), `${this.readInt(ticketStreakFile) + 1}
6023
6100
  `);
6024
6101
  } else if (topTicket) {
6025
- writeFileSync10(this.path(ticketFile), `${topTicket}
6102
+ writeFileSync11(this.path(ticketFile), `${topTicket}
6026
6103
  `);
6027
- writeFileSync10(this.path(sinceFile), `${(/* @__PURE__ */ new Date()).toISOString()}
6104
+ writeFileSync11(this.path(sinceFile), `${(/* @__PURE__ */ new Date()).toISOString()}
6028
6105
  `);
6029
- writeFileSync10(this.path(ticketStreakFile), "1\n");
6106
+ writeFileSync11(this.path(ticketStreakFile), "1\n");
6030
6107
  }
6031
6108
  }
6032
6109
  };
@@ -6047,14 +6124,14 @@ var State = class _State {
6047
6124
  return {
6048
6125
  previous: () => {
6049
6126
  try {
6050
- const parsed = JSON.parse(readFileSync14(this.path(file), "utf8"));
6127
+ const parsed = JSON.parse(readFileSync15(this.path(file), "utf8"));
6051
6128
  return parsed && typeof parsed === "object" ? parsed : {};
6052
6129
  } catch {
6053
6130
  return {};
6054
6131
  }
6055
6132
  },
6056
6133
  persist: (next) => {
6057
- writeFileSync10(this.path(file), JSON.stringify(next));
6134
+ writeFileSync11(this.path(file), JSON.stringify(next));
6058
6135
  }
6059
6136
  };
6060
6137
  }
@@ -6070,14 +6147,14 @@ var State = class _State {
6070
6147
  return {
6071
6148
  previous: () => {
6072
6149
  try {
6073
- const parsed = JSON.parse(readFileSync14(this.path(file), "utf8"));
6150
+ const parsed = JSON.parse(readFileSync15(this.path(file), "utf8"));
6074
6151
  return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : [];
6075
6152
  } catch {
6076
6153
  return [];
6077
6154
  }
6078
6155
  },
6079
6156
  persist: (next) => {
6080
- writeFileSync10(this.path(file), JSON.stringify(next));
6157
+ writeFileSync11(this.path(file), JSON.stringify(next));
6081
6158
  }
6082
6159
  };
6083
6160
  }
@@ -6091,14 +6168,14 @@ var State = class _State {
6091
6168
  return {
6092
6169
  previous: () => {
6093
6170
  try {
6094
- const parsed = JSON.parse(readFileSync14(this.path(file), "utf8"));
6171
+ const parsed = JSON.parse(readFileSync15(this.path(file), "utf8"));
6095
6172
  return Array.isArray(parsed) ? parsed : [];
6096
6173
  } catch {
6097
6174
  return [];
6098
6175
  }
6099
6176
  },
6100
6177
  persist: (next) => {
6101
- writeFileSync10(this.path(file), JSON.stringify(next));
6178
+ writeFileSync11(this.path(file), JSON.stringify(next));
6102
6179
  }
6103
6180
  };
6104
6181
  }
@@ -6111,11 +6188,11 @@ var State = class _State {
6111
6188
  const file = ".release-last-run";
6112
6189
  return {
6113
6190
  touch: () => {
6114
- writeFileSync10(this.path(file), (/* @__PURE__ */ new Date()).toISOString());
6191
+ writeFileSync11(this.path(file), (/* @__PURE__ */ new Date()).toISOString());
6115
6192
  },
6116
6193
  at: () => {
6117
6194
  try {
6118
- return statSync6(this.path(file)).mtimeMs;
6195
+ return statSync7(this.path(file)).mtimeMs;
6119
6196
  } catch {
6120
6197
  return null;
6121
6198
  }
@@ -6124,7 +6201,7 @@ var State = class _State {
6124
6201
  }
6125
6202
  readInt(file) {
6126
6203
  try {
6127
- return Number.parseInt(readFileSync14(this.path(file), "utf8").trim(), 10) || 0;
6204
+ return Number.parseInt(readFileSync15(this.path(file), "utf8").trim(), 10) || 0;
6128
6205
  } catch {
6129
6206
  return 0;
6130
6207
  }
@@ -6665,8 +6742,8 @@ var PERISCOPE_COLUMNS = [
6665
6742
  ];
6666
6743
 
6667
6744
  // src/agent-log.ts
6668
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readdirSync as readdirSync6, readFileSync as readFileSync15, rmSync as rmSync10, writeFileSync as writeFileSync11 } from "fs";
6669
- import { join as join16 } from "path";
6745
+ import { existsSync as existsSync16, mkdirSync as mkdirSync11, readdirSync as readdirSync6, readFileSync as readFileSync16, rmSync as rmSync10, writeFileSync as writeFileSync12 } from "fs";
6746
+ import { join as join17 } from "path";
6670
6747
 
6671
6748
  // src/pending-backoff.ts
6672
6749
  var sleepBackoff = (attempts) => Math.min(6e4 * 2 ** Math.max(0, attempts - 1), 30 * 6e4);
@@ -6838,20 +6915,20 @@ async function addRunToTicket(target, t, recordId, run3) {
6838
6915
  };
6839
6916
  }
6840
6917
  }
6841
- var queueDir = (stateDir) => join16(stateDir, "pending-agent-logs");
6918
+ var queueDir = (stateDir) => join17(stateDir, "pending-agent-logs");
6842
6919
  function queueAgentRun(stateDir, name, q2) {
6843
- mkdirSync10(queueDir(stateDir), { recursive: true });
6844
- writeFileSync11(join16(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
6920
+ mkdirSync11(queueDir(stateDir), { recursive: true });
6921
+ writeFileSync12(join17(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
6845
6922
  }
6846
6923
  async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
6847
6924
  const dir = queueDir(stateDir);
6848
- if (!existsSync15(dir)) return 0;
6925
+ if (!existsSync16(dir)) return 0;
6849
6926
  let sent = 0;
6850
6927
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
6851
- const path = join16(dir, f);
6928
+ const path = join17(dir, f);
6852
6929
  let q2;
6853
6930
  try {
6854
- q2 = JSON.parse(readFileSync15(path, "utf8"));
6931
+ q2 = JSON.parse(readFileSync16(path, "utf8"));
6855
6932
  } catch {
6856
6933
  continue;
6857
6934
  }
@@ -6880,7 +6957,7 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
6880
6957
  continue;
6881
6958
  }
6882
6959
  const attempts = q2.attempts + 1;
6883
- writeFileSync11(path, JSON.stringify({
6960
+ writeFileSync12(path, JSON.stringify({
6884
6961
  ...q2,
6885
6962
  attempts,
6886
6963
  warned: true,
@@ -6891,20 +6968,20 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
6891
6968
  }
6892
6969
  return sent;
6893
6970
  }
6894
- var countersDir = (stateDir) => join16(stateDir, "pending-ticket-counters");
6971
+ var countersDir = (stateDir) => join17(stateDir, "pending-ticket-counters");
6895
6972
  function queueTicketCounters(stateDir, name, q2) {
6896
- mkdirSync10(countersDir(stateDir), { recursive: true });
6897
- writeFileSync11(join16(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
6973
+ mkdirSync11(countersDir(stateDir), { recursive: true });
6974
+ writeFileSync12(join17(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
6898
6975
  }
6899
6976
  async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
6900
6977
  const dir = countersDir(stateDir);
6901
- if (!existsSync15(dir)) return 0;
6978
+ if (!existsSync16(dir)) return 0;
6902
6979
  let sent = 0;
6903
6980
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
6904
- const path = join16(dir, f);
6981
+ const path = join17(dir, f);
6905
6982
  let q2;
6906
6983
  try {
6907
- q2 = JSON.parse(readFileSync15(path, "utf8"));
6984
+ q2 = JSON.parse(readFileSync16(path, "utf8"));
6908
6985
  } catch {
6909
6986
  continue;
6910
6987
  }
@@ -6923,7 +7000,7 @@ async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /
6923
7000
  continue;
6924
7001
  }
6925
7002
  const attempts = q2.attempts + 1;
6926
- writeFileSync11(path, JSON.stringify({
7003
+ writeFileSync12(path, JSON.stringify({
6927
7004
  ...q2,
6928
7005
  attempts,
6929
7006
  warned: true,
@@ -7126,8 +7203,8 @@ var HandoffTranscriptTracker = class {
7126
7203
  };
7127
7204
 
7128
7205
  // src/pending-handoff.ts
7129
- import { existsSync as existsSync18, mkdirSync as mkdirSync11, readdirSync as readdirSync7, readFileSync as readFileSync16, rmSync as rmSync11, writeFileSync as writeFileSync12 } from "fs";
7130
- import { join as join19 } from "path";
7206
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, readdirSync as readdirSync7, readFileSync as readFileSync17, rmSync as rmSync11, writeFileSync as writeFileSync13 } from "fs";
7207
+ import { join as join20 } from "path";
7131
7208
 
7132
7209
  // src/ticket-branch.ts
7133
7210
  import { basename as basename7 } from "path";
@@ -8293,16 +8370,16 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
8293
8370
  }
8294
8371
 
8295
8372
  // src/run-worktree.ts
8296
- import { existsSync as existsSync17 } from "fs";
8297
- import { dirname as dirname7, join as join18 } from "path";
8373
+ import { existsSync as existsSync18 } from "fs";
8374
+ import { dirname as dirname8, join as join19 } from "path";
8298
8375
 
8299
8376
  // src/ports.ts
8300
- import { execFileSync as execFileSync3 } from "child_process";
8301
- import { existsSync as existsSync16 } from "fs";
8302
- import { join as join17 } from "path";
8377
+ import { execFileSync as execFileSync4 } from "child_process";
8378
+ import { existsSync as existsSync17 } from "fs";
8379
+ import { join as join18 } from "path";
8303
8380
  var run = (cmd, args) => {
8304
8381
  try {
8305
- return execFileSync3(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
8382
+ return execFileSync4(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
8306
8383
  } catch {
8307
8384
  return "";
8308
8385
  }
@@ -8344,7 +8421,7 @@ function ticketForPort(port, s = DEFAULT_PORTS) {
8344
8421
  }
8345
8422
  function worktreeExists(parent, prefix, n) {
8346
8423
  for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
8347
- if (existsSync16(join17(parent, name, ".git"))) return true;
8424
+ if (existsSync17(join18(parent, name, ".git"))) return true;
8348
8425
  }
8349
8426
  return false;
8350
8427
  }
@@ -8427,7 +8504,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
8427
8504
  const existing = worktreeForNumber(dir, num);
8428
8505
  const located = locateBranchForTicket(dir, cfg, t, role);
8429
8506
  const baseRef = hasRemote ? `${remote}/${base}` : base;
8430
- if (existing && !existing.prunable && existsSync17(existing.path)) {
8507
+ if (existing && !existing.prunable && existsSync18(existing.path)) {
8431
8508
  const branch = existing.branch;
8432
8509
  if (status(existing.path).length > 0) {
8433
8510
  return { action: opts.resumeInterrupted ? "resume-dirty" : "leave-dirty", path: existing.path, branch };
@@ -8443,7 +8520,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
8443
8520
  if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
8444
8521
  return { action: "recut", path: existing.path, branch, base: baseRef };
8445
8522
  }
8446
- const path = join18(dirname7(dir), worktreeForTicket(dir, cfg, t, role));
8523
+ const path = join19(dirname8(dir), worktreeForTicket(dir, cfg, t, role));
8447
8524
  if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
8448
8525
  return { action: "cut-remote", path, branch: located.branch };
8449
8526
  }
@@ -8497,7 +8574,7 @@ function dropEmptyRunBranch(dir, cfg, branch) {
8497
8574
  if (remoteConfigured(dir, remote) && remoteBranchExists(dir, remote, branch)) return false;
8498
8575
  if (countCommits(dir, `${baseRef}..${branch}`) !== 0) return false;
8499
8576
  const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
8500
- if (wt && existsSync17(wt.path)) {
8577
+ if (wt && existsSync18(wt.path)) {
8501
8578
  if (status(wt.path).length > 0) return false;
8502
8579
  if (gitOk(dir, ["worktree", "remove", wt.path]) === null) return false;
8503
8580
  }
@@ -8505,7 +8582,7 @@ function dropEmptyRunBranch(dir, cfg, branch) {
8505
8582
  }
8506
8583
  async function finishRunWorktree(dir, cfg, branch) {
8507
8584
  const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
8508
- if (!wt || !existsSync17(wt.path)) return { kind: "none" };
8585
+ if (!wt || !existsSync18(wt.path)) return { kind: "none" };
8509
8586
  if (cfg.hooks.handoff) return { kind: "kept-handoff", path: wt.path };
8510
8587
  if (status(wt.path).length > 0) return { kind: "kept-dirty", path: wt.path };
8511
8588
  if (remoteConfigured(dir, cfg.branch.remote) && worktreeHoldsUnpushed(wt.path, cfg.branch.remote)) {
@@ -8517,7 +8594,7 @@ async function finishRunWorktree(dir, cfg, branch) {
8517
8594
  if (pids.length) await killGently(pids);
8518
8595
  }
8519
8596
  gitOk(dir, ["worktree", "remove", wt.path]);
8520
- return existsSync17(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
8597
+ return existsSync18(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
8521
8598
  }
8522
8599
 
8523
8600
  // src/handoff-guard.ts
@@ -8900,20 +8977,20 @@ async function guardRunStrays(route2, ship, tracker, role, workingId, since2, lo
8900
8977
 
8901
8978
  // src/pending-handoff.ts
8902
8979
  var PENDING_MAX_AGE_MS = 48 * 36e5;
8903
- var pendingDirFor = (stateDir) => join19(stateDir, "pending-handoffs");
8904
- var stateDirOfStream = (streamPath) => join19(streamPath, "..", "..");
8980
+ var pendingDirFor = (stateDir) => join20(stateDir, "pending-handoffs");
8981
+ var stateDirOfStream = (streamPath) => join20(streamPath, "..", "..");
8905
8982
  function writePendingHandoff(stateDir, rec) {
8906
8983
  const dir = pendingDirFor(stateDir);
8907
- mkdirSync11(dir, { recursive: true });
8908
- writeFileSync12(join19(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
8984
+ mkdirSync12(dir, { recursive: true });
8985
+ writeFileSync13(join20(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
8909
8986
  }
8910
8987
  function listPendingHandoffs(stateDir, route2) {
8911
8988
  const dir = pendingDirFor(stateDir);
8912
- if (!existsSync18(dir)) return [];
8989
+ if (!existsSync19(dir)) return [];
8913
8990
  const out = [];
8914
8991
  for (const f of readdirSync7(dir).filter((n) => n.endsWith(".json")).sort()) {
8915
8992
  try {
8916
- const rec = JSON.parse(readFileSync16(join19(dir, f), "utf8"));
8993
+ const rec = JSON.parse(readFileSync17(join20(dir, f), "utf8"));
8917
8994
  if (rec.version === 1 && rec.ticketRecordId && (!route2 || rec.route === route2)) out.push(rec);
8918
8995
  } catch {
8919
8996
  }
@@ -8921,7 +8998,7 @@ function listPendingHandoffs(stateDir, route2) {
8921
8998
  return out;
8922
8999
  }
8923
9000
  function removePendingHandoff(stateDir, ticketRecordId) {
8924
- rmSync11(join19(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
9001
+ rmSync11(join20(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
8925
9002
  }
8926
9003
  function pendingHandoffFor(stateDir, ticketRecordId) {
8927
9004
  return listPendingHandoffs(stateDir).find((r) => r.ticketRecordId === ticketRecordId);
@@ -9031,8 +9108,8 @@ function expandSkillMarkers(text2, lookup2, opts = {}) {
9031
9108
  }
9032
9109
 
9033
9110
  // src/skills.ts
9034
- import { readFileSync as readFileSync18, readdirSync as readdirSync8, existsSync as existsSync20 } from "fs";
9035
- import { join as join21 } from "path";
9111
+ import { readFileSync as readFileSync19, readdirSync as readdirSync8, existsSync as existsSync21 } from "fs";
9112
+ import { join as join22 } from "path";
9036
9113
  import { parse as parseYaml } from "yaml";
9037
9114
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
9038
9115
 
@@ -9048,8 +9125,8 @@ ${additions}`;
9048
9125
  }
9049
9126
 
9050
9127
  // src/agents.ts
9051
- import { readFileSync as readFileSync17, existsSync as existsSync19 } from "fs";
9052
- import { join as join20 } from "path";
9128
+ import { readFileSync as readFileSync18, existsSync as existsSync20 } from "fs";
9129
+ import { join as join21 } from "path";
9053
9130
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
9054
9131
  var PERSONA_NAME = {
9055
9132
  dev: "Developer",
@@ -9063,12 +9140,12 @@ var CREW_AGENT_COLUMN = "agent_id";
9063
9140
  var AgentsSyncError = class extends Error {
9064
9141
  };
9065
9142
  function personaDefaultPrompt(promptsDir, role) {
9066
- const briefPath = join20(promptsDir, "personas", `lane-${role}.md`);
9067
- if (!existsSync19(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
9068
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync17(briefPath, "utf8");
9069
- const commonPath = join20(promptsDir, "personas", "common.md");
9070
- if (!existsSync19(commonPath)) return readFileSync17(briefPath, "utf8");
9071
- return `${readFileSync17(commonPath, "utf8")}${readFileSync17(briefPath, "utf8")}`;
9143
+ const briefPath = join21(promptsDir, "personas", `lane-${role}.md`);
9144
+ if (!existsSync20(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
9145
+ if (SELF_CONTAINED_ROLES.has(role)) return readFileSync18(briefPath, "utf8");
9146
+ const commonPath = join21(promptsDir, "personas", "common.md");
9147
+ if (!existsSync20(commonPath)) return readFileSync18(briefPath, "utf8");
9148
+ return `${readFileSync18(commonPath, "utf8")}${readFileSync18(briefPath, "utf8")}`;
9072
9149
  }
9073
9150
  async function resolveAgentId(route2, role, opts) {
9074
9151
  const cached = route2.resolved?.agentPersonas?.[role]?.agentId;
@@ -9515,7 +9592,7 @@ function describeCrewLink(o) {
9515
9592
  var SkillSyncError = class extends Error {
9516
9593
  };
9517
9594
  function parseSkillFile(path) {
9518
- const text2 = readFileSync18(path, "utf8");
9595
+ const text2 = readFileSync19(path, "utf8");
9519
9596
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text2);
9520
9597
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
9521
9598
  const frontmatter = match[1];
@@ -9528,9 +9605,9 @@ function parseSkillFile(path) {
9528
9605
  return { name: meta.name, description: meta.description, prompt: body, file: path };
9529
9606
  }
9530
9607
  function listLocalSkills(promptsDir) {
9531
- const dir = join21(promptsDir, "skills");
9532
- if (!existsSync20(dir)) return [];
9533
- return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join21(dir, f)));
9608
+ const dir = join22(promptsDir, "skills");
9609
+ if (!existsSync21(dir)) return [];
9610
+ return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join22(dir, f)));
9534
9611
  }
9535
9612
  async function syncSkills(route2, opts) {
9536
9613
  if (!route2.resolved) {
@@ -9714,10 +9791,10 @@ function skillMarkerWarnings(promptsDir, personaPrompts) {
9714
9791
  }
9715
9792
 
9716
9793
  // src/runner.ts
9717
- import { spawn as spawn2, execFile, execFileSync as execFileSync4 } from "child_process";
9718
- import { mkdtempSync, mkdirSync as mkdirSync12, writeFileSync as writeFileSync13, rmSync as rmSync12 } from "fs";
9794
+ import { spawn as spawn2, execFile, execFileSync as execFileSync5 } from "child_process";
9795
+ import { mkdtempSync, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, rmSync as rmSync12 } from "fs";
9719
9796
  import { tmpdir as tmpdir2 } from "os";
9720
- import { basename as basename8, dirname as dirname8, join as join22 } from "path";
9797
+ import { basename as basename8, dirname as dirname9, join as join23 } from "path";
9721
9798
  import { promisify } from "util";
9722
9799
 
9723
9800
  // src/run-limit.ts
@@ -9846,8 +9923,8 @@ var SandboxRunner = class {
9846
9923
  throw new Error(`cannot generate the sandbox policy: ${e.message}`);
9847
9924
  }
9848
9925
  policyFile = policyPathFor(plan.streamPath);
9849
- mkdirSync12(dirname8(policyFile), { recursive: true });
9850
- writeFileSync13(policyFile, generated.yaml, { mode: 384 });
9926
+ mkdirSync13(dirname9(policyFile), { recursive: true });
9927
+ writeFileSync14(policyFile, generated.yaml, { mode: 384 });
9851
9928
  }
9852
9929
  const create2 = ["sandbox", "create", "--name", name, "--detach"];
9853
9930
  if (policyFile) create2.push("--policy", policyFile);
@@ -9890,10 +9967,10 @@ var SandboxRunner = class {
9890
9967
  const harness = cfg.agentBin ?? basename8(plan.bin);
9891
9968
  const viaUpload = Buffer.byteLength(plan.prompt) > SANDBOX_STDIN_LIMIT;
9892
9969
  if (viaUpload) {
9893
- const tmp = mkdtempSync(join22(tmpdir2(), "crew-prompt-"));
9970
+ const tmp = mkdtempSync(join23(tmpdir2(), "crew-prompt-"));
9894
9971
  try {
9895
- const file = join22(tmp, "prompt.txt");
9896
- writeFileSync13(file, plan.prompt, { mode: 384 });
9972
+ const file = join23(tmp, "prompt.txt");
9973
+ writeFileSync14(file, plan.prompt, { mode: 384 });
9897
9974
  await this.os(["sandbox", "upload", name, file, SANDBOX_PROMPT_PATH]);
9898
9975
  } finally {
9899
9976
  rmSync12(tmp, { recursive: true, force: true });
@@ -9959,7 +10036,7 @@ function describeExecError(e) {
9959
10036
  }
9960
10037
  function gitOrigin(cwd) {
9961
10038
  try {
9962
- return execFileSync4("git", ["-C", cwd, "remote", "get-url", "origin"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
10039
+ return execFileSync5("git", ["-C", cwd, "remote", "get-url", "origin"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
9963
10040
  } catch {
9964
10041
  return null;
9965
10042
  }
@@ -9970,7 +10047,7 @@ function runnerFor(kind, sandbox) {
9970
10047
  function runnerOfSidecar(sc) {
9971
10048
  return sc.runner === "sandbox" ? new SandboxRunner(sc.sandboxBin ? { bin: sc.sandboxBin } : {}) : new HostRunner();
9972
10049
  }
9973
- function checkSandboxRunner(cfg = {}, run3 = (b, a) => execFileSync4(b, a, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 15e3 })) {
10050
+ function checkSandboxRunner(cfg = {}, run3 = (b, a) => execFileSync5(b, a, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 15e3 })) {
9974
10051
  const bin = cfg.bin ?? DEFAULT_SANDBOX_CONFIG.bin;
9975
10052
  let version;
9976
10053
  try {
@@ -10037,7 +10114,7 @@ var ALLOWANCE_REFUSAL_MARKER = "AI_ALLOWANCE_EXCEEDED";
10037
10114
  function streamShowsAllowanceRefusal(streamPath) {
10038
10115
  let raw;
10039
10116
  try {
10040
- raw = readFileSync19(streamPath, "utf8");
10117
+ raw = readFileSync20(streamPath, "utf8");
10041
10118
  } catch {
10042
10119
  return false;
10043
10120
  }
@@ -10060,26 +10137,26 @@ function streamShowsAllowanceRefusal(streamPath) {
10060
10137
  }
10061
10138
  return false;
10062
10139
  }
10063
- var digestPathFor = (stateDir, routeLabel, role, runId) => join23(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10140
+ var digestPathFor = (stateDir, routeLabel, role, runId) => join24(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10064
10141
  function planAgentRun(o) {
10065
- const briefPath = join23(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10066
- if (!existsSync21(briefPath)) {
10142
+ const briefPath = join24(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10143
+ if (!existsSync22(briefPath)) {
10067
10144
  throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
10068
10145
  }
10069
- const commonPath = join23(o.route.promptsDir, "personas", "common.md");
10070
- const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync21(commonPath);
10146
+ const commonPath = join24(o.route.promptsDir, "personas", "common.md");
10147
+ const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync22(commonPath);
10071
10148
  const now = o.now ?? (() => Date.now());
10072
10149
  const routeLabel = routeSlug(o.route.route);
10073
10150
  const ownDigest = o.runId ? digestPathFor(o.stateDir, routeLabel, o.role, o.runId) : void 0;
10074
- const digestPath2 = ownDigest && existsSync21(ownDigest) ? ownDigest : digestPathFor(o.stateDir, routeLabel, o.role);
10151
+ const digestPath2 = ownDigest && existsSync22(ownDigest) ? ownDigest : digestPathFor(o.stateDir, routeLabel, o.role);
10075
10152
  let digest;
10076
10153
  let ageSeconds;
10077
- if (existsSync21(digestPath2)) {
10078
- ageSeconds = Math.floor((now() - statSync7(digestPath2).mtimeMs) / 1e3);
10079
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync19(digestPath2, "utf8");
10154
+ if (existsSync22(digestPath2)) {
10155
+ ageSeconds = Math.floor((now() - statSync8(digestPath2).mtimeMs) / 1e3);
10156
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync20(digestPath2, "utf8");
10080
10157
  }
10081
- const common = usesCommon ? readFileSync19(commonPath, "utf8") : "";
10082
- const brief = readFileSync19(briefPath, "utf8");
10158
+ const common = usesCommon ? readFileSync20(commonPath, "utf8") : "";
10159
+ const brief = readFileSync20(briefPath, "utf8");
10083
10160
  const effectiveCommon = o.divergedPrompt ? "" : common;
10084
10161
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
10085
10162
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -10102,7 +10179,7 @@ function planAgentRun(o) {
10102
10179
  digest
10103
10180
  });
10104
10181
  const promptSha = createHash4("sha256").update(expanded.text).digest("hex");
10105
- const streamsDir = join23(o.stateDir, "streams");
10182
+ const streamsDir = join24(o.stateDir, "streams");
10106
10183
  const base = `${routeLabel}-${o.role}-${o.cycle}${o.runId ? `-${o.runId}` : ""}`;
10107
10184
  const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
10108
10185
  const { model } = seat;
@@ -10192,8 +10269,8 @@ function planAgentRun(o) {
10192
10269
  digestAttached: digest !== void 0,
10193
10270
  digestAgeSeconds: ageSeconds,
10194
10271
  model,
10195
- streamPath: join23(streamsDir, `${base}.jsonl`),
10196
- eventsPath: join23(streamsDir, `${base}.events.jsonl`),
10272
+ streamPath: join24(streamsDir, `${base}.jsonl`),
10273
+ eventsPath: join24(streamsDir, `${base}.events.jsonl`),
10197
10274
  ticket: o.ticket,
10198
10275
  ticketRecordId: o.ticketRecordId ?? null,
10199
10276
  repo: o.repo ?? null,
@@ -10224,7 +10301,7 @@ function describePlan(p) {
10224
10301
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
10225
10302
  function readRunSidecar(streamPath) {
10226
10303
  try {
10227
- const sc = JSON.parse(readFileSync19(pidPathFor(streamPath), "utf8"));
10304
+ const sc = JSON.parse(readFileSync20(pidPathFor(streamPath), "utf8"));
10228
10305
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
10229
10306
  } catch {
10230
10307
  return null;
@@ -10271,7 +10348,7 @@ async function killForDeadline(kill, pid, emit, role, ticket, spawnedAt, limitMi
10271
10348
  var withoutKey = ({ apiKey: _apiKey, ...rest }) => rest;
10272
10349
  function openSink(path) {
10273
10350
  try {
10274
- mkdirSync13(dirname9(path), { recursive: true });
10351
+ mkdirSync14(dirname10(path), { recursive: true });
10275
10352
  return createWriteStream(path, { flags: "a" });
10276
10353
  } catch {
10277
10354
  return void 0;
@@ -10453,7 +10530,7 @@ var RunObserver = class {
10453
10530
  };
10454
10531
  function writeRunMeta(id, startedAt, liveTicket, extra) {
10455
10532
  try {
10456
- writeFileSync14(`${id.streamPath}.meta.json`, JSON.stringify({
10533
+ writeFileSync15(`${id.streamPath}.meta.json`, JSON.stringify({
10457
10534
  route: id.route,
10458
10535
  role: id.role,
10459
10536
  hint: id.ticket,
@@ -10613,7 +10690,7 @@ function spawnAgent(plan, emit, hintMoved) {
10613
10690
  const id = identityOf(plan, adapter.harness);
10614
10691
  let streamFd;
10615
10692
  try {
10616
- mkdirSync13(dirname9(plan.streamPath), { recursive: true });
10693
+ mkdirSync14(dirname10(plan.streamPath), { recursive: true });
10617
10694
  streamFd = openSync3(plan.streamPath, "a");
10618
10695
  } catch (e) {
10619
10696
  void endSink(eventsSink);
@@ -10645,7 +10722,7 @@ function spawnAgent(plan, emit, hintMoved) {
10645
10722
  closeSync3(streamFd);
10646
10723
  return supervise(begun);
10647
10724
  function supervise({ child, handle, promptDelivered }) {
10648
- return new Promise((resolve7, reject) => {
10725
+ return new Promise((resolve8, reject) => {
10649
10726
  const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
10650
10727
  const deadline = deadlineFor(spawnedAt, plan.maxRunMinutes);
10651
10728
  if (child.pid !== void 0) {
@@ -10671,7 +10748,7 @@ function spawnAgent(plan, emit, hintMoved) {
10671
10748
  promptSha: plan.promptSha
10672
10749
  };
10673
10750
  try {
10674
- writeFileSync14(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
10751
+ writeFileSync15(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
10675
10752
  } catch {
10676
10753
  }
10677
10754
  }
@@ -10715,7 +10792,7 @@ function spawnAgent(plan, emit, hintMoved) {
10715
10792
  plan.agentLog?.apiKey,
10716
10793
  hintMoved
10717
10794
  );
10718
- resolve7({ code: code ?? 1, ms: ms3, ...handoffPending ? { handoffPending } : {}, ...killed ? { killed } : {} });
10795
+ resolve8({ code: code ?? 1, ms: ms3, ...handoffPending ? { handoffPending } : {}, ...killed ? { killed } : {} });
10719
10796
  });
10720
10797
  child.stdin?.on("error", (err3) => {
10721
10798
  emit.warn(`agent closed stdin before the prompt was delivered: ${err3.message}`, { step: "agent" });
@@ -10785,21 +10862,21 @@ var pidAlive = (pid) => {
10785
10862
  };
10786
10863
  function runFinished(streamPath) {
10787
10864
  const path = `${streamPath}.meta.json`;
10788
- if (!existsSync22(path)) return false;
10865
+ if (!existsSync23(path)) return false;
10789
10866
  try {
10790
- return JSON.parse(readFileSync20(path, "utf8")).running !== true;
10867
+ return JSON.parse(readFileSync21(path, "utf8")).running !== true;
10791
10868
  } catch {
10792
10869
  return true;
10793
10870
  }
10794
10871
  }
10795
10872
  function orphanedStreams(o) {
10796
- const dir = join24(o.stateDir, "streams");
10797
- if (!existsSync22(dir)) return [];
10873
+ const dir = join25(o.stateDir, "streams");
10874
+ if (!existsSync23(dir)) return [];
10798
10875
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
10799
10876
  const out = [];
10800
10877
  for (const name of readdirSync9(dir)) {
10801
10878
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
10802
- const streamPath = join24(dir, name);
10879
+ const streamPath = join25(dir, name);
10803
10880
  if (runFinished(streamPath)) continue;
10804
10881
  const sidecar = readRunSidecar(streamPath);
10805
10882
  if (!sidecar || sidecar.route !== o.routeLabel) continue;
@@ -10852,15 +10929,15 @@ async function reconcileRuns(o) {
10852
10929
  }
10853
10930
 
10854
10931
  // src/live-runs.ts
10855
- import { existsSync as existsSync23, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
10856
- import { join as join25 } from "path";
10932
+ import { existsSync as existsSync24, readdirSync as readdirSync10, statSync as statSync9 } from "fs";
10933
+ import { join as join26 } from "path";
10857
10934
  function liveRuns(stateDir, isAlive = runIsAlive) {
10858
- const dir = join25(stateDir, "streams");
10859
- if (!existsSync23(dir)) return [];
10935
+ const dir = join26(stateDir, "streams");
10936
+ if (!existsSync24(dir)) return [];
10860
10937
  const out = [];
10861
10938
  for (const name of readdirSync10(dir)) {
10862
10939
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
10863
- const streamPath = join25(dir, name);
10940
+ const streamPath = join26(dir, name);
10864
10941
  if (runFinished(streamPath)) continue;
10865
10942
  const sidecar = readRunSidecar(streamPath);
10866
10943
  if (!sidecar || !isAlive(sidecar)) continue;
@@ -10870,15 +10947,15 @@ function liveRuns(stateDir, isAlive = runIsAlive) {
10870
10947
  }
10871
10948
  function runActivity(stateDir, isAlive = runIsAlive) {
10872
10949
  const activeRuns = liveRuns(stateDir, isAlive).length;
10873
- const dir = join25(stateDir, "streams");
10950
+ const dir = join26(stateDir, "streams");
10874
10951
  let newest = 0;
10875
- if (existsSync23(dir)) {
10952
+ if (existsSync24(dir)) {
10876
10953
  for (const name of readdirSync10(dir)) {
10877
10954
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
10878
- const streamPath = join25(dir, name);
10955
+ const streamPath = join26(dir, name);
10879
10956
  if (!runFinished(streamPath)) continue;
10880
10957
  try {
10881
- newest = Math.max(newest, statSync8(`${streamPath}.meta.json`).mtimeMs);
10958
+ newest = Math.max(newest, statSync9(`${streamPath}.meta.json`).mtimeMs);
10882
10959
  } catch {
10883
10960
  }
10884
10961
  }
@@ -11246,7 +11323,7 @@ function realDeprovisionDeps(cfg, emit, crewHome, getStore) {
11246
11323
  if (host === "windows") return [];
11247
11324
  const ctx = installContext();
11248
11325
  const systemd = detectSystemd();
11249
- return ["run", "release", "passengers"].flatMap((job) => planInstall(cfg.ship, crewHome, host, systemd, job, ctx).unitPaths).filter((p) => existsSync24(p));
11326
+ return ["run", "release", "passengers"].flatMap((job) => planInstall(cfg.ship, crewHome, host, systemd, job, ctx).unitPaths).filter((p) => existsSync25(p));
11250
11327
  };
11251
11328
  return {
11252
11329
  liveRuns: () => liveRuns(stateDir).map((r) => ({ role: r.role, route: r.route, pid: r.pid, ticketRecordId: r.ticketRecordId ?? void 0, streamPath: r.streamPath })),
@@ -11307,7 +11384,7 @@ function realDeprovisionDeps(cfg, emit, crewHome, getStore) {
11307
11384
  },
11308
11385
  remaining() {
11309
11386
  const left = [];
11310
- if (existsSync24(cfg.configFile)) left.push(`config ${cfg.configFile}`);
11387
+ if (existsSync25(cfg.configFile)) left.push(`config ${cfg.configFile}`);
11311
11388
  for (const h of forgeHosts()) if (hasForgeToken(cfg.ship.forges, h, stateDir)) left.push(`forge token for ${h}`);
11312
11389
  try {
11313
11390
  for (const k of readdirSync11(keysDir(stateDir))) left.push(`seat key ${k}`);
@@ -11582,7 +11659,7 @@ function makeCachePass(o) {
11582
11659
  // src/passenger-containers.ts
11583
11660
  var realExec2 = (cmd, args, opts) => {
11584
11661
  try {
11585
- return execFileSync5(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: opts?.timeoutMs });
11662
+ return execFileSync6(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: opts?.timeoutMs });
11586
11663
  } catch (e) {
11587
11664
  const err3 = e;
11588
11665
  const why = [err3.stderr, err3.stdout].map((x) => (x ?? "").trim()).filter(Boolean).join(" / ");
@@ -11862,7 +11939,7 @@ var WARM_MAX_BYTES = 64 * 1024 * 1024;
11862
11939
  var globToRegExp = (glob) => new RegExp(`^${glob.split("*").map((p) => p.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join("[^/]*")}$`);
11863
11940
  function readWarmFiles(dir, ref, patterns) {
11864
11941
  if (patterns.length === 0) return {};
11865
- const git2 = (args) => execFileSync6("git", ["-C", dir, ...args], { encoding: "utf8", maxBuffer: WARM_MAX_BYTES, stdio: ["ignore", "pipe", "pipe"] });
11942
+ const git2 = (args) => execFileSync7("git", ["-C", dir, ...args], { encoding: "utf8", maxBuffer: WARM_MAX_BYTES, stdio: ["ignore", "pipe", "pipe"] });
11866
11943
  const all = git2(["ls-tree", "-r", "--name-only", ref]).split("\n").filter(Boolean);
11867
11944
  const res = patterns.map(globToRegExp);
11868
11945
  const out = {};
@@ -11915,19 +11992,19 @@ function planSandbox(repo, decl, warm = {}) {
11915
11992
  const layer = repoLayer(repo, decl, crew, warm);
11916
11993
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
11917
11994
  }
11918
- var sandboxStatePath = (stateDir) => join26(stateDir, "sandbox-images.json");
11995
+ var sandboxStatePath = (stateDir) => join27(stateDir, "sandbox-images.json");
11919
11996
  function readSandboxState(stateDir) {
11920
11997
  try {
11921
- const v = JSON.parse(readFileSync21(sandboxStatePath(stateDir), "utf8"));
11998
+ const v = JSON.parse(readFileSync22(sandboxStatePath(stateDir), "utf8"));
11922
11999
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
11923
12000
  } catch {
11924
12001
  return {};
11925
12002
  }
11926
12003
  }
11927
12004
  function writeSandboxState(stateDir, state) {
11928
- mkdirSync14(stateDir, { recursive: true });
12005
+ mkdirSync15(stateDir, { recursive: true });
11929
12006
  const file = sandboxStatePath(stateDir);
11930
- writeFileSync15(`${file}.tmp`, `${JSON.stringify(state, null, 2)}
12007
+ writeFileSync16(`${file}.tmp`, `${JSON.stringify(state, null, 2)}
11931
12008
  `);
11932
12009
  renameSync3(`${file}.tmp`, file);
11933
12010
  }
@@ -11941,15 +12018,15 @@ function sandboxImageStatus(state, plan) {
11941
12018
  if (current.tag !== plan.tag) return { status: "stale", detail: `running ${current.tag} (claude ${current.claude ?? "unknown"}), declaration now builds ${plan.tag} (claude ${plan.claude})` };
11942
12019
  return { status: "ok", detail: `${current.tag} built ${current.builtAt}, claude ${current.claude ?? "unknown"}` };
11943
12020
  }
11944
- var spawnBuildRunner = (engine = "docker") => (args, logPath) => new Promise((resolve7) => {
12021
+ var spawnBuildRunner = (engine = "docker") => (args, logPath) => new Promise((resolve8) => {
11945
12022
  const child = spawn4(engine, args, { stdio: ["ignore", "pipe", "pipe"] });
11946
12023
  const chunks = [];
11947
12024
  child.stdout.on("data", (d) => chunks.push(d));
11948
12025
  child.stderr.on("data", (d) => chunks.push(d));
11949
12026
  const done = (code, extra = "") => {
11950
- writeFileSync15(logPath, `$ ${engine} ${args.join(" ")}
12027
+ writeFileSync16(logPath, `$ ${engine} ${args.join(" ")}
11951
12028
  ${Buffer.concat(chunks).toString("utf8")}${extra}`, { flag: "a" });
11952
- resolve7(code);
12029
+ resolve8(code);
11953
12030
  };
11954
12031
  child.on("error", (e) => done(127, `${e.message}
11955
12032
  `));
@@ -11969,7 +12046,7 @@ var engineRemoveImage = (engine = "docker", exec = realExec2) => (tag) => {
11969
12046
  } catch {
11970
12047
  }
11971
12048
  };
11972
- var sandboxLogDir = (stateDir) => join26(stateDir, "sandbox-logs");
12049
+ var sandboxLogDir = (stateDir) => join27(stateDir, "sandbox-logs");
11973
12050
  async function buildSandboxImage(plan, deps) {
11974
12051
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
11975
12052
  const state = readSandboxState(deps.stateDir);
@@ -11978,20 +12055,20 @@ async function buildSandboxImage(plan, deps) {
11978
12055
  return { kind: "reused", tag: plan.tag };
11979
12056
  }
11980
12057
  const logDir = sandboxLogDir(deps.stateDir);
11981
- mkdirSync14(logDir, { recursive: true });
11982
- const logPath = join26(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
12058
+ mkdirSync15(logDir, { recursive: true });
12059
+ const logPath = join27(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
11983
12060
  const builtLayers = [];
11984
12061
  for (const layer of plan.layers) {
11985
12062
  if (deps.hasImage(layer.tag)) continue;
11986
- const ctx = join26(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
11987
- mkdirSync14(ctx, { recursive: true });
12063
+ const ctx = join27(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
12064
+ mkdirSync15(ctx, { recursive: true });
11988
12065
  try {
11989
- writeFileSync15(join26(ctx, "Dockerfile"), layer.dockerfile);
12066
+ writeFileSync16(join27(ctx, "Dockerfile"), layer.dockerfile);
11990
12067
  for (const [name, content] of Object.entries(layer.files)) {
11991
- mkdirSync14(dirname10(join26(ctx, name)), { recursive: true });
11992
- writeFileSync15(join26(ctx, name), content);
12068
+ mkdirSync15(dirname11(join27(ctx, name)), { recursive: true });
12069
+ writeFileSync16(join27(ctx, name), content);
11993
12070
  }
11994
- const code = await deps.run(["build", "--tag", layer.tag, "--file", join26(ctx, "Dockerfile"), ctx], logPath);
12071
+ const code = await deps.run(["build", "--tag", layer.tag, "--file", join27(ctx, "Dockerfile"), ctx], logPath);
11995
12072
  if (code !== 0) {
11996
12073
  state[plan.repo] = { ...entry, failure: { tag: plan.tag, at: now().toISOString(), logPath } };
11997
12074
  writeSandboxState(deps.stateDir, state);
@@ -12044,10 +12121,10 @@ async function sandboxBuildTick(repos, deps, force = false) {
12044
12121
  }
12045
12122
 
12046
12123
  // src/doctor-checks.ts
12047
- import { execFileSync as execFileSync7 } from "child_process";
12124
+ import { execFileSync as execFileSync8 } from "child_process";
12048
12125
  var PROBE_BRANCH = "crew-probe-branch";
12049
12126
  var realGit = (cwd, args, env) => {
12050
- execFileSync7("git", args, { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
12127
+ execFileSync8("git", args, { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
12051
12128
  };
12052
12129
  function isSshRemote(url) {
12053
12130
  return /^ssh:\/\//.test(url) || /^[\w.-]+@[\w.-]+:/.test(url);
@@ -12127,10 +12204,10 @@ function formatPushAuth(repo, r) {
12127
12204
  return `${label}FAILED \u2014 ${r.detail}; ${r.hint}
12128
12205
  `;
12129
12206
  }
12130
- async function settleRepos(items, nameOf, resolve7) {
12207
+ async function settleRepos(items, nameOf, resolve8) {
12131
12208
  const out = await Promise.all(items.map(async (t) => {
12132
12209
  try {
12133
- return { ok: true, value: await resolve7(t) };
12210
+ return { ok: true, value: await resolve8(t) };
12134
12211
  } catch (e) {
12135
12212
  return { ok: false, repo: nameOf(t), detail: e.message };
12136
12213
  }
@@ -12180,14 +12257,14 @@ function managedRuntimeWarnings(kind, ship) {
12180
12257
 
12181
12258
  // src/selftest.ts
12182
12259
  import { spawn as spawn5 } from "child_process";
12183
- import { existsSync as existsSync25, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync2, readFileSync as readFileSync22, rmSync as rmSync14, writeFileSync as writeFileSync16 } from "fs";
12260
+ import { existsSync as existsSync26, mkdirSync as mkdirSync16, mkdtempSync as mkdtempSync2, readFileSync as readFileSync23, rmSync as rmSync14, writeFileSync as writeFileSync17 } from "fs";
12184
12261
  import { tmpdir as tmpdir4 } from "os";
12185
- import { join as join27 } from "path";
12262
+ import { join as join28 } from "path";
12186
12263
  var SELFTEST_TIMEOUT_MS = 12e4;
12187
12264
  var KEY_VAR = "CREW_API_KEY";
12188
12265
  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: ";
12189
12266
  var realDeps = {
12190
- run: (o) => new Promise((resolve7) => {
12267
+ run: (o) => new Promise((resolve8) => {
12191
12268
  let stdout = "";
12192
12269
  let stderr = "";
12193
12270
  let timedOut = false;
@@ -12195,7 +12272,7 @@ var realDeps = {
12195
12272
  const done = (r) => {
12196
12273
  if (!settled) {
12197
12274
  settled = true;
12198
- resolve7(r);
12275
+ resolve8(r);
12199
12276
  }
12200
12277
  };
12201
12278
  const child = spawn5(o.bin, o.args, { cwd: o.cwd, env: o.env, stdio: ["pipe", "pipe", "pipe"], detached: true });
@@ -12343,7 +12420,7 @@ async function runSelftest(i, deps = realDeps) {
12343
12420
  ...adapter.runEnv(spec),
12344
12421
  [KEY_VAR]: i.apiKey
12345
12422
  };
12346
- const cwd = mkdtempSync2(join27(tmpdir4(), "crew-selftest-"));
12423
+ const cwd = mkdtempSync2(join28(tmpdir4(), "crew-selftest-"));
12347
12424
  try {
12348
12425
  const run3 = await deps.run({
12349
12426
  bin: invocation.bin,
@@ -12376,11 +12453,11 @@ function formatResult(r) {
12376
12453
  const secs = `${(r.ms / 1e3).toFixed(1)}s`;
12377
12454
  return r.ok ? `${r.toolchain}: PASS ${r.detail} (${secs}${tok})` : `${r.toolchain}: FAIL ${r.failure} \u2014 ${r.detail} (${secs})`;
12378
12455
  }
12379
- var fileFor2 = (stateDir) => join27(stateDir, "selftest.json");
12456
+ var fileFor2 = (stateDir) => join28(stateDir, "selftest.json");
12380
12457
  function readSelftest(stateDir) {
12381
12458
  try {
12382
12459
  const p = fileFor2(stateDir);
12383
- return existsSync25(p) ? JSON.parse(readFileSync22(p, "utf8")) : {};
12460
+ return existsSync26(p) ? JSON.parse(readFileSync23(p, "utf8")) : {};
12384
12461
  } catch {
12385
12462
  return {};
12386
12463
  }
@@ -12388,8 +12465,8 @@ function readSelftest(stateDir) {
12388
12465
  function recordSelftest(stateDir, results) {
12389
12466
  const all = readSelftest(stateDir);
12390
12467
  for (const r of results) all[r.toolchain] = r;
12391
- mkdirSync15(stateDir, { recursive: true });
12392
- writeFileSync16(fileFor2(stateDir), `${JSON.stringify(all, null, 2)}
12468
+ mkdirSync16(stateDir, { recursive: true });
12469
+ writeFileSync17(fileFor2(stateDir), `${JSON.stringify(all, null, 2)}
12393
12470
  `);
12394
12471
  }
12395
12472
  function doctorSelftestLines(stored, toolchains) {
@@ -12401,7 +12478,7 @@ function doctorSelftestLines(stored, toolchains) {
12401
12478
  }
12402
12479
 
12403
12480
  // src/cli.ts
12404
- import { resolve as resolve6, dirname as dirname28, join as join49, basename as basename16 } from "path";
12481
+ import { resolve as resolve7, dirname as dirname29, join as join50, basename as basename16 } from "path";
12405
12482
  import { fileURLToPath } from "url";
12406
12483
  import { hostname as hostname4, homedir as homedir11 } from "os";
12407
12484
  import { spawnSync as spawnSync10 } from "child_process";
@@ -12598,14 +12675,14 @@ function rosterMarkdown(roster, meId) {
12598
12675
  }
12599
12676
 
12600
12677
  // src/connect-resume.ts
12601
- import { mkdirSync as mkdirSync16, readFileSync as readFileSync23, rmSync as rmSync15, writeFileSync as writeFileSync17 } from "fs";
12602
- import { dirname as dirname11, join as join28 } from "path";
12678
+ import { mkdirSync as mkdirSync17, readFileSync as readFileSync24, rmSync as rmSync15, writeFileSync as writeFileSync18 } from "fs";
12679
+ import { dirname as dirname12, join as join29 } from "path";
12603
12680
  function lastSessionFile(stateDir) {
12604
- return join28(stateDir, "connect-session.json");
12681
+ return join29(stateDir, "connect-session.json");
12605
12682
  }
12606
12683
  function readPointers(stateDir) {
12607
12684
  try {
12608
- const parsed = JSON.parse(readFileSync23(lastSessionFile(stateDir), "utf8"));
12685
+ const parsed = JSON.parse(readFileSync24(lastSessionFile(stateDir), "utf8"));
12609
12686
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
12610
12687
  const out = {};
12611
12688
  for (const [host, v] of Object.entries(parsed)) {
@@ -12619,8 +12696,8 @@ function readPointers(stateDir) {
12619
12696
  }
12620
12697
  function writePointers(stateDir, pointers) {
12621
12698
  const file = lastSessionFile(stateDir);
12622
- mkdirSync16(dirname11(file), { recursive: true });
12623
- writeFileSync17(file, `${JSON.stringify(pointers, null, 2)}
12699
+ mkdirSync17(dirname12(file), { recursive: true });
12700
+ writeFileSync18(file, `${JSON.stringify(pointers, null, 2)}
12624
12701
  `, { mode: 384 });
12625
12702
  }
12626
12703
  function rememberSession(stateDir, host, workspaceSlug) {
@@ -12705,15 +12782,15 @@ async function signOutSessions(o) {
12705
12782
  }
12706
12783
 
12707
12784
  // src/reset.ts
12708
- import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync24, readdirSync as readdirSync12, rmSync as rmSync16, statSync as statSync9, writeFileSync as writeFileSync18 } from "fs";
12709
- import { dirname as dirname12, join as join29 } from "path";
12785
+ import { existsSync as existsSync27, mkdirSync as mkdirSync18, readFileSync as readFileSync25, readdirSync as readdirSync12, rmSync as rmSync16, statSync as statSync10, writeFileSync as writeFileSync19 } from "fs";
12786
+ import { dirname as dirname13, join as join30 } from "path";
12710
12787
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
12711
12788
  function clonedCheckoutsFile(stateDir) {
12712
- return join29(stateDir, "cloned-checkouts.json");
12789
+ return join30(stateDir, "cloned-checkouts.json");
12713
12790
  }
12714
12791
  function readClonedCheckouts(stateDir) {
12715
12792
  try {
12716
- const parsed = JSON.parse(readFileSync24(clonedCheckoutsFile(stateDir), "utf8"));
12793
+ const parsed = JSON.parse(readFileSync25(clonedCheckoutsFile(stateDir), "utf8"));
12717
12794
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
12718
12795
  } catch {
12719
12796
  return [];
@@ -12723,8 +12800,8 @@ function recordClonedCheckout(stateDir, dir) {
12723
12800
  const known = readClonedCheckouts(stateDir);
12724
12801
  if (known.includes(dir)) return;
12725
12802
  const file = clonedCheckoutsFile(stateDir);
12726
- mkdirSync17(dirname12(file), { recursive: true });
12727
- writeFileSync18(file, `${JSON.stringify([...known, dir], null, 2)}
12803
+ mkdirSync18(dirname13(file), { recursive: true });
12804
+ writeFileSync19(file, `${JSON.stringify([...known, dir], null, 2)}
12728
12805
  `);
12729
12806
  }
12730
12807
  var mark = (dryRun) => dryRun ? "would-remove" : "removed";
@@ -12778,7 +12855,7 @@ async function runReset(o, fx) {
12778
12855
  }
12779
12856
  if (o.beforeState) steps.push(...await o.beforeState());
12780
12857
  steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
12781
- if (existsSync26(o.configFile)) {
12858
+ if (existsSync27(o.configFile)) {
12782
12859
  if (!o.dryRun) rmSync16(o.configFile, { force: true });
12783
12860
  steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
12784
12861
  } else {
@@ -12787,7 +12864,7 @@ async function runReset(o, fx) {
12787
12864
  return steps;
12788
12865
  }
12789
12866
  function removeCheckout(dir, dryRun, fx) {
12790
- if (!existsSync26(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
12867
+ if (!existsSync27(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
12791
12868
  const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
12792
12869
  if (dirty.length) {
12793
12870
  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" : ""})` };
@@ -12800,15 +12877,15 @@ function removeCheckout(dir, dryRun, fx) {
12800
12877
  }
12801
12878
  function removeState(stateDir, dryRun) {
12802
12879
  const out = [];
12803
- const targets = [join29(stateDir, "resolved"), join29(stateDir, "keys"), clonedCheckoutsFile(stateDir), join29(stateDir, "connect-session.json"), join29(stateDir, "reload-requested"), join29(stateDir, "daemon.started")];
12880
+ const targets = [join30(stateDir, "resolved"), join30(stateDir, "keys"), clonedCheckoutsFile(stateDir), join30(stateDir, "connect-session.json"), join30(stateDir, "reload-requested"), join30(stateDir, "daemon.started")];
12804
12881
  try {
12805
12882
  for (const name of readdirSync12(stateDir)) {
12806
- if (name.startsWith(".") && statSync9(join29(stateDir, name)).isFile()) targets.push(join29(stateDir, name));
12883
+ if (name.startsWith(".") && statSync10(join30(stateDir, name)).isFile()) targets.push(join30(stateDir, name));
12807
12884
  }
12808
12885
  } catch {
12809
12886
  }
12810
12887
  for (const t of targets) {
12811
- if (!existsSync26(t)) continue;
12888
+ if (!existsSync27(t)) continue;
12812
12889
  if (!dryRun) rmSync16(t, { recursive: true, force: true });
12813
12890
  out.push({ step: "state", target: t, action: mark(dryRun) });
12814
12891
  }
@@ -12823,7 +12900,7 @@ function wipeStateDir(stateDir, dryRun) {
12823
12900
  return [{ step: "state", target: stateDir, action: "absent", detail: "no state directory" }];
12824
12901
  }
12825
12902
  if (!names.length) return [{ step: "state", target: stateDir, action: "absent", detail: "already empty" }];
12826
- if (!dryRun) for (const n of names) rmSync16(join29(stateDir, n), { recursive: true, force: true });
12903
+ if (!dryRun) for (const n of names) rmSync16(join30(stateDir, n), { recursive: true, force: true });
12827
12904
  return [{ step: "state", target: stateDir, action: mark(dryRun), detail: `${names.length} entr${names.length === 1 ? "y" : "ies"}` }];
12828
12905
  }
12829
12906
  function describeResetStep(s) {
@@ -12904,9 +12981,9 @@ async function runDeprovision(o, fx, deps) {
12904
12981
  }
12905
12982
 
12906
12983
  // src/pool-member.ts
12907
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync25, renameSync as renameSync4, writeFileSync as writeFileSync19 } from "fs";
12984
+ import { mkdirSync as mkdirSync19, readFileSync as readFileSync26, renameSync as renameSync4, writeFileSync as writeFileSync20 } from "fs";
12908
12985
  import { homedir as homedir8 } from "os";
12909
- import { dirname as dirname13, join as join30 } from "path";
12986
+ import { dirname as dirname14, join as join31 } from "path";
12910
12987
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
12911
12988
  function backoffMs(attempt) {
12912
12989
  return Math.min(6e4, 5e3 * 2 ** Math.max(0, attempt));
@@ -13137,22 +13214,22 @@ async function runPoolMember(deps) {
13137
13214
  }
13138
13215
  }
13139
13216
  function defaultMemberDir(env = process.env) {
13140
- return env.CREW_POOL_DIR ?? join30(env.XDG_STATE_HOME ?? join30(homedir8(), ".local", "state"), "crew-pool-member");
13217
+ return env.CREW_POOL_DIR ?? join31(env.XDG_STATE_HOME ?? join31(homedir8(), ".local", "state"), "crew-pool-member");
13141
13218
  }
13142
13219
  function fileMemberStore(dir) {
13143
- const file = join30(dir, "member.json");
13220
+ const file = join31(dir, "member.json");
13144
13221
  return {
13145
13222
  read() {
13146
13223
  try {
13147
- return JSON.parse(readFileSync25(file, "utf8"));
13224
+ return JSON.parse(readFileSync26(file, "utf8"));
13148
13225
  } catch {
13149
13226
  return {};
13150
13227
  }
13151
13228
  },
13152
13229
  write(f) {
13153
- mkdirSync18(dirname13(file), { recursive: true, mode: 448 });
13230
+ mkdirSync19(dirname14(file), { recursive: true, mode: 448 });
13154
13231
  const tmp = `${file}.tmp`;
13155
- writeFileSync19(tmp, `${JSON.stringify(f)}
13232
+ writeFileSync20(tmp, `${JSON.stringify(f)}
13156
13233
  `, { mode: 384 });
13157
13234
  renameSync4(tmp, file);
13158
13235
  }
@@ -13204,30 +13281,30 @@ function hostArchitecture(arch = process.arch) {
13204
13281
 
13205
13282
  // src/pool-member-host.ts
13206
13283
  import "child_process";
13207
- import { existsSync as existsSync30 } from "fs";
13284
+ import { existsSync as existsSync31 } from "fs";
13208
13285
  import { hostname as hostname3 } from "os";
13209
13286
 
13210
13287
  // src/machine-id.ts
13211
- import { execFileSync as execFileSync8 } from "child_process";
13288
+ import { execFileSync as execFileSync9 } from "child_process";
13212
13289
  import { createHash as createHash7 } from "crypto";
13213
- import { existsSync as existsSync27, mkdirSync as mkdirSync19, readFileSync as readFileSync26, writeFileSync as writeFileSync20 } from "fs";
13214
- import { dirname as dirname14, join as join31 } from "path";
13290
+ import { existsSync as existsSync28, mkdirSync as mkdirSync20, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
13291
+ import { dirname as dirname15, join as join32 } from "path";
13215
13292
  function machineIdPathFor(stateDir) {
13216
- return join31(stateDir, "machine-id");
13293
+ return join32(stateDir, "machine-id");
13217
13294
  }
13218
13295
  var sha256 = (s) => createHash7("sha256").update(s).digest("hex");
13219
13296
  function readRawMachineId(platform2 = process.platform) {
13220
13297
  try {
13221
13298
  if (platform2 === "darwin") {
13222
- const out = execFileSync8("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
13299
+ const out = execFileSync9("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
13223
13300
  encoding: "utf8",
13224
13301
  stdio: ["ignore", "pipe", "ignore"]
13225
13302
  });
13226
13303
  return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
13227
13304
  }
13228
13305
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
13229
- if (existsSync27(p)) {
13230
- const id = readFileSync26(p, "utf8").trim();
13306
+ if (existsSync28(p)) {
13307
+ const id = readFileSync27(p, "utf8").trim();
13231
13308
  if (id) return id;
13232
13309
  }
13233
13310
  }
@@ -13238,15 +13315,15 @@ function readRawMachineId(platform2 = process.platform) {
13238
13315
  function ensureMachineId(stateDir, opts = {}) {
13239
13316
  if (opts.override?.trim()) return sha256(opts.override.trim());
13240
13317
  const path = machineIdPathFor(stateDir);
13241
- if (existsSync27(path)) {
13242
- const cached = readFileSync26(path, "utf8").trim();
13318
+ if (existsSync28(path)) {
13319
+ const cached = readFileSync27(path, "utf8").trim();
13243
13320
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
13244
13321
  }
13245
13322
  const raw = (opts.read ?? readRawMachineId)();
13246
13323
  if (!raw) return void 0;
13247
13324
  const hashed = sha256(raw);
13248
- mkdirSync19(dirname14(path), { recursive: true });
13249
- writeFileSync20(path, `${hashed}
13325
+ mkdirSync20(dirname15(path), { recursive: true });
13326
+ writeFileSync21(path, `${hashed}
13250
13327
  `, { mode: 384 });
13251
13328
  return hashed;
13252
13329
  }
@@ -13259,7 +13336,7 @@ function matchShipRows(ships, shipName2, machineId) {
13259
13336
 
13260
13337
  // src/provision.ts
13261
13338
  import { spawnSync as spawnSync7 } from "child_process";
13262
- import { existsSync as existsSync28, readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
13339
+ import { existsSync as existsSync29, readFileSync as readFileSync28, writeFileSync as writeFileSync22 } from "fs";
13263
13340
  import { hostname as hostname2 } from "os";
13264
13341
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
13265
13342
 
@@ -13268,7 +13345,7 @@ import { parseDocument as parseDocument2 } from "yaml";
13268
13345
 
13269
13346
  // src/repos-cmd.ts
13270
13347
  import { homedir as homedir9 } from "os";
13271
- import { basename as basename10, join as join32, resolve as resolve3 } from "path";
13348
+ import { basename as basename10, join as join33, resolve as resolve4 } from "path";
13272
13349
  import { isMap, isSeq, parseDocument, stringify as stringify2 } from "yaml";
13273
13350
  var ReposError = class extends Error {
13274
13351
  };
@@ -13315,7 +13392,7 @@ function parseAddArgs(args, routes, isDir) {
13315
13392
  }
13316
13393
  function expandDir(p, base) {
13317
13394
  const home = p === "~" ? homedir9() : p.startsWith("~/") ? `${homedir9()}/${p.slice(2)}` : p;
13318
- return resolve3(base, home);
13395
+ return resolve4(base, home);
13319
13396
  }
13320
13397
  function routeMapOf(doc, route2) {
13321
13398
  const routes = doc.get("routes", true);
@@ -13546,7 +13623,7 @@ function planRepoClone(i) {
13546
13623
  }
13547
13624
  const ship = doc.get("ship", true);
13548
13625
  const basePath = String(m.get("reposBasePath") ?? (isMap(ship) ? ship.get("reposBasePath") : void 0) ?? "~/Crew");
13549
- const target = i.dir ? expandDir(i.dir, i.base) : join32(expandDir(basePath, i.base), i.route.split("/")[0], name);
13626
+ const target = i.dir ? expandDir(i.dir, i.base) : join33(expandDir(basePath, i.base), i.route.split("/")[0], name);
13550
13627
  const add = planRepoAdd({ text: i.text, base: i.base, route: i.route, path: target, origin: remote, resolved: i.resolved, name, isCheckout: i.isCheckout });
13551
13628
  const forgeHost = i.resolved?.repoForgeHosts?.[id];
13552
13629
  return { ...add, remote, target, ...forgeHost ? { forgeHost } : {} };
@@ -13853,7 +13930,7 @@ function realProvisionDeps(crewHome) {
13853
13930
  };
13854
13931
  const configFile = () => {
13855
13932
  const f = findConfigFile(crewHome);
13856
- return existsSync28(f) ? f : null;
13933
+ return existsSync29(f) ? f : null;
13857
13934
  };
13858
13935
  return {
13859
13936
  crew: spawnCrew,
@@ -13861,13 +13938,13 @@ function realProvisionDeps(crewHome) {
13861
13938
  enableRoute(route2) {
13862
13939
  const file = configFile();
13863
13940
  if (!file) return false;
13864
- const text2 = readFileSync27(file, "utf8");
13941
+ const text2 = readFileSync28(file, "utf8");
13865
13942
  const doc = parseDocument3(text2);
13866
13943
  const routes = doc.get("routes");
13867
13944
  const entry = routes?.items?.find((r) => r.get("route") === route2);
13868
13945
  if (!entry) throw new Error(`route ${route2} is not in ${file}`);
13869
13946
  if (entry.get("enabled") === true) return false;
13870
- writeFileSync21(file, planEnable(text2, route2));
13947
+ writeFileSync22(file, planEnable(text2, route2));
13871
13948
  return true;
13872
13949
  },
13873
13950
  installState() {
@@ -13887,14 +13964,14 @@ function realProvisionDeps(crewHome) {
13887
13964
  continue;
13888
13965
  }
13889
13966
  for (const p of plan.unitPaths) {
13890
- if (!existsSync28(p) || readFileSync27(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
13967
+ if (!existsSync29(p) || readFileSync28(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
13891
13968
  }
13892
13969
  }
13893
13970
  const label = labelFor(crewHome, "run");
13894
13971
  let running = false;
13895
13972
  let lingering = true;
13896
13973
  if (mechanism === "systemd") {
13897
- const system = ctx.root || existsSync28(systemUnitPathFor(label));
13974
+ const system = ctx.root || existsSync29(systemUnitPathFor(label));
13898
13975
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
13899
13976
  running = spawnSync7(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
13900
13977
  if (!system) {
@@ -13911,18 +13988,18 @@ function realProvisionDeps(crewHome) {
13911
13988
  setShipSetting(key, value2) {
13912
13989
  const file = configFile();
13913
13990
  if (!file) return false;
13914
- const doc = parseDocument3(readFileSync27(file, "utf8"));
13991
+ const doc = parseDocument3(readFileSync28(file, "utf8"));
13915
13992
  if (doc.getIn(["ship", key]) === value2) return false;
13916
13993
  doc.setIn(["ship", key], value2);
13917
- writeFileSync21(file, doc.toString());
13994
+ writeFileSync22(file, doc.toString());
13918
13995
  return true;
13919
13996
  },
13920
13997
  applyManagedDefaults() {
13921
13998
  const file = configFile();
13922
13999
  if (!file) return [];
13923
- const doc = parseDocument3(readFileSync27(file, "utf8"));
14000
+ const doc = parseDocument3(readFileSync28(file, "utf8"));
13924
14001
  const wrote = applyManagedDefaultsToDoc(doc);
13925
- if (wrote.length) writeFileSync21(file, doc.toString());
14002
+ if (wrote.length) writeFileSync22(file, doc.toString());
13926
14003
  return wrote;
13927
14004
  },
13928
14005
  storedToken(host) {
@@ -13939,9 +14016,9 @@ host=${host}
13939
14016
 
13940
14017
  // src/tunnel.ts
13941
14018
  import { spawn as spawn6 } from "child_process";
13942
- import { existsSync as existsSync29, mkdirSync as mkdirSync20, readFileSync as readFileSync28, writeFileSync as writeFileSync22, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14019
+ import { existsSync as existsSync30, mkdirSync as mkdirSync21, readFileSync as readFileSync29, writeFileSync as writeFileSync23, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
13943
14020
  import { randomInt } from "crypto";
13944
- import { join as join33 } from "path";
14021
+ import { join as join34 } from "path";
13945
14022
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
13946
14023
  var defaultSpawn = (cmd, args) => spawn6(cmd, args, DEFAULT_SPAWN_OPTIONS);
13947
14024
  var SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -14013,21 +14090,21 @@ function startTunnel(opts) {
14013
14090
  };
14014
14091
  }
14015
14092
  function tunnelStatePath(stateDir, workspaceId) {
14016
- return join33(stateDir, "passengers", `${workspaceId}.json`);
14093
+ return join34(stateDir, "passengers", `${workspaceId}.json`);
14017
14094
  }
14018
14095
  function readPersistedTunnel(stateDir, workspaceId) {
14019
14096
  const path = tunnelStatePath(stateDir, workspaceId);
14020
- if (!existsSync29(path)) return void 0;
14097
+ if (!existsSync30(path)) return void 0;
14021
14098
  try {
14022
- return JSON.parse(readFileSync28(path, "utf8"));
14099
+ return JSON.parse(readFileSync29(path, "utf8"));
14023
14100
  } catch {
14024
14101
  return void 0;
14025
14102
  }
14026
14103
  }
14027
14104
  function writePersistedTunnel(stateDir, state) {
14028
14105
  const path = tunnelStatePath(stateDir, state.workspaceId);
14029
- mkdirSync20(join33(stateDir, "passengers"), { recursive: true });
14030
- writeFileSync22(path, `${JSON.stringify(state, null, 2)}
14106
+ mkdirSync21(join34(stateDir, "passengers"), { recursive: true });
14107
+ writeFileSync23(path, `${JSON.stringify(state, null, 2)}
14031
14108
  `);
14032
14109
  }
14033
14110
  function updatePersistedTunnel(stateDir, workspaceId, patch2) {
@@ -14129,8 +14206,8 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
14129
14206
  }
14130
14207
  }
14131
14208
  function readAllPersistedWorkspaceIds(stateDir) {
14132
- const dir = join33(stateDir, "passengers");
14133
- if (!existsSync29(dir)) return [];
14209
+ const dir = join34(stateDir, "passengers");
14210
+ if (!existsSync30(dir)) return [];
14134
14211
  return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
14135
14212
  }
14136
14213
 
@@ -14155,7 +14232,7 @@ function realPoolMemberDeps(o) {
14155
14232
  crewVersion: crewVersion(o.crewHome),
14156
14233
  hostname: hostname3()
14157
14234
  },
14158
- hasShipConfig: () => existsSync30(findConfigFile(o.crewHome)),
14235
+ hasShipConfig: () => existsSync31(findConfigFile(o.crewHome)),
14159
14236
  async provision(lease, instanceClass, shipKind) {
14160
14237
  const hosts = lease.forges.map((f) => f.host.toLowerCase());
14161
14238
  const lines2 = [];
@@ -15031,8 +15108,8 @@ ${waiting.map((t) => `| ${displayKey2(t)} | ${t.status} | ${blockers(t, i)} |`).
15031
15108
  }
15032
15109
 
15033
15110
  // src/poll.ts
15034
- import { writeFileSync as writeFileSync23 } from "fs";
15035
- import { join as join34 } from "path";
15111
+ import { writeFileSync as writeFileSync24 } from "fs";
15112
+ import { join as join35 } from "path";
15036
15113
 
15037
15114
  // src/stalled.ts
15038
15115
  var STALLED_AFTER_CYCLES = 30;
@@ -15339,7 +15416,7 @@ async function decideCycle(o) {
15339
15416
  }
15340
15417
  return decision;
15341
15418
  }
15342
- var digestPath = (stateDir, route2, role, runId) => join34(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
15419
+ var digestPath = (stateDir, route2, role, runId) => join35(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
15343
15420
  function otherRunsRemoved(tickets, own) {
15344
15421
  if (!own) return tickets;
15345
15422
  return tickets.filter((t) => t.id === own.ticket.id || !own.inFlight(t.id));
@@ -15417,7 +15494,7 @@ function writeDigest(o, d, role, stateDir, runId, own) {
15417
15494
 
15418
15495
  ${body}` : body;
15419
15496
  const path = digestPath(stateDir, routeSlug(o.route.route), role, runId);
15420
- writeFileSync23(path, text2);
15497
+ writeFileSync24(path, text2);
15421
15498
  o.emit.emit(`queue digest written for ${role} (${Buffer.byteLength(text2)} bytes)`, {
15422
15499
  data: { role, bytes: Buffer.byteLength(text2) }
15423
15500
  });
@@ -15525,8 +15602,8 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
15525
15602
  }
15526
15603
 
15527
15604
  // src/repo-refresh.ts
15528
- import { existsSync as existsSync31, mkdirSync as mkdirSync21, readFileSync as readFileSync29, writeFileSync as writeFileSync24 } from "fs";
15529
- import { dirname as dirname16 } from "path";
15605
+ import { existsSync as existsSync32, mkdirSync as mkdirSync22, readFileSync as readFileSync30, writeFileSync as writeFileSync25 } from "fs";
15606
+ import { dirname as dirname17 } from "path";
15530
15607
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
15531
15608
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
15532
15609
  var lastAsked = /* @__PURE__ */ new Map();
@@ -15555,11 +15632,11 @@ function applyRepoRows(resolved, rows) {
15555
15632
  }
15556
15633
  function persistRepoRows(stateDir, routeName, rows) {
15557
15634
  const path = resolvedPathFor(stateDir, routeName);
15558
- if (!existsSync31(path)) return;
15559
- const raw = JSON.parse(readFileSync29(path, "utf8"));
15635
+ if (!existsSync32(path)) return;
15636
+ const raw = JSON.parse(readFileSync30(path, "utf8"));
15560
15637
  applyRepoRows(raw, rows);
15561
- mkdirSync21(dirname16(path), { recursive: true });
15562
- writeFileSync24(path, `${JSON.stringify(raw, null, 2)}
15638
+ mkdirSync22(dirname17(path), { recursive: true });
15639
+ writeFileSync25(path, `${JSON.stringify(raw, null, 2)}
15563
15640
  `);
15564
15641
  }
15565
15642
  async function refreshUnknownRepos(o) {
@@ -16009,13 +16086,13 @@ Ship ${shipName2} found: ${problem}. The release refuses to squash a ref nobody
16009
16086
  var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.record_id === ticket.id && e.event_type === "local_ref_skip" && (e.message ?? "").includes(problem));
16010
16087
 
16011
16088
  // src/connect-wizard.ts
16012
- import { delimiter, dirname as dirname17, join as join35 } from "path";
16013
- import { accessSync as accessSync3, constants as constants3, existsSync as existsSync32, mkdirSync as mkdirSync22, writeFileSync as writeFileSync25 } from "fs";
16089
+ import { delimiter, dirname as dirname18, join as join36 } from "path";
16090
+ import { accessSync as accessSync3, constants as constants3, existsSync as existsSync33, mkdirSync as mkdirSync23, writeFileSync as writeFileSync26 } from "fs";
16014
16091
  import { stringify as stringify3 } from "yaml";
16015
16092
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
16016
16093
  function shouldRunWizard(opts) {
16017
16094
  if (!opts.isTTY) return false;
16018
- const exists = opts.configExists ?? existsSync32;
16095
+ const exists = opts.configExists ?? existsSync33;
16019
16096
  return !configSearchPath(opts.crewHome).some((p) => exists(p));
16020
16097
  }
16021
16098
  function firstRunConfigPath() {
@@ -16032,7 +16109,7 @@ function defaultIsExecutable(p) {
16032
16109
  function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
16033
16110
  for (const dir of pathEnv.split(delimiter)) {
16034
16111
  if (!dir) continue;
16035
- const candidate = join35(dir, name);
16112
+ const candidate = join36(dir, name);
16036
16113
  if (isExecutable(candidate)) return candidate;
16037
16114
  }
16038
16115
  return void 0;
@@ -16058,9 +16135,9 @@ async function runWizard(p, env) {
16058
16135
  if (entered && isExec(entered)) agentBin = entered;
16059
16136
  else p.say(entered ? `${entered} is not an executable file.` : "A path is required.");
16060
16137
  }
16061
- const nodeDir = dirname17(env.nodePath);
16138
+ const nodeDir = dirname18(env.nodePath);
16062
16139
  const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
16063
- const baseDirs = [nodeDir, pnpmPath ? dirname17(pnpmPath) : void 0];
16140
+ const baseDirs = [nodeDir, pnpmPath ? dirname18(pnpmPath) : void 0];
16064
16141
  p.say(
16065
16142
  `
16066
16143
  Host Passengers lets this machine run the agent sessions that other people's
@@ -16071,7 +16148,7 @@ Tablation workspaces ask for, in a container. More: ${HOST_PASSENGERS_DOCS_URL}`
16071
16148
  p.say("Docker is not available here, so nothing will be hosted until it is. Your answer is recorded anyway.");
16072
16149
  }
16073
16150
  const dockerPath = hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
16074
- const extraPath = deriveExtraPath([...baseDirs, dockerPath ? dirname17(dockerPath) : void 0]);
16151
+ const extraPath = deriveExtraPath([...baseDirs, dockerPath ? dirname18(dockerPath) : void 0]);
16075
16152
  if (extraPath) {
16076
16153
  p.say(`Hooks will get ${extraPath} on their PATH (where node, pnpm${dockerPath ? " and docker" : ""} live).`);
16077
16154
  }
@@ -16093,9 +16170,9 @@ routes:
16093
16170
  ${routeBlock}`;
16094
16171
  }
16095
16172
  function writeNewConfig(path, text2) {
16096
- mkdirSync22(dirname17(path), { recursive: true });
16173
+ mkdirSync23(dirname18(path), { recursive: true });
16097
16174
  try {
16098
- writeFileSync25(path, text2, { mode: 384, flag: "wx" });
16175
+ writeFileSync26(path, text2, { mode: 384, flag: "wx" });
16099
16176
  return true;
16100
16177
  } catch (e) {
16101
16178
  if (e.code === "EEXIST") return false;
@@ -16129,7 +16206,7 @@ Wrote ${o.configPath}.
16129
16206
  // src/connect-json.ts
16130
16207
  import { accessSync as accessSync4, constants as constants4 } from "fs";
16131
16208
  import { homedir as homedir10 } from "os";
16132
- import { dirname as dirname18, join as join36 } from "path";
16209
+ import { dirname as dirname19, join as join37 } from "path";
16133
16210
  import { delimiter as delimiter2 } from "path";
16134
16211
  var CHOICE_EXIT_CODE = 3;
16135
16212
  function createEmitter(json2, write) {
@@ -16168,12 +16245,12 @@ function agentBinSearchDirs(home, pathEnv, extraPath) {
16168
16245
  const dirs = [
16169
16246
  ...pathEnv.split(delimiter2),
16170
16247
  ...(extraPath ?? "").split(delimiter2),
16171
- join36(home, ".local/bin"),
16172
- join36(home, ".claude/local"),
16248
+ join37(home, ".local/bin"),
16249
+ join37(home, ".claude/local"),
16173
16250
  "/opt/homebrew/bin",
16174
16251
  "/usr/local/bin",
16175
- join36(home, ".npm-global/bin"),
16176
- join36(home, ".volta/bin")
16252
+ join37(home, ".npm-global/bin"),
16253
+ join37(home, ".volta/bin")
16177
16254
  ];
16178
16255
  return [...new Set(dirs.filter(Boolean))];
16179
16256
  }
@@ -16195,7 +16272,7 @@ function flagWizardAnswers(f, env) {
16195
16272
  const agentBin = f.agentBin ?? found?.bin;
16196
16273
  const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
16197
16274
  const dockerPath = f.hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
16198
- const extraPath = f.extraPath ?? deriveExtraPath([dirname18(env.nodePath), pnpmPath ? dirname18(pnpmPath) : void 0, dockerPath ? dirname18(dockerPath) : void 0]);
16275
+ const extraPath = f.extraPath ?? deriveExtraPath([dirname19(env.nodePath), pnpmPath ? dirname19(pnpmPath) : void 0, dockerPath ? dirname19(dockerPath) : void 0]);
16199
16276
  return {
16200
16277
  answers: {
16201
16278
  name: f.shipName?.trim() || env.hostname,
@@ -16209,8 +16286,8 @@ function flagWizardAnswers(f, env) {
16209
16286
  }
16210
16287
 
16211
16288
  // src/release-run.ts
16212
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync26, existsSync as existsSync33, copyFileSync, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync3, rmSync as rmSync17 } from "fs";
16213
- import { dirname as dirname19, join as join37 } from "path";
16289
+ import { readFileSync as readFileSync31, writeFileSync as writeFileSync27, existsSync as existsSync34, copyFileSync, mkdirSync as mkdirSync24, mkdtempSync as mkdtempSync3, rmSync as rmSync17 } from "fs";
16290
+ import { dirname as dirname20, join as join38 } from "path";
16214
16291
  import { tmpdir as tmpdir5 } from "os";
16215
16292
 
16216
16293
  // src/release.ts
@@ -16429,7 +16506,7 @@ function decideRelease(cwd, tickets, contract, opts = {}) {
16429
16506
  const block = opts.guard === false ? null : checkGuards(cwd, base);
16430
16507
  const lastTag = latestReleaseTag(cwd, pattern, ref);
16431
16508
  const lastReleased = lastTag ? tagCommit(cwd, lastTag) : null;
16432
- const head = resolve2(cwd, ref) ?? "";
16509
+ const head = resolve3(cwd, ref) ?? "";
16433
16510
  const merges = block ? [] : planMerge(cwd, tickets, contract, lastReleased, opts.branchFor ?? ((t) => branchForIssue(cwd, t.issue_id)), opts.ref ?? base, opts.blockerInfo, ref);
16434
16511
  const unreleasedCommits = lastReleased ? countCommits(cwd, `${lastReleased}..${ref}`) : 0;
16435
16512
  const unreleasedWork = lastReleased ? (gitOk(cwd, ["log", "--format=%s", `${lastReleased}..${ref}`]) ?? "").split("\n").filter((l) => l && !RELEASE_COMMIT_SUBJECT.test(l)).length : 0;
@@ -16609,8 +16686,8 @@ function movedPast(r, t, branch, pushedAs) {
16609
16686
  if (!r.columns.verifiedSha) return null;
16610
16687
  const verified = columnValue(t, r.contract.columns.verifiedSha);
16611
16688
  if (!verified) return null;
16612
- const head = resolve2(r.cwd, branch);
16613
- const remoteHead = resolve2(r.cwd, `${r.repo.branch.remote}/${pushedAs}`);
16689
+ const head = resolve3(r.cwd, branch);
16690
+ const remoteHead = resolve3(r.cwd, `${r.repo.branch.remote}/${pushedAs}`);
16614
16691
  if (remoteHead && !isAncestor(r.cwd, remoteHead, verified) && remoteHead !== head) {
16615
16692
  return { ticket: t, verified, head: remoteHead, branch: pushedAs };
16616
16693
  }
@@ -16633,7 +16710,7 @@ async function runReview(r, verified, reviewing) {
16633
16710
  out.requeues.push(moved);
16634
16711
  continue;
16635
16712
  }
16636
- const head = resolve2(r.cwd, branch);
16713
+ const head = resolve3(r.cwd, branch);
16637
16714
  if (!head) continue;
16638
16715
  if (r.dryRun) {
16639
16716
  r.emit.emit(`would push ${branch} to ${remote}/${pushedAs} for review`, { ticket: ticket.issue_id });
@@ -16951,13 +17028,13 @@ async function bump(o, size) {
16951
17028
  }
16952
17029
  return printed;
16953
17030
  }
16954
- const files = o.repo.release.versionFiles.map((f) => join37(o.cwd, f));
17031
+ const files = o.repo.release.versionFiles.map((f) => join38(o.cwd, f));
16955
17032
  const first = files[0];
16956
- if (!existsSync33(first)) {
17033
+ if (!existsSync34(first)) {
16957
17034
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
16958
17035
  return null;
16959
17036
  }
16960
- const current = JSON.parse(readFileSync30(first, "utf8")).version;
17037
+ const current = JSON.parse(readFileSync31(first, "utf8")).version;
16961
17038
  if (!current) {
16962
17039
  o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
16963
17040
  return null;
@@ -16970,27 +17047,27 @@ async function bump(o, size) {
16970
17047
  return version;
16971
17048
  }
16972
17049
  for (const f of files) {
16973
- const src = readFileSync30(f, "utf8");
17050
+ const src = readFileSync31(f, "utf8");
16974
17051
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
16975
17052
  if (out === src) {
16976
17053
  o.emit.error(`no version field in ${f}`);
16977
17054
  return null;
16978
17055
  }
16979
- writeFileSync26(f, out);
17056
+ writeFileSync27(f, out);
16980
17057
  }
16981
17058
  return version;
16982
17059
  }
16983
17060
  function writeChangelog(o, version, entries) {
16984
17061
  const file = o.repo.release.changelog;
16985
17062
  if (!file || entries.length === 0) return;
16986
- const path = join37(o.cwd, file);
17063
+ const path = join38(o.cwd, file);
16987
17064
  const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
16988
17065
  if (o.dryRun) {
16989
17066
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
16990
17067
  return;
16991
17068
  }
16992
- const existing = existsSync33(path) ? readFileSync30(path, "utf8") : "# Changelog\n";
16993
- writeFileSync26(path, insertChangelogSection(existing, section));
17069
+ const existing = existsSync34(path) ? readFileSync31(path, "utf8") : "# Changelog\n";
17070
+ writeFileSync27(path, insertChangelogSection(existing, section));
16994
17071
  }
16995
17072
  async function confirm(o, expected) {
16996
17073
  if (!o.repo.hooks.released) return void 0;
@@ -17083,8 +17160,8 @@ function closeReleaseCheckout(cwd, root, dir, branch, run3 = gitOk) {
17083
17160
  function openReleaseCheckout(o) {
17084
17161
  const { remote, base } = o.repo.branch;
17085
17162
  const branch = `crew/release-${Date.now()}`;
17086
- const root = mkdtempSync3(join37(tmpdir5(), "crew-release-"));
17087
- const dir = join37(root, "checkout");
17163
+ const root = mkdtempSync3(join38(tmpdir5(), "crew-release-"));
17164
+ const dir = join38(root, "checkout");
17088
17165
  o.emit.emit(`release checkout: ${dir}`, { step: "release" });
17089
17166
  const close = () => closeReleaseCheckout(o.cwd, root, dir, branch);
17090
17167
  try {
@@ -17094,10 +17171,10 @@ function openReleaseCheckout(o) {
17094
17171
  throw e;
17095
17172
  }
17096
17173
  for (const f of o.repo.worktrees.copy) {
17097
- const src = join37(o.cwd, f);
17098
- if (!existsSync33(src)) continue;
17099
- mkdirSync23(dirname19(join37(dir, f)), { recursive: true });
17100
- copyFileSync(src, join37(dir, f));
17174
+ const src = join38(o.cwd, f);
17175
+ if (!existsSync34(src)) continue;
17176
+ mkdirSync24(dirname20(join38(dir, f)), { recursive: true });
17177
+ copyFileSync(src, join38(dir, f));
17101
17178
  }
17102
17179
  return { dir, branch, close };
17103
17180
  }
@@ -17520,7 +17597,7 @@ async function mergeAndRelease(o, decision, fresh, releaseBranch, isolated = fal
17520
17597
  branches: merged.filter((c) => c.branch).map((c) => ({
17521
17598
  key: c.ticket.issue_tag ?? c.ticket.issue_id,
17522
17599
  branch: c.branch,
17523
- tip: resolve2(o.cwd, c.branch) ?? ""
17600
+ tip: resolve3(o.cwd, c.branch) ?? ""
17524
17601
  }))
17525
17602
  };
17526
17603
  const hookFailure2 = {
@@ -17583,7 +17660,7 @@ async function mergeAndRelease(o, decision, fresh, releaseBranch, isolated = fal
17583
17660
  if (releaseBranch && !o.dryRun) {
17584
17661
  const { remote, base } = o.repo.branch;
17585
17662
  if (fetchRemote(o.cwd, remote) && remoteBranchExists(o.cwd, remote, base)) {
17586
- const moved = resolve2(o.cwd, `${remote}/${base}`);
17663
+ const moved = resolve3(o.cwd, `${remote}/${base}`);
17587
17664
  if (moved && !isAncestor(o.cwd, moved, "HEAD")) {
17588
17665
  o.emit.warn(`${remote}/${base} moved during the release \u2014 discarding it; the tickets stay verified and the next cycle retries`);
17589
17666
  return { merged, conflicts, unbuildable, version, deployed: false, decision, stopped: "base branch moved during release" };
@@ -18076,7 +18153,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
18076
18153
  }
18077
18154
 
18078
18155
  // src/watch.ts
18079
- import { watch as watchFile, existsSync as existsSync34, statSync as statSync10, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
18156
+ import { watch as watchFile, existsSync as existsSync35, statSync as statSync11, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
18080
18157
  var E = "\x1B[";
18081
18158
  var dim = (s) => `${E}2m${s}${E}0m`;
18082
18159
  var bold = (s) => `${E}1m${s}${E}0m`;
@@ -18177,8 +18254,8 @@ function renderHeaders(states, now = Date.now()) {
18177
18254
  return states.map((s) => renderHeader(s, now)).join("");
18178
18255
  }
18179
18256
  function readFrom(file, from) {
18180
- if (!existsSync34(file)) return { events: [], offset: from };
18181
- const size = statSync10(file).size;
18257
+ if (!existsSync35(file)) return { events: [], offset: from };
18258
+ const size = statSync11(file).size;
18182
18259
  if (size < from) return readFrom(file, 0);
18183
18260
  if (size === from) return { events: [], offset: from };
18184
18261
  const fd = openSync4(file, "r");
@@ -18420,8 +18497,8 @@ function nextRoles(decision, ran) {
18420
18497
  }
18421
18498
 
18422
18499
  // src/ship-name.ts
18423
- import { existsSync as existsSync35, mkdirSync as mkdirSync24, readFileSync as readFileSync31, renameSync as renameSync5, writeFileSync as writeFileSync27 } from "fs";
18424
- import { dirname as dirname20 } from "path";
18500
+ import { existsSync as existsSync36, mkdirSync as mkdirSync25, readFileSync as readFileSync32, renameSync as renameSync5, writeFileSync as writeFileSync28 } from "fs";
18501
+ import { dirname as dirname21 } from "path";
18425
18502
  import { parseDocument as parseDocument5 } from "yaml";
18426
18503
  var ShipNameError = class extends Error {
18427
18504
  };
@@ -18482,34 +18559,34 @@ async function syncRouteName(i) {
18482
18559
  return { status: "ok", ...base, action, name: i.local };
18483
18560
  }
18484
18561
  function readSyncedName(resolvedFile) {
18485
- if (!existsSync35(resolvedFile)) return void 0;
18562
+ if (!existsSync36(resolvedFile)) return void 0;
18486
18563
  try {
18487
- const v = JSON.parse(readFileSync31(resolvedFile, "utf8")).shipName;
18564
+ const v = JSON.parse(readFileSync32(resolvedFile, "utf8")).shipName;
18488
18565
  return typeof v === "string" ? v : void 0;
18489
18566
  } catch {
18490
18567
  return void 0;
18491
18568
  }
18492
18569
  }
18493
18570
  function writeSyncedName(resolvedFile, name) {
18494
- if (!existsSync35(resolvedFile)) return;
18571
+ if (!existsSync36(resolvedFile)) return;
18495
18572
  let raw;
18496
18573
  try {
18497
- raw = JSON.parse(readFileSync31(resolvedFile, "utf8"));
18574
+ raw = JSON.parse(readFileSync32(resolvedFile, "utf8"));
18498
18575
  } catch {
18499
18576
  return;
18500
18577
  }
18501
18578
  if (raw.shipName === name) return;
18502
18579
  raw.shipName = name;
18503
- mkdirSync24(dirname20(resolvedFile), { recursive: true });
18580
+ mkdirSync25(dirname21(resolvedFile), { recursive: true });
18504
18581
  const tmp = `${resolvedFile}.tmp${process.pid}`;
18505
- writeFileSync27(tmp, `${JSON.stringify(raw, null, 2)}
18582
+ writeFileSync28(tmp, `${JSON.stringify(raw, null, 2)}
18506
18583
  `);
18507
18584
  renameSync5(tmp, resolvedFile);
18508
18585
  }
18509
18586
  async function reconcileShipName(i) {
18510
18587
  let local = i.ship.name;
18511
18588
  try {
18512
- local = shipNameOfYaml(readFileSync31(i.configFile, "utf8")) ?? local;
18589
+ local = shipNameOfYaml(readFileSync32(i.configFile, "utf8")) ?? local;
18513
18590
  } catch {
18514
18591
  }
18515
18592
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -18533,8 +18610,8 @@ async function reconcileShipName(i) {
18533
18610
  }
18534
18611
  if (result.status !== "ok") return result;
18535
18612
  if (result.action === "adopt" || result.action === "row-wins") {
18536
- const text2 = readFileSync31(i.configFile, "utf8");
18537
- writeFileSync27(i.configFile, setShipNameInYaml(text2, result.name));
18613
+ const text2 = readFileSync32(i.configFile, "utf8");
18614
+ writeFileSync28(i.configFile, setShipNameInYaml(text2, result.name));
18538
18615
  i.ship.name = result.name;
18539
18616
  if (result.action === "row-wins") {
18540
18617
  i.emit?.warn(
@@ -19193,8 +19270,8 @@ function describeCapability(i) {
19193
19270
  }
19194
19271
 
19195
19272
  // src/agents-sync-run.ts
19196
- import { existsSync as existsSync36, mkdirSync as mkdirSync25, readFileSync as readFileSync32, writeFileSync as writeFileSync28 } from "fs";
19197
- import { dirname as dirname21 } from "path";
19273
+ import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync33, writeFileSync as writeFileSync29 } from "fs";
19274
+ import { dirname as dirname22 } from "path";
19198
19275
  async function runAgentsSync(target, o) {
19199
19276
  const dryRun = o.dryRun ?? false;
19200
19277
  const force = o.force ?? false;
@@ -19227,12 +19304,12 @@ async function runAgentsSync(target, o) {
19227
19304
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
19228
19305
  if (!dryRun) {
19229
19306
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
19230
- const raw = existsSync36(resolvedPath) ? JSON.parse(readFileSync32(resolvedPath, "utf8")) : {};
19307
+ const raw = existsSync37(resolvedPath) ? JSON.parse(readFileSync33(resolvedPath, "utf8")) : {};
19231
19308
  raw.agentPersonas = personaResult.agentPersonas;
19232
19309
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
19233
19310
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
19234
- mkdirSync25(dirname21(resolvedPath), { recursive: true });
19235
- writeFileSync28(resolvedPath, `${JSON.stringify(raw, null, 2)}
19311
+ mkdirSync26(dirname22(resolvedPath), { recursive: true });
19312
+ writeFileSync29(resolvedPath, `${JSON.stringify(raw, null, 2)}
19236
19313
  `);
19237
19314
  if (target.resolved) {
19238
19315
  target.resolved.agentPersonas = personaResult.agentPersonas;
@@ -19336,7 +19413,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
19336
19413
 
19337
19414
  // src/repos-check.ts
19338
19415
  import { spawnSync as spawnSync9 } from "child_process";
19339
- import { readFileSync as readFileSync33 } from "fs";
19416
+ import { readFileSync as readFileSync34 } from "fs";
19340
19417
  import { basename as basename11 } from "path";
19341
19418
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
19342
19419
  function checkHookSyntax(script, shell) {
@@ -19377,7 +19454,7 @@ function checkRepoSpec(dir, opts = {}) {
19377
19454
  }
19378
19455
  let parsed;
19379
19456
  try {
19380
- parsed = parseRepoConfig(readFileSync33(file, "utf8"), file);
19457
+ parsed = parseRepoConfig(readFileSync34(file, "utf8"), file);
19381
19458
  } catch (e) {
19382
19459
  if (!(e instanceof RepoConfigError)) throw e;
19383
19460
  return { ...empty, file, error: e.message, exitCode: 1 };
@@ -19656,9 +19733,9 @@ function existingRepoConfig(f) {
19656
19733
  }
19657
19734
 
19658
19735
  // src/repos-hooks-agent.ts
19659
- import { spawn as spawn7, execFileSync as execFileSync9 } from "child_process";
19736
+ import { spawn as spawn7, execFileSync as execFileSync10 } from "child_process";
19660
19737
  import { realpathSync as realpathSync2 } from "fs";
19661
- import { isAbsolute as isAbsolute2, relative } from "path";
19738
+ import { isAbsolute as isAbsolute3, relative } from "path";
19662
19739
  var AgentSuggestError = class extends Error {
19663
19740
  reason;
19664
19741
  constructor(reason, message) {
@@ -19727,7 +19804,7 @@ function parseAgentStream(out, dir) {
19727
19804
  if (ev?.type === "assistant" && Array.isArray(ev.message?.content)) {
19728
19805
  for (const b of ev.message.content) {
19729
19806
  const p = b?.type === "tool_use" && b.name === "Read" ? b.input?.file_path : void 0;
19730
- if (typeof p === "string") files.add(isAbsolute2(p) ? relative(base, p) : p);
19807
+ if (typeof p === "string") files.add(isAbsolute3(p) ? relative(base, p) : p);
19731
19808
  }
19732
19809
  } else if (ev?.type === "result") {
19733
19810
  text2 = typeof ev.result === "string" ? ev.result : "";
@@ -19759,7 +19836,7 @@ function classifyFailure(o) {
19759
19836
  }
19760
19837
  function porcelain(dir) {
19761
19838
  try {
19762
- return execFileSync9("git", ["status", "--porcelain", "--untracked-files=all"], { cwd: dir, encoding: "utf8" });
19839
+ return execFileSync10("git", ["status", "--porcelain", "--untracked-files=all"], { cwd: dir, encoding: "utf8" });
19763
19840
  } catch {
19764
19841
  return void 0;
19765
19842
  }
@@ -19774,7 +19851,7 @@ async function runAgentSuggest(o) {
19774
19851
  const before = porcelain(o.dir);
19775
19852
  const timeout = AbortSignal.timeout(o.timeoutMs ?? DEFAULT_TIMEOUT_MS);
19776
19853
  const signal = o.signal ? AbortSignal.any([o.signal, timeout]) : timeout;
19777
- const run3 = await new Promise((resolve7) => {
19854
+ const run3 = await new Promise((resolve8) => {
19778
19855
  let out = "";
19779
19856
  let err3 = "";
19780
19857
  const child = spawn7(bin, args, { cwd: o.dir, env, detached: true, stdio: ["pipe", "pipe", "pipe"] });
@@ -19783,7 +19860,7 @@ async function runAgentSuggest(o) {
19783
19860
  if (settled) return;
19784
19861
  settled = true;
19785
19862
  signal.removeEventListener("abort", onAbort);
19786
- resolve7(r);
19863
+ resolve8(r);
19787
19864
  };
19788
19865
  const onAbort = () => {
19789
19866
  try {
@@ -19828,7 +19905,7 @@ ${run3.err}` });
19828
19905
  }
19829
19906
 
19830
19907
  // src/sample-ticket.ts
19831
- import { basename as basename12, dirname as dirname22, join as join38 } from "path";
19908
+ import { basename as basename12, dirname as dirname23, join as join39 } from "path";
19832
19909
  var SAMPLE_REPO = "crew-sample";
19833
19910
  var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
19834
19911
  var SAMPLE_MARKER = "sample: true";
@@ -19851,8 +19928,8 @@ has something safe to work on: its test and build hooks always pass, it has no
19851
19928
  remote, and \`crew sample-ticket --remove\` deletes it.
19852
19929
  `;
19853
19930
  function sampleDir(existing, reposBase, workspace) {
19854
- if (existing.length > 0) return join38(dirname22(existing[0]), SAMPLE_REPO);
19855
- return join38(reposBase, workspace, SAMPLE_REPO);
19931
+ if (existing.length > 0) return join39(dirname23(existing[0]), SAMPLE_REPO);
19932
+ return join39(reposBase, workspace, SAMPLE_REPO);
19856
19933
  }
19857
19934
  var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
19858
19935
  function sampleDescription() {
@@ -19896,8 +19973,8 @@ function planSampleTicket(i) {
19896
19973
  }
19897
19974
 
19898
19975
  // src/worktree-sweep.ts
19899
- import { existsSync as existsSync37, readFileSync as readFileSync34, readdirSync as readdirSync14 } from "fs";
19900
- import { basename as basename13, dirname as dirname23, join as join39, resolve as resolve4 } from "path";
19976
+ import { existsSync as existsSync38, readFileSync as readFileSync35, readdirSync as readdirSync14 } from "fs";
19977
+ import { basename as basename13, dirname as dirname24, join as join40, resolve as resolve5 } from "path";
19901
19978
  function planWorktreeSweep(target, tickets, contract) {
19902
19979
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
19903
19980
  const verified = contract.statuses.verified;
@@ -19961,9 +20038,9 @@ function worktreeTicketNumbers(target) {
19961
20038
  return [...nums];
19962
20039
  }
19963
20040
  function siblingWorktreeDirs(target) {
19964
- const parent = dirname23(target.dir.replace(/[/\\]+$/, ""));
20041
+ const parent = dirname24(target.dir.replace(/[/\\]+$/, ""));
19965
20042
  const stem = `${basename13(target.dir.replace(/[/\\]+$/, ""))}-`;
19966
- const registered = new Set(worktrees(target.dir).map((w) => resolve4(w.path)));
20043
+ const registered = new Set(worktrees(target.dir).map((w) => resolve5(w.path)));
19967
20044
  let names;
19968
20045
  try {
19969
20046
  names = readdirSync14(parent);
@@ -19974,8 +20051,8 @@ function siblingWorktreeDirs(target) {
19974
20051
  for (const name of names) {
19975
20052
  if (!name.startsWith(stem)) continue;
19976
20053
  const m = DIR_NUMBER_SUFFIXED.exec(name);
19977
- const path = join39(parent, name);
19978
- if (m && !registered.has(resolve4(path))) out.push({ path, number: m[1] });
20054
+ const path = join40(parent, name);
20055
+ if (m && !registered.has(resolve5(path))) out.push({ path, number: m[1] });
19979
20056
  }
19980
20057
  return out;
19981
20058
  }
@@ -20009,7 +20086,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20009
20086
  let removed = 0;
20010
20087
  let keptBranches = 0;
20011
20088
  for (const a of actions) {
20012
- if (!dryRun && base && !a.keepBranch && existsSync37(a.path) && isHandBackMerge(a.path, base)) {
20089
+ if (!dryRun && base && !a.keepBranch && existsSync38(a.path) && isHandBackMerge(a.path, base)) {
20013
20090
  if (gitOk(a.path, ["merge", "--abort"]) !== null) {
20014
20091
  log.emit(
20015
20092
  `abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
@@ -20017,7 +20094,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20017
20094
  );
20018
20095
  }
20019
20096
  }
20020
- if (!dryRun && existsSync37(a.path)) {
20097
+ if (!dryRun && existsSync38(a.path)) {
20021
20098
  let dirty = false;
20022
20099
  try {
20023
20100
  dirty = status(a.path).length > 0;
@@ -20028,7 +20105,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20028
20105
  continue;
20029
20106
  }
20030
20107
  }
20031
- if (!dryRun && a.requirePushed && a.branch && existsSync37(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20108
+ if (!dryRun && a.requirePushed && a.branch && existsSync38(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20032
20109
  refuse(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
20033
20110
  continue;
20034
20111
  }
@@ -20077,7 +20154,7 @@ function isHandBackMerge(path, base) {
20077
20154
  if (!msgPath) return false;
20078
20155
  let first;
20079
20156
  try {
20080
- first = readFileSync34(resolve4(path, msgPath), "utf8").split("\n")[0] ?? "";
20157
+ first = readFileSync35(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
20081
20158
  } catch {
20082
20159
  return false;
20083
20160
  }
@@ -20182,16 +20259,16 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
20182
20259
  }
20183
20260
 
20184
20261
  // src/stream-sweep.ts
20185
- import { existsSync as existsSync38, readdirSync as readdirSync15, rmSync as rmSync18, statSync as statSync11 } from "fs";
20186
- import { join as join40 } from "path";
20262
+ import { existsSync as existsSync39, readdirSync as readdirSync15, rmSync as rmSync18, statSync as statSync12 } from "fs";
20263
+ import { join as join41 } from "path";
20187
20264
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
20188
20265
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
20189
- const dir = join40(stateDir, "streams");
20190
- if (!existsSync38(dir)) return [];
20266
+ const dir = join41(stateDir, "streams");
20267
+ if (!existsSync39(dir)) return [];
20191
20268
  const cutoff = now - retentionDays * DAY_MS2;
20192
- return readdirSync15(dir).map((name) => join40(dir, name)).filter((path) => {
20269
+ return readdirSync15(dir).map((name) => join41(dir, name)).filter((path) => {
20193
20270
  try {
20194
- return statSync11(path).mtimeMs < cutoff;
20271
+ return statSync12(path).mtimeMs < cutoff;
20195
20272
  } catch {
20196
20273
  return false;
20197
20274
  }
@@ -20215,11 +20292,11 @@ function applyStreamSweep(paths, dryRun, log) {
20215
20292
  return removed;
20216
20293
  }
20217
20294
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
20218
- if (!existsSync38(stateDir)) return [];
20295
+ if (!existsSync39(stateDir)) return [];
20219
20296
  const cutoff = now - retentionDays * DAY_MS2;
20220
- return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join40(stateDir, name)).filter((path) => {
20297
+ return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join41(stateDir, name)).filter((path) => {
20221
20298
  try {
20222
- return statSync11(path).mtimeMs < cutoff;
20299
+ return statSync12(path).mtimeMs < cutoff;
20223
20300
  } catch {
20224
20301
  return false;
20225
20302
  }
@@ -20227,12 +20304,12 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
20227
20304
  }
20228
20305
 
20229
20306
  // src/cli.ts
20230
- import { realpathSync as realpathSync3, readFileSync as readFileSync40, existsSync as existsSync45, mkdirSync as mkdirSync32, writeFileSync as writeFileSync33, rmSync as rmSync23, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync15, readdirSync as readdirSync20 } from "fs";
20307
+ import { realpathSync as realpathSync3, readFileSync as readFileSync41, existsSync as existsSync46, mkdirSync as mkdirSync33, writeFileSync as writeFileSync34, rmSync as rmSync23, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
20231
20308
  import { createInterface } from "readline/promises";
20232
20309
 
20233
20310
  // src/periscope-cmd.ts
20234
- import { existsSync as existsSync39, mkdirSync as mkdirSync26, readFileSync as readFileSync35, rmSync as rmSync19, writeFileSync as writeFileSync29 } from "fs";
20235
- import { join as join41 } from "path";
20311
+ import { existsSync as existsSync40, mkdirSync as mkdirSync27, readFileSync as readFileSync36, rmSync as rmSync19, writeFileSync as writeFileSync30 } from "fs";
20312
+ import { join as join42 } from "path";
20236
20313
  var PeriscopeError = class extends Error {
20237
20314
  };
20238
20315
  var LivePeriscopeError = class extends PeriscopeError {
@@ -20242,22 +20319,22 @@ var LivePeriscopeError = class extends PeriscopeError {
20242
20319
  this.data = { refused: "live_periscope_exists", ...live };
20243
20320
  }
20244
20321
  };
20245
- var priorDir = (stateDir) => join41(stateDir, "periscope-priors");
20246
- var priorPath = (stateDir, ticketRecordId) => join41(priorDir(stateDir), `${ticketRecordId}.json`);
20322
+ var priorDir = (stateDir) => join42(stateDir, "periscope-priors");
20323
+ var priorPath = (stateDir, ticketRecordId) => join42(priorDir(stateDir), `${ticketRecordId}.json`);
20247
20324
  function removePeriscopePrior(stateDir, ticketRecordId) {
20248
20325
  rmSync19(priorPath(stateDir, ticketRecordId), { force: true });
20249
20326
  }
20250
20327
  function writePeriscopePrior(stateDir, ticketRecordId, prior) {
20251
20328
  try {
20252
- mkdirSync26(priorDir(stateDir), { recursive: true });
20253
- writeFileSync29(priorPath(stateDir, ticketRecordId), JSON.stringify(prior));
20329
+ mkdirSync27(priorDir(stateDir), { recursive: true });
20330
+ writeFileSync30(priorPath(stateDir, ticketRecordId), JSON.stringify(prior));
20254
20331
  } catch {
20255
20332
  }
20256
20333
  }
20257
20334
  function readPeriscopePrior(stateDir, ticketRecordId) {
20258
20335
  try {
20259
20336
  const p = priorPath(stateDir, ticketRecordId);
20260
- return existsSync39(p) ? JSON.parse(readFileSync35(p, "utf8")) : void 0;
20337
+ return existsSync40(p) ? JSON.parse(readFileSync36(p, "utf8")) : void 0;
20261
20338
  } catch {
20262
20339
  return void 0;
20263
20340
  }
@@ -20419,7 +20496,12 @@ ${tail(iso.output)}`);
20419
20496
  const handoff = await d.runHook("handoff", wt.path, env);
20420
20497
  if (handoff && handoff.code !== 0) return abandon(`handoff failed (exit ${handoff.code})
20421
20498
  ${tail(handoff.output)}`);
20422
- const preview = await d.runHook("preview", wt.path, { ...env, CREW_PREVIEW_HOST: d.host });
20499
+ try {
20500
+ d.prepareDropFile(wt.path);
20501
+ } catch (e) {
20502
+ return abandon(`preparing the drop file failed: ${e.message}`);
20503
+ }
20504
+ const preview = await d.runHook("preview", wt.path, { ...env, CREW_PREVIEW_HOST: d.host, ...pointedAtEnv(wt.path) });
20423
20505
  if (!preview || preview.code !== 0) {
20424
20506
  return abandon(`preview exited ${preview?.code ?? "without running"}
20425
20507
  ${tail(preview?.output ?? "")}`);
@@ -20634,7 +20716,8 @@ async function runPeriscopeResume(d, number) {
20634
20716
  ${tail2(iso.output)}`);
20635
20717
  if (iso) Object.assign(env, parseEnvLines(iso.output));
20636
20718
  }
20637
- const preview = await d.runHook("preview", row.worktree, { ...env, CREW_PREVIEW_HOST: row.host || "localhost" });
20719
+ d.prepareDropFile?.(row.worktree);
20720
+ const preview = await d.runHook("preview", row.worktree, { ...env, CREW_PREVIEW_HOST: row.host || "localhost", ...pointedAtEnv(row.worktree) });
20638
20721
  if (!preview || preview.code !== 0) {
20639
20722
  throw new PeriscopeError(`preview exited ${preview?.code ?? "without running"}
20640
20723
  ${tail2(preview?.output ?? "")}`);
@@ -20654,14 +20737,19 @@ ${tail2(preview.output)}`);
20654
20737
  }
20655
20738
 
20656
20739
  // src/periscope-idle.ts
20657
- import { execFileSync as execFileSync10 } from "child_process";
20658
- import { statSync as statSync12 } from "fs";
20659
- import { join as join42 } from "path";
20740
+ import { execFileSync as execFileSync11 } from "child_process";
20741
+ import { statSync as statSync13 } from "fs";
20742
+ import { join as join43 } from "path";
20660
20743
  var MAX_SCANNED_FILES = 2e4;
20661
20744
  function newestWorktreeMtime(root) {
20745
+ const a = newestTrackedMtime(root);
20746
+ const b = dropFileMtime(root);
20747
+ return a === null ? b : b === null ? a : Math.max(a, b);
20748
+ }
20749
+ function newestTrackedMtime(root) {
20662
20750
  let listing;
20663
20751
  try {
20664
- listing = execFileSync10("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
20752
+ listing = execFileSync11("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
20665
20753
  cwd: root,
20666
20754
  encoding: "utf8",
20667
20755
  stdio: ["ignore", "pipe", "ignore"],
@@ -20676,7 +20764,7 @@ function newestWorktreeMtime(root) {
20676
20764
  if (!rel) continue;
20677
20765
  if (++scanned > MAX_SCANNED_FILES) break;
20678
20766
  try {
20679
- const m = statSync12(join42(root, rel)).mtimeMs;
20767
+ const m = statSync13(join43(root, rel)).mtimeMs;
20680
20768
  if (newest === null || m > newest) newest = m;
20681
20769
  } catch {
20682
20770
  }
@@ -20751,21 +20839,21 @@ async function runPeriscopeIdlePass(d) {
20751
20839
  import { dirname as dirOf2, resolve as resolvePath } from "path";
20752
20840
 
20753
20841
  // src/ssh-keys.ts
20754
- import { execFileSync as execFileSync11 } from "child_process";
20755
- import { existsSync as existsSync40, mkdirSync as mkdirSync27, readFileSync as readFileSync36, chmodSync as chmodSync3 } from "fs";
20756
- import { dirname as dirname24, join as join43 } from "path";
20842
+ import { execFileSync as execFileSync12 } from "child_process";
20843
+ import { existsSync as existsSync41, mkdirSync as mkdirSync28, readFileSync as readFileSync37, chmodSync as chmodSync3 } from "fs";
20844
+ import { dirname as dirname25, join as join44 } from "path";
20757
20845
  var SshKeyError = class extends Error {
20758
20846
  };
20759
20847
  function shipSshKeyPathFor(stateDir) {
20760
- return join43(stateDir, "ssh", "id_ed25519");
20848
+ return join44(stateDir, "ssh", "id_ed25519");
20761
20849
  }
20762
20850
  function ensureShipSshKeypair(stateDir) {
20763
20851
  const privateKeyPath = shipSshKeyPathFor(stateDir);
20764
20852
  const publicKeyPath = `${privateKeyPath}.pub`;
20765
- if (!existsSync40(privateKeyPath)) {
20766
- mkdirSync27(dirname24(privateKeyPath), { recursive: true });
20853
+ if (!existsSync41(privateKeyPath)) {
20854
+ mkdirSync28(dirname25(privateKeyPath), { recursive: true });
20767
20855
  try {
20768
- execFileSync11("ssh-keygen", [
20856
+ execFileSync12("ssh-keygen", [
20769
20857
  "-t",
20770
20858
  "ed25519",
20771
20859
  "-N",
@@ -20783,15 +20871,15 @@ function ensureShipSshKeypair(stateDir) {
20783
20871
  }
20784
20872
  chmodSync3(privateKeyPath, 384);
20785
20873
  }
20786
- if (!existsSync40(publicKeyPath)) {
20874
+ if (!existsSync41(publicKeyPath)) {
20787
20875
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
20788
20876
  }
20789
- const publicKey = readFileSync36(publicKeyPath, "utf8").trim();
20877
+ const publicKey = readFileSync37(publicKeyPath, "utf8").trim();
20790
20878
  return { privateKeyPath, publicKeyPath, publicKey };
20791
20879
  }
20792
20880
  function sshKeygenAvailable() {
20793
20881
  try {
20794
- execFileSync11("ssh-keygen", ["-V"], { stdio: ["ignore", "ignore", "ignore"] });
20882
+ execFileSync12("ssh-keygen", ["-V"], { stdio: ["ignore", "ignore", "ignore"] });
20795
20883
  return true;
20796
20884
  } catch (e) {
20797
20885
  return e.code !== "ENOENT";
@@ -20950,12 +21038,12 @@ async function unregisterEndpoint(workspaceId, info, d) {
20950
21038
  import { createServer as createHttpServer } from "http";
20951
21039
  import { spawn as spawn8 } from "child_process";
20952
21040
  import { randomBytes } from "crypto";
20953
- import { existsSync as existsSync41, mkdirSync as mkdirSync29, readFileSync as readFileSync37, writeFileSync as writeFileSync30, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
20954
- import { join as join44 } from "path";
21041
+ import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync31, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21042
+ import { join as join45 } from "path";
20955
21043
 
20956
21044
  // src/passenger-mirror.ts
20957
- import { mkdirSync as mkdirSync28 } from "fs";
20958
- import { dirname as dirname25 } from "path";
21045
+ import { mkdirSync as mkdirSync29 } from "fs";
21046
+ import { dirname as dirname26 } from "path";
20959
21047
  function mirrorRepoOf(spec) {
20960
21048
  const fromOrigin = spec.originUrl ? parseRemote(spec.originUrl) : null;
20961
21049
  if (fromOrigin) return fromOrigin;
@@ -20973,9 +21061,9 @@ async function materializeMirrorCheckouts(specs, cacheFor = (s) => new SandboxCa
20973
21061
  continue;
20974
21062
  }
20975
21063
  try {
20976
- const parent = dirname25(spec.dir);
20977
- mkdirSync28(parent, { recursive: true });
20978
- mkdirSync28(spec.dir, { recursive: true });
21064
+ const parent = dirname26(spec.dir);
21065
+ mkdirSync29(parent, { recursive: true });
21066
+ mkdirSync29(spec.dir, { recursive: true });
20979
21067
  const r = await cacheFor(spec).materialize(repo, spec.base, parent, spec.name);
20980
21068
  out.push({ ...base, action: r.changed ? "updated" : "unchanged", detail: r.tip });
20981
21069
  } catch (e) {
@@ -21081,21 +21169,21 @@ var defaultSpawn2 = (cmd, args, env) => {
21081
21169
  return child;
21082
21170
  };
21083
21171
  function syncDaemonStatePath(stateDir, workspaceId) {
21084
- return join44(stateDir, "passengers", `${workspaceId}-sync.json`);
21172
+ return join45(stateDir, "passengers", `${workspaceId}-sync.json`);
21085
21173
  }
21086
21174
  function readPersistedSyncDaemon(stateDir, workspaceId) {
21087
21175
  const path = syncDaemonStatePath(stateDir, workspaceId);
21088
- if (!existsSync41(path)) return void 0;
21176
+ if (!existsSync42(path)) return void 0;
21089
21177
  try {
21090
- return JSON.parse(readFileSync37(path, "utf8"));
21178
+ return JSON.parse(readFileSync38(path, "utf8"));
21091
21179
  } catch {
21092
21180
  return void 0;
21093
21181
  }
21094
21182
  }
21095
21183
  function writePersistedSyncDaemon(stateDir, state) {
21096
21184
  const path = syncDaemonStatePath(stateDir, state.workspaceId);
21097
- mkdirSync29(join44(stateDir, "passengers"), { recursive: true });
21098
- writeFileSync30(path, `${JSON.stringify(state, null, 2)}
21185
+ mkdirSync30(join45(stateDir, "passengers"), { recursive: true });
21186
+ writeFileSync31(path, `${JSON.stringify(state, null, 2)}
21099
21187
  `);
21100
21188
  }
21101
21189
  function removePersistedSyncDaemon(stateDir, workspaceId) {
@@ -21115,8 +21203,8 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
21115
21203
  }
21116
21204
  }
21117
21205
  function readAllPersistedSyncWorkspaceIds(stateDir) {
21118
- const dir = join44(stateDir, "passengers");
21119
- if (!existsSync41(dir)) return [];
21206
+ const dir = join45(stateDir, "passengers");
21207
+ if (!existsSync42(dir)) return [];
21120
21208
  const suffix = "-sync.json";
21121
21209
  return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
21122
21210
  }
@@ -21204,14 +21292,14 @@ function describeCause(err3) {
21204
21292
  }
21205
21293
 
21206
21294
  // src/daemon.ts
21207
- import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, readdirSync as readdirSync17, rmSync as rmSync20, statSync as statSync13, writeFileSync as writeFileSync31 } from "fs";
21208
- import { join as join45 } from "path";
21295
+ import { existsSync as existsSync43, mkdirSync as mkdirSync31, readFileSync as readFileSync39, readdirSync as readdirSync17, rmSync as rmSync20, statSync as statSync14, writeFileSync as writeFileSync32 } from "fs";
21296
+ import { join as join46 } from "path";
21209
21297
  import { randomBytes as randomBytes2 } from "crypto";
21210
21298
  var DEFAULT_FLOOR_MS = 5e3;
21211
21299
  var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
21212
21300
  var ALLOWANCE_PAUSE_MS = 5 * 60 * 1e3;
21213
21301
  var RELOAD_WARN_MS = 10 * 60 * 1e3;
21214
- var defaultSleep = (ms3) => new Promise((resolve7) => setTimeout(resolve7, ms3));
21302
+ var defaultSleep = (ms3) => new Promise((resolve8) => setTimeout(resolve8, ms3));
21215
21303
  async function runDaemonLoop(o) {
21216
21304
  const floor = o.floorMs ?? DEFAULT_FLOOR_MS;
21217
21305
  const ceiling = o.ceilingMs ?? DEFAULT_CEILING_MS;
@@ -21285,7 +21373,7 @@ async function runDaemonLoop(o) {
21285
21373
  function latestMtime(path) {
21286
21374
  let stat2;
21287
21375
  try {
21288
- stat2 = statSync13(path);
21376
+ stat2 = statSync14(path);
21289
21377
  } catch {
21290
21378
  return 0;
21291
21379
  }
@@ -21293,7 +21381,7 @@ function latestMtime(path) {
21293
21381
  if (!stat2.isDirectory()) return 0;
21294
21382
  let latest = stat2.mtimeMs;
21295
21383
  for (const entry of readdirSync17(path, { withFileTypes: true })) {
21296
- latest = Math.max(latest, latestMtime(join45(path, entry.name)));
21384
+ latest = Math.max(latest, latestMtime(join46(path, entry.name)));
21297
21385
  }
21298
21386
  return latest;
21299
21387
  }
@@ -21302,30 +21390,30 @@ function computeTreeSignature(paths) {
21302
21390
  }
21303
21391
  function makeStaleChecker(paths, opts = {}) {
21304
21392
  const initial = computeTreeSignature(paths);
21305
- return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync42(opts.reloadSentinel);
21393
+ return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync43(opts.reloadSentinel);
21306
21394
  }
21307
21395
  function reloadSentinelFor(stateDir) {
21308
- return join45(stateDir, "reload-requested");
21396
+ return join46(stateDir, "reload-requested");
21309
21397
  }
21310
21398
  function readReloadRequestedAt(stateDir) {
21311
21399
  try {
21312
- return statSync13(reloadSentinelFor(stateDir)).mtimeMs;
21400
+ return statSync14(reloadSentinelFor(stateDir)).mtimeMs;
21313
21401
  } catch {
21314
21402
  return null;
21315
21403
  }
21316
21404
  }
21317
21405
  function daemonStampFor(stateDir) {
21318
- return join45(stateDir, "daemon.started");
21406
+ return join46(stateDir, "daemon.started");
21319
21407
  }
21320
21408
  function noteDaemonStarted(stateDir, now = Date.now()) {
21321
- mkdirSync30(stateDir, { recursive: true });
21409
+ mkdirSync31(stateDir, { recursive: true });
21322
21410
  rmSync20(reloadSentinelFor(stateDir), { force: true });
21323
- writeFileSync31(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
21411
+ writeFileSync32(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
21324
21412
  `);
21325
21413
  }
21326
21414
  function readDaemonStarted(stateDir) {
21327
21415
  try {
21328
- const v = JSON.parse(readFileSync38(daemonStampFor(stateDir), "utf8")).startedAt;
21416
+ const v = JSON.parse(readFileSync39(daemonStampFor(stateDir), "utf8")).startedAt;
21329
21417
  return typeof v === "number" ? v : null;
21330
21418
  } catch {
21331
21419
  return null;
@@ -21334,7 +21422,7 @@ function readDaemonStarted(stateDir) {
21334
21422
  function configChangedAfter(files, startedAt) {
21335
21423
  for (const f of files) {
21336
21424
  try {
21337
- if (statSync13(f).mtimeMs > startedAt) return f;
21425
+ if (statSync14(f).mtimeMs > startedAt) return f;
21338
21426
  } catch {
21339
21427
  }
21340
21428
  }
@@ -21497,8 +21585,8 @@ async function runOnePassUnguarded(o) {
21497
21585
  }
21498
21586
  let settle = () => {
21499
21587
  };
21500
- const settled = new Promise((resolve7) => {
21501
- settle = resolve7;
21588
+ const settled = new Promise((resolve8) => {
21589
+ settle = resolve8;
21502
21590
  });
21503
21591
  const ready = (tookTicket2) => settle(tookTicket2);
21504
21592
  const run3 = spawn11(role, decision, ctx, { ready }).then(() => {
@@ -21758,7 +21846,7 @@ async function runRoleAgentLocked(role, decision, ctx, hooks, held) {
21758
21846
  ship.name,
21759
21847
  (t) => {
21760
21848
  const d = dirForRepo(route2, t.repo_id);
21761
- return d ? originBranchSha(d, t, resolve2) : null;
21849
+ return d ? originBranchSha(d, t, resolve3) : null;
21762
21850
  }
21763
21851
  );
21764
21852
  if (!result.ticket) {
@@ -21891,18 +21979,18 @@ import { createHash as createHash10 } from "crypto";
21891
21979
  import {
21892
21980
  chmodSync as chmodSync4,
21893
21981
  closeSync as closeSync5,
21894
- existsSync as existsSync43,
21982
+ existsSync as existsSync44,
21895
21983
  fstatSync,
21896
21984
  openSync as openSync5,
21897
21985
  readSync as readSync3,
21898
21986
  readdirSync as readdirSync18,
21899
21987
  rmSync as rmSync21,
21900
- statSync as statSync14,
21988
+ statSync as statSync15,
21901
21989
  watch
21902
21990
  } from "fs";
21903
21991
  import { createConnection } from "net";
21904
21992
  import { createServer } from "http";
21905
- import { basename as basename14, dirname as dirname26, join as join46 } from "path";
21993
+ import { basename as basename14, dirname as dirname27, join as join47 } from "path";
21906
21994
  import { execFile as execFile3, spawn as spawn10 } from "child_process";
21907
21995
 
21908
21996
  // src/periscope-control.ts
@@ -21953,7 +22041,7 @@ function lastJsonLine(stdout) {
21953
22041
  }
21954
22042
  return null;
21955
22043
  }
21956
- function parseLine(line) {
22044
+ function parseLine2(line) {
21957
22045
  try {
21958
22046
  const v = JSON.parse(line);
21959
22047
  return v && typeof v === "object" && !Array.isArray(v) ? v : null;
@@ -21981,7 +22069,7 @@ async function execute(h, args, streaming, after) {
21981
22069
  body: null,
21982
22070
  stream: async (write) => {
21983
22071
  const r = await h.run(args, (line) => {
21984
- const ev = parseLine(line);
22072
+ const ev = parseLine2(line);
21985
22073
  if (ev) write(ev);
21986
22074
  });
21987
22075
  const f = await finish(r);
@@ -22096,7 +22184,7 @@ async function handlePeriscopes(h, q2) {
22096
22184
  ], q2.wantsStream, h.mcp ? async (done) => ({ ...done, ...await h.mcp.onUp(picked, done) }) : void 0);
22097
22185
  }
22098
22186
  function runCrew(args, onLine, timeoutMs = 20 * 6e4) {
22099
- return new Promise((resolve7) => {
22187
+ return new Promise((resolve8) => {
22100
22188
  const child = spawn9(process.execPath, [process.argv[1] ?? "", ...args], { stdio: ["ignore", "pipe", "pipe"] });
22101
22189
  let stdout = "";
22102
22190
  let stderr = "";
@@ -22118,12 +22206,12 @@ function runCrew(args, onLine, timeoutMs = 20 * 6e4) {
22118
22206
  const t = setTimeout(() => child.kill("SIGTERM"), timeoutMs);
22119
22207
  child.on("error", (e) => {
22120
22208
  clearTimeout(t);
22121
- resolve7({ code: 127, stdout, stderr: `${stderr}${e.message}
22209
+ resolve8({ code: 127, stdout, stderr: `${stderr}${e.message}
22122
22210
  ` });
22123
22211
  });
22124
22212
  child.on("close", (code) => {
22125
22213
  clearTimeout(t);
22126
- resolve7({ code: code ?? 1, stdout, stderr });
22214
+ resolve8({ code: code ?? 1, stdout, stderr });
22127
22215
  });
22128
22216
  });
22129
22217
  }
@@ -22131,7 +22219,7 @@ function runCrew(args, onLine, timeoutMs = 20 * 6e4) {
22131
22219
  // src/control-api.ts
22132
22220
  var PAUSABLE = ["dev", "design", "qa"];
22133
22221
  var CONTROL_SOCKET_NAME = "control.sock";
22134
- var controlSocketFor = (stateDir) => join46(stateDir, CONTROL_SOCKET_NAME);
22222
+ var controlSocketFor = (stateDir) => join47(stateDir, CONTROL_SOCKET_NAME);
22135
22223
  var MAX_SOCKET_PATH = 100;
22136
22224
  var MAX_TAIL_BYTES = 1024 * 1024;
22137
22225
  var MAX_WAIT_SECONDS = 30;
@@ -22218,7 +22306,7 @@ function candidates(live, rotated) {
22218
22306
  const out = [live];
22219
22307
  if (!rotated) return out;
22220
22308
  try {
22221
- for (const n of readdirSync18(dirname26(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join46(dirname26(live), n));
22309
+ for (const n of readdirSync18(dirname27(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join47(dirname27(live), n));
22222
22310
  } catch {
22223
22311
  }
22224
22312
  return out;
@@ -22241,7 +22329,7 @@ function readRange(path, from, max) {
22241
22329
  }
22242
22330
  }
22243
22331
  function lastLines2(path, n) {
22244
- const st = statSync14(path);
22332
+ const st = statSync15(path);
22245
22333
  const cursorAt = (off) => encodeCursor({ dev: st.dev, ino: st.ino, off });
22246
22334
  if (n <= 0 || st.size === 0) return { lines: [], cursor: cursorAt(st.size), reset: false };
22247
22335
  let start = st.size;
@@ -22262,7 +22350,7 @@ function lastLines2(path, n) {
22262
22350
  }
22263
22351
  function readTail(live, opts) {
22264
22352
  const max = opts.maxBytes ?? MAX_TAIL_BYTES;
22265
- if (!existsSync43(live)) {
22353
+ if (!existsSync44(live)) {
22266
22354
  return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
22267
22355
  }
22268
22356
  const cur = opts.after ? decodeCursor(opts.after) : null;
@@ -22275,7 +22363,7 @@ function readTail(live, opts) {
22275
22363
  } else {
22276
22364
  const found = candidates(live, opts.rotated ?? false).find((p) => {
22277
22365
  try {
22278
- const s = statSync14(p);
22366
+ const s = statSync15(p);
22279
22367
  return s.ino === cur.ino && s.dev === cur.dev;
22280
22368
  } catch {
22281
22369
  return false;
@@ -22286,7 +22374,7 @@ function readTail(live, opts) {
22286
22374
  off = cur.off;
22287
22375
  } else reset = true;
22288
22376
  }
22289
- let size = statSync14(path).size;
22377
+ let size = statSync15(path).size;
22290
22378
  if (off > size) {
22291
22379
  off = 0;
22292
22380
  reset = true;
@@ -22294,12 +22382,12 @@ function readTail(live, opts) {
22294
22382
  if (off >= size && path !== live) {
22295
22383
  path = live;
22296
22384
  off = 0;
22297
- size = statSync14(path).size;
22385
+ size = statSync15(path).size;
22298
22386
  }
22299
22387
  const { text: text2 } = readRange(path, off, max);
22300
22388
  const cut = text2.lastIndexOf("\n");
22301
22389
  const whole = cut < 0 ? "" : text2.slice(0, cut + 1);
22302
- const st = statSync14(path);
22390
+ const st = statSync15(path);
22303
22391
  return {
22304
22392
  lines: whole.split("\n").filter((l) => l.length),
22305
22393
  cursor: encodeCursor({ dev: st.dev, ino: st.ino, off: off + Buffer.byteLength(whole) }),
@@ -22307,7 +22395,7 @@ function readTail(live, opts) {
22307
22395
  };
22308
22396
  }
22309
22397
  function waitForChange(dir, ms3, signal) {
22310
- return new Promise((resolve7) => {
22398
+ return new Promise((resolve8) => {
22311
22399
  let w;
22312
22400
  const done = () => {
22313
22401
  clearTimeout(t);
@@ -22317,7 +22405,7 @@ function waitForChange(dir, ms3, signal) {
22317
22405
  w?.close();
22318
22406
  } catch {
22319
22407
  }
22320
- resolve7();
22408
+ resolve8();
22321
22409
  };
22322
22410
  const t = setTimeout(done, ms3);
22323
22411
  const poll = setInterval(done, 500);
@@ -22378,7 +22466,7 @@ async function tailReply(path, q2, rotated, abort) {
22378
22466
  const deadline = Date.now() + Math.min(wait, MAX_WAIT_SECONDS) * 1e3;
22379
22467
  let r = readTail(path, { after, tail: tail3, rotated });
22380
22468
  while (after && !r.lines.length && !r.reset && Date.now() < deadline && !abort.aborted) {
22381
- await waitForChange(dirname26(path), deadline - Date.now(), abort);
22469
+ await waitForChange(dirname27(path), deadline - Date.now(), abort);
22382
22470
  r = readTail(path, { after: r.cursor, rotated });
22383
22471
  }
22384
22472
  return reply2(200, r);
@@ -22418,8 +22506,8 @@ async function route(deps, method, url, req, abort) {
22418
22506
  if (p.startsWith("/streams/") && get2) {
22419
22507
  const run3 = decodeURIComponent(p.slice("/streams/".length));
22420
22508
  if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
22421
- const file = join46(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
22422
- if (!existsSync43(file)) return err2(404, "no such run");
22509
+ const file = join47(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
22510
+ if (!existsSync44(file)) return err2(404, "no such run");
22423
22511
  return tailReply(file, url.searchParams, false, abort);
22424
22512
  }
22425
22513
  if ((p === "/pause" || p === "/resume") && post3) {
@@ -22463,14 +22551,14 @@ function send(res, r) {
22463
22551
  res.end(body);
22464
22552
  }
22465
22553
  async function socketIsStale(path) {
22466
- if (!existsSync43(path)) return false;
22467
- return new Promise((resolve7) => {
22554
+ if (!existsSync44(path)) return false;
22555
+ return new Promise((resolve8) => {
22468
22556
  const c = createConnection(path);
22469
22557
  c.once("connect", () => {
22470
22558
  c.destroy();
22471
- resolve7(false);
22559
+ resolve8(false);
22472
22560
  });
22473
- c.once("error", () => resolve7(true));
22561
+ c.once("error", () => resolve8(true));
22474
22562
  });
22475
22563
  }
22476
22564
  async function startControlServer(deps) {
@@ -22482,7 +22570,7 @@ async function startControlServer(deps) {
22482
22570
  return null;
22483
22571
  }
22484
22572
  if (await socketIsStale(path)) rmSync21(path, { force: true });
22485
- else if (existsSync43(path)) {
22573
+ else if (existsSync44(path)) {
22486
22574
  log(`control API not started: ${path} is in use by another process`);
22487
22575
  return null;
22488
22576
  }
@@ -22512,10 +22600,10 @@ async function startControlServer(deps) {
22512
22600
  }
22513
22601
  })();
22514
22602
  });
22515
- return new Promise((resolve7) => {
22603
+ return new Promise((resolve8) => {
22516
22604
  server.once("error", (e) => {
22517
22605
  log(`control API not started: ${e.message}`);
22518
- resolve7(null);
22606
+ resolve8(null);
22519
22607
  });
22520
22608
  server.listen(path, () => {
22521
22609
  try {
@@ -22523,7 +22611,7 @@ async function startControlServer(deps) {
22523
22611
  } catch {
22524
22612
  }
22525
22613
  server.on("error", (e) => log(`control API error: ${e.message}`));
22526
- resolve7({
22614
+ resolve8({
22527
22615
  server,
22528
22616
  path,
22529
22617
  close: () => new Promise((done) => {
@@ -22554,13 +22642,13 @@ function schedulerHandler(control, run3 = runDetached) {
22554
22642
  };
22555
22643
  }
22556
22644
  function crewJson(args, timeoutMs = 6e4) {
22557
- return new Promise((resolve7) => {
22645
+ return new Promise((resolve8) => {
22558
22646
  execFile3(process.execPath, [process.argv[1] ?? "", ...args], { timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024 }, (_e, stdout) => {
22559
22647
  try {
22560
22648
  const v = JSON.parse(String(stdout));
22561
- resolve7(v && typeof v === "object" && !Array.isArray(v) ? v : null);
22649
+ resolve8(v && typeof v === "object" && !Array.isArray(v) ? v : null);
22562
22650
  } catch {
22563
- resolve7(null);
22651
+ resolve8(null);
22564
22652
  }
22565
22653
  });
22566
22654
  });
@@ -22568,31 +22656,31 @@ function crewJson(args, timeoutMs = 6e4) {
22568
22656
 
22569
22657
  // src/periscope-mcp.ts
22570
22658
  import { lstat, mkdir, readdir, readFile, readlink, realpath, rm, stat, writeFile } from "fs/promises";
22571
- import { basename as basename15, dirname as dirname27, isAbsolute as isAbsolute3, join as join48, relative as relative2, resolve as resolve5, sep } from "path";
22659
+ import { basename as basename15, dirname as dirname28, isAbsolute as isAbsolute4, join as join49, relative as relative2, resolve as resolve6, sep } from "path";
22572
22660
  import { z } from "zod";
22573
22661
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22574
22662
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
22575
22663
 
22576
22664
  // src/periscope-endpoints.ts
22577
22665
  import { randomBytes as randomBytes3 } from "crypto";
22578
- import { chmodSync as chmodSync5, existsSync as existsSync44, mkdirSync as mkdirSync31, readdirSync as readdirSync19, readFileSync as readFileSync39, rmSync as rmSync22, writeFileSync as writeFileSync32 } from "fs";
22579
- import { join as join47 } from "path";
22580
- var secretDir = (stateDir) => join47(stateDir, "periscope-mcp");
22581
- var secretFile = (stateDir, id) => join47(secretDir(stateDir), `${id}.json`);
22582
- var endpointDir = (stateDir) => join47(stateDir, "periscope-endpoints");
22583
- var endpointFile = (stateDir, id) => join47(endpointDir(stateDir), `${id}.json`);
22666
+ import { chmodSync as chmodSync5, existsSync as existsSync45, mkdirSync as mkdirSync32, readdirSync as readdirSync19, readFileSync as readFileSync40, rmSync as rmSync22, writeFileSync as writeFileSync33 } from "fs";
22667
+ import { join as join48 } from "path";
22668
+ var secretDir = (stateDir) => join48(stateDir, "periscope-mcp");
22669
+ var secretFile = (stateDir, id) => join48(secretDir(stateDir), `${id}.json`);
22670
+ var endpointDir = (stateDir) => join48(stateDir, "periscope-endpoints");
22671
+ var endpointFile = (stateDir, id) => join48(endpointDir(stateDir), `${id}.json`);
22584
22672
  function mintPeriscopeSecret(stateDir, ticketRecordId) {
22585
- mkdirSync31(secretDir(stateDir), { recursive: true, mode: 448 });
22673
+ mkdirSync32(secretDir(stateDir), { recursive: true, mode: 448 });
22586
22674
  const secret = randomBytes3(32).toString("base64url");
22587
22675
  const file = secretFile(stateDir, ticketRecordId);
22588
- writeFileSync32(file, `${JSON.stringify({ secret })}
22676
+ writeFileSync33(file, `${JSON.stringify({ secret })}
22589
22677
  `, { mode: 384 });
22590
22678
  chmodSync5(file, 384);
22591
22679
  return secret;
22592
22680
  }
22593
22681
  function readPeriscopeSecret(stateDir, ticketRecordId) {
22594
22682
  try {
22595
- const v = JSON.parse(readFileSync39(secretFile(stateDir, ticketRecordId), "utf8"));
22683
+ const v = JSON.parse(readFileSync40(secretFile(stateDir, ticketRecordId), "utf8"));
22596
22684
  return typeof v.secret === "string" && v.secret ? v.secret : void 0;
22597
22685
  } catch {
22598
22686
  return void 0;
@@ -22603,14 +22691,14 @@ function removePeriscopeSecret(stateDir, ticketRecordId) {
22603
22691
  }
22604
22692
  function readPeriscopeEndpoint(stateDir, ticketRecordId) {
22605
22693
  try {
22606
- return JSON.parse(readFileSync39(endpointFile(stateDir, ticketRecordId), "utf8"));
22694
+ return JSON.parse(readFileSync40(endpointFile(stateDir, ticketRecordId), "utf8"));
22607
22695
  } catch {
22608
22696
  return void 0;
22609
22697
  }
22610
22698
  }
22611
22699
  function writePeriscopeEndpoint(stateDir, rec) {
22612
- mkdirSync31(endpointDir(stateDir), { recursive: true });
22613
- writeFileSync32(endpointFile(stateDir, rec.ticketRecordId), `${JSON.stringify(rec, null, 2)}
22700
+ mkdirSync32(endpointDir(stateDir), { recursive: true });
22701
+ writeFileSync33(endpointFile(stateDir, rec.ticketRecordId), `${JSON.stringify(rec, null, 2)}
22614
22702
  `);
22615
22703
  }
22616
22704
  function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
@@ -22618,7 +22706,7 @@ function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
22618
22706
  }
22619
22707
  function listPeriscopeEndpoints(stateDir) {
22620
22708
  const dir = endpointDir(stateDir);
22621
- if (!existsSync44(dir)) return [];
22709
+ if (!existsSync45(dir)) return [];
22622
22710
  const out = [];
22623
22711
  for (const name of readdirSync19(dir)) {
22624
22712
  if (!name.endsWith(".json")) continue;
@@ -22687,7 +22775,7 @@ var PathRefusal = class extends Error {
22687
22775
  };
22688
22776
  var isInside = (root, p) => {
22689
22777
  const rel = relative2(root, p);
22690
- return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute3(rel);
22778
+ return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute4(rel);
22691
22779
  };
22692
22780
  async function realAncestor(p) {
22693
22781
  let cur = p;
@@ -22706,10 +22794,10 @@ async function realAncestor(p) {
22706
22794
  }
22707
22795
  if (isLink) {
22708
22796
  if (++hops > 40) throw new PathRefusal(`too many symbolic links resolving ${p}`);
22709
- cur = resolve5(dirname27(cur), await readlink(cur));
22797
+ cur = resolve6(dirname28(cur), await readlink(cur));
22710
22798
  continue;
22711
22799
  }
22712
- const parent = dirname27(cur);
22800
+ const parent = dirname28(cur);
22713
22801
  if (parent === cur) throw new PathRefusal(`cannot resolve ${p}`);
22714
22802
  tail3.push(basename15(cur));
22715
22803
  cur = parent;
@@ -22719,15 +22807,15 @@ async function resolveInWorktree(root, requested) {
22719
22807
  if (!requested || requested.includes("\0")) throw new PathRefusal("path is required");
22720
22808
  const realRoot = await realpath(root);
22721
22809
  let abs;
22722
- if (isAbsolute3(requested)) {
22723
- abs = resolve5(requested);
22724
- if (isInside(root, abs)) abs = join48(realRoot, relative2(root, abs));
22810
+ if (isAbsolute4(requested)) {
22811
+ abs = resolve6(requested);
22812
+ if (isInside(root, abs)) abs = join49(realRoot, relative2(root, abs));
22725
22813
  } else {
22726
- abs = resolve5(realRoot, requested);
22814
+ abs = resolve6(realRoot, requested);
22727
22815
  }
22728
22816
  if (!isInside(realRoot, abs)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
22729
22817
  const { real, rest } = await realAncestor(abs);
22730
- const final = rest ? join48(real, rest) : real;
22818
+ const final = rest ? join49(real, rest) : real;
22731
22819
  if (!isInside(realRoot, final)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
22732
22820
  if (relative2(realRoot, final).split(sep).includes(".git")) throw new PathRefusal(".git is not reachable from a periscope");
22733
22821
  return final;
@@ -22762,7 +22850,7 @@ async function hookEnv(d, sc, name) {
22762
22850
  if (iso && iso.code === 0) Object.assign(env, parseEnvLines(iso.output));
22763
22851
  }
22764
22852
  }
22765
- if (name === "preview") env.CREW_PREVIEW_HOST = sc.host || "localhost";
22853
+ if (name === "preview") Object.assign(env, { CREW_PREVIEW_HOST: sc.host || "localhost" }, pointedAtEnv(sc.worktree));
22766
22854
  return env;
22767
22855
  }
22768
22856
  function createPeriscopeMcpServer(d, sc) {
@@ -22795,7 +22883,7 @@ function createPeriscopeMcpServer(d, sc) {
22795
22883
  const entries = (await readdir(abs, { withFileTypes: true })).filter((e) => e.name !== ".git");
22796
22884
  const lines2 = await Promise.all(entries.map(async (e) => {
22797
22885
  if (e.isDirectory()) return `[DIR] ${e.name}`;
22798
- const size = (await lstat(join48(abs, e.name))).size;
22886
+ const size = (await lstat(join49(abs, e.name))).size;
22799
22887
  return `[FILE] ${e.name} (${size} bytes)`;
22800
22888
  }));
22801
22889
  return text(lines2.join("\n") || "(empty)");
@@ -22820,7 +22908,7 @@ function createPeriscopeMcpServer(d, sc) {
22820
22908
  if (Buffer.byteLength(content) > MAX_WRITE_BYTES) return text(`content exceeds ${MAX_WRITE_BYTES} bytes`, true);
22821
22909
  const abs = await inTree(path);
22822
22910
  if (abs === await realpath(root)) return text("path must name a file", true);
22823
- await mkdir(dirname27(abs), { recursive: true });
22911
+ await mkdir(dirname28(abs), { recursive: true });
22824
22912
  await inTree(path);
22825
22913
  await writeFile(abs, content);
22826
22914
  return text(`Wrote ${Buffer.byteLength(content)} bytes to ${await rel(abs)}`);
@@ -22860,8 +22948,8 @@ function createPeriscopeMcpServer(d, sc) {
22860
22948
  inputSchema: { path: z.string() },
22861
22949
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
22862
22950
  }, async ({ path }) => {
22863
- const parent = await inTree(dirname27(path) === "" ? "." : dirname27(path));
22864
- const abs = join48(parent, basename15(path));
22951
+ const parent = await inTree(dirname28(path) === "" ? "." : dirname28(path));
22952
+ const abs = join49(parent, basename15(path));
22865
22953
  if (basename15(path) === ".git" || abs === parent) return text("path must name a file", true);
22866
22954
  const st = await lstat(abs).catch(() => void 0);
22867
22955
  if (!st) return text(`${path} does not exist`, true);
@@ -22982,7 +23070,7 @@ function findPeriscope(sidecars, segment) {
22982
23070
  }
22983
23071
 
22984
23072
  // src/cli.ts
22985
- var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname28(process.execPath) : resolve6(dirname28(fileURLToPath(import.meta.url)), "..");
23073
+ var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname29(process.execPath) : resolve7(dirname29(fileURLToPath(import.meta.url)), "..");
22986
23074
  var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
22987
23075
  var CYCLE_SECONDS2 = 120;
22988
23076
  var STALE_CYCLES = 3;
@@ -23350,7 +23438,7 @@ if (command === "provision") {
23350
23438
  process.exit(outcome.code);
23351
23439
  }
23352
23440
  if (command === "pool-member") {
23353
- const pool = poolConfigFrom(argv, process.env, () => readFileSync40(0, "utf8"));
23441
+ const pool = poolConfigFrom(argv, process.env, () => readFileSync41(0, "utf8"));
23354
23442
  if (typeof pool === "string") {
23355
23443
  process.stderr.write(`crew pool-member: ${pool}
23356
23444
  `);
@@ -23578,7 +23666,7 @@ if (command === "passenger-sync-daemon") {
23578
23666
  } else {
23579
23667
  const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
23580
23668
  const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
23581
- if (configless && !existsSync45(findConfigFile(CREW_HOME))) {
23669
+ if (configless && !existsSync46(findConfigFile(CREW_HOME))) {
23582
23670
  cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
23583
23671
  } else {
23584
23672
  cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
@@ -23593,7 +23681,7 @@ if (command === "passenger-sync-daemon") {
23593
23681
  throw e;
23594
23682
  }
23595
23683
  if (command === "credential") {
23596
- const r = runCredential(positional[1], readFileSync40(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
23684
+ const r = runCredential(positional[1], readFileSync41(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
23597
23685
  process.stdout.write(r.out);
23598
23686
  process.stderr.write(r.err);
23599
23687
  process.exit(r.code);
@@ -23605,9 +23693,9 @@ if (command === "passenger-sync-daemon") {
23605
23693
  forges: cfg.ship.forges,
23606
23694
  stateDir: cfg.ship.stateDir,
23607
23695
  configFile: cfg.configFile,
23608
- readFile: (p) => readFileSync40(p, "utf8"),
23609
- writeFile: (p, t) => writeFileSync33(p, t),
23610
- stdin: () => readFileSync40(0, "utf8"),
23696
+ readFile: (p) => readFileSync41(p, "utf8"),
23697
+ writeFile: (p, t) => writeFileSync34(p, t),
23698
+ stdin: () => readFileSync41(0, "utf8"),
23611
23699
  env: process.env
23612
23700
  }
23613
23701
  );
@@ -23900,8 +23988,8 @@ if (command === "passenger-sync-daemon") {
23900
23988
  }
23901
23989
  if (host === "linux" && hasSystemd) {
23902
23990
  const label = labelFor(CREW_HOME, "run");
23903
- const staleTimer = join49(homedir11(), ".config", "systemd", "user", `${label}.timer`);
23904
- if (existsSync45(staleTimer)) {
23991
+ const staleTimer = join50(homedir11(), ".config", "systemd", "user", `${label}.timer`);
23992
+ if (existsSync46(staleTimer)) {
23905
23993
  process.stdout.write(
23906
23994
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
23907
23995
  `
@@ -23928,7 +24016,7 @@ if (command === "passenger-sync-daemon") {
23928
24016
  const tracker = new Tracker(c, cfg.ship);
23929
24017
  problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
23930
24018
  for (const r of await resolvedRepos(c)) {
23931
- if (!existsSync45(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
24019
+ if (!existsSync46(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
23932
24020
  if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
23933
24021
  readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
23934
24022
  }
@@ -23955,7 +24043,7 @@ if (command === "passenger-sync-daemon") {
23955
24043
  }
23956
24044
  async function doctorFix(o) {
23957
24045
  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))));
23958
- const unitExists = (p) => existsSync45(p);
24046
+ const unitExists = (p) => existsSync46(p);
23959
24047
  const crontab = o.host === "linux" ? spawnSync10("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
23960
24048
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
23961
24049
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
@@ -23972,7 +24060,7 @@ if (command === "passenger-sync-daemon") {
23972
24060
  for (const a of plan.actions) {
23973
24061
  if (a.kind === "enable") {
23974
24062
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
23975
- writeFileSync33(cfg.configFile, planEnable(readFileSync40(cfg.configFile, "utf8"), a.route));
24063
+ writeFileSync34(cfg.configFile, planEnable(readFileSync41(cfg.configFile, "utf8"), a.route));
23976
24064
  enabledSound.add(a.route);
23977
24065
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
23978
24066
  } else {
@@ -24713,7 +24801,7 @@ ${cu.tail.trimEnd()}
24713
24801
  cfg.ship.name,
24714
24802
  (t) => {
24715
24803
  const d = dirForRepo(w.route, t.repo_id);
24716
- return d ? originBranchSha(d, t, resolve2) : null;
24804
+ return d ? originBranchSha(d, t, resolve3) : null;
24717
24805
  }
24718
24806
  );
24719
24807
  if (!result.ticket) {
@@ -24925,7 +25013,7 @@ ${cu.tail.trimEnd()}
24925
25013
  cfg.ship.name,
24926
25014
  (t) => {
24927
25015
  const d = dirForRepo(route2, t.repo_id);
24928
- return d ? originBranchSha(d, t, resolve2) : null;
25016
+ return d ? originBranchSha(d, t, resolve3) : null;
24929
25017
  }
24930
25018
  );
24931
25019
  if (!result.ticket) {
@@ -25111,7 +25199,7 @@ ${cu.tail.trimEnd()}
25111
25199
  process.exit(1);
25112
25200
  }
25113
25201
  requireArmed("run the daemon loop");
25114
- const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join49(CREW_HOME, "src"), join49(CREW_HOME, "dist"), join49(CREW_HOME, "bin")];
25202
+ const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join50(CREW_HOME, "src"), join50(CREW_HOME, "dist"), join50(CREW_HOME, "bin")];
25115
25203
  const configPaths = daemonConfigPaths(cfg);
25116
25204
  watchPaths.push(...configPaths);
25117
25205
  noteDaemonStarted(cfg.ship.stateDir);
@@ -25416,10 +25504,10 @@ ${cu.tail.trimEnd()}
25416
25504
  `);
25417
25505
  if (!dryRun) {
25418
25506
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
25419
- const raw = existsSync45(resolvedPath) ? JSON.parse(readFileSync40(resolvedPath, "utf8")) : {};
25507
+ const raw = existsSync46(resolvedPath) ? JSON.parse(readFileSync41(resolvedPath, "utf8")) : {};
25420
25508
  raw.agentSkills = result.agentSkills;
25421
- mkdirSync32(dirOf2(resolvedPath), { recursive: true });
25422
- writeFileSync33(resolvedPath, `${JSON.stringify(raw, null, 2)}
25509
+ mkdirSync33(dirOf2(resolvedPath), { recursive: true });
25510
+ writeFileSync34(resolvedPath, `${JSON.stringify(raw, null, 2)}
25423
25511
  `);
25424
25512
  }
25425
25513
  if (result.outcomes.some((o) => o.action === "diverged")) process.exitCode = 1;
@@ -25442,7 +25530,7 @@ ${cu.tail.trimEnd()}
25442
25530
  throw e;
25443
25531
  }
25444
25532
  const oldName = cfg.ship.name;
25445
- writeFileSync33(cfg.configFile, setShipNameInYaml(readFileSync40(cfg.configFile, "utf8"), newName));
25533
+ writeFileSync34(cfg.configFile, setShipNameInYaml(readFileSync41(cfg.configFile, "utf8"), newName));
25446
25534
  cfg.ship.name = newName;
25447
25535
  let renameMachineId;
25448
25536
  try {
@@ -25489,14 +25577,14 @@ ${cu.tail.trimEnd()}
25489
25577
  process.exit(2);
25490
25578
  };
25491
25579
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
25492
- const configText = readFileSync40(cfg.configFile, "utf8");
25580
+ const configText = readFileSync41(cfg.configFile, "utf8");
25493
25581
  if (sub === "check") {
25494
25582
  try {
25495
25583
  const routeArg = value("route") ?? positional[2];
25496
25584
  const found = cfg.routes.find((r) => r.route === routeArg);
25497
25585
  if (!found) throw new ReposError(`no route "${routeArg}" in the config (have: ${cfg.routes.map((r) => r.route).join(", ") || "none"})`);
25498
25586
  const named2 = value("route") ? void 0 : positional[3];
25499
- const dir = named2 ? repoDirOf(configText, dirname28(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
25587
+ const dir = named2 ? repoDirOf(configText, dirname29(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
25500
25588
  const target = reposOf(found).find((t) => resolvePath(t.dir) === dir || t.name === named2);
25501
25589
  const o = target ? found.repoOverrides[target.name] : void 0;
25502
25590
  runReposCheck(dir, {
@@ -25519,9 +25607,9 @@ ${cu.tail.trimEnd()}
25519
25607
  if (sub === "hooks") {
25520
25608
  try {
25521
25609
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
25522
- const dir = repoDirOf(configText, dirname28(cfg.configFile), a.route, a.name);
25523
- if (!existsSync45(dir)) throw new ReposError(`${dir} does not exist on disk`);
25524
- const files = { exists: (r) => existsSync45(resolvePath(dir, r)), list: (r) => {
25610
+ const dir = repoDirOf(configText, dirname29(cfg.configFile), a.route, a.name);
25611
+ if (!existsSync46(dir)) throw new ReposError(`${dir} does not exist on disk`);
25612
+ const files = { exists: (r) => existsSync46(resolvePath(dir, r)), list: (r) => {
25525
25613
  try {
25526
25614
  return readdirSync20(resolvePath(dir, r));
25527
25615
  } catch {
@@ -25529,7 +25617,7 @@ ${cu.tail.trimEnd()}
25529
25617
  }
25530
25618
  }, read: (r) => {
25531
25619
  try {
25532
- return readFileSync40(resolvePath(dir, r), "utf8");
25620
+ return readFileSync41(resolvePath(dir, r), "utf8");
25533
25621
  } catch {
25534
25622
  return void 0;
25535
25623
  }
@@ -25571,19 +25659,19 @@ ${cu.tail.trimEnd()}
25571
25659
  let cleared;
25572
25660
  if (clear2) {
25573
25661
  const plan = clearRouteHooks(configText, a.route, a.name);
25574
- if (plan.removed.length > 0) writeFileSync33(cfg.configFile, plan.text);
25662
+ if (plan.removed.length > 0) writeFileSync34(cfg.configFile, plan.text);
25575
25663
  cleared = { file: cfg.configFile, removed: plan.removed };
25576
25664
  }
25577
25665
  if (write && suggestion.source === null && recipeIds.length === 0 && hookSpecs.length === 0) throw new ReposError(`nothing to write: ${suggestion.notes[0]}`);
25578
25666
  if (write === "route") {
25579
25667
  const plan = planRouteHooks(configText, a.route, a.name, picked.hooks, { replace: flag("replace") });
25580
- writeFileSync33(cfg.configFile, plan.text);
25668
+ writeFileSync34(cfg.configFile, plan.text);
25581
25669
  written = { target: "route", file: cfg.configFile, hooks: plan.written, kept: plan.kept, replaced: plan.replaced };
25582
25670
  } else if (write === "repo") {
25583
25671
  const have = existingRepoConfig(files);
25584
25672
  if (have) throw new ReposError(`${resolvePath(dir, have)} already exists; not overwriting it`);
25585
25673
  const file = resolvePath(dir, ".crew.yaml");
25586
- writeFileSync33(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
25674
+ writeFileSync34(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
25587
25675
  written = { target: "repo", file, hooks: Object.keys(picked.hooks), kept: [], replaced: [] };
25588
25676
  }
25589
25677
  if (flag("json")) {
@@ -25639,7 +25727,7 @@ ${cu.tail.trimEnd()}
25639
25727
  if (sub === "list" && !routeName) usage2();
25640
25728
  if (sub === "add") {
25641
25729
  try {
25642
- const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync45(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
25730
+ const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync46(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
25643
25731
  routeName = a.route;
25644
25732
  addPath = a.path;
25645
25733
  addName = a.name;
@@ -25669,7 +25757,7 @@ ${cu.tail.trimEnd()}
25669
25757
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
25670
25758
  if (sub === "list" || sub === "add") {
25671
25759
  const liveRoute = cfg.routes.find((r) => r.route === routeName);
25672
- if (liveRoute?.resolved?.reposModelId && existsSync45(resolvedFile) && !dryRun) {
25760
+ if (liveRoute?.resolved?.reposModelId && existsSync46(resolvedFile) && !dryRun) {
25673
25761
  try {
25674
25762
  await hydrateApiKeys([liveRoute]);
25675
25763
  persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
@@ -25678,16 +25766,16 @@ ${cu.tail.trimEnd()}
25678
25766
  }
25679
25767
  }
25680
25768
  let resolvedRemotes;
25681
- if (existsSync45(resolvedFile)) {
25769
+ if (existsSync46(resolvedFile)) {
25682
25770
  try {
25683
- resolvedRemotes = JSON.parse(readFileSync40(resolvedFile, "utf8"));
25771
+ resolvedRemotes = JSON.parse(readFileSync41(resolvedFile, "utf8"));
25684
25772
  } catch {
25685
25773
  }
25686
25774
  }
25687
25775
  const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
25688
25776
  const text2 = configText;
25689
- const base = dirname28(cfg.configFile);
25690
- const isCheckout = (d) => existsSync45(`${d}/.git`);
25777
+ const base = dirname29(cfg.configFile);
25778
+ const isCheckout = (d) => existsSync46(`${d}/.git`);
25691
25779
  try {
25692
25780
  if (sub === "list") {
25693
25781
  if (flag("json")) {
@@ -25697,11 +25785,11 @@ ${cu.tail.trimEnd()}
25697
25785
  base,
25698
25786
  resolved: resolvedRemotes,
25699
25787
  originOf,
25700
- exists: (d) => existsSync45(d),
25788
+ exists: (d) => existsSync46(d),
25701
25789
  isCheckout,
25702
25790
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
25703
25791
  try {
25704
- return Object.keys(parseRepoConfig(readFileSync40(resolvePath(d, f), "utf8"), f).hooks).length > 0;
25792
+ return Object.keys(parseRepoConfig(readFileSync41(resolvePath(d, f), "utf8"), f).hooks).length > 0;
25705
25793
  } catch {
25706
25794
  return false;
25707
25795
  }
@@ -25718,7 +25806,7 @@ ${cu.tail.trimEnd()}
25718
25806
  for (const r of repos) {
25719
25807
  const origin2 = originOf(r.dir);
25720
25808
  const row = matchRepoName(origin2, resolvedRemotes);
25721
- const note = !existsSync45(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
25809
+ const note = !existsSync46(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
25722
25810
  process.stdout.write(`${r.name} ${r.dir} ${note}
25723
25811
  `);
25724
25812
  }
@@ -25730,9 +25818,9 @@ ${cu.tail.trimEnd()}
25730
25818
  ` : `${human}
25731
25819
  `);
25732
25820
  const plan2 = planRepoClone({ text: text2, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
25733
- if (existsSync45(plan2.target) && (existsSync45(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
25821
+ if (existsSync46(plan2.target) && (existsSync46(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
25734
25822
  throw new ReposError(
25735
- existsSync45(`${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`
25823
+ existsSync46(`${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`
25736
25824
  );
25737
25825
  }
25738
25826
  if (dryRun) {
@@ -25750,14 +25838,14 @@ ${cu.tail.trimEnd()}
25750
25838
  throw new ReposError(`${e.message}${authHint}
25751
25839
  crew.yaml was not changed`);
25752
25840
  }
25753
- writeFileSync33(cfg.configFile, plan2.text);
25841
+ writeFileSync34(cfg.configFile, plan2.text);
25754
25842
  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:
25755
25843
  `);
25756
25844
  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}`);
25757
25845
  break;
25758
25846
  }
25759
25847
  const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
25760
- const top = existsSync45(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
25848
+ const top = existsSync46(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
25761
25849
  if (!top) throw new ReposError(`${target} is not a git checkout`);
25762
25850
  const origin = originOf(top);
25763
25851
  const plan = planRepoAdd({
@@ -25809,15 +25897,15 @@ ${cu.tail.trimEnd()}
25809
25897
  const created = await rowTracker.createRepoRow(rowPayload);
25810
25898
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
25811
25899
  `);
25812
- if (existsSync45(resolvedFile)) {
25813
- const raw = JSON.parse(readFileSync40(resolvedFile, "utf8"));
25900
+ if (existsSync46(resolvedFile)) {
25901
+ const raw = JSON.parse(readFileSync41(resolvedFile, "utf8"));
25814
25902
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
25815
25903
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
25816
- writeFileSync33(resolvedFile, `${JSON.stringify(raw, null, 2)}
25904
+ writeFileSync34(resolvedFile, `${JSON.stringify(raw, null, 2)}
25817
25905
  `);
25818
25906
  }
25819
25907
  }
25820
- writeFileSync33(cfg.configFile, plan.text);
25908
+ writeFileSync34(cfg.configFile, plan.text);
25821
25909
  process.stdout.write(`added ${top} to ${routeName} as ${plan.mode === "dir" ? "dir" : `repos.${plan.name}`} in ${cfg.configFile}
25822
25910
  `);
25823
25911
  } catch (e) {
@@ -26186,7 +26274,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
26186
26274
  process.exit(2);
26187
26275
  }
26188
26276
  if (found.shipNameAdopted && !dryRun) {
26189
- writeFileSync33(cfg.configFile, setShipNameInYaml(readFileSync40(cfg.configFile, "utf8"), found.shipNameAdopted));
26277
+ writeFileSync34(cfg.configFile, setShipNameInYaml(readFileSync41(cfg.configFile, "utf8"), found.shipNameAdopted));
26190
26278
  cfg.ship.name = found.shipNameAdopted;
26191
26279
  }
26192
26280
  if (found.shipRow) {
@@ -26229,10 +26317,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
26229
26317
  }
26230
26318
  }
26231
26319
  let previous;
26232
- const hadPreviousFile = existsSync45(resolvedPath);
26320
+ const hadPreviousFile = existsSync46(resolvedPath);
26233
26321
  if (hadPreviousFile) {
26234
26322
  try {
26235
- previous = JSON.parse(readFileSync40(resolvedPath, "utf8"));
26323
+ previous = JSON.parse(readFileSync41(resolvedPath, "utf8"));
26236
26324
  } catch {
26237
26325
  }
26238
26326
  }
@@ -26362,10 +26450,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
26362
26450
  process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
26363
26451
  `);
26364
26452
  } else {
26365
- mkdirSync32(dirname28(resolvedPath), { recursive: true });
26453
+ mkdirSync33(dirname29(resolvedPath), { recursive: true });
26366
26454
  if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
26367
26455
  const tmpPath = `${resolvedPath}.tmp${process.pid}`;
26368
- writeFileSync33(tmpPath, `${JSON.stringify(resolved, null, 2)}
26456
+ writeFileSync34(tmpPath, `${JSON.stringify(resolved, null, 2)}
26369
26457
  `);
26370
26458
  renameSync6(tmpPath, resolvedPath);
26371
26459
  process.stderr.write(`Resolved ids written to ${resolvedPath}
@@ -26411,8 +26499,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
26411
26499
  process.stderr.write(`(dry run) would write the minted API key to ${apiKeyFile}
26412
26500
  `);
26413
26501
  } else {
26414
- mkdirSync32(dirname28(apiKeyFile), { recursive: true });
26415
- writeFileSync33(apiKeyFile, `${apiKeyVar}=${apiKey}
26502
+ mkdirSync33(dirname29(apiKeyFile), { recursive: true });
26503
+ writeFileSync34(apiKeyFile, `${apiKeyVar}=${apiKey}
26416
26504
  `, { mode: 384 });
26417
26505
  process.stderr.write(`API key written to ${apiKeyFile}
26418
26506
  `);
@@ -27139,7 +27227,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27139
27227
  };
27140
27228
  if (subP === "list") {
27141
27229
  const entries = periscopeListEntries(
27142
- (await Promise.all(routesP.map(gather))).flat(),
27230
+ (await Promise.all(routesP.map(gather))).flat().map((r) => ({ ...r, pointedAt: readPointedAt(r.worktree) })),
27143
27231
  /* @__PURE__ */ new Date(),
27144
27232
  (r) => pidsInWorktree(r.worktree, Number(r.number)).length > 0
27145
27233
  );
@@ -27287,7 +27375,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27287
27375
  const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
27288
27376
  const resumed = await runPeriscopeResume({
27289
27377
  find: async () => hit && { row: hit.row, tag: hit.tag },
27290
- worktreeExists: (p) => existsSync45(p),
27378
+ worktreeExists: (p) => existsSync46(p),
27379
+ prepareDropFile,
27291
27380
  caps: periscopeCaps(cfg.ship),
27292
27381
  runHook: async (name, cwd, env) => {
27293
27382
  const script = hit?.repo.config.hooks[name];
@@ -27381,10 +27470,10 @@ ${where.join("\n")}`);
27381
27470
  },
27382
27471
  prepare: async (path) => {
27383
27472
  for (const f of repoP.config.worktrees.copy) {
27384
- const src = join49(repoP.dir, f);
27385
- if (!existsSync45(src)) continue;
27386
- mkdirSync32(dirOf2(join49(path, f)), { recursive: true });
27387
- copyFileSync2(src, join49(path, f));
27473
+ const src = join50(repoP.dir, f);
27474
+ if (!existsSync46(src)) continue;
27475
+ mkdirSync33(dirOf2(join50(path, f)), { recursive: true });
27476
+ copyFileSync2(src, join50(path, f));
27388
27477
  }
27389
27478
  const setup = repoP.config.hooks.setup;
27390
27479
  return setup ? runScript(setup, { cwd: path, shell: shellP, onLine: (l) => {
@@ -27396,6 +27485,7 @@ ${where.join("\n")}`);
27396
27485
  return script ? runScript(script, { cwd, env, shell: shellP, timeoutMs: name === "preview" ? 18e4 : void 0 }) : null;
27397
27486
  },
27398
27487
  createRow: (fields) => trackerP.createPeriscope(fields),
27488
+ prepareDropFile,
27399
27489
  recordLocal: (sc) => {
27400
27490
  writePeriscopeSidecar(cfg.ship.stateDir, { ...sc, route: routeP.route });
27401
27491
  if (flag("serve-mcp")) mintPeriscopeSecret(cfg.ship.stateDir, sc.ticketRecordId);
@@ -27679,7 +27769,7 @@ ${where.join("\n")}`);
27679
27769
  };
27680
27770
  const droppedRoutes = [];
27681
27771
  try {
27682
- const rawCfg = parseYaml2(readFileSync40(cfg.configFile, "utf8"));
27772
+ const rawCfg = parseYaml2(readFileSync41(cfg.configFile, "utf8"));
27683
27773
  for (const r of rawCfg?.routes ?? []) {
27684
27774
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
27685
27775
  try {
@@ -27732,8 +27822,8 @@ ${where.join("\n")}`);
27732
27822
  process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
27733
27823
  break;
27734
27824
  }
27735
- mkdirSync32(cfg.ship.stateDir, { recursive: true });
27736
- writeFileSync33(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
27825
+ mkdirSync33(cfg.ship.stateDir, { recursive: true });
27826
+ writeFileSync34(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
27737
27827
  `);
27738
27828
  process.stdout.write(
27739
27829
  "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"
@@ -27798,7 +27888,7 @@ ${where.join("\n")}`);
27798
27888
  }
27799
27889
  case "log": {
27800
27890
  try {
27801
- const text2 = readFileSync40(cfg.ship.logFile, "utf8").split("\n");
27891
+ const text2 = readFileSync41(cfg.ship.logFile, "utf8").split("\n");
27802
27892
  process.stdout.write(`${text2.slice(-(Number(value("tail")) || 40)).join("\n")}
27803
27893
  `);
27804
27894
  } catch {
@@ -27821,8 +27911,8 @@ ${where.join("\n")}`);
27821
27911
  };
27822
27912
  if (!route2.resolved) fail(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
27823
27913
  const tracker = new Tracker(route2, cfg.ship);
27824
- const configText = readFileSync40(cfg.configFile, "utf8");
27825
- const cfgBase = dirname28(cfg.configFile);
27914
+ const configText = readFileSync41(cfg.configFile, "utf8");
27915
+ const cfgBase = dirname29(cfg.configFile);
27826
27916
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
27827
27917
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
27828
27918
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, route2.route);
@@ -27850,7 +27940,7 @@ ${where.join("\n")}`);
27850
27940
  }
27851
27941
  if (configured2.some((r) => r.dir === dir)) {
27852
27942
  try {
27853
- writeFileSync33(cfg.configFile, planRepoRemove(configText, cfgBase, route2.route, dir).text);
27943
+ writeFileSync34(cfg.configFile, planRepoRemove(configText, cfgBase, route2.route, dir).text);
27854
27944
  } catch (e) {
27855
27945
  if (e instanceof ReposError) fail(e.message);
27856
27946
  throw e;
@@ -27858,29 +27948,29 @@ ${where.join("\n")}`);
27858
27948
  }
27859
27949
  if (row) {
27860
27950
  await tracker.deleteRepoRow(row.id);
27861
- if (existsSync45(resolvedFile)) {
27862
- const raw = JSON.parse(readFileSync40(resolvedFile, "utf8"));
27951
+ if (existsSync46(resolvedFile)) {
27952
+ const raw = JSON.parse(readFileSync41(resolvedFile, "utf8"));
27863
27953
  if (raw.repoNames) delete raw.repoNames[row.id];
27864
27954
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
27865
- writeFileSync33(resolvedFile, `${JSON.stringify(raw, null, 2)}
27955
+ writeFileSync34(resolvedFile, `${JSON.stringify(raw, null, 2)}
27866
27956
  `);
27867
27957
  }
27868
27958
  }
27869
- if (existsSync45(join49(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync23(dir, { recursive: true, force: true });
27959
+ if (existsSync46(join50(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync23(dir, { recursive: true, force: true });
27870
27960
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
27871
27961
  break;
27872
27962
  }
27873
27963
  const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
27874
- const fresh = !existsSync45(join49(dir, ".git"));
27875
- if (existsSync45(dir) && fresh && readdirSync20(dir).length > 0) fail(`${dir} exists and is not a git checkout \u2014 move it aside`);
27964
+ const fresh = !existsSync46(join50(dir, ".git"));
27965
+ if (existsSync46(dir) && fresh && readdirSync20(dir).length > 0) fail(`${dir} exists and is not a git checkout \u2014 move it aside`);
27876
27966
  if (dryRun) {
27877
27967
  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`);
27878
27968
  break;
27879
27969
  }
27880
27970
  if (fresh) {
27881
- mkdirSync32(dir, { recursive: true });
27882
- writeFileSync33(join49(dir, "README.md"), SAMPLE_README);
27883
- writeFileSync33(join49(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
27971
+ mkdirSync33(dir, { recursive: true });
27972
+ writeFileSync34(join50(dir, "README.md"), SAMPLE_README);
27973
+ writeFileSync34(join50(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
27884
27974
  const g = (...a) => {
27885
27975
  const r = spawnSync10("git", a, { cwd: dir, encoding: "utf8" });
27886
27976
  if (r.status !== 0) fail(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
@@ -27898,17 +27988,17 @@ ${where.join("\n")}`);
27898
27988
  spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
27899
27989
  });
27900
27990
  repoId = (await tracker.createRepoRow(payload)).id;
27901
- if (existsSync45(resolvedFile)) {
27902
- const raw = JSON.parse(readFileSync40(resolvedFile, "utf8"));
27991
+ if (existsSync46(resolvedFile)) {
27992
+ const raw = JSON.parse(readFileSync41(resolvedFile, "utf8"));
27903
27993
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
27904
27994
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
27905
- writeFileSync33(resolvedFile, `${JSON.stringify(raw, null, 2)}
27995
+ writeFileSync34(resolvedFile, `${JSON.stringify(raw, null, 2)}
27906
27996
  `);
27907
27997
  }
27908
27998
  }
27909
27999
  if (!configured2.some((r) => r.dir === dir)) {
27910
28000
  try {
27911
- writeFileSync33(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route2.route, path: dir, name: SAMPLE_REPO }).text);
28001
+ writeFileSync34(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route2.route, path: dir, name: SAMPLE_REPO }).text);
27912
28002
  } catch (e) {
27913
28003
  if (e instanceof ReposError) fail(e.message);
27914
28004
  throw e;
@@ -28254,7 +28344,7 @@ watermark: ${state.watermark()}
28254
28344
  }
28255
28345
  for (const r of repos) {
28256
28346
  const remote = r.config.branch.remote;
28257
- if (existsSync45(r.dir)) {
28347
+ if (existsSync46(r.dir)) {
28258
28348
  pushAuth.push({
28259
28349
  route: c.route,
28260
28350
  repo: r.name,
@@ -28343,7 +28433,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
28343
28433
  }
28344
28434
  try {
28345
28435
  const evFile = eventFileFor(cfg.ship.stateDir);
28346
- const evSize = existsSync45(evFile) ? statSync15(evFile).size : 0;
28436
+ const evSize = existsSync46(evFile) ? statSync16(evFile).size : 0;
28347
28437
  process.stdout.write(
28348
28438
  evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
28349
28439
  ` : "events log: ok\n"
@@ -28354,7 +28444,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
28354
28444
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run", installContext());
28355
28445
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
28356
28446
  try {
28357
- return readFileSync40(p, "utf8");
28447
+ return readFileSync41(p, "utf8");
28358
28448
  } catch {
28359
28449
  return null;
28360
28450
  }
@@ -28377,7 +28467,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
28377
28467
  const changed2 = configChangedAfter(daemonConfigPaths(cfg), daemonStartedAt);
28378
28468
  if (changed2) {
28379
28469
  process.stdout.write(
28380
- `daemon config: WARN ${changed2} changed at ${new Date(statSync15(changed2).mtimeMs).toISOString()} but the daemon started at ${new Date(daemonStartedAt).toISOString()} \u2014 run \`crew reload\`
28470
+ `daemon config: WARN ${changed2} changed at ${new Date(statSync16(changed2).mtimeMs).toISOString()} but the daemon started at ${new Date(daemonStartedAt).toISOString()} \u2014 run \`crew reload\`
28381
28471
  `
28382
28472
  );
28383
28473
  }