@tablation/crew 0.50.1 → 0.50.2

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 +122 -93
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,5 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ // src/doctor-repo.ts
4
+ import { existsSync } from "fs";
5
+ function doctorBaseReport(r, base, health) {
6
+ const pad = " ";
7
+ if (!existsSync(r.dir)) {
8
+ return {
9
+ text: `${pad}- ${r.dir} is missing on disk \u2014 no .crew.yaml read, settings above are defaults
10
+ `,
11
+ failed: true,
12
+ skipRest: true
13
+ };
14
+ }
15
+ try {
16
+ const bh = health(r);
17
+ if (bh.action === "not-applicable") return { text: "", failed: false, skipRest: false };
18
+ return {
19
+ text: `${pad}base ${base}: ${bh.unsafe ? "ERROR \u2014 " : ""}${bh.detail}
20
+ `,
21
+ failed: bh.unsafe,
22
+ skipRest: false
23
+ };
24
+ } catch (e) {
25
+ return {
26
+ text: `${pad}base ${base}: could not check \u2014 ${e instanceof Error ? e.message : String(e)}
27
+ `,
28
+ failed: true,
29
+ skipRest: false
30
+ };
31
+ }
32
+ }
33
+
3
34
  // src/cli.ts
4
35
  import { resolve as resolve4, dirname as dirname12, join as join25, basename as basename9 } from "path";
5
36
  import { fileURLToPath } from "url";
@@ -62,7 +93,7 @@ function pickWorktreeRoute(ticketNumber, candidates) {
62
93
  }
63
94
 
64
95
  // src/config.ts
65
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
96
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
66
97
  import { resolve, dirname as dirname3, isAbsolute, join as join7, basename as basename3 } from "path";
67
98
  import { homedir as homedir5, tmpdir } from "os";
68
99
  import { parse as parse2 } from "yaml";
