@tablation/crew 0.73.4 → 0.74.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 +467 -427
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -128,176 +128,10 @@ function formatStaleOwnLock(scope, holder) {
128
128
  `;
129
129
  }
130
130
 
131
- // src/selftest.ts
132
- import { spawn as spawn3 } from "child_process";
133
- import { existsSync as existsSync19, mkdirSync as mkdirSync10, mkdtempSync, readFileSync as readFileSync16, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "fs";
134
- import { tmpdir as tmpdir2 } from "os";
135
- import { join as join19 } from "path";
136
-
137
- // src/agent.ts
138
- import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
139
- import { readFileSync as readFileSync15, existsSync as existsSync18, statSync as statSync5, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
140
- import { StringDecoder } from "string_decoder";
141
- import { finished } from "stream/promises";
142
- import { join as join18, dirname as dirname7, basename as basename7 } from "path";
143
- import { createHash as createHash3 } from "crypto";
144
-
145
- // src/config.ts
146
- import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
147
- import { resolve, dirname as dirname3, isAbsolute, join as join8, basename as basename3 } from "path";
148
- import { homedir as homedir5, tmpdir } from "os";
149
- import { parse as parse2 } from "yaml";
150
-
151
- // src/events.ts
152
- import {
153
- appendFileSync,
154
- mkdirSync,
155
- existsSync as existsSync2,
156
- statSync,
157
- renameSync,
158
- openSync,
159
- closeSync,
160
- rmSync
161
- } from "fs";
162
- import { join, dirname } from "path";
163
- function render(e) {
164
- const scope = e.role ? `${e.step}[${e.role}]` : e.step;
165
- const ticket = e.ticket ? ` ${e.ticket}` : "";
166
- const level = e.level === "info" ? "" : `${e.level.toUpperCase()}: `;
167
- return `${e.at} ${scope}:${ticket} ${level}${e.message}`;
168
- }
169
- var DEFAULT_EVENTS_ROTATE_BYTES = 64 * 1024 * 1024;
170
- var ROTATED_EVENTS_RE = /^events\.\d{8}T\d{6}Z(?:-\d+)?\.jsonl$/;
171
- var ROTATE_LOCK_STALE_MS = 6e4;
172
- function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
173
- try {
174
- if (!existsSync2(file) || statSync(file).size <= maxBytes) return void 0;
175
- } catch {
176
- return void 0;
177
- }
178
- const lock = `${file}.rotate.lock`;
179
- try {
180
- try {
181
- closeSync(openSync(lock, "wx"));
182
- } catch {
183
- try {
184
- if (Date.now() - statSync(lock).mtimeMs < ROTATE_LOCK_STALE_MS) return void 0;
185
- rmSync(lock, { force: true });
186
- closeSync(openSync(lock, "wx"));
187
- } catch {
188
- return void 0;
189
- }
190
- }
191
- try {
192
- if (statSync(file).size <= maxBytes) return void 0;
193
- const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
194
- let dest = join(dirname(file), `events.${stamp}.jsonl`);
195
- for (let n = 1; existsSync2(dest); n++) dest = join(dirname(file), `events.${stamp}-${n}.jsonl`);
196
- renameSync(file, dest);
197
- return dest;
198
- } finally {
199
- rmSync(lock, { force: true });
200
- }
201
- } catch {
202
- return void 0;
203
- }
204
- }
205
- var Emitter = class _Emitter {
206
- cycle;
207
- opts;
208
- now;
209
- step = "poll";
210
- role;
211
- constructor(opts) {
212
- this.opts = opts;
213
- this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
214
- this.cycle = opts.cycleId ?? this.now().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
215
- for (const f of [opts.eventFile, opts.logFile]) {
216
- if (!f) continue;
217
- try {
218
- mkdirSync(dirname(f), { recursive: true });
219
- } catch {
220
- }
221
- }
222
- }
223
- /** Everything emitted after this belongs to `step`, until the next one. */
224
- enter(step, role) {
225
- this.step = step;
226
- this.role = role;
227
- }
228
- emit(message, extra = {}) {
229
- const e = {
230
- at: this.now().toISOString(),
231
- cycle: this.cycle,
232
- route: this.opts.route,
233
- step: extra.step ?? this.step,
234
- level: extra.level ?? "info",
235
- message,
236
- ...extra.role ?? this.role ? { role: extra.role ?? this.role } : {},
237
- ...extra.ticket ? { ticket: extra.ticket } : {},
238
- ...extra.data ? { data: extra.data } : {}
239
- };
240
- const line = render(e);
241
- try {
242
- if (this.opts.eventFile && this.opts.rotateBytes) rotateEventsFile(this.opts.eventFile, this.opts.rotateBytes, this.now());
243
- } catch {
244
- }
245
- try {
246
- if (this.opts.eventFile) appendFileSync(this.opts.eventFile, `${JSON.stringify(e)}
247
- `);
248
- } catch {
249
- }
250
- try {
251
- if (this.opts.logFile) appendFileSync(this.opts.logFile, `${line}
252
- `);
253
- } catch {
254
- }
255
- try {
256
- (this.opts.console ?? ((l) => process.stderr.write(`${l}
257
- `)))(line);
258
- } catch {
259
- }
260
- return e;
261
- }
262
- warn(message, extra = {}) {
263
- return this.emit(message, { ...extra, level: "warn" });
264
- }
265
- error(message, extra = {}) {
266
- return this.emit(message, { ...extra, level: "error" });
267
- }
268
- /**
269
- * A view of this emitter labelling every event with a different route,
270
- * sharing the same sinks and cycle id. For a fleet-wide run, the top-level
271
- * Emitter is stamped with just one route (see cli.ts) — a phase that
272
- * actually knows which route it's working (release, per-repo poll) uses
273
- * this instead, so its events aren't all attributed to `routes[0]`.
274
- */
275
- forRoute(route) {
276
- return new _Emitter({ ...this.opts, route, cycleId: this.cycle });
277
- }
278
- /** Times a step and emits its outcome, so a view can show duration. */
279
- async timed(step, message, fn, role) {
280
- this.enter(step, role);
281
- const started = this.now().getTime();
282
- try {
283
- const result = await fn();
284
- this.emit(message, { data: { ms: this.now().getTime() - started, ok: true } });
285
- return result;
286
- } catch (err) {
287
- this.error(`${message} \u2014 ${err.message}`, {
288
- data: { ms: this.now().getTime() - started, ok: false }
289
- });
290
- throw err;
291
- }
292
- }
293
- };
294
- var eventFileFor = (stateDir) => join(stateDir, "events.jsonl");
295
-
296
- // src/pair-install.ts
297
- import { existsSync as existsSync5, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
298
- import { spawnSync as spawnSync3 } from "child_process";
299
- import { homedir as homedir4 } from "os";
300
- import { join as join6 } from "path";
131
+ // src/adapters/claude.ts
132
+ import { readFileSync as readFileSync4 } from "fs";
133
+ import { homedir as homedir3 } from "os";
134
+ import { join as join4 } from "path";
301
135
 
302
136
  // src/stream.ts
303
137
  function toolTarget(name, input) {
@@ -349,19 +183,19 @@ function extractResult(line) {
349
183
 
350
184
  // src/claude-mcp.ts
351
185
  import { spawnSync } from "child_process";
352
- import { readFileSync as readFileSync3 } from "fs";
186
+ import { readFileSync as readFileSync2 } from "fs";
353
187
  import { homedir } from "os";
354
- import { join as join3 } from "path";
188
+ import { join as join2 } from "path";
355
189
 
356
190
  // src/mcp-registrations.ts
357
191
  import { createHash } from "crypto";
358
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
359
- import { join as join2 } from "path";
360
- var fileFor = (stateDir) => join2(stateDir, "mcp-registrations.json");
192
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
193
+ import { join } from "path";
194
+ var fileFor = (stateDir) => join(stateDir, "mcp-registrations.json");
361
195
  var keyFingerprint = (key) => createHash("sha256").update(key).digest("hex").slice(0, 8);
362
196
  function readMcpRegistrations(stateDir) {
363
197
  try {
364
- const j = JSON.parse(readFileSync2(fileFor(stateDir), "utf8"));
198
+ const j = JSON.parse(readFileSync(fileFor(stateDir), "utf8"));
365
199
  return Array.isArray(j) ? j : [];
366
200
  } catch {
367
201
  return [];
@@ -369,7 +203,7 @@ function readMcpRegistrations(stateDir) {
369
203
  }
370
204
  function recordMcpRegistration(stateDir, r, now = /* @__PURE__ */ new Date()) {
371
205
  const rest = readMcpRegistrations(stateDir).filter((x) => !(x.harness === r.harness && x.scope === r.scope && x.name === r.name));
372
- mkdirSync2(stateDir, { recursive: true });
206
+ mkdirSync(stateDir, { recursive: true });
373
207
  writeFileSync(fileFor(stateDir), `${JSON.stringify([...rest, { ...r, at: now.toISOString() }], null, 2)}
374
208
  `, { mode: 384 });
375
209
  }
@@ -393,7 +227,7 @@ function runClaude(bin, args) {
393
227
  }
394
228
  function userScopeEntry(home) {
395
229
  try {
396
- const j = JSON.parse(readFileSync3(join3(home, ".claude.json"), "utf8"));
230
+ const j = JSON.parse(readFileSync2(join2(home, ".claude.json"), "utf8"));
397
231
  return j.mcpServers?.[MCP_SERVER_NAME];
398
232
  } catch {
399
233
  return void 0;
@@ -477,8 +311,8 @@ function removeClaudeMcp(o) {
477
311
  }
478
312
 
479
313
  // src/launcher.ts
480
- import { basename, join as join4 } from "path";
481
- import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync } from "fs";
314
+ import { basename, join as join3 } from "path";
315
+ import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync } from "fs";
482
316
  import { homedir as homedir2 } from "os";
483
317
  function isOriLauncher(spec) {
484
318
  return !!spec.launcher && basename(spec.launcher) === "ori";
@@ -487,7 +321,7 @@ function isOmlxLauncher(spec) {
487
321
  return !!spec.launcher && basename(spec.launcher) === "omlx";
488
322
  }
489
323
  function defaultOmlxBin(home = homedir2()) {
490
- return join4(home, ".omlx", "bin", "omlx");
324
+ return join3(home, ".omlx", "bin", "omlx");
491
325
  }
492
326
  function launcherExecutable(spec, home) {
493
327
  if (!spec.launcher) return void 0;
@@ -540,8 +374,8 @@ function omlxModelPresent(model, dirs) {
540
374
  });
541
375
  }
542
376
  function readOmlxSettings(home = homedir2()) {
543
- const p = join4(home, ".omlx", "settings.json");
544
- return existsSync3(p) ? parseOmlxSettings(readFileSync4(p, "utf8")) : void 0;
377
+ const p = join3(home, ".omlx", "settings.json");
378
+ return existsSync2(p) ? parseOmlxSettings(readFileSync3(p, "utf8")) : void 0;
545
379
  }
546
380
 
547
381
  // src/capability.ts
@@ -592,6 +426,24 @@ var claudePair = {
592
426
  },
593
427
  removePair: (o) => removeClaudeMcp({ bin: o.bin ?? "claude", dryRun: o.dryRun })
594
428
  };
429
+ function subscriptionSettings() {
430
+ return JSON.stringify({ env: Object.fromEntries(BILLING_VARS_TO_UNSET.map((v) => [v, ""])) });
431
+ }
432
+ function describeClaudeAuth(spec, env = process.env, home = homedir3()) {
433
+ if (spec.auth === "subscription") return "subscription (settings override)";
434
+ let fromSettings;
435
+ try {
436
+ const parsed = JSON.parse(readFileSync4(join4(home, ".claude", "settings.json"), "utf8"));
437
+ const e = parsed.env ?? {};
438
+ const on = (k) => e[k] !== void 0 && e[k] !== "" && e[k] !== "0";
439
+ if (on("CLAUDE_CODE_USE_VERTEX")) fromSettings = "vertex from ~/.claude/settings.json";
440
+ else if (on("ANTHROPIC_API_KEY") || on("ANTHROPIC_AUTH_TOKEN")) fromSettings = "API key from ~/.claude/settings.json";
441
+ } catch {
442
+ }
443
+ if (fromSettings) return `inherit: ${fromSettings}`;
444
+ const shell = BILLING_VARS_TO_UNSET.find((v) => v.startsWith("ANTHROPIC_API") && env[v]);
445
+ return shell ? `inherit: ${shell} in env (unset at exec)` : "inherit: subscription login";
446
+ }
595
447
  var claudeAdapter = {
596
448
  harness: "claude",
597
449
  displayName: "Claude Code",
@@ -646,6 +498,7 @@ var claudeAdapter = {
646
498
  }
647
499
  }
648
500
  })] : [],
501
+ ...spec.auth === "subscription" ? ["--settings", subscriptionSettings()] : [],
649
502
  ...spec.args
650
503
  ];
651
504
  return wrapInvocation(spec, { model: o.model, args });
@@ -672,14 +525,185 @@ var claudeAdapter = {
672
525
  ensureMcpResult: ensureClaudeMcpResult
673
526
  };
674
527
 
528
+ // src/selftest.ts
529
+ import { spawn as spawn3 } from "child_process";
530
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10, mkdtempSync, readFileSync as readFileSync17, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "fs";
531
+ import { tmpdir as tmpdir2 } from "os";
532
+ import { join as join20 } from "path";
533
+
534
+ // src/agent.ts
535
+ import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
536
+ import { readFileSync as readFileSync16, existsSync as existsSync18, statSync as statSync5, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
537
+ import { StringDecoder } from "string_decoder";
538
+ import { finished } from "stream/promises";
539
+ import { join as join19, dirname as dirname7, basename as basename7 } from "path";
540
+ import { createHash as createHash3 } from "crypto";
541
+
542
+ // src/config.ts
543
+ import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
544
+ import { resolve, dirname as dirname3, isAbsolute, join as join9, basename as basename3 } from "path";
545
+ import { homedir as homedir6, tmpdir } from "os";
546
+ import { parse as parse2 } from "yaml";
547
+
548
+ // src/events.ts
549
+ import {
550
+ appendFileSync,
551
+ mkdirSync as mkdirSync2,
552
+ existsSync as existsSync3,
553
+ statSync,
554
+ renameSync,
555
+ openSync,
556
+ closeSync,
557
+ rmSync
558
+ } from "fs";
559
+ import { join as join5, dirname } from "path";
560
+ function render(e) {
561
+ const scope = e.role ? `${e.step}[${e.role}]` : e.step;
562
+ const ticket = e.ticket ? ` ${e.ticket}` : "";
563
+ const level = e.level === "info" ? "" : `${e.level.toUpperCase()}: `;
564
+ return `${e.at} ${scope}:${ticket} ${level}${e.message}`;
565
+ }
566
+ var DEFAULT_EVENTS_ROTATE_BYTES = 64 * 1024 * 1024;
567
+ var ROTATED_EVENTS_RE = /^events\.\d{8}T\d{6}Z(?:-\d+)?\.jsonl$/;
568
+ var ROTATE_LOCK_STALE_MS = 6e4;
569
+ function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
570
+ try {
571
+ if (!existsSync3(file) || statSync(file).size <= maxBytes) return void 0;
572
+ } catch {
573
+ return void 0;
574
+ }
575
+ const lock = `${file}.rotate.lock`;
576
+ try {
577
+ try {
578
+ closeSync(openSync(lock, "wx"));
579
+ } catch {
580
+ try {
581
+ if (Date.now() - statSync(lock).mtimeMs < ROTATE_LOCK_STALE_MS) return void 0;
582
+ rmSync(lock, { force: true });
583
+ closeSync(openSync(lock, "wx"));
584
+ } catch {
585
+ return void 0;
586
+ }
587
+ }
588
+ try {
589
+ if (statSync(file).size <= maxBytes) return void 0;
590
+ const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
591
+ let dest = join5(dirname(file), `events.${stamp}.jsonl`);
592
+ for (let n = 1; existsSync3(dest); n++) dest = join5(dirname(file), `events.${stamp}-${n}.jsonl`);
593
+ renameSync(file, dest);
594
+ return dest;
595
+ } finally {
596
+ rmSync(lock, { force: true });
597
+ }
598
+ } catch {
599
+ return void 0;
600
+ }
601
+ }
602
+ var Emitter = class _Emitter {
603
+ cycle;
604
+ opts;
605
+ now;
606
+ step = "poll";
607
+ role;
608
+ constructor(opts) {
609
+ this.opts = opts;
610
+ this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
611
+ this.cycle = opts.cycleId ?? this.now().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
612
+ for (const f of [opts.eventFile, opts.logFile]) {
613
+ if (!f) continue;
614
+ try {
615
+ mkdirSync2(dirname(f), { recursive: true });
616
+ } catch {
617
+ }
618
+ }
619
+ }
620
+ /** Everything emitted after this belongs to `step`, until the next one. */
621
+ enter(step, role) {
622
+ this.step = step;
623
+ this.role = role;
624
+ }
625
+ emit(message, extra = {}) {
626
+ const e = {
627
+ at: this.now().toISOString(),
628
+ cycle: this.cycle,
629
+ route: this.opts.route,
630
+ step: extra.step ?? this.step,
631
+ level: extra.level ?? "info",
632
+ message,
633
+ ...extra.role ?? this.role ? { role: extra.role ?? this.role } : {},
634
+ ...extra.ticket ? { ticket: extra.ticket } : {},
635
+ ...extra.data ? { data: extra.data } : {}
636
+ };
637
+ const line = render(e);
638
+ try {
639
+ if (this.opts.eventFile && this.opts.rotateBytes) rotateEventsFile(this.opts.eventFile, this.opts.rotateBytes, this.now());
640
+ } catch {
641
+ }
642
+ try {
643
+ if (this.opts.eventFile) appendFileSync(this.opts.eventFile, `${JSON.stringify(e)}
644
+ `);
645
+ } catch {
646
+ }
647
+ try {
648
+ if (this.opts.logFile) appendFileSync(this.opts.logFile, `${line}
649
+ `);
650
+ } catch {
651
+ }
652
+ try {
653
+ (this.opts.console ?? ((l) => process.stderr.write(`${l}
654
+ `)))(line);
655
+ } catch {
656
+ }
657
+ return e;
658
+ }
659
+ warn(message, extra = {}) {
660
+ return this.emit(message, { ...extra, level: "warn" });
661
+ }
662
+ error(message, extra = {}) {
663
+ return this.emit(message, { ...extra, level: "error" });
664
+ }
665
+ /**
666
+ * A view of this emitter labelling every event with a different route,
667
+ * sharing the same sinks and cycle id. For a fleet-wide run, the top-level
668
+ * Emitter is stamped with just one route (see cli.ts) — a phase that
669
+ * actually knows which route it's working (release, per-repo poll) uses
670
+ * this instead, so its events aren't all attributed to `routes[0]`.
671
+ */
672
+ forRoute(route) {
673
+ return new _Emitter({ ...this.opts, route, cycleId: this.cycle });
674
+ }
675
+ /** Times a step and emits its outcome, so a view can show duration. */
676
+ async timed(step, message, fn, role) {
677
+ this.enter(step, role);
678
+ const started = this.now().getTime();
679
+ try {
680
+ const result = await fn();
681
+ this.emit(message, { data: { ms: this.now().getTime() - started, ok: true } });
682
+ return result;
683
+ } catch (err) {
684
+ this.error(`${message} \u2014 ${err.message}`, {
685
+ data: { ms: this.now().getTime() - started, ok: false }
686
+ });
687
+ throw err;
688
+ }
689
+ }
690
+ };
691
+ var eventFileFor = (stateDir) => join5(stateDir, "events.jsonl");
692
+
693
+ // src/pair-install.ts
694
+ import { existsSync as existsSync5, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
695
+ import { spawnSync as spawnSync3 } from "child_process";
696
+ import { homedir as homedir5 } from "os";
697
+ import { join as join7 } from "path";
698
+
675
699
  // src/adapters/codex.ts
676
700
  import { spawnSync as spawnSync2 } from "child_process";
677
- import { homedir as homedir3 } from "os";
678
- import { join as join5 } from "path";
701
+ import { homedir as homedir4 } from "os";
702
+ import { join as join6 } from "path";
679
703
  import { rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
680
704
 
681
705
  // src/pair-files.ts
682
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2, rmSync as rmSync2 } from "fs";
706
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2, rmSync as rmSync2 } from "fs";
683
707
  import { dirname as dirname2 } from "path";
684
708
  var PAIR_BLOCK_START = "<!-- crew:pair start -->";
685
709
  var PAIR_BLOCK_END = "<!-- crew:pair end -->";
@@ -720,7 +744,7 @@ function removePairBlock(text) {
720
744
  return text.replace(new RegExp(`\\n*${BLOCK_RE.source}`), "\n").replace(/^\n+/, "").replace(/\n{3,}/g, "\n\n");
721
745
  }
722
746
  function readIfExists(path) {
723
- return existsSync4(path) ? readFileSync5(path, "utf8") : void 0;
747
+ return existsSync4(path) ? readFileSync6(path, "utf8") : void 0;
724
748
  }
725
749
  function writeFileMkdir(path, text) {
726
750
  mkdirSync3(dirname2(path), { recursive: true });
@@ -867,8 +891,8 @@ function codexTable(url) {
867
891
  return [`[mcp_servers.${SERVER}]`, `url = ${tomlString(url)}`, `bearer_token_env_var = ${tomlString(PAIR_KEY_VAR)}`];
868
892
  }
869
893
  var codexPaths = (home) => ({
870
- instructions: join5(home, ".codex", "AGENTS.md"),
871
- config: join5(home, ".codex", "config.toml")
894
+ instructions: join6(home, ".codex", "AGENTS.md"),
895
+ config: join6(home, ".codex", "config.toml")
872
896
  });
873
897
  function codexMcpState(config, url) {
874
898
  const lines = config.split("\n");
@@ -884,7 +908,7 @@ var codexPair = {
884
908
  harness: "codex",
885
909
  displayName: "Codex",
886
910
  installPair(o) {
887
- const p = codexPaths(o.home ?? homedir3());
911
+ const p = codexPaths(o.home ?? homedir4());
888
912
  const config = readIfExists(p.config) ?? "";
889
913
  const st = codexMcpState(config, o.url);
890
914
  if (st.state === "different" && !o.replace) {
@@ -901,7 +925,7 @@ var codexPair = {
901
925
  return `pair(codex): ${o.dryRun ? "(dry run) would install" : "installed"} the Pair block in ${p.instructions} and ${SERVER} in ${p.config} (key read from $${PAIR_KEY_VAR})`;
902
926
  },
903
927
  verifyPair(o) {
904
- const p = codexPaths(o.home ?? homedir3());
928
+ const p = codexPaths(o.home ?? homedir4());
905
929
  const b = blockVerdict(p.instructions, "codex");
906
930
  if (!b.ok) return b;
907
931
  const st = codexMcpState(readIfExists(p.config) ?? "", o.url);
@@ -910,7 +934,7 @@ var codexPair = {
910
934
  return { ok: true, detail: `${p.instructions}, ${p.config}` };
911
935
  },
912
936
  removePair(o) {
913
- const p = codexPaths(o.home ?? homedir3());
937
+ const p = codexPaths(o.home ?? homedir4());
914
938
  const hadBlock = removeBlockFile(p.instructions, o.dryRun);
915
939
  const config = readIfExists(p.config);
916
940
  const st = codexMcpState(config ?? "", o.url);
@@ -996,11 +1020,11 @@ var codexAdapter = {
996
1020
  extractTurnTokens: extractCodexTurnTokens,
997
1021
  inspectMcp(o) {
998
1022
  if (binMissing(o.bin)) return { state: "no-binary" };
999
- const st = codexMcpState(readIfExists(codexPaths(o.home ?? homedir3()).config) ?? "", o.url);
1023
+ const st = codexMcpState(readIfExists(codexPaths(o.home ?? homedir4()).config) ?? "", o.url);
1000
1024
  return st.state === "registered" ? { state: "registered", url: o.url } : st;
1001
1025
  },
1002
1026
  ensureMcp(o) {
1003
- const p = codexPaths(o.home ?? homedir3());
1027
+ const p = codexPaths(o.home ?? homedir4());
1004
1028
  const config = readIfExists(p.config) ?? "";
1005
1029
  const st = codexMcpState(config, o.url);
1006
1030
  if (st.state === "registered") return `mcp: ${SERVER} already registered with Codex \u2192 ${o.url}`;
@@ -1015,8 +1039,8 @@ var codexAdapter = {
1015
1039
 
1016
1040
  // src/pair-install.ts
1017
1041
  var geminiPaths = (home) => ({
1018
- instructions: join6(home, ".gemini", "GEMINI.md"),
1019
- config: join6(home, ".gemini", "settings.json")
1042
+ instructions: join7(home, ".gemini", "GEMINI.md"),
1043
+ config: join7(home, ".gemini", "settings.json")
1020
1044
  });
1021
1045
  function readGeminiSettings(path) {
1022
1046
  const text = readIfExists(path);
@@ -1037,14 +1061,14 @@ function geminiMcpState(s, url) {
1037
1061
  return { state: "registered" };
1038
1062
  }
1039
1063
  function inspectGeminiMcp(o) {
1040
- const p = geminiPaths(o.home ?? homedir4());
1064
+ const p = geminiPaths(o.home ?? homedir5());
1041
1065
  const settings = readGeminiSettings(p.config);
1042
1066
  if (!settings) return { state: "different", detail: `${p.config} is not a JSON object` };
1043
1067
  const st = geminiMcpState(settings, o.url);
1044
1068
  return st.state === "registered" ? { state: "registered", url: o.url } : st;
1045
1069
  }
1046
1070
  function ensureGeminiMcp(o) {
1047
- const p = geminiPaths(o.home ?? homedir4());
1071
+ const p = geminiPaths(o.home ?? homedir5());
1048
1072
  const settings = readGeminiSettings(p.config);
1049
1073
  if (!settings) return `gemini: ${p.config} is not a JSON object \u2014 fix it by hand; nothing was written`;
1050
1074
  const st = geminiMcpState(settings, o.url);
@@ -1062,7 +1086,7 @@ var geminiPair = {
1062
1086
  harness: "gemini",
1063
1087
  displayName: "Gemini CLI",
1064
1088
  installPair(o) {
1065
- const p = geminiPaths(o.home ?? homedir4());
1089
+ const p = geminiPaths(o.home ?? homedir5());
1066
1090
  const settings = readGeminiSettings(p.config);
1067
1091
  if (!settings) return `pair(gemini): ${p.config} is not a JSON object \u2014 fix it by hand; nothing was written`;
1068
1092
  const st = geminiMcpState(settings, o.url);
@@ -1081,7 +1105,7 @@ var geminiPair = {
1081
1105
  return `pair(gemini): ${o.dryRun ? "(dry run) would install" : "installed"} the Pair block in ${p.instructions} and ${SERVER} in ${p.config} (key read from $${PAIR_KEY_VAR})`;
1082
1106
  },
1083
1107
  verifyPair(o) {
1084
- const p = geminiPaths(o.home ?? homedir4());
1108
+ const p = geminiPaths(o.home ?? homedir5());
1085
1109
  const b = blockVerdict(p.instructions, "gemini");
1086
1110
  if (!b.ok) return b;
1087
1111
  const settings = readGeminiSettings(p.config);
@@ -1092,7 +1116,7 @@ var geminiPair = {
1092
1116
  return { ok: true, detail: `${p.instructions}, ${p.config}` };
1093
1117
  },
1094
1118
  removePair(o) {
1095
- const p = geminiPaths(o.home ?? homedir4());
1119
+ const p = geminiPaths(o.home ?? homedir5());
1096
1120
  const hadBlock = removeBlockFile(p.instructions, o.dryRun);
1097
1121
  const settings = readGeminiSettings(p.config);
1098
1122
  const st = settings ? geminiMcpState(settings, o.url) : { state: "missing" };
@@ -1113,10 +1137,10 @@ var geminiPair = {
1113
1137
  };
1114
1138
  var VSCODE_FLAVOURS = ["Code", "Code - Insiders"];
1115
1139
  function vscodeUserDirs(o) {
1116
- const home = o.home ?? homedir4();
1140
+ const home = o.home ?? homedir5();
1117
1141
  const platform2 = o.platform ?? process.platform;
1118
- const root = platform2 === "darwin" ? join6(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join6(home, "AppData", "Roaming") : join6(home, ".config");
1119
- const all = VSCODE_FLAVOURS.map((f) => join6(root, f, "User"));
1142
+ const root = platform2 === "darwin" ? join7(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join7(home, "AppData", "Roaming") : join7(home, ".config");
1143
+ const all = VSCODE_FLAVOURS.map((f) => join7(root, f, "User"));
1120
1144
  const present = all.filter((d) => existsSync5(d));
1121
1145
  return present.length > 0 ? present : [all[0]];
1122
1146
  }
@@ -1164,8 +1188,8 @@ var copilotPair = {
1164
1188
  installPair(o) {
1165
1189
  const lines = [];
1166
1190
  for (const dir of vscodeUserDirs(o)) {
1167
- const mcpPath = join6(dir, "mcp.json");
1168
- const instrPath = join6(dir, "prompts", "crew-pair.instructions.md");
1191
+ const mcpPath = join7(dir, "mcp.json");
1192
+ const instrPath = join7(dir, "prompts", "crew-pair.instructions.md");
1169
1193
  const settings = readVscodeMcp(mcpPath);
1170
1194
  if (!settings) {
1171
1195
  lines.push(`pair(copilot): ${mcpPath} is not a plain JSON object (comments are not supported) \u2014 fix it by hand; nothing was written there`);
@@ -1193,8 +1217,8 @@ var copilotPair = {
1193
1217
  },
1194
1218
  verifyPair(o) {
1195
1219
  const verdicts = vscodeUserDirs(o).map((dir) => {
1196
- const instrPath = join6(dir, "prompts", "crew-pair.instructions.md");
1197
- const mcpPath = join6(dir, "mcp.json");
1220
+ const instrPath = join7(dir, "prompts", "crew-pair.instructions.md");
1221
+ const mcpPath = join7(dir, "mcp.json");
1198
1222
  const b = blockVerdict(instrPath, "copilot");
1199
1223
  if (!b.ok) return b;
1200
1224
  const settings = readVscodeMcp(mcpPath);
@@ -1209,8 +1233,8 @@ var copilotPair = {
1209
1233
  removePair(o) {
1210
1234
  const lines = [];
1211
1235
  for (const dir of vscodeUserDirs(o)) {
1212
- const mcpPath = join6(dir, "mcp.json");
1213
- const hadBlock = removeCopilotInstructions(join6(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1236
+ const mcpPath = join7(dir, "mcp.json");
1237
+ const hadBlock = removeCopilotInstructions(join7(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
1214
1238
  const settings = readVscodeMcp(mcpPath);
1215
1239
  const st = settings ? copilotMcpState(settings, o.url) : { state: "missing" };
1216
1240
  const hadMcp = st.state === "registered";
@@ -1509,8 +1533,8 @@ function explain(ship, required) {
1509
1533
  }
1510
1534
 
1511
1535
  // src/repo-config.ts
1512
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
1513
- import { join as join7, basename as basename2 } from "path";
1536
+ import { readFileSync as readFileSync7, existsSync as existsSync6 } from "fs";
1537
+ import { join as join8, basename as basename2 } from "path";
1514
1538
  import { parse } from "yaml";
1515
1539
  var SPEC_VERSION = 1;
1516
1540
  var REPO_CONFIG_FILENAMES = [".crew.yaml", ".crew.yml", ".crew.json"];
@@ -1593,7 +1617,7 @@ var MODES = ["local", "integrate", "ci_manual", "ci_auto", "external"];
1593
1617
  var PROVIDERS = ["github", "buildkite", "other", "none"];
1594
1618
  function findRepoConfig(dir) {
1595
1619
  for (const name of REPO_CONFIG_FILENAMES) {
1596
- const p = join7(dir, name);
1620
+ const p = join8(dir, name);
1597
1621
  if (existsSync6(p)) return p;
1598
1622
  }
1599
1623
  return null;
@@ -1832,7 +1856,7 @@ function parseRepoConfig(text, file) {
1832
1856
  function loadRepoConfig(dir) {
1833
1857
  const file = findRepoConfig(dir);
1834
1858
  if (!file) return null;
1835
- return parseRepoConfig(readFileSync6(file, "utf8"), file);
1859
+ return parseRepoConfig(readFileSync7(file, "utf8"), file);
1836
1860
  }
1837
1861
  function hookLabel(cfg, hook2) {
1838
1862
  return cfg.labels[hook2] ?? cfg.hooks[hook2] ?? hook2;
@@ -2281,16 +2305,16 @@ function mergeRouteRelease(route, override) {
2281
2305
  };
2282
2306
  }
2283
2307
  function expand(p, base) {
2284
- let out = p.startsWith("~") ? p.replace(/^~/, homedir5()) : p;
2285
- out = out.replace(/\$\{?HOME\}?/g, homedir5());
2308
+ let out = p.startsWith("~") ? p.replace(/^~/, homedir6()) : p;
2309
+ out = out.replace(/\$\{?HOME\}?/g, homedir6());
2286
2310
  return isAbsolute(out) ? out : resolve(base, out);
2287
2311
  }
2288
2312
  function resolvePromptsDir(promptSet, crewHome, base) {
2289
- if (!promptSet) return join8(crewHome, "prompts", "default");
2313
+ if (!promptSet) return join9(crewHome, "prompts", "default");
2290
2314
  if (promptSet.includes("/") || promptSet.startsWith(".") || promptSet.startsWith("~")) {
2291
2315
  return expand(promptSet, base);
2292
2316
  }
2293
- return join8(crewHome, "prompts", promptSet);
2317
+ return join9(crewHome, "prompts", promptSet);
2294
2318
  }
2295
2319
  var Missing = class {
2296
2320
  paths = [];
@@ -2314,7 +2338,7 @@ var Missing = class {
2314
2338
  };
2315
2339
  function crewVersion(crewHome) {
2316
2340
  try {
2317
- const pkg = JSON.parse(readFileSync7(join8(crewHome, "package.json"), "utf8"));
2341
+ const pkg = JSON.parse(readFileSync8(join9(crewHome, "package.json"), "utf8"));
2318
2342
  return pkg.version ?? "0.0.0";
2319
2343
  } catch {
2320
2344
  return "0.0.0";
@@ -2324,8 +2348,8 @@ function configSearchPath(crewHome) {
2324
2348
  if (process.env.CREW_CONFIG) return [resolve(process.env.CREW_CONFIG)];
2325
2349
  const xdg = process.env.XDG_CONFIG_HOME;
2326
2350
  return [
2327
- ...xdg ? [join8(xdg, "crew", "crew.yaml")] : [],
2328
- join8(homedir5(), ".config", "crew", "crew.yaml"),
2351
+ ...xdg ? [join9(xdg, "crew", "crew.yaml")] : [],
2352
+ join9(homedir6(), ".config", "crew", "crew.yaml"),
2329
2353
  resolve(crewHome, "crew.yaml")
2330
2354
  ];
2331
2355
  }
@@ -2333,17 +2357,17 @@ function findConfigFile(crewHome) {
2333
2357
  const candidates = configSearchPath(crewHome);
2334
2358
  return candidates.find((c) => existsSync7(c)) ?? candidates[candidates.length - 1];
2335
2359
  }
2336
- var defaultConfigPath = () => join8(process.env.XDG_CONFIG_HOME ?? join8(homedir5(), ".config"), "crew", "crew.yaml");
2360
+ var defaultConfigPath = () => join9(process.env.XDG_CONFIG_HOME ?? join9(homedir6(), ".config"), "crew", "crew.yaml");
2337
2361
  function routeSlug(route) {
2338
2362
  return route.replace(/\//g, "-");
2339
2363
  }
2340
2364
  function resolvedPathFor(stateDir, route) {
2341
2365
  const [workspace, project] = splitRoute(route);
2342
- return join8(stateDir, "resolved", workspace, `${project}.json`);
2366
+ return join9(stateDir, "resolved", workspace, `${project}.json`);
2343
2367
  }
2344
2368
  function apiKeyPathFor(stateDir, route) {
2345
2369
  const [workspace, project] = splitRoute(route);
2346
- return join8(stateDir, "keys", workspace, `${project}.env`);
2370
+ return join9(stateDir, "keys", workspace, `${project}.env`);
2347
2371
  }
2348
2372
  function splitRoute(route) {
2349
2373
  const parts = route.split("/");
@@ -2356,7 +2380,7 @@ function readResolvedFile(stateDir, route) {
2356
2380
  const path = resolvedPathFor(stateDir, route);
2357
2381
  if (!existsSync7(path)) return void 0;
2358
2382
  try {
2359
- return JSON.parse(readFileSync7(path, "utf8"));
2383
+ return JSON.parse(readFileSync8(path, "utf8"));
2360
2384
  } catch (e) {
2361
2385
  throw new ConfigError(`${path}: not valid JSON \u2014 ${e.message}`);
2362
2386
  }
@@ -2421,12 +2445,12 @@ function loadConfig(crewHome, configFile, opts = {}) {
2421
2445
  ` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
2422
2446
 
2423
2447
  Create the first of those:
2424
- mkdir -p ${dirname3(defaultConfigPath())} && cp ${join8(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
2448
+ mkdir -p ${dirname3(defaultConfigPath())} && cp ${join9(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
2425
2449
  );
2426
2450
  }
2427
2451
  let raw;
2428
2452
  try {
2429
- raw = parse2(opts.text ?? readFileSync7(file, "utf8"));
2453
+ raw = parse2(opts.text ?? readFileSync8(file, "utf8"));
2430
2454
  } catch (e) {
2431
2455
  throw new ConfigError(`${file}: not valid YAML \u2014 ${e.message}`);
2432
2456
  }
@@ -2504,7 +2528,7 @@ Create the first of those:
2504
2528
  useNvm: shipRaw.useNvm !== false,
2505
2529
  nvmSh: shipRaw.nvmSh,
2506
2530
  stateDir,
2507
- logFile: expand(shipRaw.logFile ?? join8(tmpdir(), "crew.log"), base),
2531
+ logFile: expand(shipRaw.logFile ?? join9(tmpdir(), "crew.log"), base),
2508
2532
  userAgent: shipRaw.userAgent ?? `Mozilla/5.0 CrewAgent/${crewVersion(crewHome)}`,
2509
2533
  maxConcurrentAgents,
2510
2534
  streamRetentionDays,
@@ -2526,7 +2550,7 @@ Create the first of those:
2526
2550
  function expandCommand(cmd, base) {
2527
2551
  return cmd.includes("/") || cmd.startsWith("~") ? expand(cmd, base) : cmd;
2528
2552
  }
2529
- var TOOLCHAIN_KEYS = /* @__PURE__ */ new Set(["harness", "bin", "launcher", "launcherBin", "model", "tiers", "args", "env", "maxThinkingTokens"]);
2553
+ var TOOLCHAIN_KEYS = /* @__PURE__ */ new Set(["harness", "bin", "launcher", "launcherBin", "model", "tiers", "args", "env", "maxThinkingTokens", "auth"]);
2530
2554
  function parsePair(raw, missing) {
2531
2555
  if (raw === void 0) return void 0;
2532
2556
  if (!Array.isArray(raw)) {
@@ -2583,9 +2607,20 @@ function parseToolchains(raw, base, missing, file) {
2583
2607
  if (!Number.isInteger(maxThinkingTokens) || maxThinkingTokens < 0) {
2584
2608
  missing.add(`${where}.maxThinkingTokens must be a non-negative integer (got "${entryRaw.maxThinkingTokens}")`);
2585
2609
  }
2610
+ let auth;
2611
+ if (entryRaw.auth !== void 0) {
2612
+ if (entryRaw.auth !== "subscription" && entryRaw.auth !== "inherit") {
2613
+ missing.add(`${where}.auth must be subscription|inherit (got "${entryRaw.auth}")`);
2614
+ } else if (harness !== "claude") {
2615
+ missing.add(`${where}.auth is only supported on the claude harness`);
2616
+ } else {
2617
+ auth = entryRaw.auth;
2618
+ }
2619
+ }
2586
2620
  out.push({
2587
2621
  name,
2588
2622
  harness,
2623
+ ...auth ? { auth } : {},
2589
2624
  bin: expandCommand(String(entryRaw.bin ?? adapter.defaultBin), base),
2590
2625
  ...entryRaw.launcher ? { launcher: expandCommand(String(entryRaw.launcher), base) } : {},
2591
2626
  ...entryRaw.launcher && entryRaw.launcherBin ? { launcherBin: expandCommand(String(entryRaw.launcherBin), base) } : {},
@@ -2645,7 +2680,7 @@ function parseOneRoute(c, where, file, base, stateDir, raw, missing, crewHome, t
2645
2680
  const hasRepos = Object.keys(repoDirs).length > 0;
2646
2681
  const reposBasePath = expand(String(c?.reposBasePath ?? raw.ship?.reposBasePath ?? DEFAULT_REPOS_BASE_PATH), base);
2647
2682
  const dirIsCloneBase = !c?.dir && !hasRepos && c?.repos && typeof c.repos === "object" && !Array.isArray(c.repos);
2648
- const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join8(reposBasePath, splitRoute(route)[0]) : missing.req(void 0, `${where}.dir (or ${where}.repos)`) ?? "";
2683
+ const dir = c?.dir ? expand(String(c.dir), base) : hasRepos ? Object.values(repoDirs)[0] : dirIsCloneBase ? join9(reposBasePath, splitRoute(route)[0]) : missing.req(void 0, `${where}.dir (or ${where}.repos)`) ?? "";
2649
2684
  const weight = c?.weight === void 0 ? void 0 : Number(c.weight);
2650
2685
  if (weight !== void 0 && !(Number.isFinite(weight) && weight > 0)) {
2651
2686
  missing.add(`${where}.weight must be a positive number (got "${c?.weight}")`);
@@ -2729,7 +2764,7 @@ function repoIdForName(r, name) {
2729
2764
  function resolveApiKey(r) {
2730
2765
  if (r.apiKey) return r.apiKey;
2731
2766
  if (r.apiKeyFile && r.apiKeyVar && existsSync7(r.apiKeyFile)) {
2732
- for (const line of readFileSync7(r.apiKeyFile, "utf8").split("\n")) {
2767
+ for (const line of readFileSync8(r.apiKeyFile, "utf8").split("\n")) {
2733
2768
  const m = line.match(new RegExp(`^${r.apiKeyVar}=(.*)$`));
2734
2769
  if (m) return (m[1] ?? "").trim().replace(/^["']|["']$/g, "");
2735
2770
  }
@@ -2744,7 +2779,7 @@ function credentialSource(r) {
2744
2779
  if (r.apiKey) return KEYCHAIN_ELIGIBLE_ROUTES.has(r) ? "keychain" : "inline";
2745
2780
  if (r.apiKeyFile && r.apiKeyVar && existsSync7(r.apiKeyFile)) {
2746
2781
  const re = new RegExp(`^${r.apiKeyVar}=`);
2747
- if (readFileSync7(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
2782
+ if (readFileSync8(r.apiKeyFile, "utf8").split("\n").some((l) => re.test(l))) return "apiKeyFile";
2748
2783
  }
2749
2784
  return process.env.CREW_API_KEY?.trim() ? "CREW_API_KEY" : "none";
2750
2785
  }
@@ -2800,7 +2835,7 @@ function configuredMembers(rt) {
2800
2835
  return out;
2801
2836
  }
2802
2837
  function defaultRepoDir(basePath, workspace, repoName) {
2803
- return join8(basePath, workspace, repoName);
2838
+ return join9(basePath, workspace, repoName);
2804
2839
  }
2805
2840
  function reposOf(r) {
2806
2841
  const named = Object.entries(r.repos);
@@ -2833,7 +2868,7 @@ function passengerRepoTargets(r) {
2833
2868
  return reposOf(r).filter((t) => r.repoOverrides[t.name]?.hostPassengers !== false);
2834
2869
  }
2835
2870
  function passengerCheckoutDir(basePath, workspace, repoName) {
2836
- return join8(basePath, workspace, ".passenger-checkouts", repoName);
2871
+ return join9(basePath, workspace, ".passenger-checkouts", repoName);
2837
2872
  }
2838
2873
  function passengerCheckoutTargets(r) {
2839
2874
  if (!r.hostPassengers) return [];
@@ -3181,8 +3216,8 @@ function renderEnvironment(i) {
3181
3216
  }
3182
3217
 
3183
3218
  // src/agent-log.ts
3184
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
3185
- import { join as join9 } from "path";
3219
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync9, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
3220
+ import { join as join10 } from "path";
3186
3221
 
3187
3222
  // src/pending-backoff.ts
3188
3223
  var sleepBackoff = (attempts) => Math.min(6e4 * 2 ** Math.max(0, attempts - 1), 30 * 6e4);
@@ -3249,20 +3284,20 @@ async function reportAgentRun(target, run2) {
3249
3284
  }
3250
3285
  return { agentLogId: entry.id };
3251
3286
  }
3252
- var queueDir = (stateDir) => join9(stateDir, "pending-agent-logs");
3287
+ var queueDir = (stateDir) => join10(stateDir, "pending-agent-logs");
3253
3288
  function queueAgentRun(stateDir, name, q) {
3254
3289
  mkdirSync4(queueDir(stateDir), { recursive: true });
3255
- writeFileSync5(join9(queueDir(stateDir), `${name}.json`), JSON.stringify(q));
3290
+ writeFileSync5(join10(queueDir(stateDir), `${name}.json`), JSON.stringify(q));
3256
3291
  }
3257
3292
  async function replayQueuedAgentRuns(stateDir, route, keyFor, log, now = /* @__PURE__ */ new Date()) {
3258
3293
  const dir = queueDir(stateDir);
3259
3294
  if (!existsSync8(dir)) return 0;
3260
3295
  let sent = 0;
3261
3296
  for (const f of readdirSync2(dir).filter((n) => n.endsWith(".json")).sort()) {
3262
- const path = join9(dir, f);
3297
+ const path = join10(dir, f);
3263
3298
  let q;
3264
3299
  try {
3265
- q = JSON.parse(readFileSync8(path, "utf8"));
3300
+ q = JSON.parse(readFileSync9(path, "utf8"));
3266
3301
  } catch {
3267
3302
  continue;
3268
3303
  }
@@ -3462,8 +3497,8 @@ var HandoffTranscriptTracker = class {
3462
3497
  };
3463
3498
 
3464
3499
  // src/pending-handoff.ts
3465
- import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync5, readFileSync as readFileSync12, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
3466
- import { join as join15 } from "path";
3500
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync5, readFileSync as readFileSync13, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
3501
+ import { join as join16 } from "path";
3467
3502
 
3468
3503
  // src/git.ts
3469
3504
  import { execFileSync as execFileSync2 } from "child_process";
@@ -3475,12 +3510,12 @@ import {
3475
3510
  writeSync,
3476
3511
  statSync as statSync2,
3477
3512
  unlinkSync,
3478
- readFileSync as readFileSync9,
3513
+ readFileSync as readFileSync10,
3479
3514
  writeFileSync as writeFileSync6,
3480
3515
  readdirSync as readdirSync3,
3481
3516
  rmSync as rmSync6
3482
3517
  } from "fs";
3483
- import { dirname as dirname4, basename as basename5, join as join10 } from "path";
3518
+ import { dirname as dirname4, basename as basename5, join as join11 } from "path";
3484
3519
  var GitError = class extends Error {
3485
3520
  };
3486
3521
  function streamText(x) {
@@ -3878,11 +3913,11 @@ function sweepStaleReleaseRoots(tmp, now = Date.now(), maxAgeMs = 60 * 60 * 1e3)
3878
3913
  }
3879
3914
  for (const name of names) {
3880
3915
  if (!name.startsWith("crew-release-")) continue;
3881
- const root = join10(tmp, name);
3916
+ const root = join11(tmp, name);
3882
3917
  try {
3883
3918
  const st = statSync2(root);
3884
3919
  if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
3885
- if (existsSync9(join10(root, "checkout"))) continue;
3920
+ if (existsSync9(join11(root, "checkout"))) continue;
3886
3921
  rmSync6(root, { recursive: true, force: true });
3887
3922
  removed.push(name);
3888
3923
  } catch {
@@ -3996,8 +4031,8 @@ function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base, clone = {
3996
4031
  if (!existsSync9(`${dir}/.git`) && githubRemote) {
3997
4032
  const url = cloneUrlFor(githubRemote, clone);
3998
4033
  const marker = cloneFailureMarker(dir);
3999
- if (existsSync9(marker) && readFileSync9(marker, "utf8").split("\n")[0] === url) {
4000
- return { action: "clone-failed", detail: readFileSync9(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
4034
+ if (existsSync9(marker) && readFileSync10(marker, "utf8").split("\n")[0] === url) {
4035
+ return { action: "clone-failed", detail: readFileSync10(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
4001
4036
  }
4002
4037
  try {
4003
4038
  ensureRepoCheckout(dir, githubRemote, clone);
@@ -4424,9 +4459,9 @@ function recoveredFailureHeadlines(o, route) {
4424
4459
  }
4425
4460
 
4426
4461
  // src/install.ts
4427
- import { dirname as dirname5, join as join11, extname } from "path";
4428
- import { homedir as homedir6 } from "os";
4429
- import { accessSync, constants, existsSync as existsSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync3, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2, realpathSync } from "fs";
4462
+ import { dirname as dirname5, join as join12, extname } from "path";
4463
+ import { homedir as homedir7 } from "os";
4464
+ import { accessSync, constants, existsSync as existsSync10, mkdirSync as mkdirSync6, readdirSync as readdirSync4, readFileSync as readFileSync11, statSync as statSync3, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2, realpathSync } from "fs";
4430
4465
  import { spawnSync as spawnSync4 } from "child_process";
4431
4466
  import { createHash as createHash2 } from "crypto";
4432
4467
 
@@ -4438,7 +4473,7 @@ function isCompiledBinary(moduleUrl) {
4438
4473
  // src/install.ts
4439
4474
  function crewInvocation(crewHome) {
4440
4475
  if (isCompiledBinary(import.meta.url)) return [process.execPath];
4441
- return [process.execPath, join11(crewHome, "bin", "crew")];
4476
+ return [process.execPath, join12(crewHome, "bin", "crew")];
4442
4477
  }
4443
4478
  var INTERVAL_SECONDS = 120;
4444
4479
  function labelFor(crewHome, job) {
@@ -4476,7 +4511,7 @@ function pathFor(ship) {
4476
4511
  return parts.join(":");
4477
4512
  }
4478
4513
  function findOnPath(bin, searchPath) {
4479
- const candidates = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join11(dir, bin));
4514
+ const candidates = bin.includes("/") ? [bin] : searchPath.split(":").filter(Boolean).map((dir) => join12(dir, bin));
4480
4515
  for (const candidate of candidates) {
4481
4516
  try {
4482
4517
  accessSync(candidate, constants.X_OK);
@@ -4486,7 +4521,7 @@ function findOnPath(bin, searchPath) {
4486
4521
  }
4487
4522
  return void 0;
4488
4523
  }
4489
- var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join11(homedir6(), ".docker", "bin")].join(":");
4524
+ var COMMON_DOCKER_DIRS = ["/usr/local/bin", "/opt/homebrew/bin", join12(homedir7(), ".docker", "bin")].join(":");
4490
4525
  function dockerPathProblem(ship, shellPath = process.env.PATH ?? "") {
4491
4526
  const schedulerPath = pathFor(ship);
4492
4527
  if (findOnPath("docker", schedulerPath)) return void 0;
@@ -4498,7 +4533,7 @@ function xmlEscape(s) {
4498
4533
  }
4499
4534
  function planLaunchd(ship, crewHome, job) {
4500
4535
  const label = labelFor(crewHome, job);
4501
- const unitPath = join11(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4536
+ const unitPath = join12(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4502
4537
  const crewLog = ship.logFile;
4503
4538
  const schedulerLog = schedulerLogFor(crewLog, job);
4504
4539
  const persistent = job === "run";
@@ -4552,8 +4587,8 @@ ${scheduleKeys}
4552
4587
  }
4553
4588
  function planSystemd(ship, crewHome, job) {
4554
4589
  const label = labelFor(crewHome, job);
4555
- const unitDir = join11(homedir6(), ".config", "systemd", "user");
4556
- const servicePath = join11(unitDir, `${label}.service`);
4590
+ const unitDir = join12(homedir7(), ".config", "systemd", "user");
4591
+ const servicePath = join12(unitDir, `${label}.service`);
4557
4592
  const crewLog = ship.logFile;
4558
4593
  const schedulerLog = schedulerLogFor(crewLog, job);
4559
4594
  const description = job === "release" ? "Tablation crew \u2014 release" : job === "passengers" ? "Tablation crew \u2014 Host Passengers sync" : "Tablation crew \u2014 persistent supervisor loop";
@@ -4593,7 +4628,7 @@ WantedBy=default.target
4593
4628
  schedulerLog
4594
4629
  };
4595
4630
  }
4596
- const timerPath = join11(unitDir, `${label}.timer`);
4631
+ const timerPath = join12(unitDir, `${label}.timer`);
4597
4632
  const service = `# Written by \`crew install\` \u2014 re-run it rather than hand-editing this file.
4598
4633
  [Unit]
4599
4634
  Description=${description}
@@ -4667,7 +4702,7 @@ function planInstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4667
4702
  function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4668
4703
  const label = labelFor(crewHome, job);
4669
4704
  if (hostShip === "macos") {
4670
- const unitPath = join11(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4705
+ const unitPath = join12(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4671
4706
  return {
4672
4707
  mechanism: "launchd",
4673
4708
  unitPaths: [unitPath],
@@ -4677,18 +4712,18 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4677
4712
  };
4678
4713
  }
4679
4714
  if (hostShip === "linux" && hasSystemd) {
4680
- const unitDir = join11(homedir6(), ".config", "systemd", "user");
4715
+ const unitDir = join12(homedir7(), ".config", "systemd", "user");
4681
4716
  if (job === "run") {
4682
4717
  return {
4683
4718
  mechanism: "systemd",
4684
- unitPaths: [join11(unitDir, `${label}.service`)],
4719
+ unitPaths: [join12(unitDir, `${label}.service`)],
4685
4720
  crontabLine: null,
4686
4721
  unloadCommands: [["systemctl", "--user", "disable", "--now", `${label}.service`]]
4687
4722
  };
4688
4723
  }
4689
4724
  return {
4690
4725
  mechanism: "systemd",
4691
- unitPaths: [join11(unitDir, `${label}.service`), join11(unitDir, `${label}.timer`)],
4726
+ unitPaths: [join12(unitDir, `${label}.service`), join12(unitDir, `${label}.timer`)],
4692
4727
  crontabLine: null,
4693
4728
  unloadCommands: [["systemctl", "--user", "disable", "--now", `${label}.timer`]]
4694
4729
  };
@@ -4706,7 +4741,7 @@ function planUninstall(ship, crewHome, hostShip, hasSystemd, job = "run") {
4706
4741
  function planDaemonControl(crewHome, hostShip, hasSystemd) {
4707
4742
  const label = labelFor(crewHome, "run");
4708
4743
  if (hostShip === "macos") {
4709
- const unitPath = join11(homedir6(), "Library", "LaunchAgents", `${label}.plist`);
4744
+ const unitPath = join12(homedir7(), "Library", "LaunchAgents", `${label}.plist`);
4710
4745
  return {
4711
4746
  mechanism: "launchd",
4712
4747
  startCommand: ["launchctl", "load", unitPath],
@@ -4820,7 +4855,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
4820
4855
  }
4821
4856
  var CREW_LAUNCHD_PREFIX = "com.tablation.crew";
4822
4857
  function launchAgentsDir() {
4823
- return join11(homedir6(), "Library", "LaunchAgents");
4858
+ return join12(homedir7(), "Library", "LaunchAgents");
4824
4859
  }
4825
4860
  function xmlUnescape(s) {
4826
4861
  return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
@@ -4839,10 +4874,10 @@ function loadedCrewLabels() {
4839
4874
  function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
4840
4875
  const files = existsSync10(dir) ? readdirSync4(dir) : [];
4841
4876
  const units = files.filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
4842
- const plistPath = join11(dir, f);
4877
+ const plistPath = join12(dir, f);
4843
4878
  let program = null;
4844
4879
  try {
4845
- program = programFromPlist(readFileSync10(plistPath, "utf8"));
4880
+ program = programFromPlist(readFileSync11(plistPath, "utf8"));
4846
4881
  } catch {
4847
4882
  }
4848
4883
  return {
@@ -4855,7 +4890,7 @@ function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
4855
4890
  const seen = new Set(units.map((u) => u.label));
4856
4891
  for (const label of [...loaded].sort()) {
4857
4892
  if (!label.startsWith(CREW_LAUNCHD_PREFIX) || seen.has(label)) continue;
4858
- units.push({ label, plistPath: join11(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
4893
+ units.push({ label, plistPath: join12(dir, `${label}.plist`), program: null, programExists: false, orphan: true });
4859
4894
  }
4860
4895
  return units;
4861
4896
  }
@@ -5201,8 +5236,8 @@ var SHELL_WORDS = /* @__PURE__ */ new Set([
5201
5236
  ]);
5202
5237
 
5203
5238
  // src/state.ts
5204
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync7, rmSync as rmSync7, statSync as statSync4 } from "fs";
5205
- import { join as join12 } from "path";
5239
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync12, mkdirSync as mkdirSync7, rmSync as rmSync7, statSync as statSync4 } from "fs";
5240
+ import { join as join13 } from "path";
5206
5241
  var EPOCH = "1970-01-01T00:00:00.000Z";
5207
5242
  var State = class _State {
5208
5243
  dir;
@@ -5211,7 +5246,7 @@ var State = class _State {
5211
5246
  mkdirSync7(dir, { recursive: true });
5212
5247
  }
5213
5248
  path(...parts) {
5214
- return join12(this.dir, ...parts);
5249
+ return join13(this.dir, ...parts);
5215
5250
  }
5216
5251
  /**
5217
5252
  * The timestamp every "new since last poll" check is measured against.
@@ -5222,7 +5257,7 @@ var State = class _State {
5222
5257
  */
5223
5258
  watermark() {
5224
5259
  try {
5225
- const v = readFileSync11(this.path(".poll-watermark"), "utf8").trim();
5260
+ const v = readFileSync12(this.path(".poll-watermark"), "utf8").trim();
5226
5261
  return v || EPOCH;
5227
5262
  } catch {
5228
5263
  return EPOCH;
@@ -5308,7 +5343,7 @@ var State = class _State {
5308
5343
  const name = _State.safe(rawName);
5309
5344
  const file = this.path(`.${name}.lock`);
5310
5345
  try {
5311
- const existing = Number.parseInt(readFileSync11(file, "utf8").trim(), 10);
5346
+ const existing = Number.parseInt(readFileSync12(file, "utf8").trim(), 10);
5312
5347
  if (Number.isFinite(existing) && existing > 0) {
5313
5348
  try {
5314
5349
  process.kill(existing, 0);
@@ -5402,7 +5437,7 @@ var State = class _State {
5402
5437
  const heldReported = `.release-held-${suffix}`;
5403
5438
  const read = (f) => {
5404
5439
  try {
5405
- return readFileSync11(this.path(f), "utf8").trim() || null;
5440
+ return readFileSync12(this.path(f), "utf8").trim() || null;
5406
5441
  } catch {
5407
5442
  return null;
5408
5443
  }
@@ -5452,7 +5487,7 @@ var State = class _State {
5452
5487
  /** Whether the board was already told about this head's red gate. */
5453
5488
  heldReported: () => {
5454
5489
  try {
5455
- return readFileSync11(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
5490
+ return readFileSync12(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
5456
5491
  } catch {
5457
5492
  return null;
5458
5493
  }
@@ -5489,7 +5524,7 @@ var State = class _State {
5489
5524
  const ticketStreakFile = `.fairness-ticket-streak-${suffix}`;
5490
5525
  const read = (f) => {
5491
5526
  try {
5492
- return readFileSync11(this.path(f), "utf8").trim() || null;
5527
+ return readFileSync12(this.path(f), "utf8").trim() || null;
5493
5528
  } catch {
5494
5529
  return null;
5495
5530
  }
@@ -5554,7 +5589,7 @@ var State = class _State {
5554
5589
  return {
5555
5590
  previous: () => {
5556
5591
  try {
5557
- const parsed = JSON.parse(readFileSync11(this.path(file), "utf8"));
5592
+ const parsed = JSON.parse(readFileSync12(this.path(file), "utf8"));
5558
5593
  return parsed && typeof parsed === "object" ? parsed : {};
5559
5594
  } catch {
5560
5595
  return {};
@@ -5575,7 +5610,7 @@ var State = class _State {
5575
5610
  return {
5576
5611
  previous: () => {
5577
5612
  try {
5578
- const parsed = JSON.parse(readFileSync11(this.path(file), "utf8"));
5613
+ const parsed = JSON.parse(readFileSync12(this.path(file), "utf8"));
5579
5614
  return Array.isArray(parsed) ? parsed : [];
5580
5615
  } catch {
5581
5616
  return [];
@@ -5608,7 +5643,7 @@ var State = class _State {
5608
5643
  }
5609
5644
  readInt(file) {
5610
5645
  try {
5611
- return Number.parseInt(readFileSync11(this.path(file), "utf8").trim(), 10) || 0;
5646
+ return Number.parseInt(readFileSync12(this.path(file), "utf8").trim(), 10) || 0;
5612
5647
  } catch {
5613
5648
  return 0;
5614
5649
  }
@@ -6611,12 +6646,12 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
6611
6646
 
6612
6647
  // src/run-worktree.ts
6613
6648
  import { existsSync as existsSync14 } from "fs";
6614
- import { dirname as dirname6, join as join14 } from "path";
6649
+ import { dirname as dirname6, join as join15 } from "path";
6615
6650
 
6616
6651
  // src/ports.ts
6617
6652
  import { execFileSync as execFileSync3 } from "child_process";
6618
6653
  import { existsSync as existsSync13 } from "fs";
6619
- import { join as join13 } from "path";
6654
+ import { join as join14 } from "path";
6620
6655
  var run = (cmd, args) => {
6621
6656
  try {
6622
6657
  return execFileSync3(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
@@ -6661,7 +6696,7 @@ function ticketForPort(port, s = DEFAULT_PORTS) {
6661
6696
  }
6662
6697
  function worktreeExists(parent, prefix, n) {
6663
6698
  for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
6664
- if (existsSync13(join13(parent, name, ".git"))) return true;
6699
+ if (existsSync13(join14(parent, name, ".git"))) return true;
6665
6700
  }
6666
6701
  return false;
6667
6702
  }
@@ -6753,7 +6788,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
6753
6788
  if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
6754
6789
  return { action: "recut", path: existing.path, branch, base: baseRef };
6755
6790
  }
6756
- const path = join14(dirname6(dir), worktreeForTicket(dir, cfg, t, role));
6791
+ const path = join15(dirname6(dir), worktreeForTicket(dir, cfg, t, role));
6757
6792
  if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
6758
6793
  return { action: "cut-remote", path, branch: located.branch };
6759
6794
  }
@@ -7116,12 +7151,12 @@ async function guardRunStrays(route, ship, tracker, role, workingId, since2, log
7116
7151
 
7117
7152
  // src/pending-handoff.ts
7118
7153
  var PENDING_MAX_AGE_MS = 48 * 36e5;
7119
- var pendingDirFor = (stateDir) => join15(stateDir, "pending-handoffs");
7120
- var stateDirOfStream = (streamPath) => join15(streamPath, "..", "..");
7154
+ var pendingDirFor = (stateDir) => join16(stateDir, "pending-handoffs");
7155
+ var stateDirOfStream = (streamPath) => join16(streamPath, "..", "..");
7121
7156
  function writePendingHandoff(stateDir, rec) {
7122
7157
  const dir = pendingDirFor(stateDir);
7123
7158
  mkdirSync8(dir, { recursive: true });
7124
- writeFileSync9(join15(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
7159
+ writeFileSync9(join16(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
7125
7160
  }
7126
7161
  function listPendingHandoffs(stateDir, route) {
7127
7162
  const dir = pendingDirFor(stateDir);
@@ -7129,7 +7164,7 @@ function listPendingHandoffs(stateDir, route) {
7129
7164
  const out = [];
7130
7165
  for (const f of readdirSync5(dir).filter((n) => n.endsWith(".json")).sort()) {
7131
7166
  try {
7132
- const rec = JSON.parse(readFileSync12(join15(dir, f), "utf8"));
7167
+ const rec = JSON.parse(readFileSync13(join16(dir, f), "utf8"));
7133
7168
  if (rec.version === 1 && rec.ticketRecordId && (!route || rec.route === route)) out.push(rec);
7134
7169
  } catch {
7135
7170
  }
@@ -7137,7 +7172,7 @@ function listPendingHandoffs(stateDir, route) {
7137
7172
  return out;
7138
7173
  }
7139
7174
  function removePendingHandoff(stateDir, ticketRecordId) {
7140
- rmSync8(join15(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
7175
+ rmSync8(join16(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
7141
7176
  }
7142
7177
  function pendingHandoffFor(stateDir, ticketRecordId) {
7143
7178
  return listPendingHandoffs(stateDir).find((r) => r.ticketRecordId === ticketRecordId);
@@ -7247,8 +7282,8 @@ function expandSkillMarkers(text, lookup, opts = {}) {
7247
7282
  }
7248
7283
 
7249
7284
  // src/skills.ts
7250
- import { readFileSync as readFileSync14, readdirSync as readdirSync6, existsSync as existsSync17 } from "fs";
7251
- import { join as join17 } from "path";
7285
+ import { readFileSync as readFileSync15, readdirSync as readdirSync6, existsSync as existsSync17 } from "fs";
7286
+ import { join as join18 } from "path";
7252
7287
  import { parse as parseYaml } from "yaml";
7253
7288
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
7254
7289
 
@@ -7264,8 +7299,8 @@ ${additions}`;
7264
7299
  }
7265
7300
 
7266
7301
  // src/agents.ts
7267
- import { readFileSync as readFileSync13, existsSync as existsSync16 } from "fs";
7268
- import { join as join16 } from "path";
7302
+ import { readFileSync as readFileSync14, existsSync as existsSync16 } from "fs";
7303
+ import { join as join17 } from "path";
7269
7304
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
7270
7305
  var PERSONA_NAME = {
7271
7306
  dev: "Developer",
@@ -7279,12 +7314,12 @@ var CREW_AGENT_COLUMN = "agent_id";
7279
7314
  var AgentsSyncError = class extends Error {
7280
7315
  };
7281
7316
  function personaDefaultPrompt(promptsDir, role) {
7282
- const briefPath = join16(promptsDir, "personas", `lane-${role}.md`);
7317
+ const briefPath = join17(promptsDir, "personas", `lane-${role}.md`);
7283
7318
  if (!existsSync16(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
7284
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync13(briefPath, "utf8");
7285
- const commonPath = join16(promptsDir, "personas", "common.md");
7286
- if (!existsSync16(commonPath)) return readFileSync13(briefPath, "utf8");
7287
- return `${readFileSync13(commonPath, "utf8")}${readFileSync13(briefPath, "utf8")}`;
7319
+ if (SELF_CONTAINED_ROLES.has(role)) return readFileSync14(briefPath, "utf8");
7320
+ const commonPath = join17(promptsDir, "personas", "common.md");
7321
+ if (!existsSync16(commonPath)) return readFileSync14(briefPath, "utf8");
7322
+ return `${readFileSync14(commonPath, "utf8")}${readFileSync14(briefPath, "utf8")}`;
7288
7323
  }
7289
7324
  async function resolveAgentId(route, role, opts) {
7290
7325
  const cached = route.resolved?.agentPersonas?.[role]?.agentId;
@@ -7731,7 +7766,7 @@ function describeCrewLink(o) {
7731
7766
  var SkillSyncError = class extends Error {
7732
7767
  };
7733
7768
  function parseSkillFile(path) {
7734
- const text = readFileSync14(path, "utf8");
7769
+ const text = readFileSync15(path, "utf8");
7735
7770
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text);
7736
7771
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
7737
7772
  const frontmatter = match[1];
@@ -7744,9 +7779,9 @@ function parseSkillFile(path) {
7744
7779
  return { name: meta.name, description: meta.description, prompt: body, file: path };
7745
7780
  }
7746
7781
  function listLocalSkills(promptsDir) {
7747
- const dir = join17(promptsDir, "skills");
7782
+ const dir = join18(promptsDir, "skills");
7748
7783
  if (!existsSync17(dir)) return [];
7749
- return readdirSync6(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join17(dir, f)));
7784
+ return readdirSync6(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join18(dir, f)));
7750
7785
  }
7751
7786
  async function syncSkills(route, opts) {
7752
7787
  if (!route.resolved) {
@@ -7949,23 +7984,23 @@ var AgentError = class extends Error {
7949
7984
  var AgentSpawnError = class extends AgentError {
7950
7985
  };
7951
7986
  function planAgentRun(o) {
7952
- const briefPath = join18(o.route.promptsDir, "personas", `lane-${o.role}.md`);
7987
+ const briefPath = join19(o.route.promptsDir, "personas", `lane-${o.role}.md`);
7953
7988
  if (!existsSync18(briefPath)) {
7954
7989
  throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
7955
7990
  }
7956
- const commonPath = join18(o.route.promptsDir, "personas", "common.md");
7991
+ const commonPath = join19(o.route.promptsDir, "personas", "common.md");
7957
7992
  const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync18(commonPath);
7958
7993
  const now = o.now ?? (() => Date.now());
7959
7994
  const routeLabel = routeSlug(o.route.route);
7960
- const digestPath2 = join18(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
7995
+ const digestPath2 = join19(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
7961
7996
  let digest;
7962
7997
  let ageSeconds;
7963
7998
  if (existsSync18(digestPath2)) {
7964
7999
  ageSeconds = Math.floor((now() - statSync5(digestPath2).mtimeMs) / 1e3);
7965
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync15(digestPath2, "utf8");
8000
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync16(digestPath2, "utf8");
7966
8001
  }
7967
- const common = usesCommon ? readFileSync15(commonPath, "utf8") : "";
7968
- const brief = readFileSync15(briefPath, "utf8");
8002
+ const common = usesCommon ? readFileSync16(commonPath, "utf8") : "";
8003
+ const brief = readFileSync16(briefPath, "utf8");
7969
8004
  const effectiveCommon = o.divergedPrompt ? "" : common;
7970
8005
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
7971
8006
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -7987,7 +8022,7 @@ function planAgentRun(o) {
7987
8022
  digest
7988
8023
  });
7989
8024
  const promptSha = createHash3("sha256").update(expanded.text).digest("hex");
7990
- const streamsDir = join18(o.stateDir, "streams");
8025
+ const streamsDir = join19(o.stateDir, "streams");
7991
8026
  const base = `${routeLabel}-${o.role}-${o.cycle}`;
7992
8027
  const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
7993
8028
  const { toolchain, model } = seat;
@@ -8043,8 +8078,8 @@ function planAgentRun(o) {
8043
8078
  digestAttached: digest !== void 0,
8044
8079
  digestAgeSeconds: ageSeconds,
8045
8080
  model,
8046
- streamPath: join18(streamsDir, `${base}.jsonl`),
8047
- eventsPath: join18(streamsDir, `${base}.events.jsonl`),
8081
+ streamPath: join19(streamsDir, `${base}.jsonl`),
8082
+ eventsPath: join19(streamsDir, `${base}.events.jsonl`),
8048
8083
  ticket: o.ticket,
8049
8084
  ticketRecordId: o.ticketRecordId ?? null,
8050
8085
  agentLog,
@@ -8073,7 +8108,7 @@ function describePlan(p) {
8073
8108
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
8074
8109
  function readRunSidecar(streamPath) {
8075
8110
  try {
8076
- const sc = JSON.parse(readFileSync15(pidPathFor(streamPath), "utf8"));
8111
+ const sc = JSON.parse(readFileSync16(pidPathFor(streamPath), "utf8"));
8077
8112
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
8078
8113
  } catch {
8079
8114
  return null;
@@ -8657,7 +8692,7 @@ async function runSelftest(i, deps = realDeps) {
8657
8692
  ...adapter.runEnv(spec),
8658
8693
  [KEY_VAR]: i.apiKey
8659
8694
  };
8660
- const cwd = mkdtempSync(join19(tmpdir2(), "crew-selftest-"));
8695
+ const cwd = mkdtempSync(join20(tmpdir2(), "crew-selftest-"));
8661
8696
  try {
8662
8697
  const run2 = await deps.run({
8663
8698
  bin: invocation.bin,
@@ -8672,7 +8707,8 @@ async function runSelftest(i, deps = realDeps) {
8672
8707
  const answer = [seen.finalText ?? "", ...seen.texts].join("\n");
8673
8708
  const usage2 = { ...seen.tokens !== void 0 ? { tokens: seen.tokens } : {}, ...seen.costUsd !== void 0 ? { costUsd: seen.costUsd } : {} };
8674
8709
  if (!run2.spawnError && !run2.timedOut && answer.toLowerCase().includes(i.workspaceName.toLowerCase())) {
8675
- return finish({ ok: true, workspace: i.workspaceName, detail: `read workspace "${i.workspaceName}"`, ...usage2 });
8710
+ const auth = spec.harness === "claude" ? ` on ${describeClaudeAuth(spec)}` : "";
8711
+ return finish({ ok: true, workspace: i.workspaceName, detail: `read workspace "${i.workspaceName}"${auth}`, ...usage2 });
8676
8712
  }
8677
8713
  const { failure, detail } = classify(spec, run2, seen);
8678
8714
  return finish({ ok: false, failure, detail, ...usage2 });
@@ -8689,11 +8725,11 @@ function formatResult(r) {
8689
8725
  const secs = `${(r.ms / 1e3).toFixed(1)}s`;
8690
8726
  return r.ok ? `${r.toolchain}: PASS ${r.detail} (${secs}${tok})` : `${r.toolchain}: FAIL ${r.failure} \u2014 ${r.detail} (${secs})`;
8691
8727
  }
8692
- var fileFor2 = (stateDir) => join19(stateDir, "selftest.json");
8728
+ var fileFor2 = (stateDir) => join20(stateDir, "selftest.json");
8693
8729
  function readSelftest(stateDir) {
8694
8730
  try {
8695
8731
  const p = fileFor2(stateDir);
8696
- return existsSync19(p) ? JSON.parse(readFileSync16(p, "utf8")) : {};
8732
+ return existsSync19(p) ? JSON.parse(readFileSync17(p, "utf8")) : {};
8697
8733
  } catch {
8698
8734
  return {};
8699
8735
  }
@@ -8714,9 +8750,9 @@ function doctorSelftestLines(stored, toolchains) {
8714
8750
  }
8715
8751
 
8716
8752
  // src/cli.ts
8717
- import { resolve as resolve5, dirname as dirname20, join as join36, basename as basename11 } from "path";
8753
+ import { resolve as resolve5, dirname as dirname20, join as join37, basename as basename11 } from "path";
8718
8754
  import { fileURLToPath } from "url";
8719
- import { hostname as hostname2, homedir as homedir9 } from "os";
8755
+ import { hostname as hostname2, homedir as homedir10 } from "os";
8720
8756
  import { spawnSync as spawnSync6 } from "child_process";
8721
8757
  import { parse as parseYaml2 } from "yaml";
8722
8758
 
@@ -8894,14 +8930,14 @@ function rosterMarkdown(roster, meId) {
8894
8930
  }
8895
8931
 
8896
8932
  // src/connect-resume.ts
8897
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync17, rmSync as rmSync10, writeFileSync as writeFileSync12 } from "fs";
8898
- import { dirname as dirname8, join as join20 } from "path";
8933
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync18, rmSync as rmSync10, writeFileSync as writeFileSync12 } from "fs";
8934
+ import { dirname as dirname8, join as join21 } from "path";
8899
8935
  function lastSessionFile(stateDir) {
8900
- return join20(stateDir, "connect-session.json");
8936
+ return join21(stateDir, "connect-session.json");
8901
8937
  }
8902
8938
  function readPointers(stateDir) {
8903
8939
  try {
8904
- const parsed = JSON.parse(readFileSync17(lastSessionFile(stateDir), "utf8"));
8940
+ const parsed = JSON.parse(readFileSync18(lastSessionFile(stateDir), "utf8"));
8905
8941
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
8906
8942
  const out = {};
8907
8943
  for (const [host, v] of Object.entries(parsed)) {
@@ -8972,15 +9008,15 @@ async function signOutSessions(o) {
8972
9008
  }
8973
9009
 
8974
9010
  // src/reset.ts
8975
- import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync18, readdirSync as readdirSync7, rmSync as rmSync11, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
8976
- import { dirname as dirname9, join as join21 } from "path";
9011
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync19, readdirSync as readdirSync7, rmSync as rmSync11, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
9012
+ import { dirname as dirname9, join as join22 } from "path";
8977
9013
  import { hostFromUrl as hostFromUrl2 } from "@tablation/client";
8978
9014
  function clonedCheckoutsFile(stateDir) {
8979
- return join21(stateDir, "cloned-checkouts.json");
9015
+ return join22(stateDir, "cloned-checkouts.json");
8980
9016
  }
8981
9017
  function readClonedCheckouts(stateDir) {
8982
9018
  try {
8983
- const parsed = JSON.parse(readFileSync18(clonedCheckoutsFile(stateDir), "utf8"));
9019
+ const parsed = JSON.parse(readFileSync19(clonedCheckoutsFile(stateDir), "utf8"));
8984
9020
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
8985
9021
  } catch {
8986
9022
  return [];
@@ -9065,10 +9101,10 @@ function removeCheckout(dir, dryRun, fx) {
9065
9101
  }
9066
9102
  function removeState(stateDir, dryRun) {
9067
9103
  const out = [];
9068
- const targets = [join21(stateDir, "resolved"), join21(stateDir, "keys"), clonedCheckoutsFile(stateDir), join21(stateDir, "connect-session.json"), join21(stateDir, "reload-requested"), join21(stateDir, "daemon.started")];
9104
+ const targets = [join22(stateDir, "resolved"), join22(stateDir, "keys"), clonedCheckoutsFile(stateDir), join22(stateDir, "connect-session.json"), join22(stateDir, "reload-requested"), join22(stateDir, "daemon.started")];
9069
9105
  try {
9070
9106
  for (const name of readdirSync7(stateDir)) {
9071
- if (name.startsWith(".") && statSync6(join21(stateDir, name)).isFile()) targets.push(join21(stateDir, name));
9107
+ if (name.startsWith(".") && statSync6(join22(stateDir, name)).isFile()) targets.push(join22(stateDir, name));
9072
9108
  }
9073
9109
  } catch {
9074
9110
  }
@@ -9854,7 +9890,7 @@ ${waiting.map((t) => `| ${displayKey2(t)} | ${t.status} | ${blockers(t, i)} |`).
9854
9890
 
9855
9891
  // src/poll.ts
9856
9892
  import { writeFileSync as writeFileSync14 } from "fs";
9857
- import { join as join22 } from "path";
9893
+ import { join as join23 } from "path";
9858
9894
 
9859
9895
  // src/stalled.ts
9860
9896
  var STALLED_AFTER_CYCLES = 30;
@@ -10145,7 +10181,7 @@ async function decideCycle(o) {
10145
10181
  }
10146
10182
  return decision;
10147
10183
  }
10148
- var digestPath = (stateDir, route, role) => join22(stateDir, `digest-${route}-${role}.md`);
10184
+ var digestPath = (stateDir, route, role) => join23(stateDir, `digest-${route}-${role}.md`);
10149
10185
  function writeDigest(o, d, role, stateDir) {
10150
10186
  const me = o.route.resolved?.seats[role];
10151
10187
  if (!me) return false;
@@ -10634,7 +10670,7 @@ Ship ${shipName2} found: ${problem}. The release refuses to squash a ref nobody
10634
10670
  var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.record_id === ticket.id && e.event_type === "local_ref_skip" && (e.message ?? "").includes(problem));
10635
10671
 
10636
10672
  // src/connect-wizard.ts
10637
- import { delimiter, dirname as dirname10, join as join23 } from "path";
10673
+ import { delimiter, dirname as dirname10, join as join24 } from "path";
10638
10674
  import { accessSync as accessSync2, constants as constants2, existsSync as existsSync21, mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
10639
10675
  import { stringify } from "yaml";
10640
10676
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
@@ -10657,7 +10693,7 @@ function defaultIsExecutable(p) {
10657
10693
  function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
10658
10694
  for (const dir of pathEnv.split(delimiter)) {
10659
10695
  if (!dir) continue;
10660
- const candidate = join23(dir, name);
10696
+ const candidate = join24(dir, name);
10661
10697
  if (isExecutable(candidate)) return candidate;
10662
10698
  }
10663
10699
  return void 0;
@@ -10753,8 +10789,8 @@ Wrote ${o.configPath}.
10753
10789
 
10754
10790
  // src/connect-json.ts
10755
10791
  import { accessSync as accessSync3, constants as constants3 } from "fs";
10756
- import { homedir as homedir7 } from "os";
10757
- import { dirname as dirname11, join as join24 } from "path";
10792
+ import { homedir as homedir8 } from "os";
10793
+ import { dirname as dirname11, join as join25 } from "path";
10758
10794
  import { delimiter as delimiter2 } from "path";
10759
10795
  var CHOICE_EXIT_CODE = 3;
10760
10796
  function createEmitter(json, write) {
@@ -10793,17 +10829,17 @@ function agentBinSearchDirs(home, pathEnv, extraPath) {
10793
10829
  const dirs = [
10794
10830
  ...pathEnv.split(delimiter2),
10795
10831
  ...(extraPath ?? "").split(delimiter2),
10796
- join24(home, ".local/bin"),
10797
- join24(home, ".claude/local"),
10832
+ join25(home, ".local/bin"),
10833
+ join25(home, ".claude/local"),
10798
10834
  "/opt/homebrew/bin",
10799
10835
  "/usr/local/bin",
10800
- join24(home, ".npm-global/bin"),
10801
- join24(home, ".volta/bin")
10836
+ join25(home, ".npm-global/bin"),
10837
+ join25(home, ".volta/bin")
10802
10838
  ];
10803
10839
  return [...new Set(dirs.filter(Boolean))];
10804
10840
  }
10805
10841
  function findAgentBin(name, env, extraPath, isExec) {
10806
- const searched = agentBinSearchDirs(env.home ?? homedir7(), env.pathEnv, extraPath);
10842
+ const searched = agentBinSearchDirs(env.home ?? homedir8(), env.pathEnv, extraPath);
10807
10843
  return { bin: findOnPath2(name, searched.join(delimiter2), isExec), searched };
10808
10844
  }
10809
10845
  function flagWizardAnswers(f, env) {
@@ -10834,8 +10870,8 @@ function flagWizardAnswers(f, env) {
10834
10870
  }
10835
10871
 
10836
10872
  // src/release-run.ts
10837
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync16, existsSync as existsSync22, copyFileSync, mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync12 } from "fs";
10838
- import { dirname as dirname12, join as join25 } from "path";
10873
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync16, existsSync as existsSync22, copyFileSync, mkdirSync as mkdirSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync12 } from "fs";
10874
+ import { dirname as dirname12, join as join26 } from "path";
10839
10875
  import { tmpdir as tmpdir3 } from "os";
10840
10876
 
10841
10877
  // src/release.ts
@@ -11553,13 +11589,13 @@ async function bump(o, size) {
11553
11589
  }
11554
11590
  return printed;
11555
11591
  }
11556
- const files = o.repo.release.versionFiles.map((f) => join25(o.cwd, f));
11592
+ const files = o.repo.release.versionFiles.map((f) => join26(o.cwd, f));
11557
11593
  const first = files[0];
11558
11594
  if (!existsSync22(first)) {
11559
11595
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
11560
11596
  return null;
11561
11597
  }
11562
- const current = JSON.parse(readFileSync19(first, "utf8")).version;
11598
+ const current = JSON.parse(readFileSync20(first, "utf8")).version;
11563
11599
  if (!current) {
11564
11600
  o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
11565
11601
  return null;
@@ -11572,7 +11608,7 @@ async function bump(o, size) {
11572
11608
  return version;
11573
11609
  }
11574
11610
  for (const f of files) {
11575
- const src = readFileSync19(f, "utf8");
11611
+ const src = readFileSync20(f, "utf8");
11576
11612
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
11577
11613
  if (out === src) {
11578
11614
  o.emit.error(`no version field in ${f}`);
@@ -11585,13 +11621,13 @@ async function bump(o, size) {
11585
11621
  function writeChangelog(o, version, entries) {
11586
11622
  const file = o.repo.release.changelog;
11587
11623
  if (!file || entries.length === 0) return;
11588
- const path = join25(o.cwd, file);
11624
+ const path = join26(o.cwd, file);
11589
11625
  const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
11590
11626
  if (o.dryRun) {
11591
11627
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
11592
11628
  return;
11593
11629
  }
11594
- const existing = existsSync22(path) ? readFileSync19(path, "utf8") : "# Changelog\n";
11630
+ const existing = existsSync22(path) ? readFileSync20(path, "utf8") : "# Changelog\n";
11595
11631
  writeFileSync16(path, insertChangelogSection(existing, section));
11596
11632
  }
11597
11633
  async function confirm(o, expected) {
@@ -11685,8 +11721,8 @@ function closeReleaseCheckout(cwd, root, dir, branch, run2 = gitOk) {
11685
11721
  function openReleaseCheckout(o) {
11686
11722
  const { remote, base } = o.repo.branch;
11687
11723
  const branch = `crew/release-${Date.now()}`;
11688
- const root = mkdtempSync2(join25(tmpdir3(), "crew-release-"));
11689
- const dir = join25(root, "checkout");
11724
+ const root = mkdtempSync2(join26(tmpdir3(), "crew-release-"));
11725
+ const dir = join26(root, "checkout");
11690
11726
  o.emit.emit(`release checkout: ${dir}`, { step: "release" });
11691
11727
  const close = () => closeReleaseCheckout(o.cwd, root, dir, branch);
11692
11728
  try {
@@ -11696,10 +11732,10 @@ function openReleaseCheckout(o) {
11696
11732
  throw e;
11697
11733
  }
11698
11734
  for (const f of o.repo.worktrees.copy) {
11699
- const src = join25(o.cwd, f);
11735
+ const src = join26(o.cwd, f);
11700
11736
  if (!existsSync22(src)) continue;
11701
- mkdirSync14(dirname12(join25(dir, f)), { recursive: true });
11702
- copyFileSync(src, join25(dir, f));
11737
+ mkdirSync14(dirname12(join26(dir, f)), { recursive: true });
11738
+ copyFileSync(src, join26(dir, f));
11703
11739
  }
11704
11740
  return { dir, branch, close };
11705
11741
  }
@@ -12780,17 +12816,17 @@ function nextRoles(decision, ran) {
12780
12816
  }
12781
12817
 
12782
12818
  // src/ship-name.ts
12783
- import { existsSync as existsSync25, mkdirSync as mkdirSync16, readFileSync as readFileSync21, renameSync as renameSync2, writeFileSync as writeFileSync18 } from "fs";
12819
+ import { existsSync as existsSync25, mkdirSync as mkdirSync16, readFileSync as readFileSync22, renameSync as renameSync2, writeFileSync as writeFileSync18 } from "fs";
12784
12820
  import { dirname as dirname14 } from "path";
12785
12821
  import { parseDocument } from "yaml";
12786
12822
 
12787
12823
  // src/machine-id.ts
12788
12824
  import { execFileSync as execFileSync4 } from "child_process";
12789
12825
  import { createHash as createHash4 } from "crypto";
12790
- import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "fs";
12791
- import { dirname as dirname13, join as join26 } from "path";
12826
+ import { existsSync as existsSync24, mkdirSync as mkdirSync15, readFileSync as readFileSync21, writeFileSync as writeFileSync17 } from "fs";
12827
+ import { dirname as dirname13, join as join27 } from "path";
12792
12828
  function machineIdPathFor(stateDir) {
12793
- return join26(stateDir, "machine-id");
12829
+ return join27(stateDir, "machine-id");
12794
12830
  }
12795
12831
  var sha256 = (s) => createHash4("sha256").update(s).digest("hex");
12796
12832
  function readRawMachineId(platform2 = process.platform) {
@@ -12804,7 +12840,7 @@ function readRawMachineId(platform2 = process.platform) {
12804
12840
  }
12805
12841
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
12806
12842
  if (existsSync24(p)) {
12807
- const id = readFileSync20(p, "utf8").trim();
12843
+ const id = readFileSync21(p, "utf8").trim();
12808
12844
  if (id) return id;
12809
12845
  }
12810
12846
  }
@@ -12816,7 +12852,7 @@ function ensureMachineId(stateDir, opts = {}) {
12816
12852
  if (opts.override?.trim()) return sha256(opts.override.trim());
12817
12853
  const path = machineIdPathFor(stateDir);
12818
12854
  if (existsSync24(path)) {
12819
- const cached = readFileSync20(path, "utf8").trim();
12855
+ const cached = readFileSync21(path, "utf8").trim();
12820
12856
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
12821
12857
  }
12822
12858
  const raw = (opts.read ?? readRawMachineId)();
@@ -12896,7 +12932,7 @@ async function syncRouteName(i) {
12896
12932
  function readSyncedName(resolvedFile) {
12897
12933
  if (!existsSync25(resolvedFile)) return void 0;
12898
12934
  try {
12899
- const v = JSON.parse(readFileSync21(resolvedFile, "utf8")).shipName;
12935
+ const v = JSON.parse(readFileSync22(resolvedFile, "utf8")).shipName;
12900
12936
  return typeof v === "string" ? v : void 0;
12901
12937
  } catch {
12902
12938
  return void 0;
@@ -12906,7 +12942,7 @@ function writeSyncedName(resolvedFile, name) {
12906
12942
  if (!existsSync25(resolvedFile)) return;
12907
12943
  let raw;
12908
12944
  try {
12909
- raw = JSON.parse(readFileSync21(resolvedFile, "utf8"));
12945
+ raw = JSON.parse(readFileSync22(resolvedFile, "utf8"));
12910
12946
  } catch {
12911
12947
  return;
12912
12948
  }
@@ -12921,7 +12957,7 @@ function writeSyncedName(resolvedFile, name) {
12921
12957
  async function reconcileShipName(i) {
12922
12958
  let local = i.ship.name;
12923
12959
  try {
12924
- local = shipNameOfYaml(readFileSync21(i.configFile, "utf8")) ?? local;
12960
+ local = shipNameOfYaml(readFileSync22(i.configFile, "utf8")) ?? local;
12925
12961
  } catch {
12926
12962
  }
12927
12963
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -12945,7 +12981,7 @@ async function reconcileShipName(i) {
12945
12981
  }
12946
12982
  if (result.status !== "ok") return result;
12947
12983
  if (result.action === "adopt" || result.action === "row-wins") {
12948
- const text = readFileSync21(i.configFile, "utf8");
12984
+ const text = readFileSync22(i.configFile, "utf8");
12949
12985
  writeFileSync18(i.configFile, setShipNameInYaml(text, result.name));
12950
12986
  i.ship.name = result.name;
12951
12987
  if (result.action === "row-wins") {
@@ -13854,7 +13890,7 @@ function describeCapability(i) {
13854
13890
  }
13855
13891
 
13856
13892
  // src/agents-sync-run.ts
13857
- import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
13893
+ import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync19 } from "fs";
13858
13894
  import { dirname as dirname15 } from "path";
13859
13895
  async function runAgentsSync(target, o) {
13860
13896
  const dryRun = o.dryRun ?? false;
@@ -13888,7 +13924,7 @@ async function runAgentsSync(target, o) {
13888
13924
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
13889
13925
  if (!dryRun) {
13890
13926
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
13891
- const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync22(resolvedPath, "utf8")) : {};
13927
+ const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync23(resolvedPath, "utf8")) : {};
13892
13928
  raw.agentPersonas = personaResult.agentPersonas;
13893
13929
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
13894
13930
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -13999,8 +14035,8 @@ async function showLogEntry(route, entryId, opts = {}) {
13999
14035
  import { isMap as isMap2, parseDocument as parseDocument3, YAMLMap } from "yaml";
14000
14036
 
14001
14037
  // src/repos-cmd.ts
14002
- import { homedir as homedir8 } from "os";
14003
- import { basename as basename8, join as join27, resolve as resolve3 } from "path";
14038
+ import { homedir as homedir9 } from "os";
14039
+ import { basename as basename8, join as join28, resolve as resolve3 } from "path";
14004
14040
  import { isMap, isSeq, parseDocument as parseDocument2, stringify as stringify2 } from "yaml";
14005
14041
  var ReposError = class extends Error {
14006
14042
  };
@@ -14046,7 +14082,7 @@ function parseAddArgs(args, routes, isDir) {
14046
14082
  return { route, path: "." };
14047
14083
  }
14048
14084
  function expandDir(p, base) {
14049
- const home = p === "~" ? homedir8() : p.startsWith("~/") ? `${homedir8()}/${p.slice(2)}` : p;
14085
+ const home = p === "~" ? homedir9() : p.startsWith("~/") ? `${homedir9()}/${p.slice(2)}` : p;
14050
14086
  return resolve3(base, home);
14051
14087
  }
14052
14088
  function routeMapOf(doc, route) {
@@ -14278,7 +14314,7 @@ function planRepoClone(i) {
14278
14314
  }
14279
14315
  const ship = doc.get("ship", true);
14280
14316
  const basePath = String(m.get("reposBasePath") ?? (isMap(ship) ? ship.get("reposBasePath") : void 0) ?? "~/Crew");
14281
- const target = i.dir ? expandDir(i.dir, i.base) : join27(expandDir(basePath, i.base), i.route.split("/")[0], name);
14317
+ const target = i.dir ? expandDir(i.dir, i.base) : join28(expandDir(basePath, i.base), i.route.split("/")[0], name);
14282
14318
  const add = planRepoAdd({ text: i.text, base: i.base, route: i.route, path: target, origin: remote, resolved: i.resolved, name, isCheckout: i.isCheckout });
14283
14319
  return { ...add, remote, target };
14284
14320
  }
@@ -14677,7 +14713,7 @@ ${run2.err}` });
14677
14713
  }
14678
14714
 
14679
14715
  // src/sample-ticket.ts
14680
- import { basename as basename9, dirname as dirname17, join as join28 } from "path";
14716
+ import { basename as basename9, dirname as dirname17, join as join29 } from "path";
14681
14717
  var SAMPLE_REPO = "crew-sample";
14682
14718
  var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
14683
14719
  var SAMPLE_MARKER = "sample: true";
@@ -14700,8 +14736,8 @@ has something safe to work on: its test and build hooks always pass, it has no
14700
14736
  remote, and \`crew sample-ticket --remove\` deletes it.
14701
14737
  `;
14702
14738
  function sampleDir(existing, reposBase, workspace) {
14703
- if (existing.length > 0) return join28(dirname17(existing[0]), SAMPLE_REPO);
14704
- return join28(reposBase, workspace, SAMPLE_REPO);
14739
+ if (existing.length > 0) return join29(dirname17(existing[0]), SAMPLE_REPO);
14740
+ return join29(reposBase, workspace, SAMPLE_REPO);
14705
14741
  }
14706
14742
  var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
14707
14743
  function sampleDescription() {
@@ -14793,8 +14829,8 @@ function schedulerInstalled(plan, exists, crontab) {
14793
14829
  }
14794
14830
 
14795
14831
  // src/worktree-sweep.ts
14796
- import { existsSync as existsSync27, readFileSync as readFileSync23, readdirSync as readdirSync8 } from "fs";
14797
- import { basename as basename10, dirname as dirname18, join as join29, resolve as resolve4 } from "path";
14832
+ import { existsSync as existsSync27, readFileSync as readFileSync24, readdirSync as readdirSync8 } from "fs";
14833
+ import { basename as basename10, dirname as dirname18, join as join30, resolve as resolve4 } from "path";
14798
14834
  function planWorktreeSweep(target, tickets, contract) {
14799
14835
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
14800
14836
  const verified = contract.statuses.verified;
@@ -14861,7 +14897,7 @@ function siblingWorktreeDirs(target) {
14861
14897
  for (const name of names) {
14862
14898
  if (!name.startsWith(stem)) continue;
14863
14899
  const m = DIR_NUMBER_SUFFIXED.exec(name);
14864
- const path = join29(parent, name);
14900
+ const path = join30(parent, name);
14865
14901
  if (m && !registered.has(resolve4(path))) out.push({ path, number: m[1] });
14866
14902
  }
14867
14903
  return out;
@@ -14964,7 +15000,7 @@ function isHandBackMerge(path, base) {
14964
15000
  if (!msgPath) return false;
14965
15001
  let first;
14966
15002
  try {
14967
- first = readFileSync23(resolve4(path, msgPath), "utf8").split("\n")[0] ?? "";
15003
+ first = readFileSync24(resolve4(path, msgPath), "utf8").split("\n")[0] ?? "";
14968
15004
  } catch {
14969
15005
  return false;
14970
15006
  }
@@ -15070,13 +15106,13 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
15070
15106
 
15071
15107
  // src/stream-sweep.ts
15072
15108
  import { existsSync as existsSync28, readdirSync as readdirSync9, rmSync as rmSync13, statSync as statSync8 } from "fs";
15073
- import { join as join30 } from "path";
15109
+ import { join as join31 } from "path";
15074
15110
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
15075
15111
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
15076
- const dir = join30(stateDir, "streams");
15112
+ const dir = join31(stateDir, "streams");
15077
15113
  if (!existsSync28(dir)) return [];
15078
15114
  const cutoff = now - retentionDays * DAY_MS2;
15079
- return readdirSync9(dir).map((name) => join30(dir, name)).filter((path) => {
15115
+ return readdirSync9(dir).map((name) => join31(dir, name)).filter((path) => {
15080
15116
  try {
15081
15117
  return statSync8(path).mtimeMs < cutoff;
15082
15118
  } catch {
@@ -15104,7 +15140,7 @@ function applyStreamSweep(paths, dryRun, log) {
15104
15140
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
15105
15141
  if (!existsSync28(stateDir)) return [];
15106
15142
  const cutoff = now - retentionDays * DAY_MS2;
15107
- return readdirSync9(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join30(stateDir, name)).filter((path) => {
15143
+ return readdirSync9(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join31(stateDir, name)).filter((path) => {
15108
15144
  try {
15109
15145
  return statSync8(path).mtimeMs < cutoff;
15110
15146
  } catch {
@@ -15114,18 +15150,18 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
15114
15150
  }
15115
15151
 
15116
15152
  // src/cli.ts
15117
- import { realpathSync as realpathSync3, readFileSync as readFileSync29, existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync23, rmSync as rmSync15, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync10, readdirSync as readdirSync14 } from "fs";
15153
+ import { realpathSync as realpathSync3, readFileSync as readFileSync30, existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync23, rmSync as rmSync15, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync10, readdirSync as readdirSync14 } from "fs";
15118
15154
  import { createInterface } from "readline/promises";
15119
15155
  import { dirname as dirOf, resolve as resolvePath } from "path";
15120
15156
 
15121
15157
  // src/ssh-keys.ts
15122
15158
  import { execFileSync as execFileSync6 } from "child_process";
15123
- import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync24, chmodSync } from "fs";
15124
- import { dirname as dirname19, join as join31 } from "path";
15159
+ import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync25, chmodSync } from "fs";
15160
+ import { dirname as dirname19, join as join32 } from "path";
15125
15161
  var SshKeyError = class extends Error {
15126
15162
  };
15127
15163
  function shipSshKeyPathFor(stateDir) {
15128
- return join31(stateDir, "ssh", "id_ed25519");
15164
+ return join32(stateDir, "ssh", "id_ed25519");
15129
15165
  }
15130
15166
  function ensureShipSshKeypair(stateDir) {
15131
15167
  const privateKeyPath = shipSshKeyPathFor(stateDir);
@@ -15154,7 +15190,7 @@ function ensureShipSshKeypair(stateDir) {
15154
15190
  if (!existsSync29(publicKeyPath)) {
15155
15191
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
15156
15192
  }
15157
- const publicKey = readFileSync24(publicKeyPath, "utf8").trim();
15193
+ const publicKey = readFileSync25(publicKeyPath, "utf8").trim();
15158
15194
  return { privateKeyPath, publicKeyPath, publicKey };
15159
15195
  }
15160
15196
  function sshKeygenAvailable() {
@@ -15370,9 +15406,9 @@ function syncAllPassengerCheckouts(cfg) {
15370
15406
 
15371
15407
  // src/tunnel.ts
15372
15408
  import { spawn as spawn5 } from "child_process";
15373
- import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync20, unlinkSync as unlinkSync3, readdirSync as readdirSync10 } from "fs";
15409
+ import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync26, writeFileSync as writeFileSync20, unlinkSync as unlinkSync3, readdirSync as readdirSync10 } from "fs";
15374
15410
  import { randomInt } from "crypto";
15375
- import { join as join32 } from "path";
15411
+ import { join as join33 } from "path";
15376
15412
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
15377
15413
  var defaultSpawn = (cmd, args) => spawn5(cmd, args, DEFAULT_SPAWN_OPTIONS);
15378
15414
  var SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -15439,20 +15475,20 @@ function startTunnel(opts) {
15439
15475
  };
15440
15476
  }
15441
15477
  function tunnelStatePath(stateDir, workspaceId) {
15442
- return join32(stateDir, "passengers", `${workspaceId}.json`);
15478
+ return join33(stateDir, "passengers", `${workspaceId}.json`);
15443
15479
  }
15444
15480
  function readPersistedTunnel(stateDir, workspaceId) {
15445
15481
  const path = tunnelStatePath(stateDir, workspaceId);
15446
15482
  if (!existsSync30(path)) return void 0;
15447
15483
  try {
15448
- return JSON.parse(readFileSync25(path, "utf8"));
15484
+ return JSON.parse(readFileSync26(path, "utf8"));
15449
15485
  } catch {
15450
15486
  return void 0;
15451
15487
  }
15452
15488
  }
15453
15489
  function writePersistedTunnel(stateDir, state) {
15454
15490
  const path = tunnelStatePath(stateDir, state.workspaceId);
15455
- mkdirSync19(join32(stateDir, "passengers"), { recursive: true });
15491
+ mkdirSync19(join33(stateDir, "passengers"), { recursive: true });
15456
15492
  writeFileSync20(path, `${JSON.stringify(state, null, 2)}
15457
15493
  `);
15458
15494
  }
@@ -15550,7 +15586,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
15550
15586
  }
15551
15587
  }
15552
15588
  function readAllPersistedWorkspaceIds(stateDir) {
15553
- const dir = join32(stateDir, "passengers");
15589
+ const dir = join33(stateDir, "passengers");
15554
15590
  if (!existsSync30(dir)) return [];
15555
15591
  return readdirSync10(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
15556
15592
  }
@@ -15672,8 +15708,8 @@ async function unregisterEndpoint(workspaceId, info, d) {
15672
15708
  import { createServer as createHttpServer } from "http";
15673
15709
  import { spawn as spawn6 } from "child_process";
15674
15710
  import { randomBytes } from "crypto";
15675
- import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as readFileSync26, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readdirSync as readdirSync11 } from "fs";
15676
- import { join as join33 } from "path";
15711
+ import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as readFileSync27, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readdirSync as readdirSync11 } from "fs";
15712
+ import { join as join34 } from "path";
15677
15713
 
15678
15714
  // src/secret-compare.ts
15679
15715
  import { timingSafeEqual } from "crypto";
@@ -15762,20 +15798,20 @@ var defaultSpawn2 = (cmd, args, env) => {
15762
15798
  return child;
15763
15799
  };
15764
15800
  function syncDaemonStatePath(stateDir, workspaceId) {
15765
- return join33(stateDir, "passengers", `${workspaceId}-sync.json`);
15801
+ return join34(stateDir, "passengers", `${workspaceId}-sync.json`);
15766
15802
  }
15767
15803
  function readPersistedSyncDaemon(stateDir, workspaceId) {
15768
15804
  const path = syncDaemonStatePath(stateDir, workspaceId);
15769
15805
  if (!existsSync31(path)) return void 0;
15770
15806
  try {
15771
- return JSON.parse(readFileSync26(path, "utf8"));
15807
+ return JSON.parse(readFileSync27(path, "utf8"));
15772
15808
  } catch {
15773
15809
  return void 0;
15774
15810
  }
15775
15811
  }
15776
15812
  function writePersistedSyncDaemon(stateDir, state) {
15777
15813
  const path = syncDaemonStatePath(stateDir, state.workspaceId);
15778
- mkdirSync20(join33(stateDir, "passengers"), { recursive: true });
15814
+ mkdirSync20(join34(stateDir, "passengers"), { recursive: true });
15779
15815
  writeFileSync21(path, `${JSON.stringify(state, null, 2)}
15780
15816
  `);
15781
15817
  }
@@ -15796,7 +15832,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
15796
15832
  }
15797
15833
  }
15798
15834
  function readAllPersistedSyncWorkspaceIds(stateDir) {
15799
- const dir = join33(stateDir, "passengers");
15835
+ const dir = join34(stateDir, "passengers");
15800
15836
  if (!existsSync31(dir)) return [];
15801
15837
  const suffix = "-sync.json";
15802
15838
  return readdirSync11(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
@@ -15885,12 +15921,12 @@ function describeCause(err) {
15885
15921
  }
15886
15922
 
15887
15923
  // src/daemon.ts
15888
- import { existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as readFileSync28, readdirSync as readdirSync13, rmSync as rmSync14, statSync as statSync9, writeFileSync as writeFileSync22 } from "fs";
15889
- import { join as join35 } from "path";
15924
+ import { existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as readFileSync29, readdirSync as readdirSync13, rmSync as rmSync14, statSync as statSync9, writeFileSync as writeFileSync22 } from "fs";
15925
+ import { join as join36 } from "path";
15890
15926
 
15891
15927
  // src/interrupted.ts
15892
- import { existsSync as existsSync32, readdirSync as readdirSync12, readFileSync as readFileSync27 } from "fs";
15893
- import { join as join34 } from "path";
15928
+ import { existsSync as existsSync32, readdirSync as readdirSync12, readFileSync as readFileSync28 } from "fs";
15929
+ import { join as join35 } from "path";
15894
15930
  var INTERRUPTED_MARKER = calloutHeader("NOTE", "Interrupted");
15895
15931
  function interruptedComment(b) {
15896
15932
  const tree = b.dirty === null ? "The worktree could not be checked." : b.dirty ? "The worktree has uncommitted changes \u2014 they are this seat's own, from that run." : "The worktree is clean.";
@@ -15911,19 +15947,19 @@ function runFinished(streamPath) {
15911
15947
  const path = `${streamPath}.meta.json`;
15912
15948
  if (!existsSync32(path)) return false;
15913
15949
  try {
15914
- return JSON.parse(readFileSync27(path, "utf8")).running !== true;
15950
+ return JSON.parse(readFileSync28(path, "utf8")).running !== true;
15915
15951
  } catch {
15916
15952
  return true;
15917
15953
  }
15918
15954
  }
15919
15955
  function orphanedStreams(o) {
15920
- const dir = join34(o.stateDir, "streams");
15956
+ const dir = join35(o.stateDir, "streams");
15921
15957
  if (!existsSync32(dir)) return [];
15922
15958
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
15923
15959
  const out = [];
15924
15960
  for (const name of readdirSync12(dir)) {
15925
15961
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
15926
- const streamPath = join34(dir, name);
15962
+ const streamPath = join35(dir, name);
15927
15963
  if (runFinished(streamPath)) continue;
15928
15964
  const sidecar = readRunSidecar(streamPath);
15929
15965
  if (!sidecar || sidecar.route !== o.routeLabel) continue;
@@ -16043,7 +16079,7 @@ function latestMtime(path) {
16043
16079
  if (!stat.isDirectory()) return 0;
16044
16080
  let latest = stat.mtimeMs;
16045
16081
  for (const entry of readdirSync13(path, { withFileTypes: true })) {
16046
- latest = Math.max(latest, latestMtime(join35(path, entry.name)));
16082
+ latest = Math.max(latest, latestMtime(join36(path, entry.name)));
16047
16083
  }
16048
16084
  return latest;
16049
16085
  }
@@ -16055,10 +16091,10 @@ function makeStaleChecker(paths, opts = {}) {
16055
16091
  return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync33(opts.reloadSentinel);
16056
16092
  }
16057
16093
  function reloadSentinelFor(stateDir) {
16058
- return join35(stateDir, "reload-requested");
16094
+ return join36(stateDir, "reload-requested");
16059
16095
  }
16060
16096
  function daemonStampFor(stateDir) {
16061
- return join35(stateDir, "daemon.started");
16097
+ return join36(stateDir, "daemon.started");
16062
16098
  }
16063
16099
  function noteDaemonStarted(stateDir, now = Date.now()) {
16064
16100
  mkdirSync21(stateDir, { recursive: true });
@@ -16068,7 +16104,7 @@ function noteDaemonStarted(stateDir, now = Date.now()) {
16068
16104
  }
16069
16105
  function readDaemonStarted(stateDir) {
16070
16106
  try {
16071
- const v = JSON.parse(readFileSync28(daemonStampFor(stateDir), "utf8")).startedAt;
16107
+ const v = JSON.parse(readFileSync29(daemonStampFor(stateDir), "utf8")).startedAt;
16072
16108
  return typeof v === "number" ? v : null;
16073
16109
  } catch {
16074
16110
  return null;
@@ -17089,7 +17125,7 @@ if (command === "passenger-sync-daemon") {
17089
17125
  }
17090
17126
  if (host === "linux" && hasSystemd) {
17091
17127
  const label = labelFor(CREW_HOME, "run");
17092
- const staleTimer = join36(homedir9(), ".config", "systemd", "user", `${label}.timer`);
17128
+ const staleTimer = join37(homedir10(), ".config", "systemd", "user", `${label}.timer`);
17093
17129
  if (existsSync34(staleTimer)) {
17094
17130
  process.stdout.write(
17095
17131
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
@@ -17152,7 +17188,7 @@ if (command === "passenger-sync-daemon") {
17152
17188
  for (const a of plan.actions) {
17153
17189
  if (a.kind === "enable") {
17154
17190
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
17155
- writeFileSync23(cfg.configFile, planEnable(readFileSync29(cfg.configFile, "utf8"), a.route));
17191
+ writeFileSync23(cfg.configFile, planEnable(readFileSync30(cfg.configFile, "utf8"), a.route));
17156
17192
  enabledSound.add(a.route);
17157
17193
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
17158
17194
  } else {
@@ -18174,7 +18210,7 @@ ${changed2.map((c) => ` - ${c}
18174
18210
  process.exit(1);
18175
18211
  }
18176
18212
  requireArmed("run the daemon loop");
18177
- const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join36(CREW_HOME, "src"), join36(CREW_HOME, "dist"), join36(CREW_HOME, "bin")];
18213
+ const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join37(CREW_HOME, "src"), join37(CREW_HOME, "dist"), join37(CREW_HOME, "bin")];
18178
18214
  const configPaths = daemonConfigPaths(cfg);
18179
18215
  watchPaths.push(...configPaths);
18180
18216
  noteDaemonStarted(cfg.ship.stateDir);
@@ -18393,7 +18429,7 @@ ${changed2.map((c) => ` - ${c}
18393
18429
  `);
18394
18430
  if (!dryRun) {
18395
18431
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
18396
- const raw = existsSync34(resolvedPath) ? JSON.parse(readFileSync29(resolvedPath, "utf8")) : {};
18432
+ const raw = existsSync34(resolvedPath) ? JSON.parse(readFileSync30(resolvedPath, "utf8")) : {};
18397
18433
  raw.agentSkills = result.agentSkills;
18398
18434
  mkdirSync22(dirOf(resolvedPath), { recursive: true });
18399
18435
  writeFileSync23(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -18419,7 +18455,7 @@ ${changed2.map((c) => ` - ${c}
18419
18455
  throw e;
18420
18456
  }
18421
18457
  const oldName = cfg.ship.name;
18422
- writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync29(cfg.configFile, "utf8"), newName));
18458
+ writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync30(cfg.configFile, "utf8"), newName));
18423
18459
  cfg.ship.name = newName;
18424
18460
  let renameMachineId;
18425
18461
  try {
@@ -18466,7 +18502,7 @@ ${changed2.map((c) => ` - ${c}
18466
18502
  process.exit(2);
18467
18503
  };
18468
18504
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks") usage2();
18469
- const configText = readFileSync29(cfg.configFile, "utf8");
18505
+ const configText = readFileSync30(cfg.configFile, "utf8");
18470
18506
  if (sub === "hooks") {
18471
18507
  try {
18472
18508
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
@@ -18480,7 +18516,7 @@ ${changed2.map((c) => ` - ${c}
18480
18516
  }
18481
18517
  }, read: (r) => {
18482
18518
  try {
18483
- return readFileSync29(resolvePath(dir, r), "utf8");
18519
+ return readFileSync30(resolvePath(dir, r), "utf8");
18484
18520
  } catch {
18485
18521
  return void 0;
18486
18522
  }
@@ -18590,7 +18626,7 @@ ${changed2.map((c) => ` - ${c}
18590
18626
  if (sub === "list" && !routeName) usage2();
18591
18627
  if (sub === "add") {
18592
18628
  try {
18593
- const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync34(resolvePath(p.replace(/^~(?=\/|$)/, homedir9()))));
18629
+ const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync34(resolvePath(p.replace(/^~(?=\/|$)/, homedir10()))));
18594
18630
  routeName = a.route;
18595
18631
  addPath = a.path;
18596
18632
  addName = a.name;
@@ -18621,7 +18657,7 @@ ${changed2.map((c) => ` - ${c}
18621
18657
  let resolvedRemotes;
18622
18658
  if (existsSync34(resolvedFile)) {
18623
18659
  try {
18624
- resolvedRemotes = JSON.parse(readFileSync29(resolvedFile, "utf8"));
18660
+ resolvedRemotes = JSON.parse(readFileSync30(resolvedFile, "utf8"));
18625
18661
  } catch {
18626
18662
  }
18627
18663
  }
@@ -18642,7 +18678,7 @@ ${changed2.map((c) => ` - ${c}
18642
18678
  isCheckout,
18643
18679
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
18644
18680
  try {
18645
- return Object.keys(parseRepoConfig(readFileSync29(resolvePath(d, f), "utf8"), f).hooks).length > 0;
18681
+ return Object.keys(parseRepoConfig(readFileSync30(resolvePath(d, f), "utf8"), f).hooks).length > 0;
18646
18682
  } catch {
18647
18683
  return false;
18648
18684
  }
@@ -18697,7 +18733,7 @@ ${changed2.map((c) => ` - ${c}
18697
18733
  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}`);
18698
18734
  break;
18699
18735
  }
18700
- const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir9()));
18736
+ const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir10()));
18701
18737
  const top = existsSync34(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
18702
18738
  if (!top) throw new ReposError(`${target} is not a git checkout`);
18703
18739
  const origin = originOf(top);
@@ -18751,7 +18787,7 @@ ${changed2.map((c) => ` - ${c}
18751
18787
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
18752
18788
  `);
18753
18789
  if (existsSync34(resolvedFile)) {
18754
- const raw = JSON.parse(readFileSync29(resolvedFile, "utf8"));
18790
+ const raw = JSON.parse(readFileSync30(resolvedFile, "utf8"));
18755
18791
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
18756
18792
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
18757
18793
  writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -19109,7 +19145,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19109
19145
  process.exit(2);
19110
19146
  }
19111
19147
  if (found.shipNameAdopted && !dryRun) {
19112
- writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync29(cfg.configFile, "utf8"), found.shipNameAdopted));
19148
+ writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync30(cfg.configFile, "utf8"), found.shipNameAdopted));
19113
19149
  cfg.ship.name = found.shipNameAdopted;
19114
19150
  }
19115
19151
  if (found.shipRow) {
@@ -19155,7 +19191,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
19155
19191
  const hadPreviousFile = existsSync34(resolvedPath);
19156
19192
  if (hadPreviousFile) {
19157
19193
  try {
19158
- previous = JSON.parse(readFileSync29(resolvedPath, "utf8"));
19194
+ previous = JSON.parse(readFileSync30(resolvedPath, "utf8"));
19159
19195
  } catch {
19160
19196
  }
19161
19197
  }
@@ -20013,7 +20049,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20013
20049
  };
20014
20050
  const droppedRoutes = [];
20015
20051
  try {
20016
- const rawCfg = parseYaml2(readFileSync29(cfg.configFile, "utf8"));
20052
+ const rawCfg = parseYaml2(readFileSync30(cfg.configFile, "utf8"));
20017
20053
  for (const r of rawCfg?.routes ?? []) {
20018
20054
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
20019
20055
  try {
@@ -20107,7 +20143,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20107
20143
  }
20108
20144
  case "log": {
20109
20145
  try {
20110
- const text = readFileSync29(cfg.ship.logFile, "utf8").split("\n");
20146
+ const text = readFileSync30(cfg.ship.logFile, "utf8").split("\n");
20111
20147
  process.stdout.write(`${text.slice(-(Number(value("tail")) || 40)).join("\n")}
20112
20148
  `);
20113
20149
  } catch {
@@ -20130,7 +20166,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20130
20166
  };
20131
20167
  if (!route.resolved) fail(`route ${route.route} is not connected \u2014 run \`crew connect\` first`);
20132
20168
  const tracker = new Tracker(route, cfg.ship);
20133
- const configText = readFileSync29(cfg.configFile, "utf8");
20169
+ const configText = readFileSync30(cfg.configFile, "utf8");
20134
20170
  const cfgBase = dirname20(cfg.configFile);
20135
20171
  const configured = configuredRepos(configText, route.route, cfgBase);
20136
20172
  const dir = configured.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured.map((r) => r.dir), route.reposBasePath, route.route.split("/")[0]);
@@ -20168,19 +20204,19 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20168
20204
  if (row) {
20169
20205
  await tracker.deleteRepoRow(row.id);
20170
20206
  if (existsSync34(resolvedFile)) {
20171
- const raw = JSON.parse(readFileSync29(resolvedFile, "utf8"));
20207
+ const raw = JSON.parse(readFileSync30(resolvedFile, "utf8"));
20172
20208
  if (raw.repoNames) delete raw.repoNames[row.id];
20173
20209
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
20174
20210
  writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
20175
20211
  `);
20176
20212
  }
20177
20213
  }
20178
- if (existsSync34(join36(dir, ".git")) && basename11(dir) === SAMPLE_REPO) rmSync15(dir, { recursive: true, force: true });
20214
+ if (existsSync34(join37(dir, ".git")) && basename11(dir) === SAMPLE_REPO) rmSync15(dir, { recursive: true, force: true });
20179
20215
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
20180
20216
  break;
20181
20217
  }
20182
20218
  const projectId = route.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
20183
- const fresh = !existsSync34(join36(dir, ".git"));
20219
+ const fresh = !existsSync34(join37(dir, ".git"));
20184
20220
  if (existsSync34(dir) && fresh && readdirSync14(dir).length > 0) fail(`${dir} exists and is not a git checkout \u2014 move it aside`);
20185
20221
  if (dryRun) {
20186
20222
  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`);
@@ -20188,8 +20224,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20188
20224
  }
20189
20225
  if (fresh) {
20190
20226
  mkdirSync22(dir, { recursive: true });
20191
- writeFileSync23(join36(dir, "README.md"), SAMPLE_README);
20192
- writeFileSync23(join36(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
20227
+ writeFileSync23(join37(dir, "README.md"), SAMPLE_README);
20228
+ writeFileSync23(join37(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
20193
20229
  const g = (...a) => {
20194
20230
  const r = spawnSync6("git", a, { cwd: dir, encoding: "utf8" });
20195
20231
  if (r.status !== 0) fail(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
@@ -20208,7 +20244,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
20208
20244
  });
20209
20245
  repoId = (await tracker.createRepoRow(payload)).id;
20210
20246
  if (existsSync34(resolvedFile)) {
20211
- const raw = JSON.parse(readFileSync29(resolvedFile, "utf8"));
20247
+ const raw = JSON.parse(readFileSync30(resolvedFile, "utf8"));
20212
20248
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
20213
20249
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
20214
20250
  writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -20574,7 +20610,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
20574
20610
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run");
20575
20611
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
20576
20612
  try {
20577
- return readFileSync29(p, "utf8");
20613
+ return readFileSync30(p, "utf8");
20578
20614
  } catch {
20579
20615
  return null;
20580
20616
  }
@@ -20962,6 +20998,10 @@ ${foreign.join("\n")}
20962
20998
  }
20963
20999
  for (const l of doctorSelftestLines(readSelftest(cfg.ship.stateDir), shipToolchains(cfg.ship).map((t) => t.name))) {
20964
21000
  process.stdout.write(`${l}
21001
+ `);
21002
+ }
21003
+ for (const t of shipToolchains(cfg.ship).filter((t2) => t2.harness === "claude")) {
21004
+ process.stdout.write(`auth: ${t.name}: ${describeClaudeAuth(t)}
20965
21005
  `);
20966
21006
  }
20967
21007
  const interactive = !!(process.stdin.isTTY && process.stdout.isTTY);