@@ -71,7 +102,7 @@ import { parse as parse2 } from "yaml";
71
102
  import {
72
103
  appendFileSync,
73
104
  mkdirSync,
74
- existsSync,
105
+ existsSync as existsSync2,
75
106
  statSync,
76
107
  renameSync,
77
108
  openSync,
@@ -90,7 +121,7 @@ var ROTATED_EVENTS_RE = /^events\.\d{8}T\d{6}Z(?:-\d+)?\.jsonl$/;
90
121
  var ROTATE_LOCK_STALE_MS = 6e4;
91
122
  function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
92
123
  try {
93
- if (!existsSync(file) || statSync(file).size <= maxBytes) return void 0;
124
+ if (!existsSync2(file) || statSync(file).size <= maxBytes) return void 0;
94
125
  } catch {
95
126
  return void 0;
96
127
  }
@@ -111,7 +142,7 @@ function rotateEventsFile(file, maxBytes, now = /* @__PURE__ */ new Date()) {
111
142
  if (statSync(file).size <= maxBytes) return void 0;
112
143
  const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
113
144
  let dest = join(dirname(file), `events.${stamp}.jsonl`);
114
- for (let n = 1; existsSync(dest); n++) dest = join(dirname(file), `events.${stamp}-${n}.jsonl`);
145
+ for (let n = 1; existsSync2(dest); n++) dest = join(dirname(file), `events.${stamp}-${n}.jsonl`);
115
146
  renameSync(file, dest);
116
147
  return dest;
117
148
  } finally {
@@ -213,7 +244,7 @@ var Emitter = class _Emitter {
213
244
  var eventFileFor = (stateDir) => join(stateDir, "events.jsonl");
214
245
 
215
246
  // src/pair-install.ts
216
- import { existsSync as existsSync4, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "fs";
247
+ import { existsSync as existsSync5, rmSync as rmSync4, writeFileSync as writeFileSync3 } from "fs";
217
248
  import { spawnSync as spawnSync3 } from "child_process";
218
249
  import { homedir as homedir4 } from "os";
219
250
  import { join as join5 } from "path";
@@ -342,7 +373,7 @@ function removeClaudeMcp(o) {
342
373
 
343
374
  // src/launcher.ts
344
375
  import { basename, join as join3 } from "path";
345
- import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync } from "fs";
376
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync } from "fs";
346
377
  import { homedir as homedir2 } from "os";
347
378
  function isOriLauncher(spec) {
348
379
  return !!spec.launcher && basename(spec.launcher) === "ori";
@@ -405,7 +436,7 @@ function omlxModelPresent(model, dirs) {
405
436
  }
406
437
  function readOmlxSettings(home = homedir2()) {
407
438
  const p = join3(home, ".omlx", "settings.json");
408
- return existsSync2(p) ? parseOmlxSettings(readFileSync3(p, "utf8")) : void 0;
439
+ return existsSync3(p) ? parseOmlxSettings(readFileSync3(p, "utf8")) : void 0;
409
440
  }
410
441
 
411
442
  // src/capability.ts
@@ -542,7 +573,7 @@ import { join as join4 } from "path";
542
573
  import { rmSync as rmSync3, writeFileSync as writeFileSync2 } from "fs";
543
574
 
544
575
  // src/pair-files.ts
545
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync, rmSync as rmSync2 } from "fs";
576
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync, rmSync as rmSync2 } from "fs";
546
577
  import { dirname as dirname2 } from "path";
547
578
  var PAIR_BLOCK_START = "<!-- crew:pair start -->";
548
579
  var PAIR_BLOCK_END = "<!-- crew:pair end -->";
@@ -583,7 +614,7 @@ function removePairBlock(text) {
583
614
  return text.replace(new RegExp(`\\n*${BLOCK_RE.source}`), "\n").replace(/^\n+/, "").replace(/\n{3,}/g, "\n\n");
584
615
  }
585
616
  function readIfExists(path) {
586
- return existsSync3(path) ? readFileSync4(path, "utf8") : void 0;
617
+ return existsSync4(path) ? readFileSync4(path, "utf8") : void 0;
587
618
  }
588
619
  function writeFileMkdir(path, text) {
589
620
  mkdirSync2(dirname2(path), { recursive: true });
@@ -980,7 +1011,7 @@ function vscodeUserDirs(o) {
980
1011
  const platform2 = o.platform ?? process.platform;
981
1012
  const root = platform2 === "darwin" ? join5(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join5(home, "AppData", "Roaming") : join5(home, ".config");
982
1013
  const all = VSCODE_FLAVOURS.map((f) => join5(root, f, "User"));
983
- const present = all.filter((d) => existsSync4(d));
1014
+ const present = all.filter((d) => existsSync5(d));
984
1015
  return present.length > 0 ? present : [all[0]];
985
1016
  }
986
1017
  var COPILOT_FRONTMATTER = '---\napplyTo: "**"\n---\n\n';
@@ -1372,7 +1403,7 @@ function explain(ship, required) {
1372
1403
  }
1373
1404
 
1374
1405
  // src/repo-config.ts
1375
- import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
1406
+ import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
1376
1407
  import { join as join6, basename as basename2 } from "path";
1377
1408
  import { parse } from "yaml";
1378
1409
  var SPEC_VERSION = 1;
@@ -1456,7 +1487,7 @@ var PROVIDERS = ["github", "buildkite", "other", "none"];
1456
1487
  function findRepoConfig(dir) {
1457
1488
  for (const name of REPO_CONFIG_FILENAMES) {
1458
1489
  const p = join6(dir, name);
1459
- if (existsSync5(p)) return p;
1490
+ if (existsSync6(p)) return p;
1460
1491
  }
1461
1492
  return null;
1462
1493
  }
@@ -2181,7 +2212,7 @@ function configSearchPath(crewHome) {
2181
2212
  }
2182
2213
  function findConfigFile(crewHome) {
2183
2214
  const candidates = configSearchPath(crewHome);
2184
- return candidates.find((c) => existsSync6(c)) ?? candidates[candidates.length - 1];
2215
+ return candidates.find((c) => existsSync7(c)) ?? candidates[candidates.length - 1];
2185
2216
  }
2186
2217
  var defaultConfigPath = () => join7(process.env.XDG_CONFIG_HOME ?? join7(homedir5(), ".config"), "crew", "crew.yaml");
2187
2218
  function routeSlug(route) {
@@ -2204,7 +2235,7 @@ function splitRoute(route) {
2204
2235
  }
2205
2236
  function readResolvedFile(stateDir, route) {
2206
2237
  const path = resolvedPathFor(stateDir, route);
2207
- if (!existsSync6(path)) return void 0;
2238
+ if (!existsSync7(path)) return void 0;
2208
2239
  try {
2209
2240
  return JSON.parse(readFileSync6(path, "utf8"));
2210
2241
  } catch (e) {
@@ -2263,7 +2294,7 @@ function parseResolved(raw, m, where) {
2263
2294
  }
2264
2295
  function loadConfig(crewHome, configFile, opts = {}) {
2265
2296
  const file = configFile ?? findConfigFile(crewHome);
2266
- if (opts.text === void 0 && !existsSync6(file)) {
2297
+ if (opts.text === void 0 && !existsSync7(file)) {
2267
2298
  throw new ConfigError(
2268
2299
  `no config found. Looked in:
2269
2300
  ` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
@@ -2573,7 +2604,7 @@ function repoIdForName(r, name) {
2573
2604
  }
2574
2605
  function resolveApiKey(r) {
2575
2606
  if (r.apiKey) return r.apiKey;
2576
- if (r.apiKeyFile && r.apiKeyVar && existsSync6(r.apiKeyFile)) {
2607
+ if (r.apiKeyFile && r.apiKeyVar && existsSync7(r.apiKeyFile)) {
2577
2608
  for (const line of readFileSync6(r.apiKeyFile, "utf8").split("\n")) {
2578
2609
  const m = line.match(new RegExp(`^${r.apiKeyVar}=(.*)$`));
2579
2610
  if (m) return (m[1] ?? "").trim().replace(/^["']|["']$/g, "");
@@ -2793,7 +2824,7 @@ function rosterMarkdown(roster, meId) {
2793
2824
  }
2794
2825
 
2795
2826
  // src/state.ts
2796
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync3, rmSync as rmSync5, statSync as statSync2 } from "fs";
2827
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync3, rmSync as rmSync5, statSync as statSync2 } from "fs";
2797
2828
  import { join as join8 } from "path";
2798
2829
  var EPOCH = "1970-01-01T00:00:00.000Z";
2799
2830
  var State = class _State {
@@ -2839,10 +2870,10 @@ var State = class _State {
2839
2870
  }
2840
2871
  /** Whole-crew pause, and the per-role sentinels one level down. */
2841
2872
  isPaused() {
2842
- return existsSync7(this.path(".crew-paused"));
2873
+ return existsSync8(this.path(".crew-paused"));
2843
2874
  }
2844
2875
  isRolePaused(role) {
2845
- return existsSync7(this.path(`.role-paused-${role}`));
2876
+ return existsSync8(this.path(`.role-paused-${role}`));
2846
2877
  }
2847
2878
  pausedRoles(roles) {
2848
2879
  return new Set(roles.filter((r) => this.isRolePaused(r)));
@@ -2859,7 +2890,7 @@ var State = class _State {
2859
2890
  * while agents keep working.
2860
2891
  */
2861
2892
  isReleasePaused() {
2862
- return existsSync7(this.path(".release-paused"));
2893
+ return existsSync8(this.path(".release-paused"));
2863
2894
  }
2864
2895
  setReleasePaused(paused) {
2865
2896
  if (paused) writeFileSync4(this.path(".release-paused"), "");
@@ -3507,7 +3538,7 @@ function describeRelease(o, route) {
3507
3538
  // src/install.ts
3508
3539
  import { dirname as dirname4, join as join9, extname } from "path";
3509
3540
  import { homedir as homedir6 } from "os";
3510
- import { accessSync, constants, existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3, writeFileSync as writeFileSync5, unlinkSync, realpathSync } from "fs";
3541
+ import { accessSync, constants, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3, writeFileSync as writeFileSync5, unlinkSync, realpathSync } from "fs";
3511
3542
  import { spawnSync as spawnSync4 } from "child_process";
3512
3543
  import { createHash } from "crypto";
3513
3544
 
@@ -3882,7 +3913,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
3882
3913
  }
3883
3914
  }
3884
3915
  for (const path of plan.unitPaths) {
3885
- if (!existsSync8(path)) continue;
3916
+ if (!existsSync9(path)) continue;
3886
3917
  if (dryRun) {
3887
3918
  log.emit(`would remove ${path}`);
3888
3919
  continue;
@@ -3918,7 +3949,7 @@ function loadedCrewLabels() {
3918
3949
  return [...parseLaunchctlList(list.stdout ?? "").keys()].filter((l) => l.startsWith(CREW_LAUNCHD_PREFIX));
3919
3950
  }
3920
3951
  function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
3921
- const files = existsSync8(dir) ? readdirSync2(dir) : [];
3952
+ const files = existsSync9(dir) ? readdirSync2(dir) : [];
3922
3953
  const units = files.filter((f) => f.startsWith(CREW_LAUNCHD_PREFIX) && f.endsWith(".plist")).sort().map((f) => {
3923
3954
  const plistPath = join9(dir, f);
3924
3955
  let program = null;
@@ -3930,7 +3961,7 @@ function listLaunchdUnits(dir = launchAgentsDir(), loaded = []) {
3930
3961
  label: f.slice(0, -".plist".length),
3931
3962
  plistPath,
3932
3963
  program,
3933
- programExists: program !== null && existsSync8(program)
3964
+ programExists: program !== null && existsSync9(program)
3934
3965
  };
3935
3966
  });
3936
3967
  const seen = new Set(units.map((u) => u.label));
@@ -3949,9 +3980,9 @@ function removeLaunchdUnit(unit, dryRun, log) {
3949
3980
  log.emit(`would unload and remove ${unit.label}`);
3950
3981
  return;
3951
3982
  }
3952
- if (existsSync8(unit.plistPath)) spawnSync4("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
3983
+ if (existsSync9(unit.plistPath)) spawnSync4("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
3953
3984
  spawnSync4("launchctl", ["remove", unit.label], { encoding: "utf8" });
3954
- if (existsSync8(unit.plistPath)) unlinkSync(unit.plistPath);
3985
+ if (existsSync9(unit.plistPath)) unlinkSync(unit.plistPath);
3955
3986
  log.emit(`removed ${unit.label} (${unit.plistPath})`);
3956
3987
  }
3957
3988
  function clearForeignLaunchdUnits(crewHome, replace, dryRun, log, dir, loaded) {
@@ -4034,7 +4065,7 @@ import { statfsSync } from "fs";
4034
4065
  // src/git.ts
4035
4066
  import { execFileSync } from "child_process";
4036
4067
  import {
4037
- existsSync as existsSync9,
4068
+ existsSync as existsSync10,
4038
4069
  mkdirSync as mkdirSync5,
4039
4070
  openSync as openSync2,
4040
4071
  closeSync as closeSync2,
@@ -4434,7 +4465,7 @@ function sweepStaleReleaseRoots(tmp, now = Date.now(), maxAgeMs = 60 * 60 * 1e3)
4434
4465
  try {
4435
4466
  const st = statSync4(root);
4436
4467
  if (!st.isDirectory() || now - st.mtimeMs < maxAgeMs) continue;
4437
- if (existsSync9(join10(root, "checkout"))) continue;
4468
+ if (existsSync10(join10(root, "checkout"))) continue;
4438
4469
  rmSync6(root, { recursive: true, force: true });
4439
4470
  removed.push(name);
4440
4471
  } catch {
@@ -4477,7 +4508,7 @@ function originUrlOf(dir, remoteName) {
4477
4508
  }
4478
4509
  }
4479
4510
  function ensureRepoCheckout(dir, remote, opts = {}) {
4480
- if (existsSync9(`${dir}/.git`)) return false;
4511
+ if (existsSync10(`${dir}/.git`)) return false;
4481
4512
  if (!remote) {
4482
4513
  throw new GitError(
4483
4514
  `${dir} does not exist and this repo has no discovered remote to clone from \u2014 re-run \`crew connect\`, or check out the repository yourself and point \`repos:\` at it`
@@ -4540,10 +4571,10 @@ function acquireCheckoutLock(dir, timeoutMs = 3e4) {
4540
4571
  function syncPassengerCheckout(dir, githubRemote, gitRemoteName, base, clone = {}) {
4541
4572
  const release = acquireCheckoutLock(dir);
4542
4573
  try {
4543
- if (!existsSync9(`${dir}/.git`) && githubRemote) {
4574
+ if (!existsSync10(`${dir}/.git`) && githubRemote) {
4544
4575
  const url = cloneUrlFor(githubRemote, clone);
4545
4576
  const marker = cloneFailureMarker(dir);
4546
- if (existsSync9(marker) && readFileSync9(marker, "utf8").split("\n")[0] === url) {
4577
+ if (existsSync10(marker) && readFileSync9(marker, "utf8").split("\n")[0] === url) {
4547
4578
  return { action: "clone-failed", detail: readFileSync9(marker, "utf8").split("\n").slice(1).join("\n"), repeat: true };
4548
4579
  }
4549
4580
  try {
@@ -7126,7 +7157,7 @@ var alreadyReported = (ticket, g, events) => events.some((e) => e.record_id ===
7126
7157
 
7127
7158
  // src/agent.ts
7128
7159
  import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
7129
- import { readFileSync as readFileSync12, existsSync as existsSync12, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
7160
+ import { readFileSync as readFileSync12, existsSync as existsSync13, statSync as statSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, createWriteStream, openSync as openSync3, closeSync as closeSync3, readSync } from "fs";
7130
7161
  import { StringDecoder } from "string_decoder";
7131
7162
  import { finished } from "stream/promises";
7132
7163
  import { join as join14, dirname as dirname6 } from "path";
@@ -7544,7 +7575,7 @@ function expandSkillMarkers(text, lookup, opts = {}) {
7544
7575
  }
7545
7576
 
7546
7577
  // src/skills.ts
7547
- import { readFileSync as readFileSync11, readdirSync as readdirSync4, existsSync as existsSync11 } from "fs";
7578
+ import { readFileSync as readFileSync11, readdirSync as readdirSync4, existsSync as existsSync12 } from "fs";
7548
7579
  import { join as join13 } from "path";
7549
7580
  import { parse as parseYaml } from "yaml";
7550
7581
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError8, TablationApiError as TablationApiError2 } from "@tablation/client";
@@ -7561,7 +7592,7 @@ ${additions}`;
7561
7592
  }
7562
7593
 
7563
7594
  // src/agents.ts
7564
- import { readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
7595
+ import { readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
7565
7596
  import { join as join12 } from "path";
7566
7597
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError7, TablationApiError } from "@tablation/client";
7567
7598
  var PERSONA_NAME = {
@@ -7577,10 +7608,10 @@ var AgentsSyncError = class extends Error {
7577
7608
  };
7578
7609
  function personaDefaultPrompt(promptsDir, role) {
7579
7610
  const briefPath = join12(promptsDir, "personas", `lane-${role}.md`);
7580
- if (!existsSync10(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
7611
+ if (!existsSync11(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
7581
7612
  if (SELF_CONTAINED_ROLES.has(role)) return readFileSync10(briefPath, "utf8");
7582
7613
  const commonPath = join12(promptsDir, "personas", "common.md");
7583
- if (!existsSync10(commonPath)) return readFileSync10(briefPath, "utf8");
7614
+ if (!existsSync11(commonPath)) return readFileSync10(briefPath, "utf8");
7584
7615
  return `${readFileSync10(commonPath, "utf8")}${readFileSync10(briefPath, "utf8")}`;
7585
7616
  }
7586
7617
  async function resolveAgentId(route, role, opts) {
@@ -7969,7 +8000,7 @@ function parseSkillFile(path) {
7969
8000
  }
7970
8001
  function listLocalSkills(promptsDir) {
7971
8002
  const dir = join13(promptsDir, "skills");
7972
- if (!existsSync11(dir)) return [];
8003
+ if (!existsSync12(dir)) return [];
7973
8004
  return readdirSync4(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join13(dir, f)));
7974
8005
  }
7975
8006
  async function syncSkills(route, opts) {
@@ -8222,17 +8253,17 @@ var AgentSpawnError = class extends AgentError {
8222
8253
  };
8223
8254
  function planAgentRun(o) {
8224
8255
  const briefPath = join14(o.route.promptsDir, "personas", `lane-${o.role}.md`);
8225
- if (!existsSync12(briefPath)) {
8256
+ if (!existsSync13(briefPath)) {
8226
8257
  throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
8227
8258
  }
8228
8259
  const commonPath = join14(o.route.promptsDir, "personas", "common.md");
8229
- const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync12(commonPath);
8260
+ const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync13(commonPath);
8230
8261
  const now = o.now ?? (() => Date.now());
8231
8262
  const routeLabel = routeSlug(o.route.route);
8232
8263
  const digestPath2 = join14(o.stateDir, `digest-${routeLabel}-${o.role}.md`);
8233
8264
  let digest;
8234
8265
  let ageSeconds;
8235
- if (existsSync12(digestPath2)) {
8266
+ if (existsSync13(digestPath2)) {
8236
8267
  ageSeconds = Math.floor((now() - statSync5(digestPath2).mtimeMs) / 1e3);
8237
8268
  if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync12(digestPath2, "utf8");
8238
8269
  }
@@ -8735,12 +8766,12 @@ async function reattachRun(streamPath, sc, emit, o = {}) {
8735
8766
  }
8736
8767
 
8737
8768
  // src/run-worktree.ts
8738
- import { existsSync as existsSync14 } from "fs";
8769
+ import { existsSync as existsSync15 } from "fs";
8739
8770
  import { dirname as dirname7, join as join16 } from "path";
8740
8771
 
8741
8772
  // src/ports.ts
8742
8773
  import { execFileSync as execFileSync2 } from "child_process";
8743
- import { existsSync as existsSync13 } from "fs";
8774
+ import { existsSync as existsSync14 } from "fs";
8744
8775
  import { join as join15 } from "path";
8745
8776
  var run = (cmd, args) => {
8746
8777
  try {
@@ -8786,7 +8817,7 @@ function ticketForPort(port, s = DEFAULT_PORTS) {
8786
8817
  }
8787
8818
  function worktreeExists(parent, prefix, n) {
8788
8819
  for (const name of [`${prefix}${n}`, `${prefix}${String(n).padStart(3, "0")}`]) {
8789
- if (existsSync13(join15(parent, name, ".git"))) return true;
8820
+ if (existsSync14(join15(parent, name, ".git"))) return true;
8790
8821
  }
8791
8822
  return false;
8792
8823
  }
@@ -8862,7 +8893,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
8862
8893
  const existing = worktreeForNumber(dir, num);
8863
8894
  const located = locateBranchForTicket(dir, cfg, t, role);
8864
8895
  const baseRef = hasRemote ? `${remote}/${base}` : base;
8865
- if (existing && !existing.prunable && existsSync14(existing.path)) {
8896
+ if (existing && !existing.prunable && existsSync15(existing.path)) {
8866
8897
  const branch = existing.branch;
8867
8898
  if (status(existing.path).length > 0) {
8868
8899
  return { action: opts.resumeInterrupted ? "resume-dirty" : "leave-dirty", path: existing.path, branch };
@@ -8927,7 +8958,7 @@ function worktreeHoldsUnpushed(path, remote) {
8927
8958
  }
8928
8959
  async function finishRunWorktree(dir, cfg, branch) {
8929
8960
  const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
8930
- if (!wt || !existsSync14(wt.path)) return { kind: "none" };
8961
+ if (!wt || !existsSync15(wt.path)) return { kind: "none" };
8931
8962
  if (cfg.hooks.handoff) return { kind: "kept-handoff", path: wt.path };
8932
8963
  if (status(wt.path).length > 0) return { kind: "kept-dirty", path: wt.path };
8933
8964
  if (remoteConfigured(dir, cfg.branch.remote) && worktreeHoldsUnpushed(wt.path, cfg.branch.remote)) {
@@ -8939,7 +8970,7 @@ async function finishRunWorktree(dir, cfg, branch) {
8939
8970
  if (pids.length) await killGently(pids);
8940
8971
  }
8941
8972
  gitOk(dir, ["worktree", "remove", wt.path]);
8942
- return existsSync14(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
8973
+ return existsSync15(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
8943
8974
  }
8944
8975
 
8945
8976
  // src/handoff-guard.ts
@@ -9175,12 +9206,12 @@ async function guardRunStrays(route, ship, tracker, role, workingId, since2, log
9175
9206
 
9176
9207
  // src/connect-wizard.ts
9177
9208
  import { delimiter, dirname as dirname8, join as join17 } from "path";
9178
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync15, mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
9209
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
9179
9210
  import { stringify } from "yaml";
9180
9211
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
9181
9212
  function shouldRunWizard(opts) {
9182
9213
  if (!opts.isTTY) return false;
9183
- const exists = opts.configExists ?? existsSync15;
9214
+ const exists = opts.configExists ?? existsSync16;
9184
9215
  return !configSearchPath(opts.crewHome).some((p) => exists(p));
9185
9216
  }
9186
9217
  function firstRunConfigPath() {
@@ -9292,7 +9323,7 @@ Wrote ${o.configPath}.
9292
9323
  }
9293
9324
 
9294
9325
  // src/release-run.ts
9295
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, copyFileSync, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync7 } from "fs";
9326
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync17, copyFileSync, mkdirSync as mkdirSync8, mkdtempSync, rmSync as rmSync7 } from "fs";
9296
9327
  import { dirname as dirname9, join as join18 } from "path";
9297
9328
  import { tmpdir as tmpdir2 } from "os";
9298
9329
 
@@ -9983,7 +10014,7 @@ async function bump(o, size) {
9983
10014
  }
9984
10015
  const files = o.repo.release.versionFiles.map((f) => join18(o.cwd, f));
9985
10016
  const first = files[0];
9986
- if (!existsSync16(first)) {
10017
+ if (!existsSync17(first)) {
9987
10018
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
9988
10019
  return null;
9989
10020
  }
@@ -10019,7 +10050,7 @@ function writeChangelog(o, version, entries) {
10019
10050
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
10020
10051
  return;
10021
10052
  }
10022
- const existing = existsSync16(path) ? readFileSync13(path, "utf8") : "# Changelog\n";
10053
+ const existing = existsSync17(path) ? readFileSync13(path, "utf8") : "# Changelog\n";
10023
10054
  writeFileSync10(path, insertChangelogSection(existing, section));
10024
10055
  }
10025
10056
  async function confirm(o, expected) {
@@ -10125,7 +10156,7 @@ function openReleaseCheckout(o) {
10125
10156
  }
10126
10157
  for (const f of o.repo.worktrees.copy) {
10127
10158
  const src = join18(o.cwd, f);
10128
- if (!existsSync16(src)) continue;
10159
+ if (!existsSync17(src)) continue;
10129
10160
  mkdirSync8(dirname9(join18(dir, f)), { recursive: true });
10130
10161
  copyFileSync(src, join18(dir, f));
10131
10162
  }
@@ -10710,7 +10741,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
10710
10741
  }
10711
10742
 
10712
10743
  // src/watch.ts
10713
- import { watch as watchFile, existsSync as existsSync17, statSync as statSync6, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
10744
+ import { watch as watchFile, existsSync as existsSync18, statSync as statSync6, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
10714
10745
  var E = "\x1B[";
10715
10746
  var dim = (s) => `${E}2m${s}${E}0m`;
10716
10747
  var bold = (s) => `${E}1m${s}${E}0m`;
@@ -10811,7 +10842,7 @@ function renderHeaders(states, now = Date.now()) {
10811
10842
  return states.map((s) => renderHeader(s, now)).join("");
10812
10843
  }
10813
10844
  function readFrom(file, from) {
10814
- if (!existsSync17(file)) return { events: [], offset: from };
10845
+ if (!existsSync18(file)) return { events: [], offset: from };
10815
10846
  const size = statSync6(file).size;
10816
10847
  if (size < from) return readFrom(file, 0);
10817
10848
  if (size === from) return { events: [], offset: from };
@@ -11830,7 +11861,7 @@ function schedulerInstalled(plan, exists, crontab) {
11830
11861
  }
11831
11862
 
11832
11863
  // src/worktree-sweep.ts
11833
- import { existsSync as existsSync18 } from "fs";
11864
+ import { existsSync as existsSync19 } from "fs";
11834
11865
  import { basename as basename8 } from "path";
11835
11866
  function planWorktreeSweep(target, tickets, contract) {
11836
11867
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
@@ -11866,7 +11897,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin")
11866
11897
  let removed = 0;
11867
11898
  let keptBranches = 0;
11868
11899
  for (const a of actions) {
11869
- if (!dryRun && existsSync18(a.path)) {
11900
+ if (!dryRun && existsSync19(a.path)) {
11870
11901
  let dirty = false;
11871
11902
  try {
11872
11903
  dirty = status(a.path).length > 0;
@@ -11880,7 +11911,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin")
11880
11911
  continue;
11881
11912
  }
11882
11913
  }
11883
- if (!dryRun && a.requirePushed && a.branch && existsSync18(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
11914
+ if (!dryRun && a.requirePushed && a.branch && existsSync19(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
11884
11915
  log.warn(
11885
11916
  `not removing worktree ${a.path} for ${a.ticket.issue_id} (${a.ticket.status}): ${a.branch} has commits that are not on ${remote}`,
11886
11917
  { ticket: a.ticket.issue_id, step: "worktree" }
@@ -12012,12 +12043,12 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
12012
12043
  }
12013
12044
 
12014
12045
  // src/stream-sweep.ts
12015
- import { existsSync as existsSync19, readdirSync as readdirSync5, rmSync as rmSync8, statSync as statSync7 } from "fs";
12046
+ import { existsSync as existsSync20, readdirSync as readdirSync5, rmSync as rmSync8, statSync as statSync7 } from "fs";
12016
12047
  import { join as join19 } from "path";
12017
12048
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
12018
12049
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
12019
12050
  const dir = join19(stateDir, "streams");
12020
- if (!existsSync19(dir)) return [];
12051
+ if (!existsSync20(dir)) return [];
12021
12052
  const cutoff = now - retentionDays * DAY_MS2;
12022
12053
  return readdirSync5(dir).map((name) => join19(dir, name)).filter((path) => {
12023
12054
  try {
@@ -12045,7 +12076,7 @@ function applyStreamSweep(paths, dryRun, log) {
12045
12076
  return removed;
12046
12077
  }
12047
12078
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
12048
- if (!existsSync19(stateDir)) return [];
12079
+ if (!existsSync20(stateDir)) return [];
12049
12080
  const cutoff = now - retentionDays * DAY_MS2;
12050
12081
  return readdirSync5(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join19(stateDir, name)).filter((path) => {
12051
12082
  try {
@@ -12057,13 +12088,13 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
12057
12088
  }
12058
12089
 
12059
12090
  // src/cli.ts
12060
- import { readFileSync as readFileSync19, existsSync as existsSync25, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync2, statSync as statSync9 } from "fs";
12091
+ import { readFileSync as readFileSync19, existsSync as existsSync26, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync2, statSync as statSync9 } from "fs";
12061
12092
  import { createInterface } from "readline/promises";
12062
12093
  import { dirname as dirOf, resolve as resolvePath } from "path";
12063
12094
 
12064
12095
  // src/ssh-keys.ts
12065
12096
  import { execFileSync as execFileSync3 } from "child_process";
12066
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync14, chmodSync } from "fs";
12097
+ import { existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync14, chmodSync } from "fs";
12067
12098
  import { dirname as dirname11, join as join20 } from "path";
12068
12099
  var SshKeyError = class extends Error {
12069
12100
  };
@@ -12073,7 +12104,7 @@ function shipSshKeyPathFor(stateDir) {
12073
12104
  function ensureShipSshKeypair(stateDir) {
12074
12105
  const privateKeyPath = shipSshKeyPathFor(stateDir);
12075
12106
  const publicKeyPath = `${privateKeyPath}.pub`;
12076
- if (!existsSync20(privateKeyPath)) {
12107
+ if (!existsSync21(privateKeyPath)) {
12077
12108
  mkdirSync9(dirname11(privateKeyPath), { recursive: true });
12078
12109
  try {
12079
12110
  execFileSync3("ssh-keygen", [
@@ -12094,7 +12125,7 @@ function ensureShipSshKeypair(stateDir) {
12094
12125
  }
12095
12126
  chmodSync(privateKeyPath, 384);
12096
12127
  }
12097
- if (!existsSync20(publicKeyPath)) {
12128
+ if (!existsSync21(publicKeyPath)) {
12098
12129
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
12099
12130
  }
12100
12131
  const publicKey = readFileSync14(publicKeyPath, "utf8").trim();
@@ -12313,7 +12344,7 @@ function syncAllPassengerCheckouts(cfg) {
12313
12344
 
12314
12345
  // src/tunnel.ts
12315
12346
  import { spawn as spawn3 } from "child_process";
12316
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync3, readdirSync as readdirSync6 } from "fs";
12347
+ import { existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync3, readdirSync as readdirSync6 } from "fs";
12317
12348
  import { randomInt } from "crypto";
12318
12349
  import { join as join21 } from "path";
12319
12350
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
@@ -12386,7 +12417,7 @@ function tunnelStatePath(stateDir, workspaceId) {
12386
12417
  }
12387
12418
  function readPersistedTunnel(stateDir, workspaceId) {
12388
12419
  const path = tunnelStatePath(stateDir, workspaceId);
12389
- if (!existsSync21(path)) return void 0;
12420
+ if (!existsSync22(path)) return void 0;
12390
12421
  try {
12391
12422
  return JSON.parse(readFileSync15(path, "utf8"));
12392
12423
  } catch {
@@ -12494,7 +12525,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
12494
12525
  }
12495
12526
  function readAllPersistedWorkspaceIds(stateDir) {
12496
12527
  const dir = join21(stateDir, "passengers");
12497
- if (!existsSync21(dir)) return [];
12528
+ if (!existsSync22(dir)) return [];
12498
12529
  return readdirSync6(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
12499
12530
  }
12500
12531
 
@@ -12615,7 +12646,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
12615
12646
  import { createServer as createHttpServer } from "http";
12616
12647
  import { spawn as spawn4 } from "child_process";
12617
12648
  import { randomBytes } from "crypto";
12618
- import { existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, readdirSync as readdirSync7 } from "fs";
12649
+ import { existsSync as existsSync23, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, readdirSync as readdirSync7 } from "fs";
12619
12650
  import { join as join22 } from "path";
12620
12651
 
12621
12652
  // src/secret-compare.ts
@@ -12709,7 +12740,7 @@ function syncDaemonStatePath(stateDir, workspaceId) {
12709
12740
  }
12710
12741
  function readPersistedSyncDaemon(stateDir, workspaceId) {
12711
12742
  const path = syncDaemonStatePath(stateDir, workspaceId);
12712
- if (!existsSync22(path)) return void 0;
12743
+ if (!existsSync23(path)) return void 0;
12713
12744
  try {
12714
12745
  return JSON.parse(readFileSync16(path, "utf8"));
12715
12746
  } catch {
@@ -12740,7 +12771,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
12740
12771
  }
12741
12772
  function readAllPersistedSyncWorkspaceIds(stateDir) {
12742
12773
  const dir = join22(stateDir, "passengers");
12743
- if (!existsSync22(dir)) return [];
12774
+ if (!existsSync23(dir)) return [];
12744
12775
  const suffix = "-sync.json";
12745
12776
  return readdirSync7(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
12746
12777
  }
@@ -12797,11 +12828,11 @@ function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
12797
12828
  }
12798
12829
 
12799
12830
  // src/daemon.ts
12800
- import { existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync18, readdirSync as readdirSync9, rmSync as rmSync9, statSync as statSync8, writeFileSync as writeFileSync13 } from "fs";
12831
+ import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync18, readdirSync as readdirSync9, rmSync as rmSync9, statSync as statSync8, writeFileSync as writeFileSync13 } from "fs";
12801
12832
  import { join as join24 } from "path";
12802
12833
 
12803
12834
  // src/interrupted.ts
12804
- import { existsSync as existsSync23, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
12835
+ import { existsSync as existsSync24, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
12805
12836
  import { join as join23 } from "path";
12806
12837
  var INTERRUPTED_MARKER = "<!-- crew:interrupted -->";
12807
12838
  function interruptedComment(b) {
@@ -12820,7 +12851,7 @@ var pidAlive = (pid) => {
12820
12851
  };
12821
12852
  function runFinished(streamPath) {
12822
12853
  const path = `${streamPath}.meta.json`;
12823
- if (!existsSync23(path)) return false;
12854
+ if (!existsSync24(path)) return false;
12824
12855
  try {
12825
12856
  return JSON.parse(readFileSync17(path, "utf8")).running !== true;
12826
12857
  } catch {
@@ -12829,7 +12860,7 @@ function runFinished(streamPath) {
12829
12860
  }
12830
12861
  function orphanedStreams(o) {
12831
12862
  const dir = join23(o.stateDir, "streams");
12832
- if (!existsSync23(dir)) return [];
12863
+ if (!existsSync24(dir)) return [];
12833
12864
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
12834
12865
  const out = [];
12835
12866
  for (const name of readdirSync8(dir)) {
@@ -12943,7 +12974,7 @@ function computeTreeSignature(paths) {
12943
12974
  }
12944
12975
  function makeStaleChecker(paths, opts = {}) {
12945
12976
  const initial = computeTreeSignature(paths);
12946
- return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync24(opts.reloadSentinel);
12977
+ return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync25(opts.reloadSentinel);
12947
12978
  }
12948
12979
  function reloadSentinelFor(stateDir) {
12949
12980
  return join24(stateDir, "reload-requested");
@@ -13829,7 +13860,7 @@ if (command === "passenger-sync-daemon") {
13829
13860
  if (host === "linux" && hasSystemd) {
13830
13861
  const label = labelFor(CREW_HOME, "run");
13831
13862
  const staleTimer = join25(homedir8(), ".config", "systemd", "user", `${label}.timer`);
13832
- if (existsSync25(staleTimer)) {
13863
+ if (existsSync26(staleTimer)) {
13833
13864
  process.stdout.write(
13834
13865
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
13835
13866
  `
@@ -13855,7 +13886,7 @@ if (command === "passenger-sync-daemon") {
13855
13886
  const tracker = new Tracker(c, cfg.ship);
13856
13887
  problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
13857
13888
  for (const r of await resolvedRepos(c)) {
13858
- if (!existsSync25(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
13889
+ if (!existsSync26(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
13859
13890
  if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
13860
13891
  problems.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
13861
13892
  }
@@ -13873,7 +13904,7 @@ if (command === "passenger-sync-daemon") {
13873
13904
  }
13874
13905
  async function doctorFix(o) {
13875
13906
  const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
13876
- const unitExists = (p) => existsSync25(p);
13907
+ const unitExists = (p) => existsSync26(p);
13877
13908
  const crontab = o.host === "linux" ? spawnSync6("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
13878
13909
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run"), unitExists, crontab) : true;
13879
13910
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
@@ -15028,7 +15059,7 @@ ${changed2.map((c) => ` - ${c}
15028
15059
  `);
15029
15060
  if (!dryRun) {
15030
15061
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
15031
- const raw = existsSync25(resolvedPath) ? JSON.parse(readFileSync19(resolvedPath, "utf8")) : {};
15062
+ const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync19(resolvedPath, "utf8")) : {};
15032
15063
  raw.agentPersonas = personaResult.agentPersonas;
15033
15064
  raw.agentSkills = skillResult.agentSkills;
15034
15065
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -15088,7 +15119,7 @@ ${changed2.map((c) => ` - ${c}
15088
15119
  `);
15089
15120
  if (!dryRun) {
15090
15121
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
15091
- const raw = existsSync25(resolvedPath) ? JSON.parse(readFileSync19(resolvedPath, "utf8")) : {};
15122
+ const raw = existsSync26(resolvedPath) ? JSON.parse(readFileSync19(resolvedPath, "utf8")) : {};
15092
15123
  raw.agentSkills = result.agentSkills;
15093
15124
  mkdirSync13(dirOf(resolvedPath), { recursive: true });
15094
15125
  writeFileSync14(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -15111,7 +15142,7 @@ ${changed2.map((c) => ` - ${c}
15111
15142
  if (sub === "list" && !routeName) usage2();
15112
15143
  if (sub === "add") {
15113
15144
  try {
15114
- const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync25(resolvePath(p.replace(/^~(?=\/|$)/, homedir8()))));
15145
+ const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync26(resolvePath(p.replace(/^~(?=\/|$)/, homedir8()))));
15115
15146
  routeName = a.route;
15116
15147
  addPath = a.path;
15117
15148
  addName = a.name;
@@ -15126,7 +15157,7 @@ ${changed2.map((c) => ` - ${c}
15126
15157
  }
15127
15158
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
15128
15159
  let resolvedRemotes;
15129
- if (existsSync25(resolvedFile)) {
15160
+ if (existsSync26(resolvedFile)) {
15130
15161
  try {
15131
15162
  resolvedRemotes = JSON.parse(readFileSync19(resolvedFile, "utf8"));
15132
15163
  } catch {
@@ -15143,14 +15174,14 @@ ${changed2.map((c) => ` - ${c}
15143
15174
  for (const r of repos) {
15144
15175
  const origin2 = originOf(r.dir);
15145
15176
  const row = matchRepoName(origin2, resolvedRemotes);
15146
- const note = !existsSync25(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
15177
+ const note = !existsSync26(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
15147
15178
  process.stdout.write(`${r.name} ${r.dir} ${note}
15148
15179
  `);
15149
15180
  }
15150
15181
  break;
15151
15182
  }
15152
15183
  const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir8()));
15153
- const top = existsSync25(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
15184
+ const top = existsSync26(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
15154
15185
  if (!top) throw new ReposError(`${target} is not a git checkout`);
15155
15186
  const origin = originOf(top);
15156
15187
  const plan = planRepoAdd({
@@ -15199,7 +15230,7 @@ ${changed2.map((c) => ` - ${c}
15199
15230
  const created = await rowTracker.createRepoRow(rowPayload);
15200
15231
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
15201
15232
  `);
15202
- if (existsSync25(resolvedFile)) {
15233
+ if (existsSync26(resolvedFile)) {
15203
15234
  const raw = JSON.parse(readFileSync19(resolvedFile, "utf8"));
15204
15235
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
15205
15236
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
@@ -15446,7 +15477,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
15446
15477
  operator = await pickOperator(found.holds);
15447
15478
  }
15448
15479
  let previous;
15449
- const hadPreviousFile = existsSync25(resolvedPath);
15480
+ const hadPreviousFile = existsSync26(resolvedPath);
15450
15481
  if (hadPreviousFile) {
15451
15482
  try {
15452
15483
  previous = JSON.parse(readFileSync19(resolvedPath, "utf8"));
@@ -16351,7 +16382,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
16351
16382
  );
16352
16383
  try {
16353
16384
  const evFile = eventFileFor(cfg.ship.stateDir);
16354
- const evSize = existsSync25(evFile) ? statSync9(evFile).size : 0;
16385
+ const evSize = existsSync26(evFile) ? statSync9(evFile).size : 0;
16355
16386
  process.stdout.write(
16356
16387
  evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
16357
16388
  ` : "events log: ok\n"
@@ -16409,12 +16440,10 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
16409
16440
  process.stdout.write(` - ${p2}
16410
16441
  `);
16411
16442
  }
16412
- const bh = baseHealth(r);
16413
- if (bh.action !== "not-applicable") {
16414
- process.stdout.write(` base ${config.branch.base}: ${bh.unsafe ? "ERROR \u2014 " : ""}${bh.detail}
16415
- `);
16416
- if (bh.unsafe) process.exitCode = 1;
16417
- }
16443
+ const dh = doctorBaseReport(r, config.branch.base, baseHealth);
16444
+ process.stdout.write(dh.text);
16445
+ if (dh.failed) process.exitCode = 1;
16446
+ if (dh.skipRest) continue;
16418
16447
  if (config.release.ship) {
16419
16448
  process.stdout.write(` release.ship ${config.release.ship}${config.release.ship === cfg.ship.name ? " (this ship)" : ""}
16420
16449
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.50.1",
3
+ "version": "0.50.2",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",