@tablation/crew 0.122.0 → 0.123.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 +343 -292
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5850,6 +5850,78 @@ function cloneFailureMarker(dir) {
5850
5850
  return `${dir}.clone-failed`;
5851
5851
  }
5852
5852
 
5853
+ // src/repo-map.ts
5854
+ import { readFileSync as readFileSync18 } from "fs";
5855
+ import { join as join20 } from "path";
5856
+ var CHARS_PER_TOKEN = 4;
5857
+ var MAP_TIMEOUT_MS = 6e4;
5858
+ function budgetMap(text2, tokenBudget) {
5859
+ const body = text2.replace(/\s+$/, "");
5860
+ const limit = tokenBudget * CHARS_PER_TOKEN;
5861
+ if (body.length <= limit) return body;
5862
+ const lines2 = body.split("\n");
5863
+ const noteFor = (kept2) => `\u2026 ${lines2.length - kept2} of ${lines2.length} lines cut to fit ship.mapTokenBudget (${tokenBudget} tokens)`;
5864
+ let kept = lines2.length;
5865
+ let size = body.length;
5866
+ while (kept > 0 && size + 1 + noteFor(kept).length > limit) {
5867
+ kept--;
5868
+ size -= lines2[kept].length + 1;
5869
+ }
5870
+ return [...lines2.slice(0, kept), noteFor(kept)].join("\n");
5871
+ }
5872
+ function mapFilePath(value2) {
5873
+ if (!value2) return null;
5874
+ const v = value2.trim();
5875
+ if (!/^[A-Za-z0-9_.+@-]+(?:\/[A-Za-z0-9_.+@-]+)*\.md$/i.test(v)) return null;
5876
+ if (v.split("/").some((seg) => seg === ".." || seg === ".")) return null;
5877
+ return v;
5878
+ }
5879
+ function inspectMapFile(dir, path) {
5880
+ try {
5881
+ const text2 = readFileSync18(join20(dir, path), "utf8").replace(/\s+$/, "");
5882
+ return { path, exists: true, lines: text2 ? text2.split("\n").length : 0, tokens: Math.ceil(text2.length / CHARS_PER_TOKEN) };
5883
+ } catch {
5884
+ return { path, exists: false, lines: 0, tokens: 0 };
5885
+ }
5886
+ }
5887
+ function loadRepoMap(repo, tokenBudget, shellFallback) {
5888
+ const script = repo.config.hooks.map;
5889
+ if (!script) return Promise.resolve({ skipped: "" });
5890
+ const file = mapFilePath(script);
5891
+ if (file) {
5892
+ const m = inspectMapFile(repo.dir, file);
5893
+ if (!m.exists) return Promise.resolve({ skipped: `the \`map\` file ${file} for ${repo.name} is missing or unreadable, so the brief has no repository map` });
5894
+ let text2 = "";
5895
+ try {
5896
+ text2 = readFileSync18(join20(repo.dir, file), "utf8");
5897
+ } catch {
5898
+ }
5899
+ if (!text2.trim()) return Promise.resolve({ skipped: `the \`map\` file ${file} for ${repo.name} is empty, so the brief has no repository map` });
5900
+ return Promise.resolve({ text: budgetMap(text2, tokenBudget) });
5901
+ }
5902
+ return runScript(script, {
5903
+ cwd: repo.dir,
5904
+ shell: resolveShell(repo.config.shell ?? shellFallback),
5905
+ env: { CREW_REPO: repo.name },
5906
+ timeoutMs: MAP_TIMEOUT_MS
5907
+ }).then(
5908
+ (r) => {
5909
+ if (r.code !== 0) return { skipped: `the \`map\` hook for ${repo.name} exited ${r.code}, so the brief has no repository map` };
5910
+ if (!r.stdout.trim()) return { skipped: `the \`map\` hook for ${repo.name} printed nothing, so the brief has no repository map` };
5911
+ return { text: budgetMap(r.stdout, tokenBudget) };
5912
+ },
5913
+ (e) => ({ skipped: `the \`map\` hook for ${repo.name} could not run (${e instanceof Error ? e.message : String(e)}), so the brief has no repository map` })
5914
+ );
5915
+ }
5916
+ async function withRepoMaps(repos, tokenBudget, shellFallback) {
5917
+ return Promise.all(repos.map(async (r) => {
5918
+ if (!r.config.hooks.map) return r;
5919
+ const out = await loadRepoMap(r, tokenBudget, shellFallback);
5920
+ if ("skipped" in out) return out.skipped ? { ...r, mapSkipped: out.skipped } : r;
5921
+ return { ...r, map: out.text };
5922
+ }));
5923
+ }
5924
+
5853
5925
  // src/ship-attention.ts
5854
5926
  var RELEASE_STALE_AFTER_INTERVALS = 3;
5855
5927
  function upsert(items, item) {
@@ -6091,6 +6163,7 @@ function clearShipAttention(state, key) {
6091
6163
  if (next.length !== previous.length) store.persist(next);
6092
6164
  }
6093
6165
  function hookCommand(script) {
6166
+ if (mapFilePath(script)) return null;
6094
6167
  for (const raw of script.split("\n")) {
6095
6168
  const line = raw.trim();
6096
6169
  if (!line || line.startsWith("#")) continue;
@@ -6170,6 +6243,18 @@ function repoFindings(o) {
6170
6243
  remedy: "add the directory holding it to ship.extraPath in crew.yaml"
6171
6244
  });
6172
6245
  }
6246
+ if (o.mapFile) {
6247
+ if (!o.mapFile.exists) {
6248
+ problems.push({
6249
+ severity: "attention",
6250
+ kind: "map_file_missing",
6251
+ ...at,
6252
+ hook: "map",
6253
+ message: `${o.route}/${o.repo}: hooks.map names ${o.mapFile.path}, which is not in the checkout, so briefs carry no repository map`,
6254
+ remedy: `commit ${o.mapFile.path}, or correct hooks.map in the repository's .crew.yaml`
6255
+ });
6256
+ } else passed++;
6257
+ }
6173
6258
  if (o.intelSyntax) {
6174
6259
  if (o.intelSyntax.status === "error") {
6175
6260
  problems.push({
@@ -6205,8 +6290,8 @@ function attentionProblems(items, already) {
6205
6290
  import { createHash as createHash6 } from "crypto";
6206
6291
  import { spawn as spawn5 } from "child_process";
6207
6292
  import { execFileSync as execFileSync8 } from "child_process";
6208
- import { mkdirSync as mkdirSync19, readFileSync as readFileSync26, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6209
- import { dirname as dirname14, join as join31 } from "path";
6293
+ import { mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6294
+ import { dirname as dirname14, join as join32 } from "path";
6210
6295
  import { tmpdir as tmpdir3 } from "os";
6211
6296
 
6212
6297
  // src/passenger-containers.ts
@@ -6222,20 +6307,20 @@ import { spawnSync as spawnSync7 } from "child_process";
6222
6307
  import { existsSync as existsSync29, readdirSync as readdirSync11 } from "fs";
6223
6308
 
6224
6309
  // src/interrupted.ts
6225
- import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync25 } from "fs";
6226
- import { join as join29 } from "path";
6310
+ import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync26 } from "fs";
6311
+ import { join as join30 } from "path";
6227
6312
 
6228
6313
  // src/agent.ts
6229
6314
  import { spawnSync as spawnSync6 } from "child_process";
6230
- import { readFileSync as readFileSync24, existsSync as existsSync26, statSync as statSync8, mkdirSync as mkdirSync18, writeFileSync as writeFileSync19, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
6315
+ import { readFileSync as readFileSync25, existsSync as existsSync26, statSync as statSync8, mkdirSync as mkdirSync18, writeFileSync as writeFileSync19, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
6231
6316
  import { StringDecoder } from "string_decoder";
6232
6317
  import { finished } from "stream/promises";
6233
- import { join as join28, dirname as dirname13, basename as basename9 } from "path";
6318
+ import { join as join29, dirname as dirname13, basename as basename9 } from "path";
6234
6319
  import { createHash as createHash4 } from "crypto";
6235
6320
 
6236
6321
  // src/state.ts
6237
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync19, mkdirSync as mkdirSync13, rmSync as rmSync10, statSync as statSync7, readdirSync as readdirSync5 } from "fs";
6238
- import { join as join20 } from "path";
6322
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync14, existsSync as existsSync19, mkdirSync as mkdirSync13, rmSync as rmSync10, statSync as statSync7, readdirSync as readdirSync5 } from "fs";
6323
+ import { join as join21 } from "path";
6239
6324
  var EPOCH = "1970-01-01T00:00:00.000Z";
6240
6325
  var State = class _State {
6241
6326
  dir;
@@ -6244,7 +6329,7 @@ var State = class _State {
6244
6329
  mkdirSync13(dir, { recursive: true });
6245
6330
  }
6246
6331
  path(...parts) {
6247
- return join20(this.dir, ...parts);
6332
+ return join21(this.dir, ...parts);
6248
6333
  }
6249
6334
  /**
6250
6335
  * The timestamp every "new since last poll" check is measured against.
@@ -6255,7 +6340,7 @@ var State = class _State {
6255
6340
  */
6256
6341
  watermark() {
6257
6342
  try {
6258
- const v = readFileSync18(this.path(".poll-watermark"), "utf8").trim();
6343
+ const v = readFileSync19(this.path(".poll-watermark"), "utf8").trim();
6259
6344
  return v || EPOCH;
6260
6345
  } catch {
6261
6346
  return EPOCH;
@@ -6341,7 +6426,7 @@ var State = class _State {
6341
6426
  const name = _State.safe(rawName);
6342
6427
  const file = this.path(`.${name}.lock`);
6343
6428
  try {
6344
- const existing = Number.parseInt(readFileSync18(file, "utf8").trim(), 10);
6429
+ const existing = Number.parseInt(readFileSync19(file, "utf8").trim(), 10);
6345
6430
  if (Number.isFinite(existing) && existing > 0) {
6346
6431
  try {
6347
6432
  process.kill(existing, 0);
@@ -6379,7 +6464,7 @@ var State = class _State {
6379
6464
  if (!/^\.release-.+\.lock$/.test(n)) continue;
6380
6465
  let pid = 0;
6381
6466
  try {
6382
- pid = Number.parseInt(readFileSync18(this.path(n), "utf8").trim(), 10);
6467
+ pid = Number.parseInt(readFileSync19(this.path(n), "utf8").trim(), 10);
6383
6468
  } catch {
6384
6469
  continue;
6385
6470
  }
@@ -6465,7 +6550,7 @@ var State = class _State {
6465
6550
  ticketInFlight(recordId) {
6466
6551
  const file = this.path(`.${_State.safe(`crew-ticket-${recordId}`)}.lock`);
6467
6552
  try {
6468
- const pid = Number.parseInt(readFileSync18(file, "utf8").trim(), 10);
6553
+ const pid = Number.parseInt(readFileSync19(file, "utf8").trim(), 10);
6469
6554
  if (!Number.isFinite(pid) || pid <= 0) return false;
6470
6555
  process.kill(pid, 0);
6471
6556
  return true;
@@ -6499,7 +6584,7 @@ var State = class _State {
6499
6584
  const heldReported = `.release-held-${suffix}`;
6500
6585
  const read = (f) => {
6501
6586
  try {
6502
- return readFileSync18(this.path(f), "utf8").trim() || null;
6587
+ return readFileSync19(this.path(f), "utf8").trim() || null;
6503
6588
  } catch {
6504
6589
  return null;
6505
6590
  }
@@ -6549,7 +6634,7 @@ var State = class _State {
6549
6634
  /** Whether the board was already told about this head's red gate. */
6550
6635
  heldReported: () => {
6551
6636
  try {
6552
- return readFileSync18(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
6637
+ return readFileSync19(this.path(heldReported), "utf8").replace(/\n$/, "") || null;
6553
6638
  } catch {
6554
6639
  return null;
6555
6640
  }
@@ -6586,7 +6671,7 @@ var State = class _State {
6586
6671
  const ticketStreakFile = `.fairness-ticket-streak-${suffix}`;
6587
6672
  const read = (f) => {
6588
6673
  try {
6589
- return readFileSync18(this.path(f), "utf8").trim() || null;
6674
+ return readFileSync19(this.path(f), "utf8").trim() || null;
6590
6675
  } catch {
6591
6676
  return null;
6592
6677
  }
@@ -6651,7 +6736,7 @@ var State = class _State {
6651
6736
  return {
6652
6737
  previous: () => {
6653
6738
  try {
6654
- const parsed = JSON.parse(readFileSync18(this.path(file), "utf8"));
6739
+ const parsed = JSON.parse(readFileSync19(this.path(file), "utf8"));
6655
6740
  return parsed && typeof parsed === "object" ? parsed : {};
6656
6741
  } catch {
6657
6742
  return {};
@@ -6674,7 +6759,7 @@ var State = class _State {
6674
6759
  return {
6675
6760
  previous: () => {
6676
6761
  try {
6677
- const parsed = JSON.parse(readFileSync18(this.path(file), "utf8"));
6762
+ const parsed = JSON.parse(readFileSync19(this.path(file), "utf8"));
6678
6763
  return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : [];
6679
6764
  } catch {
6680
6765
  return [];
@@ -6695,7 +6780,7 @@ var State = class _State {
6695
6780
  return {
6696
6781
  previous: () => {
6697
6782
  try {
6698
- const parsed = JSON.parse(readFileSync18(this.path(file), "utf8"));
6783
+ const parsed = JSON.parse(readFileSync19(this.path(file), "utf8"));
6699
6784
  return Array.isArray(parsed) ? parsed : [];
6700
6785
  } catch {
6701
6786
  return [];
@@ -6728,7 +6813,7 @@ var State = class _State {
6728
6813
  }
6729
6814
  readInt(file) {
6730
6815
  try {
6731
- return Number.parseInt(readFileSync18(this.path(file), "utf8").trim(), 10) || 0;
6816
+ return Number.parseInt(readFileSync19(this.path(file), "utf8").trim(), 10) || 0;
6732
6817
  } catch {
6733
6818
  return 0;
6734
6819
  }
@@ -7117,8 +7202,8 @@ function renderEnvironment(i) {
7117
7202
  }
7118
7203
 
7119
7204
  // src/intel-bridge.ts
7120
- import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync19, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7121
- import { dirname as dirname10, join as join21 } from "path";
7205
+ import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7206
+ import { dirname as dirname10, join as join22 } from "path";
7122
7207
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7123
7208
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7124
7209
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -7132,9 +7217,9 @@ var STATUS_WAITING = "The symbol service is not available yet: the worktree has
7132
7217
  var STATUS_LOST = "The symbol service stopped answering, so its tools are unavailable. They return if the service comes back; until then use text search.";
7133
7218
  function intelDir(stateDir, route2, ticket) {
7134
7219
  const safe = (s) => s.replace(/[^A-Za-z0-9._-]+/g, "_");
7135
- return join21(stateDir, "intel", safe(route2), safe(ticket));
7220
+ return join22(stateDir, "intel", safe(route2), safe(ticket));
7136
7221
  }
7137
- var attachFilePath = (stateDir, route2, ticket) => join21(intelDir(stateDir, route2, ticket), "url");
7222
+ var attachFilePath = (stateDir, route2, ticket) => join22(intelDir(stateDir, route2, ticket), "url");
7138
7223
  function writeAttachFile(file, url) {
7139
7224
  mkdirSync14(dirname10(file), { recursive: true });
7140
7225
  writeFileSync15(file, `${url}
@@ -7142,7 +7227,7 @@ function writeAttachFile(file, url) {
7142
7227
  }
7143
7228
  function readAttachFile(file) {
7144
7229
  try {
7145
- const url = readFileSync19(file, "utf8").trim();
7230
+ const url = readFileSync20(file, "utf8").trim();
7146
7231
  return /^https?:\/\/\S+$/i.test(url) ? url : void 0;
7147
7232
  } catch {
7148
7233
  return void 0;
@@ -7482,8 +7567,8 @@ var BeforeFirstEditTracker = class {
7482
7567
  };
7483
7568
 
7484
7569
  // src/agent-log.ts
7485
- import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync6, readFileSync as readFileSync20, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7486
- import { join as join22 } from "path";
7570
+ import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync6, readFileSync as readFileSync21, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7571
+ import { join as join23 } from "path";
7487
7572
 
7488
7573
  // src/pending-backoff.ts
7489
7574
  var sleepBackoff = (attempts) => Math.min(6e4 * 2 ** Math.max(0, attempts - 1), 30 * 6e4);
@@ -7686,20 +7771,20 @@ async function addRunToTicket(target, t, recordId, run3) {
7686
7771
  };
7687
7772
  }
7688
7773
  }
7689
- var queueDir = (stateDir) => join22(stateDir, "pending-agent-logs");
7774
+ var queueDir = (stateDir) => join23(stateDir, "pending-agent-logs");
7690
7775
  function queueAgentRun(stateDir, name, q2) {
7691
7776
  mkdirSync15(queueDir(stateDir), { recursive: true });
7692
- writeFileSync16(join22(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
7777
+ writeFileSync16(join23(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
7693
7778
  }
7694
7779
  async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
7695
7780
  const dir = queueDir(stateDir);
7696
7781
  if (!existsSync21(dir)) return 0;
7697
7782
  let sent = 0;
7698
7783
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
7699
- const path = join22(dir, f);
7784
+ const path = join23(dir, f);
7700
7785
  let q2;
7701
7786
  try {
7702
- q2 = JSON.parse(readFileSync20(path, "utf8"));
7787
+ q2 = JSON.parse(readFileSync21(path, "utf8"));
7703
7788
  } catch {
7704
7789
  continue;
7705
7790
  }
@@ -7739,20 +7824,20 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
7739
7824
  }
7740
7825
  return sent;
7741
7826
  }
7742
- var countersDir = (stateDir) => join22(stateDir, "pending-ticket-counters");
7827
+ var countersDir = (stateDir) => join23(stateDir, "pending-ticket-counters");
7743
7828
  function queueTicketCounters(stateDir, name, q2) {
7744
7829
  mkdirSync15(countersDir(stateDir), { recursive: true });
7745
- writeFileSync16(join22(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
7830
+ writeFileSync16(join23(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
7746
7831
  }
7747
7832
  async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
7748
7833
  const dir = countersDir(stateDir);
7749
7834
  if (!existsSync21(dir)) return 0;
7750
7835
  let sent = 0;
7751
7836
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
7752
- const path = join22(dir, f);
7837
+ const path = join23(dir, f);
7753
7838
  let q2;
7754
7839
  try {
7755
- q2 = JSON.parse(readFileSync20(path, "utf8"));
7840
+ q2 = JSON.parse(readFileSync21(path, "utf8"));
7756
7841
  } catch {
7757
7842
  continue;
7758
7843
  }
@@ -7974,8 +8059,8 @@ var HandoffTranscriptTracker = class {
7974
8059
  };
7975
8060
 
7976
8061
  // src/pending-handoff.ts
7977
- import { existsSync as existsSync23, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync21, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "fs";
7978
- import { join as join24 } from "path";
8062
+ import { existsSync as existsSync23, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync22, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "fs";
8063
+ import { join as join25 } from "path";
7979
8064
 
7980
8065
  // src/ticket-branch.ts
7981
8066
  import { basename as basename7 } from "path";
@@ -9142,7 +9227,7 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
9142
9227
 
9143
9228
  // src/run-worktree.ts
9144
9229
  import { existsSync as existsSync22 } from "fs";
9145
- import { dirname as dirname11, join as join23 } from "path";
9230
+ import { dirname as dirname11, join as join24 } from "path";
9146
9231
  var describeRunWorktreePlan = (p) => {
9147
9232
  switch (p.action) {
9148
9233
  case "cut-remote":
@@ -9199,7 +9284,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
9199
9284
  if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
9200
9285
  return { action: "recut", path: existing.path, branch, base: baseRef };
9201
9286
  }
9202
- const path = join23(dirname11(dir), worktreeForTicket(dir, cfg, t, role));
9287
+ const path = join24(dirname11(dir), worktreeForTicket(dir, cfg, t, role));
9203
9288
  if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
9204
9289
  return { action: "cut-remote", path, branch: located.branch };
9205
9290
  }
@@ -9656,12 +9741,12 @@ async function guardRunStrays(route2, ship, tracker, role, workingId, since2, lo
9656
9741
 
9657
9742
  // src/pending-handoff.ts
9658
9743
  var PENDING_MAX_AGE_MS = 48 * 36e5;
9659
- var pendingDirFor = (stateDir) => join24(stateDir, "pending-handoffs");
9660
- var stateDirOfStream = (streamPath) => join24(streamPath, "..", "..");
9744
+ var pendingDirFor = (stateDir) => join25(stateDir, "pending-handoffs");
9745
+ var stateDirOfStream = (streamPath) => join25(streamPath, "..", "..");
9661
9746
  function writePendingHandoff(stateDir, rec) {
9662
9747
  const dir = pendingDirFor(stateDir);
9663
9748
  mkdirSync16(dir, { recursive: true });
9664
- writeFileSync17(join24(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
9749
+ writeFileSync17(join25(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
9665
9750
  }
9666
9751
  function listPendingHandoffs(stateDir, route2) {
9667
9752
  const dir = pendingDirFor(stateDir);
@@ -9669,7 +9754,7 @@ function listPendingHandoffs(stateDir, route2) {
9669
9754
  const out = [];
9670
9755
  for (const f of readdirSync7(dir).filter((n) => n.endsWith(".json")).sort()) {
9671
9756
  try {
9672
- const rec = JSON.parse(readFileSync21(join24(dir, f), "utf8"));
9757
+ const rec = JSON.parse(readFileSync22(join25(dir, f), "utf8"));
9673
9758
  if (rec.version === 1 && rec.ticketRecordId && (!route2 || rec.route === route2)) out.push(rec);
9674
9759
  } catch {
9675
9760
  }
@@ -9677,7 +9762,7 @@ function listPendingHandoffs(stateDir, route2) {
9677
9762
  return out;
9678
9763
  }
9679
9764
  function removePendingHandoff(stateDir, ticketRecordId) {
9680
- rmSync13(join24(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
9765
+ rmSync13(join25(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
9681
9766
  }
9682
9767
  function pendingHandoffFor(stateDir, ticketRecordId) {
9683
9768
  return listPendingHandoffs(stateDir).find((r) => r.ticketRecordId === ticketRecordId);
@@ -9787,8 +9872,8 @@ function expandSkillMarkers(text2, lookup2, opts = {}) {
9787
9872
  }
9788
9873
 
9789
9874
  // src/skills.ts
9790
- import { readFileSync as readFileSync23, readdirSync as readdirSync8, existsSync as existsSync25 } from "fs";
9791
- import { join as join26 } from "path";
9875
+ import { readFileSync as readFileSync24, readdirSync as readdirSync8, existsSync as existsSync25 } from "fs";
9876
+ import { join as join27 } from "path";
9792
9877
  import { parse as parseYaml } from "yaml";
9793
9878
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
9794
9879
 
@@ -9804,8 +9889,8 @@ ${additions}`;
9804
9889
  }
9805
9890
 
9806
9891
  // src/agents.ts
9807
- import { readFileSync as readFileSync22, existsSync as existsSync24 } from "fs";
9808
- import { join as join25 } from "path";
9892
+ import { readFileSync as readFileSync23, existsSync as existsSync24 } from "fs";
9893
+ import { join as join26 } from "path";
9809
9894
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
9810
9895
  var PERSONA_NAME = {
9811
9896
  dev: "Developer",
@@ -9819,12 +9904,12 @@ var CREW_AGENT_COLUMN = "agent_id";
9819
9904
  var AgentsSyncError = class extends Error {
9820
9905
  };
9821
9906
  function personaDefaultPrompt(promptsDir, role) {
9822
- const briefPath = join25(promptsDir, "personas", `lane-${role}.md`);
9907
+ const briefPath = join26(promptsDir, "personas", `lane-${role}.md`);
9823
9908
  if (!existsSync24(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
9824
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync22(briefPath, "utf8");
9825
- const commonPath = join25(promptsDir, "personas", "common.md");
9826
- if (!existsSync24(commonPath)) return readFileSync22(briefPath, "utf8");
9827
- return `${readFileSync22(commonPath, "utf8")}${readFileSync22(briefPath, "utf8")}`;
9909
+ if (SELF_CONTAINED_ROLES.has(role)) return readFileSync23(briefPath, "utf8");
9910
+ const commonPath = join26(promptsDir, "personas", "common.md");
9911
+ if (!existsSync24(commonPath)) return readFileSync23(briefPath, "utf8");
9912
+ return `${readFileSync23(commonPath, "utf8")}${readFileSync23(briefPath, "utf8")}`;
9828
9913
  }
9829
9914
  async function resolveAgentId(route2, role, opts) {
9830
9915
  const cached = route2.resolved?.agentPersonas?.[role]?.agentId;
@@ -10271,7 +10356,7 @@ function describeCrewLink(o) {
10271
10356
  var SkillSyncError = class extends Error {
10272
10357
  };
10273
10358
  function parseSkillFile(path) {
10274
- const text2 = readFileSync23(path, "utf8");
10359
+ const text2 = readFileSync24(path, "utf8");
10275
10360
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text2);
10276
10361
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
10277
10362
  const frontmatter = match[1];
@@ -10284,9 +10369,9 @@ function parseSkillFile(path) {
10284
10369
  return { name: meta.name, description: meta.description, prompt: body, file: path };
10285
10370
  }
10286
10371
  function listLocalSkills(promptsDir) {
10287
- const dir = join26(promptsDir, "skills");
10372
+ const dir = join27(promptsDir, "skills");
10288
10373
  if (!existsSync25(dir)) return [];
10289
- return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join26(dir, f)));
10374
+ return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join27(dir, f)));
10290
10375
  }
10291
10376
  async function syncSkills(route2, opts) {
10292
10377
  if (!route2.resolved) {
@@ -10473,7 +10558,7 @@ function skillMarkerWarnings(promptsDir, personaPrompts) {
10473
10558
  import { spawn as spawn3, execFile, execFileSync as execFileSync6 } from "child_process";
10474
10559
  import { mkdtempSync, mkdirSync as mkdirSync17, writeFileSync as writeFileSync18, rmSync as rmSync14 } from "fs";
10475
10560
  import { tmpdir as tmpdir2 } from "os";
10476
- import { basename as basename8, dirname as dirname12, join as join27 } from "path";
10561
+ import { basename as basename8, dirname as dirname12, join as join28 } from "path";
10477
10562
  import { promisify } from "util";
10478
10563
 
10479
10564
  // src/run-limit.ts
@@ -10646,9 +10731,9 @@ var SandboxRunner = class {
10646
10731
  const harness = cfg.agentBin ?? basename8(plan.bin);
10647
10732
  const viaUpload = Buffer.byteLength(plan.prompt) > SANDBOX_STDIN_LIMIT;
10648
10733
  if (viaUpload) {
10649
- const tmp = mkdtempSync(join27(tmpdir2(), "crew-prompt-"));
10734
+ const tmp = mkdtempSync(join28(tmpdir2(), "crew-prompt-"));
10650
10735
  try {
10651
- const file = join27(tmp, "prompt.txt");
10736
+ const file = join28(tmp, "prompt.txt");
10652
10737
  writeFileSync18(file, plan.prompt, { mode: 384 });
10653
10738
  await this.os(["sandbox", "upload", name, file, SANDBOX_PROMPT_PATH]);
10654
10739
  } finally {
@@ -10802,7 +10887,7 @@ var ALLOWANCE_REFUSAL_MARKER = "AI_ALLOWANCE_EXCEEDED";
10802
10887
  function streamShowsAllowanceRefusal(streamPath) {
10803
10888
  let raw;
10804
10889
  try {
10805
- raw = readFileSync24(streamPath, "utf8");
10890
+ raw = readFileSync25(streamPath, "utf8");
10806
10891
  } catch {
10807
10892
  return false;
10808
10893
  }
@@ -10825,7 +10910,7 @@ function streamShowsAllowanceRefusal(streamPath) {
10825
10910
  }
10826
10911
  return false;
10827
10912
  }
10828
- var digestPathFor = (stateDir, routeLabel, role, runId) => join28(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10913
+ var digestPathFor = (stateDir, routeLabel, role, runId) => join29(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10829
10914
  function codeBridgeFor(o, harness) {
10830
10915
  if (o.ship.runner === "sandbox" || !o.ticket) return void 0;
10831
10916
  const number = /(\d+)$/.exec(o.ticket)?.[1];
@@ -10841,7 +10926,7 @@ function codeBridgeFor(o, harness) {
10841
10926
  const args = [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
10842
10927
  const bridge = { command: process.execPath, args };
10843
10928
  if (harness === "gemini") {
10844
- const file = join28(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
10929
+ const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
10845
10930
  mkdirSync18(dirname13(file), { recursive: true });
10846
10931
  writeFileSync19(file, `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
10847
10932
  `);
@@ -10850,11 +10935,11 @@ function codeBridgeFor(o, harness) {
10850
10935
  return bridge;
10851
10936
  }
10852
10937
  function planAgentRun(o) {
10853
- const briefPath = join28(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10938
+ const briefPath = join29(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10854
10939
  if (!existsSync26(briefPath)) {
10855
10940
  throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
10856
10941
  }
10857
- const commonPath = join28(o.route.promptsDir, "personas", "common.md");
10942
+ const commonPath = join29(o.route.promptsDir, "personas", "common.md");
10858
10943
  const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync26(commonPath);
10859
10944
  const now = o.now ?? (() => Date.now());
10860
10945
  const routeLabel = routeSlug(o.route.route);
@@ -10864,10 +10949,10 @@ function planAgentRun(o) {
10864
10949
  let ageSeconds;
10865
10950
  if (existsSync26(digestPath2)) {
10866
10951
  ageSeconds = Math.floor((now() - statSync8(digestPath2).mtimeMs) / 1e3);
10867
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync24(digestPath2, "utf8");
10952
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync25(digestPath2, "utf8");
10868
10953
  }
10869
- const common = usesCommon ? readFileSync24(commonPath, "utf8") : "";
10870
- const brief = readFileSync24(briefPath, "utf8");
10954
+ const common = usesCommon ? readFileSync25(commonPath, "utf8") : "";
10955
+ const brief = readFileSync25(briefPath, "utf8");
10871
10956
  const effectiveCommon = o.divergedPrompt ? "" : common;
10872
10957
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
10873
10958
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -10890,7 +10975,7 @@ function planAgentRun(o) {
10890
10975
  digest
10891
10976
  });
10892
10977
  const promptSha = createHash4("sha256").update(expanded.text).digest("hex");
10893
- const streamsDir = join28(o.stateDir, "streams");
10978
+ const streamsDir = join29(o.stateDir, "streams");
10894
10979
  const base = `${routeLabel}-${o.role}-${o.cycle}${o.runId ? `-${o.runId}` : ""}`;
10895
10980
  const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
10896
10981
  const { model } = seat;
@@ -10983,8 +11068,8 @@ function planAgentRun(o) {
10983
11068
  digestAttached: digest !== void 0,
10984
11069
  digestAgeSeconds: ageSeconds,
10985
11070
  model,
10986
- streamPath: join28(streamsDir, `${base}.jsonl`),
10987
- eventsPath: join28(streamsDir, `${base}.events.jsonl`),
11071
+ streamPath: join29(streamsDir, `${base}.jsonl`),
11072
+ eventsPath: join29(streamsDir, `${base}.events.jsonl`),
10988
11073
  ticket: o.ticket,
10989
11074
  ticketRecordId: o.ticketRecordId ?? null,
10990
11075
  repo: o.repo ?? null,
@@ -11015,7 +11100,7 @@ function describePlan(p) {
11015
11100
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
11016
11101
  function readRunSidecar(streamPath) {
11017
11102
  try {
11018
- const sc = JSON.parse(readFileSync24(pidPathFor(streamPath), "utf8"));
11103
+ const sc = JSON.parse(readFileSync25(pidPathFor(streamPath), "utf8"));
11019
11104
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
11020
11105
  } catch {
11021
11106
  return null;
@@ -11588,19 +11673,19 @@ function runFinished(streamPath) {
11588
11673
  const path = `${streamPath}.meta.json`;
11589
11674
  if (!existsSync27(path)) return false;
11590
11675
  try {
11591
- return JSON.parse(readFileSync25(path, "utf8")).running !== true;
11676
+ return JSON.parse(readFileSync26(path, "utf8")).running !== true;
11592
11677
  } catch {
11593
11678
  return true;
11594
11679
  }
11595
11680
  }
11596
11681
  function orphanedStreams(o) {
11597
- const dir = join29(o.stateDir, "streams");
11682
+ const dir = join30(o.stateDir, "streams");
11598
11683
  if (!existsSync27(dir)) return [];
11599
11684
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
11600
11685
  const out = [];
11601
11686
  for (const name of readdirSync9(dir)) {
11602
11687
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11603
- const streamPath = join29(dir, name);
11688
+ const streamPath = join30(dir, name);
11604
11689
  if (runFinished(streamPath)) continue;
11605
11690
  const sidecar = readRunSidecar(streamPath);
11606
11691
  if (!sidecar || sidecar.route !== o.routeLabel) continue;
@@ -11654,14 +11739,14 @@ async function reconcileRuns(o) {
11654
11739
 
11655
11740
  // src/live-runs.ts
11656
11741
  import { existsSync as existsSync28, readdirSync as readdirSync10, statSync as statSync9 } from "fs";
11657
- import { join as join30 } from "path";
11742
+ import { join as join31 } from "path";
11658
11743
  function liveRuns(stateDir, isAlive = runIsAlive) {
11659
- const dir = join30(stateDir, "streams");
11744
+ const dir = join31(stateDir, "streams");
11660
11745
  if (!existsSync28(dir)) return [];
11661
11746
  const out = [];
11662
11747
  for (const name of readdirSync10(dir)) {
11663
11748
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11664
- const streamPath = join30(dir, name);
11749
+ const streamPath = join31(dir, name);
11665
11750
  if (runFinished(streamPath)) continue;
11666
11751
  const sidecar = readRunSidecar(streamPath);
11667
11752
  if (!sidecar || !isAlive(sidecar)) continue;
@@ -11671,12 +11756,12 @@ function liveRuns(stateDir, isAlive = runIsAlive) {
11671
11756
  }
11672
11757
  function runActivity(stateDir, isAlive = runIsAlive) {
11673
11758
  const activeRuns = liveRuns(stateDir, isAlive).length;
11674
- const dir = join30(stateDir, "streams");
11759
+ const dir = join31(stateDir, "streams");
11675
11760
  let newest = 0;
11676
11761
  if (existsSync28(dir)) {
11677
11762
  for (const name of readdirSync10(dir)) {
11678
11763
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11679
- const streamPath = join30(dir, name);
11764
+ const streamPath = join31(dir, name);
11680
11765
  if (!runFinished(streamPath)) continue;
11681
11766
  try {
11682
11767
  newest = Math.max(newest, statSync9(`${streamPath}.meta.json`).mtimeMs);
@@ -12716,10 +12801,10 @@ function planSandbox(repo, decl, warm = {}) {
12716
12801
  const layer = repoLayer(repo, decl, crew, warm);
12717
12802
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
12718
12803
  }
12719
- var sandboxStatePath = (stateDir) => join31(stateDir, "sandbox-images.json");
12804
+ var sandboxStatePath = (stateDir) => join32(stateDir, "sandbox-images.json");
12720
12805
  function readSandboxState(stateDir) {
12721
12806
  try {
12722
- const v = JSON.parse(readFileSync26(sandboxStatePath(stateDir), "utf8"));
12807
+ const v = JSON.parse(readFileSync27(sandboxStatePath(stateDir), "utf8"));
12723
12808
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
12724
12809
  } catch {
12725
12810
  return {};
@@ -12770,7 +12855,7 @@ var engineRemoveImage = (engine = "docker", exec = realExec2) => (tag) => {
12770
12855
  } catch {
12771
12856
  }
12772
12857
  };
12773
- var sandboxLogDir = (stateDir) => join31(stateDir, "sandbox-logs");
12858
+ var sandboxLogDir = (stateDir) => join32(stateDir, "sandbox-logs");
12774
12859
  async function buildSandboxImage(plan, deps) {
12775
12860
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
12776
12861
  const state = readSandboxState(deps.stateDir);
@@ -12780,19 +12865,19 @@ async function buildSandboxImage(plan, deps) {
12780
12865
  }
12781
12866
  const logDir = sandboxLogDir(deps.stateDir);
12782
12867
  mkdirSync19(logDir, { recursive: true });
12783
- const logPath = join31(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
12868
+ const logPath = join32(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
12784
12869
  const builtLayers = [];
12785
12870
  for (const layer of plan.layers) {
12786
12871
  if (deps.hasImage(layer.tag)) continue;
12787
- const ctx = join31(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
12872
+ const ctx = join32(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
12788
12873
  mkdirSync19(ctx, { recursive: true });
12789
12874
  try {
12790
- writeFileSync20(join31(ctx, "Dockerfile"), layer.dockerfile);
12875
+ writeFileSync20(join32(ctx, "Dockerfile"), layer.dockerfile);
12791
12876
  for (const [name, content] of Object.entries(layer.files)) {
12792
- mkdirSync19(dirname14(join31(ctx, name)), { recursive: true });
12793
- writeFileSync20(join31(ctx, name), content);
12877
+ mkdirSync19(dirname14(join32(ctx, name)), { recursive: true });
12878
+ writeFileSync20(join32(ctx, name), content);
12794
12879
  }
12795
- const code = await deps.run(["build", "--tag", layer.tag, "--file", join31(ctx, "Dockerfile"), ctx], logPath);
12880
+ const code = await deps.run(["build", "--tag", layer.tag, "--file", join32(ctx, "Dockerfile"), ctx], logPath);
12796
12881
  if (code !== 0) {
12797
12882
  state[plan.repo] = { ...entry, failure: { tag: plan.tag, at: now().toISOString(), logPath } };
12798
12883
  writeSandboxState(deps.stateDir, state);
@@ -12981,9 +13066,9 @@ function managedRuntimeWarnings(kind, ship) {
12981
13066
 
12982
13067
  // src/selftest.ts
12983
13068
  import { spawn as spawn6 } from "child_process";
12984
- import { existsSync as existsSync30, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync27, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13069
+ import { existsSync as existsSync30, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
12985
13070
  import { tmpdir as tmpdir4 } from "os";
12986
- import { join as join32 } from "path";
13071
+ import { join as join33 } from "path";
12987
13072
  var SELFTEST_TIMEOUT_MS = 12e4;
12988
13073
  var KEY_VAR = "CREW_API_KEY";
12989
13074
  var SELFTEST_PROMPT = "This is a read-only connectivity check. Use the tracker MCP tool to read this workspace (the workspaces find-one tool, using the workspace id below) and reply with the workspace's name alone, on one line. Do not write anything, do not create files, and do not use any other tool.\n\nWorkspace id: ";
@@ -13144,7 +13229,7 @@ async function runSelftest(i, deps = realDeps) {
13144
13229
  ...adapter.runEnv(spec),
13145
13230
  [KEY_VAR]: i.apiKey
13146
13231
  };
13147
- const cwd = mkdtempSync2(join32(tmpdir4(), "crew-selftest-"));
13232
+ const cwd = mkdtempSync2(join33(tmpdir4(), "crew-selftest-"));
13148
13233
  try {
13149
13234
  const run3 = await deps.run({
13150
13235
  bin: invocation.bin,
@@ -13177,11 +13262,11 @@ function formatResult(r) {
13177
13262
  const secs = `${(r.ms / 1e3).toFixed(1)}s`;
13178
13263
  return r.ok ? `${r.toolchain}: PASS ${r.detail} (${secs}${tok})` : `${r.toolchain}: FAIL ${r.failure} \u2014 ${r.detail} (${secs})`;
13179
13264
  }
13180
- var fileFor2 = (stateDir) => join32(stateDir, "selftest.json");
13265
+ var fileFor2 = (stateDir) => join33(stateDir, "selftest.json");
13181
13266
  function readSelftest(stateDir) {
13182
13267
  try {
13183
13268
  const p = fileFor2(stateDir);
13184
- return existsSync30(p) ? JSON.parse(readFileSync27(p, "utf8")) : {};
13269
+ return existsSync30(p) ? JSON.parse(readFileSync28(p, "utf8")) : {};
13185
13270
  } catch {
13186
13271
  return {};
13187
13272
  }
@@ -13202,7 +13287,7 @@ function doctorSelftestLines(stored, toolchains) {
13202
13287
  }
13203
13288
 
13204
13289
  // src/cli.ts
13205
- import { resolve as resolve7, dirname as dirname33, join as join54, basename as basename16 } from "path";
13290
+ import { resolve as resolve7, dirname as dirname33, join as join55, basename as basename16 } from "path";
13206
13291
  import { fileURLToPath } from "url";
13207
13292
  import { hostname as hostname4, homedir as homedir11 } from "os";
13208
13293
  import { spawnSync as spawnSync11 } from "child_process";
@@ -13400,14 +13485,14 @@ function rosterMarkdown(roster, meId) {
13400
13485
  }
13401
13486
 
13402
13487
  // src/connect-resume.ts
13403
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync28, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
13404
- import { dirname as dirname15, join as join33 } from "path";
13488
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync29, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
13489
+ import { dirname as dirname15, join as join34 } from "path";
13405
13490
  function lastSessionFile(stateDir) {
13406
- return join33(stateDir, "connect-session.json");
13491
+ return join34(stateDir, "connect-session.json");
13407
13492
  }
13408
13493
  function readPointers(stateDir) {
13409
13494
  try {
13410
- const parsed = JSON.parse(readFileSync28(lastSessionFile(stateDir), "utf8"));
13495
+ const parsed = JSON.parse(readFileSync29(lastSessionFile(stateDir), "utf8"));
13411
13496
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
13412
13497
  const out = {};
13413
13498
  for (const [host, v] of Object.entries(parsed)) {
@@ -13507,15 +13592,15 @@ async function signOutSessions(o) {
13507
13592
  }
13508
13593
 
13509
13594
  // src/reset.ts
13510
- import { existsSync as existsSync31, mkdirSync as mkdirSync22, readFileSync as readFileSync29, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
13511
- import { dirname as dirname16, join as join34 } from "path";
13595
+ import { existsSync as existsSync31, mkdirSync as mkdirSync22, readFileSync as readFileSync30, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
13596
+ import { dirname as dirname16, join as join35 } from "path";
13512
13597
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13513
13598
  function clonedCheckoutsFile(stateDir) {
13514
- return join34(stateDir, "cloned-checkouts.json");
13599
+ return join35(stateDir, "cloned-checkouts.json");
13515
13600
  }
13516
13601
  function readClonedCheckouts(stateDir) {
13517
13602
  try {
13518
- const parsed = JSON.parse(readFileSync29(clonedCheckoutsFile(stateDir), "utf8"));
13603
+ const parsed = JSON.parse(readFileSync30(clonedCheckoutsFile(stateDir), "utf8"));
13519
13604
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
13520
13605
  } catch {
13521
13606
  return [];
@@ -13602,10 +13687,10 @@ function removeCheckout(dir, dryRun, fx) {
13602
13687
  }
13603
13688
  function removeState(stateDir, dryRun) {
13604
13689
  const out = [];
13605
- const targets = [join34(stateDir, "resolved"), join34(stateDir, "keys"), clonedCheckoutsFile(stateDir), join34(stateDir, "connect-session.json"), join34(stateDir, "reload-requested"), join34(stateDir, "daemon.started")];
13690
+ const targets = [join35(stateDir, "resolved"), join35(stateDir, "keys"), clonedCheckoutsFile(stateDir), join35(stateDir, "connect-session.json"), join35(stateDir, "reload-requested"), join35(stateDir, "daemon.started")];
13606
13691
  try {
13607
13692
  for (const name of readdirSync12(stateDir)) {
13608
- if (name.startsWith(".") && statSync10(join34(stateDir, name)).isFile()) targets.push(join34(stateDir, name));
13693
+ if (name.startsWith(".") && statSync10(join35(stateDir, name)).isFile()) targets.push(join35(stateDir, name));
13609
13694
  }
13610
13695
  } catch {
13611
13696
  }
@@ -13625,7 +13710,7 @@ function wipeStateDir(stateDir, dryRun) {
13625
13710
  return [{ step: "state", target: stateDir, action: "absent", detail: "no state directory" }];
13626
13711
  }
13627
13712
  if (!names.length) return [{ step: "state", target: stateDir, action: "absent", detail: "already empty" }];
13628
- if (!dryRun) for (const n of names) rmSync18(join34(stateDir, n), { recursive: true, force: true });
13713
+ if (!dryRun) for (const n of names) rmSync18(join35(stateDir, n), { recursive: true, force: true });
13629
13714
  return [{ step: "state", target: stateDir, action: mark(dryRun), detail: `${names.length} entr${names.length === 1 ? "y" : "ies"}` }];
13630
13715
  }
13631
13716
  function describeResetStep(s) {
@@ -13706,9 +13791,9 @@ async function runDeprovision(o, fx, deps) {
13706
13791
  }
13707
13792
 
13708
13793
  // src/pool-member.ts
13709
- import { mkdirSync as mkdirSync23, readFileSync as readFileSync30, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
13794
+ import { mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
13710
13795
  import { homedir as homedir8 } from "os";
13711
- import { dirname as dirname17, join as join35 } from "path";
13796
+ import { dirname as dirname17, join as join36 } from "path";
13712
13797
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
13713
13798
  function backoffMs(attempt) {
13714
13799
  return Math.min(6e4, 5e3 * 2 ** Math.max(0, attempt));
@@ -13939,14 +14024,14 @@ async function runPoolMember(deps) {
13939
14024
  }
13940
14025
  }
13941
14026
  function defaultMemberDir(env = process.env) {
13942
- return env.CREW_POOL_DIR ?? join35(env.XDG_STATE_HOME ?? join35(homedir8(), ".local", "state"), "crew-pool-member");
14027
+ return env.CREW_POOL_DIR ?? join36(env.XDG_STATE_HOME ?? join36(homedir8(), ".local", "state"), "crew-pool-member");
13943
14028
  }
13944
14029
  function fileMemberStore(dir) {
13945
- const file = join35(dir, "member.json");
14030
+ const file = join36(dir, "member.json");
13946
14031
  return {
13947
14032
  read() {
13948
14033
  try {
13949
- return JSON.parse(readFileSync30(file, "utf8"));
14034
+ return JSON.parse(readFileSync31(file, "utf8"));
13950
14035
  } catch {
13951
14036
  return {};
13952
14037
  }
@@ -14012,10 +14097,10 @@ import { hostname as hostname3 } from "os";
14012
14097
  // src/machine-id.ts
14013
14098
  import { execFileSync as execFileSync10 } from "child_process";
14014
14099
  import { createHash as createHash7 } from "crypto";
14015
- import { existsSync as existsSync32, mkdirSync as mkdirSync24, readFileSync as readFileSync31, writeFileSync as writeFileSync25 } from "fs";
14016
- import { dirname as dirname18, join as join36 } from "path";
14100
+ import { existsSync as existsSync32, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14101
+ import { dirname as dirname18, join as join37 } from "path";
14017
14102
  function machineIdPathFor(stateDir) {
14018
- return join36(stateDir, "machine-id");
14103
+ return join37(stateDir, "machine-id");
14019
14104
  }
14020
14105
  var sha256 = (s) => createHash7("sha256").update(s).digest("hex");
14021
14106
  function readRawMachineId(platform2 = process.platform) {
@@ -14029,7 +14114,7 @@ function readRawMachineId(platform2 = process.platform) {
14029
14114
  }
14030
14115
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
14031
14116
  if (existsSync32(p)) {
14032
- const id = readFileSync31(p, "utf8").trim();
14117
+ const id = readFileSync32(p, "utf8").trim();
14033
14118
  if (id) return id;
14034
14119
  }
14035
14120
  }
@@ -14041,7 +14126,7 @@ function ensureMachineId(stateDir, opts = {}) {
14041
14126
  if (opts.override?.trim()) return sha256(opts.override.trim());
14042
14127
  const path = machineIdPathFor(stateDir);
14043
14128
  if (existsSync32(path)) {
14044
- const cached = readFileSync31(path, "utf8").trim();
14129
+ const cached = readFileSync32(path, "utf8").trim();
14045
14130
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
14046
14131
  }
14047
14132
  const raw = (opts.read ?? readRawMachineId)();
@@ -14061,7 +14146,7 @@ function matchShipRows(ships, shipName2, machineId) {
14061
14146
 
14062
14147
  // src/provision.ts
14063
14148
  import { spawnSync as spawnSync8 } from "child_process";
14064
- import { existsSync as existsSync33, readFileSync as readFileSync32, writeFileSync as writeFileSync26 } from "fs";
14149
+ import { existsSync as existsSync33, readFileSync as readFileSync33, writeFileSync as writeFileSync26 } from "fs";
14065
14150
  import { hostname as hostname2 } from "os";
14066
14151
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
14067
14152
 
@@ -14070,7 +14155,7 @@ import { parseDocument as parseDocument2 } from "yaml";
14070
14155
 
14071
14156
  // src/repos-cmd.ts
14072
14157
  import { homedir as homedir9 } from "os";
14073
- import { basename as basename10, join as join37, resolve as resolve4 } from "path";
14158
+ import { basename as basename10, join as join38, resolve as resolve4 } from "path";
14074
14159
  import { isMap, isSeq, parseDocument, stringify as stringify2 } from "yaml";
14075
14160
  var ReposError = class extends Error {
14076
14161
  };
@@ -14348,7 +14433,7 @@ function planRepoClone(i) {
14348
14433
  }
14349
14434
  const ship = doc.get("ship", true);
14350
14435
  const basePath = String(m.get("reposBasePath") ?? (isMap(ship) ? ship.get("reposBasePath") : void 0) ?? "~/Crew");
14351
- const target = i.dir ? expandDir(i.dir, i.base) : join37(expandDir(basePath, i.base), i.route.split("/")[0], name);
14436
+ const target = i.dir ? expandDir(i.dir, i.base) : join38(expandDir(basePath, i.base), i.route.split("/")[0], name);
14352
14437
  const add = planRepoAdd({ text: i.text, base: i.base, route: i.route, path: target, origin: remote, resolved: i.resolved, name, isCheckout: i.isCheckout });
14353
14438
  const forgeHost = i.resolved?.repoForgeHosts?.[id];
14354
14439
  return { ...add, remote, target, ...forgeHost ? { forgeHost } : {} };
@@ -14663,7 +14748,7 @@ function realProvisionDeps(crewHome) {
14663
14748
  enableRoute(route2) {
14664
14749
  const file = configFile();
14665
14750
  if (!file) return false;
14666
- const text2 = readFileSync32(file, "utf8");
14751
+ const text2 = readFileSync33(file, "utf8");
14667
14752
  const doc = parseDocument3(text2);
14668
14753
  const routes = doc.get("routes");
14669
14754
  const entry = routes?.items?.find((r) => r.get("route") === route2);
@@ -14689,7 +14774,7 @@ function realProvisionDeps(crewHome) {
14689
14774
  continue;
14690
14775
  }
14691
14776
  for (const p of plan.unitPaths) {
14692
- if (!existsSync33(p) || readFileSync32(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14777
+ if (!existsSync33(p) || readFileSync33(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14693
14778
  }
14694
14779
  }
14695
14780
  const label = labelFor(crewHome, "run");
@@ -14713,7 +14798,7 @@ function realProvisionDeps(crewHome) {
14713
14798
  setShipSetting(key, value2) {
14714
14799
  const file = configFile();
14715
14800
  if (!file) return false;
14716
- const doc = parseDocument3(readFileSync32(file, "utf8"));
14801
+ const doc = parseDocument3(readFileSync33(file, "utf8"));
14717
14802
  if (doc.getIn(["ship", key]) === value2) return false;
14718
14803
  doc.setIn(["ship", key], value2);
14719
14804
  writeFileSync26(file, doc.toString());
@@ -14722,7 +14807,7 @@ function realProvisionDeps(crewHome) {
14722
14807
  applyManagedDefaults() {
14723
14808
  const file = configFile();
14724
14809
  if (!file) return [];
14725
- const doc = parseDocument3(readFileSync32(file, "utf8"));
14810
+ const doc = parseDocument3(readFileSync33(file, "utf8"));
14726
14811
  const wrote = applyManagedDefaultsToDoc(doc);
14727
14812
  if (wrote.length) writeFileSync26(file, doc.toString());
14728
14813
  return wrote;
@@ -14741,9 +14826,9 @@ host=${host}
14741
14826
 
14742
14827
  // src/tunnel.ts
14743
14828
  import { spawn as spawn7 } from "child_process";
14744
- import { existsSync as existsSync34, mkdirSync as mkdirSync25, readFileSync as readFileSync33, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14829
+ import { existsSync as existsSync34, mkdirSync as mkdirSync25, readFileSync as readFileSync34, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14745
14830
  import { randomInt } from "crypto";
14746
- import { join as join38 } from "path";
14831
+ import { join as join39 } from "path";
14747
14832
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
14748
14833
  var defaultSpawn = (cmd, args) => spawn7(cmd, args, DEFAULT_SPAWN_OPTIONS);
14749
14834
  var SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -14815,20 +14900,20 @@ function startTunnel(opts) {
14815
14900
  };
14816
14901
  }
14817
14902
  function tunnelStatePath(stateDir, workspaceId) {
14818
- return join38(stateDir, "passengers", `${workspaceId}.json`);
14903
+ return join39(stateDir, "passengers", `${workspaceId}.json`);
14819
14904
  }
14820
14905
  function readPersistedTunnel(stateDir, workspaceId) {
14821
14906
  const path = tunnelStatePath(stateDir, workspaceId);
14822
14907
  if (!existsSync34(path)) return void 0;
14823
14908
  try {
14824
- return JSON.parse(readFileSync33(path, "utf8"));
14909
+ return JSON.parse(readFileSync34(path, "utf8"));
14825
14910
  } catch {
14826
14911
  return void 0;
14827
14912
  }
14828
14913
  }
14829
14914
  function writePersistedTunnel(stateDir, state) {
14830
14915
  const path = tunnelStatePath(stateDir, state.workspaceId);
14831
- mkdirSync25(join38(stateDir, "passengers"), { recursive: true });
14916
+ mkdirSync25(join39(stateDir, "passengers"), { recursive: true });
14832
14917
  writeFileSync27(path, `${JSON.stringify(state, null, 2)}
14833
14918
  `);
14834
14919
  }
@@ -14931,7 +15016,7 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
14931
15016
  }
14932
15017
  }
14933
15018
  function readAllPersistedWorkspaceIds(stateDir) {
14934
- const dir = join38(stateDir, "passengers");
15019
+ const dir = join39(stateDir, "passengers");
14935
15020
  if (!existsSync34(dir)) return [];
14936
15021
  return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
14937
15022
  }
@@ -15834,7 +15919,7 @@ ${waiting.map((t) => `| ${displayKey2(t)} | ${t.status} | ${blockers(t, i)} |`).
15834
15919
 
15835
15920
  // src/poll.ts
15836
15921
  import { writeFileSync as writeFileSync28 } from "fs";
15837
- import { join as join39 } from "path";
15922
+ import { join as join40 } from "path";
15838
15923
 
15839
15924
  // src/stalled.ts
15840
15925
  var STALLED_AFTER_CYCLES = 30;
@@ -16141,7 +16226,7 @@ async function decideCycle(o) {
16141
16226
  }
16142
16227
  return decision;
16143
16228
  }
16144
- var digestPath = (stateDir, route2, role, runId) => join39(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
16229
+ var digestPath = (stateDir, route2, role, runId) => join40(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
16145
16230
  function otherRunsRemoved(tickets, own) {
16146
16231
  if (!own) return tickets;
16147
16232
  return tickets.filter((t) => t.id === own.ticket.id || !own.inFlight(t.id));
@@ -16327,7 +16412,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16327
16412
  }
16328
16413
 
16329
16414
  // src/repo-refresh.ts
16330
- import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync34, writeFileSync as writeFileSync29 } from "fs";
16415
+ import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16331
16416
  import { dirname as dirname20 } from "path";
16332
16417
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16333
16418
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
@@ -16358,7 +16443,7 @@ function applyRepoRows(resolved, rows) {
16358
16443
  function persistRepoRows(stateDir, routeName, rows) {
16359
16444
  const path = resolvedPathFor(stateDir, routeName);
16360
16445
  if (!existsSync36(path)) return;
16361
- const raw = JSON.parse(readFileSync34(path, "utf8"));
16446
+ const raw = JSON.parse(readFileSync35(path, "utf8"));
16362
16447
  applyRepoRows(raw, rows);
16363
16448
  mkdirSync26(dirname20(path), { recursive: true });
16364
16449
  writeFileSync29(path, `${JSON.stringify(raw, null, 2)}
@@ -16811,7 +16896,7 @@ Ship ${shipName2} found: ${problem}. The release refuses to squash a ref nobody
16811
16896
  var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.record_id === ticket.id && e.event_type === "local_ref_skip" && (e.message ?? "").includes(problem));
16812
16897
 
16813
16898
  // src/connect-wizard.ts
16814
- import { delimiter, dirname as dirname21, join as join40 } from "path";
16899
+ import { delimiter, dirname as dirname21, join as join41 } from "path";
16815
16900
  import { accessSync as accessSync3, constants as constants3, existsSync as existsSync37, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
16816
16901
  import { stringify as stringify3 } from "yaml";
16817
16902
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
@@ -16834,7 +16919,7 @@ function defaultIsExecutable(p) {
16834
16919
  function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
16835
16920
  for (const dir of pathEnv.split(delimiter)) {
16836
16921
  if (!dir) continue;
16837
- const candidate = join40(dir, name);
16922
+ const candidate = join41(dir, name);
16838
16923
  if (isExecutable(candidate)) return candidate;
16839
16924
  }
16840
16925
  return void 0;
@@ -16931,7 +17016,7 @@ Wrote ${o.configPath}.
16931
17016
  // src/connect-json.ts
16932
17017
  import { accessSync as accessSync4, constants as constants4 } from "fs";
16933
17018
  import { homedir as homedir10 } from "os";
16934
- import { dirname as dirname22, join as join41 } from "path";
17019
+ import { dirname as dirname22, join as join42 } from "path";
16935
17020
  import { delimiter as delimiter2 } from "path";
16936
17021
  var CHOICE_EXIT_CODE = 3;
16937
17022
  function createEmitter(json2, write) {
@@ -16970,12 +17055,12 @@ function agentBinSearchDirs(home, pathEnv, extraPath) {
16970
17055
  const dirs = [
16971
17056
  ...pathEnv.split(delimiter2),
16972
17057
  ...(extraPath ?? "").split(delimiter2),
16973
- join41(home, ".local/bin"),
16974
- join41(home, ".claude/local"),
17058
+ join42(home, ".local/bin"),
17059
+ join42(home, ".claude/local"),
16975
17060
  "/opt/homebrew/bin",
16976
17061
  "/usr/local/bin",
16977
- join41(home, ".npm-global/bin"),
16978
- join41(home, ".volta/bin")
17062
+ join42(home, ".npm-global/bin"),
17063
+ join42(home, ".volta/bin")
16979
17064
  ];
16980
17065
  return [...new Set(dirs.filter(Boolean))];
16981
17066
  }
@@ -17011,8 +17096,8 @@ function flagWizardAnswers(f, env) {
17011
17096
  }
17012
17097
 
17013
17098
  // src/release-run.ts
17014
- import { readFileSync as readFileSync35, writeFileSync as writeFileSync31, existsSync as existsSync38, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17015
- import { dirname as dirname23, join as join42 } from "path";
17099
+ import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync38, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17100
+ import { dirname as dirname23, join as join43 } from "path";
17016
17101
  import { tmpdir as tmpdir5 } from "os";
17017
17102
 
17018
17103
  // src/release.ts
@@ -17753,13 +17838,13 @@ async function bump(o, size) {
17753
17838
  }
17754
17839
  return printed;
17755
17840
  }
17756
- const files = o.repo.release.versionFiles.map((f) => join42(o.cwd, f));
17841
+ const files = o.repo.release.versionFiles.map((f) => join43(o.cwd, f));
17757
17842
  const first = files[0];
17758
17843
  if (!existsSync38(first)) {
17759
17844
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
17760
17845
  return null;
17761
17846
  }
17762
- const current = JSON.parse(readFileSync35(first, "utf8")).version;
17847
+ const current = JSON.parse(readFileSync36(first, "utf8")).version;
17763
17848
  if (!current) {
17764
17849
  o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
17765
17850
  return null;
@@ -17772,7 +17857,7 @@ async function bump(o, size) {
17772
17857
  return version;
17773
17858
  }
17774
17859
  for (const f of files) {
17775
- const src = readFileSync35(f, "utf8");
17860
+ const src = readFileSync36(f, "utf8");
17776
17861
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
17777
17862
  if (out === src) {
17778
17863
  o.emit.error(`no version field in ${f}`);
@@ -17785,13 +17870,13 @@ async function bump(o, size) {
17785
17870
  function writeChangelog(o, version, entries) {
17786
17871
  const file = o.repo.release.changelog;
17787
17872
  if (!file || entries.length === 0) return;
17788
- const path = join42(o.cwd, file);
17873
+ const path = join43(o.cwd, file);
17789
17874
  const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
17790
17875
  if (o.dryRun) {
17791
17876
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
17792
17877
  return;
17793
17878
  }
17794
- const existing = existsSync38(path) ? readFileSync35(path, "utf8") : "# Changelog\n";
17879
+ const existing = existsSync38(path) ? readFileSync36(path, "utf8") : "# Changelog\n";
17795
17880
  writeFileSync31(path, insertChangelogSection(existing, section));
17796
17881
  }
17797
17882
  async function confirm(o, expected) {
@@ -17885,8 +17970,8 @@ function closeReleaseCheckout(cwd, root, dir, branch, run3 = gitOk) {
17885
17970
  function openReleaseCheckout(o) {
17886
17971
  const { remote, base } = o.repo.branch;
17887
17972
  const branch = `crew/release-${Date.now()}`;
17888
- const root = mkdtempSync3(join42(tmpdir5(), "crew-release-"));
17889
- const dir = join42(root, "checkout");
17973
+ const root = mkdtempSync3(join43(tmpdir5(), "crew-release-"));
17974
+ const dir = join43(root, "checkout");
17890
17975
  o.emit.emit(`release checkout: ${dir}`, { step: "release" });
17891
17976
  const close = () => closeReleaseCheckout(o.cwd, root, dir, branch);
17892
17977
  try {
@@ -17896,10 +17981,10 @@ function openReleaseCheckout(o) {
17896
17981
  throw e;
17897
17982
  }
17898
17983
  for (const f of o.repo.worktrees.copy) {
17899
- const src = join42(o.cwd, f);
17984
+ const src = join43(o.cwd, f);
17900
17985
  if (!existsSync38(src)) continue;
17901
- mkdirSync28(dirname23(join42(dir, f)), { recursive: true });
17902
- copyFileSync(src, join42(dir, f));
17986
+ mkdirSync28(dirname23(join43(dir, f)), { recursive: true });
17987
+ copyFileSync(src, join43(dir, f));
17903
17988
  }
17904
17989
  return { dir, branch, close };
17905
17990
  }
@@ -18489,49 +18574,6 @@ async function mergeAndRelease(o, decision, fresh, releaseBranch, isolated = fal
18489
18574
  return { merged, conflicts, unbuildable, version, tag, deployed, integrated, confirmed, decision };
18490
18575
  }
18491
18576
 
18492
- // src/repo-map.ts
18493
- var CHARS_PER_TOKEN = 4;
18494
- var MAP_TIMEOUT_MS = 6e4;
18495
- function budgetMap(text2, tokenBudget) {
18496
- const body = text2.replace(/\s+$/, "");
18497
- const limit = tokenBudget * CHARS_PER_TOKEN;
18498
- if (body.length <= limit) return body;
18499
- const lines2 = body.split("\n");
18500
- const noteFor = (kept2) => `\u2026 ${lines2.length - kept2} of ${lines2.length} lines cut to fit ship.mapTokenBudget (${tokenBudget} tokens)`;
18501
- let kept = lines2.length;
18502
- let size = body.length;
18503
- while (kept > 0 && size + 1 + noteFor(kept).length > limit) {
18504
- kept--;
18505
- size -= lines2[kept].length + 1;
18506
- }
18507
- return [...lines2.slice(0, kept), noteFor(kept)].join("\n");
18508
- }
18509
- function loadRepoMap(repo, tokenBudget, shellFallback) {
18510
- const script = repo.config.hooks.map;
18511
- if (!script) return Promise.resolve({ skipped: "" });
18512
- return runScript(script, {
18513
- cwd: repo.dir,
18514
- shell: resolveShell(repo.config.shell ?? shellFallback),
18515
- env: { CREW_REPO: repo.name },
18516
- timeoutMs: MAP_TIMEOUT_MS
18517
- }).then(
18518
- (r) => {
18519
- if (r.code !== 0) return { skipped: `the \`map\` hook for ${repo.name} exited ${r.code}, so the brief has no repository map` };
18520
- if (!r.stdout.trim()) return { skipped: `the \`map\` hook for ${repo.name} printed nothing, so the brief has no repository map` };
18521
- return { text: budgetMap(r.stdout, tokenBudget) };
18522
- },
18523
- (e) => ({ skipped: `the \`map\` hook for ${repo.name} could not run (${e instanceof Error ? e.message : String(e)}), so the brief has no repository map` })
18524
- );
18525
- }
18526
- async function withRepoMaps(repos, tokenBudget, shellFallback) {
18527
- return Promise.all(repos.map(async (r) => {
18528
- if (!r.config.hooks.map) return r;
18529
- const out = await loadRepoMap(r, tokenBudget, shellFallback);
18530
- if ("skipped" in out) return out.skipped ? { ...r, mapSkipped: out.skipped } : r;
18531
- return { ...r, map: out.text };
18532
- }));
18533
- }
18534
-
18535
18577
  // src/failure-alert.ts
18536
18578
  var FAILURE_ALERT_MARKER = calloutHeader("CAUTION", "Failure Alert");
18537
18579
  function findOpenFailureAlert(tickets, headline) {
@@ -19265,7 +19307,7 @@ function nextRoles(decision, ran) {
19265
19307
  }
19266
19308
 
19267
19309
  // src/ship-name.ts
19268
- import { existsSync as existsSync40, mkdirSync as mkdirSync29, readFileSync as readFileSync36, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19310
+ import { existsSync as existsSync40, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19269
19311
  import { dirname as dirname24 } from "path";
19270
19312
  import { parseDocument as parseDocument5 } from "yaml";
19271
19313
  var ShipNameError = class extends Error {
@@ -19329,7 +19371,7 @@ async function syncRouteName(i) {
19329
19371
  function readSyncedName(resolvedFile) {
19330
19372
  if (!existsSync40(resolvedFile)) return void 0;
19331
19373
  try {
19332
- const v = JSON.parse(readFileSync36(resolvedFile, "utf8")).shipName;
19374
+ const v = JSON.parse(readFileSync37(resolvedFile, "utf8")).shipName;
19333
19375
  return typeof v === "string" ? v : void 0;
19334
19376
  } catch {
19335
19377
  return void 0;
@@ -19339,7 +19381,7 @@ function writeSyncedName(resolvedFile, name) {
19339
19381
  if (!existsSync40(resolvedFile)) return;
19340
19382
  let raw;
19341
19383
  try {
19342
- raw = JSON.parse(readFileSync36(resolvedFile, "utf8"));
19384
+ raw = JSON.parse(readFileSync37(resolvedFile, "utf8"));
19343
19385
  } catch {
19344
19386
  return;
19345
19387
  }
@@ -19354,7 +19396,7 @@ function writeSyncedName(resolvedFile, name) {
19354
19396
  async function reconcileShipName(i) {
19355
19397
  let local = i.ship.name;
19356
19398
  try {
19357
- local = shipNameOfYaml(readFileSync36(i.configFile, "utf8")) ?? local;
19399
+ local = shipNameOfYaml(readFileSync37(i.configFile, "utf8")) ?? local;
19358
19400
  } catch {
19359
19401
  }
19360
19402
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -19378,7 +19420,7 @@ async function reconcileShipName(i) {
19378
19420
  }
19379
19421
  if (result.status !== "ok") return result;
19380
19422
  if (result.action === "adopt" || result.action === "row-wins") {
19381
- const text2 = readFileSync36(i.configFile, "utf8");
19423
+ const text2 = readFileSync37(i.configFile, "utf8");
19382
19424
  writeFileSync32(i.configFile, setShipNameInYaml(text2, result.name));
19383
19425
  i.ship.name = result.name;
19384
19426
  if (result.action === "row-wins") {
@@ -20038,7 +20080,7 @@ function describeCapability(i) {
20038
20080
  }
20039
20081
 
20040
20082
  // src/agents-sync-run.ts
20041
- import { existsSync as existsSync41, mkdirSync as mkdirSync30, readFileSync as readFileSync37, writeFileSync as writeFileSync33 } from "fs";
20083
+ import { existsSync as existsSync41, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20042
20084
  import { dirname as dirname25 } from "path";
20043
20085
  async function runAgentsSync(target, o) {
20044
20086
  const dryRun = o.dryRun ?? false;
@@ -20072,7 +20114,7 @@ async function runAgentsSync(target, o) {
20072
20114
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
20073
20115
  if (!dryRun) {
20074
20116
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
20075
- const raw = existsSync41(resolvedPath) ? JSON.parse(readFileSync37(resolvedPath, "utf8")) : {};
20117
+ const raw = existsSync41(resolvedPath) ? JSON.parse(readFileSync38(resolvedPath, "utf8")) : {};
20076
20118
  raw.agentPersonas = personaResult.agentPersonas;
20077
20119
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20078
20120
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
@@ -20194,7 +20236,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
20194
20236
 
20195
20237
  // src/repos-check.ts
20196
20238
  import { spawnSync as spawnSync10 } from "child_process";
20197
- import { readFileSync as readFileSync38 } from "fs";
20239
+ import { readFileSync as readFileSync39 } from "fs";
20198
20240
  import { basename as basename11 } from "path";
20199
20241
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
20200
20242
  function checkHookSyntax(script, shell) {
@@ -20228,14 +20270,14 @@ function renderSamples(cfg, dir) {
20228
20270
  });
20229
20271
  }
20230
20272
  function checkRepoSpec(dir, opts = {}) {
20231
- const empty = { parsed: null, effective: null, readiness: [], hooks: [], shadowed: [], samples: [] };
20273
+ const empty = { parsed: null, effective: null, readiness: [], hooks: [], shadowed: [], mapFile: null, samples: [] };
20232
20274
  const file = findRepoConfig(dir);
20233
20275
  if (!file) {
20234
20276
  return { ...empty, file: null, error: `no .crew.yaml, .crew.yml or .crew.json in ${dir}`, exitCode: 1 };
20235
20277
  }
20236
20278
  let parsed;
20237
20279
  try {
20238
- parsed = parseRepoConfig(readFileSync38(file, "utf8"), file);
20280
+ parsed = parseRepoConfig(readFileSync39(file, "utf8"), file);
20239
20281
  } catch (e) {
20240
20282
  if (!(e instanceof RepoConfigError)) throw e;
20241
20283
  return { ...empty, file, error: e.message, exitCode: 1 };
@@ -20246,14 +20288,16 @@ function checkRepoSpec(dir, opts = {}) {
20246
20288
  const engine = effective.hooks.setup ? containerEngineUse(effective.hooks.setup) : null;
20247
20289
  if (engine) readiness.push(`hooks.setup runs \`${engine}\`, and a sandbox has no container engine \u2014 start the service as a process instead (REPO_SPEC.md, "Services inside the sandbox")`);
20248
20290
  }
20249
- const hooks = Object.keys(effective.hooks).map((name) => ({
20291
+ const mapPath = mapFilePath(effective.hooks.map);
20292
+ const mapFile = mapPath ? { ...inspectMapFile(dir, mapPath), budget: opts.ship?.mapTokenBudget ?? DEFAULT_MAP_TOKEN_BUDGET } : null;
20293
+ const hooks = Object.keys(effective.hooks).filter((name) => !(name === "map" && mapPath)).map((name) => ({
20250
20294
  name,
20251
20295
  label: hookLabel(effective, name),
20252
20296
  // CREW-2914: a flavor name is not shell, so it is not syntax-checked.
20253
20297
  syntax: name === "intel" && intelFlavor(effective.hooks.intel) ? { status: "ok" } : checkHookSyntax(effective.hooks[name], effective.shell),
20254
20298
  ...name === "intel" && intelFlavor(effective.hooks.intel) ? { flavor: intelFlavor(effective.hooks.intel) } : {}
20255
20299
  }));
20256
- const failed = hooks.some((h) => h.syntax.status === "error");
20300
+ const failed = hooks.some((h) => h.syntax.status === "error") || mapFile !== null && !mapFile.exists;
20257
20301
  return {
20258
20302
  file,
20259
20303
  parsed,
@@ -20261,6 +20305,7 @@ function checkRepoSpec(dir, opts = {}) {
20261
20305
  readiness,
20262
20306
  hooks,
20263
20307
  shadowed: effective.shadowed,
20308
+ mapFile,
20264
20309
  samples: renderSamples(effective, dir),
20265
20310
  error: null,
20266
20311
  exitCode: failed || opts.strict && readiness.length > 0 ? 1 : 0
@@ -20280,6 +20325,10 @@ function renderRepoCheck(r) {
20280
20325
  ` branch.push ${e.branch.push}`
20281
20326
  ];
20282
20327
  for (const s of r.samples) lines2.push(` ${s.case}: branch ${s.branch}, worktree ../${s.worktree}`);
20328
+ if (r.mapFile) {
20329
+ const m = r.mapFile;
20330
+ lines2.push(m.exists ? ` map: file ${m.path} (${m.lines} lines, about ${m.tokens} tokens; ship budget ${m.budget})` : ` map: file ${m.path} NOT FOUND \u2014 the brief will carry an absence note`);
20331
+ }
20283
20332
  lines2.push(r.hooks.length ? " hooks" : " hooks none");
20284
20333
  for (const h of r.hooks) {
20285
20334
  const named = h.label !== e.hooks[h.name] ? ` (${h.label})` : "";
@@ -20692,7 +20741,7 @@ ${run3.err}` });
20692
20741
  }
20693
20742
 
20694
20743
  // src/sample-ticket.ts
20695
- import { basename as basename12, dirname as dirname26, join as join43 } from "path";
20744
+ import { basename as basename12, dirname as dirname26, join as join44 } from "path";
20696
20745
  var SAMPLE_REPO = "crew-sample";
20697
20746
  var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
20698
20747
  var SAMPLE_MARKER = "sample: true";
@@ -20715,8 +20764,8 @@ has something safe to work on: its test and build hooks always pass, it has no
20715
20764
  remote, and \`crew sample-ticket --remove\` deletes it.
20716
20765
  `;
20717
20766
  function sampleDir(existing, reposBase, workspace) {
20718
- if (existing.length > 0) return join43(dirname26(existing[0]), SAMPLE_REPO);
20719
- return join43(reposBase, workspace, SAMPLE_REPO);
20767
+ if (existing.length > 0) return join44(dirname26(existing[0]), SAMPLE_REPO);
20768
+ return join44(reposBase, workspace, SAMPLE_REPO);
20720
20769
  }
20721
20770
  var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
20722
20771
  function sampleDescription() {
@@ -20760,8 +20809,8 @@ function planSampleTicket(i) {
20760
20809
  }
20761
20810
 
20762
20811
  // src/worktree-sweep.ts
20763
- import { existsSync as existsSync42, readFileSync as readFileSync39, readdirSync as readdirSync14 } from "fs";
20764
- import { basename as basename13, dirname as dirname27, join as join44, resolve as resolve5 } from "path";
20812
+ import { existsSync as existsSync42, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
20813
+ import { basename as basename13, dirname as dirname27, join as join45, resolve as resolve5 } from "path";
20765
20814
  function planWorktreeSweep(target, tickets, contract) {
20766
20815
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
20767
20816
  const verified = contract.statuses.verified;
@@ -20838,7 +20887,7 @@ function siblingWorktreeDirs(target) {
20838
20887
  for (const name of names) {
20839
20888
  if (!name.startsWith(stem)) continue;
20840
20889
  const m = DIR_NUMBER_SUFFIXED.exec(name);
20841
- const path = join44(parent, name);
20890
+ const path = join45(parent, name);
20842
20891
  if (m && !registered.has(resolve5(path))) out.push({ path, number: m[1] });
20843
20892
  }
20844
20893
  return out;
@@ -20941,7 +20990,7 @@ function isHandBackMerge(path, base) {
20941
20990
  if (!msgPath) return false;
20942
20991
  let first;
20943
20992
  try {
20944
- first = readFileSync39(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
20993
+ first = readFileSync40(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
20945
20994
  } catch {
20946
20995
  return false;
20947
20996
  }
@@ -21047,13 +21096,13 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
21047
21096
 
21048
21097
  // src/stream-sweep.ts
21049
21098
  import { existsSync as existsSync43, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
21050
- import { join as join45 } from "path";
21099
+ import { join as join46 } from "path";
21051
21100
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
21052
21101
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
21053
- const dir = join45(stateDir, "streams");
21102
+ const dir = join46(stateDir, "streams");
21054
21103
  if (!existsSync43(dir)) return [];
21055
21104
  const cutoff = now - retentionDays * DAY_MS2;
21056
- return readdirSync15(dir).map((name) => join45(dir, name)).filter((path) => {
21105
+ return readdirSync15(dir).map((name) => join46(dir, name)).filter((path) => {
21057
21106
  try {
21058
21107
  return statSync12(path).mtimeMs < cutoff;
21059
21108
  } catch {
@@ -21081,7 +21130,7 @@ function applyStreamSweep(paths, dryRun, log) {
21081
21130
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21082
21131
  if (!existsSync43(stateDir)) return [];
21083
21132
  const cutoff = now - retentionDays * DAY_MS2;
21084
- return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join45(stateDir, name)).filter((path) => {
21133
+ return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join46(stateDir, name)).filter((path) => {
21085
21134
  try {
21086
21135
  return statSync12(path).mtimeMs < cutoff;
21087
21136
  } catch {
@@ -21091,12 +21140,12 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
21091
21140
  }
21092
21141
 
21093
21142
  // src/cli.ts
21094
- import { realpathSync as realpathSync3, readFileSync as readFileSync45, existsSync as existsSync50, mkdirSync as mkdirSync38, writeFileSync as writeFileSync38, rmSync as rmSync25, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
21143
+ import { realpathSync as realpathSync3, readFileSync as readFileSync46, existsSync as existsSync50, mkdirSync as mkdirSync38, writeFileSync as writeFileSync38, rmSync as rmSync25, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
21095
21144
  import { createInterface } from "readline/promises";
21096
21145
 
21097
21146
  // src/periscope-cmd.ts
21098
- import { existsSync as existsSync44, mkdirSync as mkdirSync31, readFileSync as readFileSync40, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21099
- import { join as join46 } from "path";
21147
+ import { existsSync as existsSync44, mkdirSync as mkdirSync31, readFileSync as readFileSync41, rmSync as rmSync21, writeFileSync as writeFileSync34 } from "fs";
21148
+ import { join as join47 } from "path";
21100
21149
  var PeriscopeError = class extends Error {
21101
21150
  };
21102
21151
  var LivePeriscopeError = class extends PeriscopeError {
@@ -21106,8 +21155,8 @@ var LivePeriscopeError = class extends PeriscopeError {
21106
21155
  this.data = { refused: "live_periscope_exists", ...live };
21107
21156
  }
21108
21157
  };
21109
- var priorDir = (stateDir) => join46(stateDir, "periscope-priors");
21110
- var priorPath = (stateDir, ticketRecordId) => join46(priorDir(stateDir), `${ticketRecordId}.json`);
21158
+ var priorDir = (stateDir) => join47(stateDir, "periscope-priors");
21159
+ var priorPath = (stateDir, ticketRecordId) => join47(priorDir(stateDir), `${ticketRecordId}.json`);
21111
21160
  function removePeriscopePrior(stateDir, ticketRecordId) {
21112
21161
  rmSync21(priorPath(stateDir, ticketRecordId), { force: true });
21113
21162
  }
@@ -21121,7 +21170,7 @@ function writePeriscopePrior(stateDir, ticketRecordId, prior) {
21121
21170
  function readPeriscopePrior(stateDir, ticketRecordId) {
21122
21171
  try {
21123
21172
  const p = priorPath(stateDir, ticketRecordId);
21124
- return existsSync44(p) ? JSON.parse(readFileSync40(p, "utf8")) : void 0;
21173
+ return existsSync44(p) ? JSON.parse(readFileSync41(p, "utf8")) : void 0;
21125
21174
  } catch {
21126
21175
  return void 0;
21127
21176
  }
@@ -21575,7 +21624,7 @@ ${tail2(preview.output)}`);
21575
21624
  // src/periscope-idle.ts
21576
21625
  import { execFileSync as execFileSync12 } from "child_process";
21577
21626
  import { statSync as statSync13 } from "fs";
21578
- import { join as join47 } from "path";
21627
+ import { join as join48 } from "path";
21579
21628
  var MAX_SCANNED_FILES = 2e4;
21580
21629
  function newestWorktreeMtime(root) {
21581
21630
  const a = newestTrackedMtime(root);
@@ -21600,7 +21649,7 @@ function newestTrackedMtime(root) {
21600
21649
  if (!rel) continue;
21601
21650
  if (++scanned > MAX_SCANNED_FILES) break;
21602
21651
  try {
21603
- const m = statSync13(join47(root, rel)).mtimeMs;
21652
+ const m = statSync13(join48(root, rel)).mtimeMs;
21604
21653
  if (newest === null || m > newest) newest = m;
21605
21654
  } catch {
21606
21655
  }
@@ -21676,12 +21725,12 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
21676
21725
 
21677
21726
  // src/ssh-keys.ts
21678
21727
  import { execFileSync as execFileSync13 } from "child_process";
21679
- import { existsSync as existsSync45, mkdirSync as mkdirSync32, readFileSync as readFileSync41, chmodSync as chmodSync3 } from "fs";
21680
- import { dirname as dirname28, join as join48 } from "path";
21728
+ import { existsSync as existsSync45, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
21729
+ import { dirname as dirname28, join as join49 } from "path";
21681
21730
  var SshKeyError = class extends Error {
21682
21731
  };
21683
21732
  function shipSshKeyPathFor(stateDir) {
21684
- return join48(stateDir, "ssh", "id_ed25519");
21733
+ return join49(stateDir, "ssh", "id_ed25519");
21685
21734
  }
21686
21735
  function ensureShipSshKeypair(stateDir) {
21687
21736
  const privateKeyPath = shipSshKeyPathFor(stateDir);
@@ -21710,7 +21759,7 @@ function ensureShipSshKeypair(stateDir) {
21710
21759
  if (!existsSync45(publicKeyPath)) {
21711
21760
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
21712
21761
  }
21713
- const publicKey = readFileSync41(publicKeyPath, "utf8").trim();
21762
+ const publicKey = readFileSync42(publicKeyPath, "utf8").trim();
21714
21763
  return { privateKeyPath, publicKeyPath, publicKey };
21715
21764
  }
21716
21765
  function sshKeygenAvailable() {
@@ -21874,8 +21923,8 @@ async function unregisterEndpoint(workspaceId, info, d) {
21874
21923
  import { createServer as createHttpServer } from "http";
21875
21924
  import { spawn as spawn9 } from "child_process";
21876
21925
  import { randomBytes } from "crypto";
21877
- import { existsSync as existsSync46, mkdirSync as mkdirSync34, readFileSync as readFileSync42, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21878
- import { join as join49 } from "path";
21926
+ import { existsSync as existsSync46, mkdirSync as mkdirSync34, readFileSync as readFileSync43, writeFileSync as writeFileSync35, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21927
+ import { join as join50 } from "path";
21879
21928
 
21880
21929
  // src/passenger-mirror.ts
21881
21930
  import { mkdirSync as mkdirSync33 } from "fs";
@@ -22005,20 +22054,20 @@ var defaultSpawn2 = (cmd, args, env) => {
22005
22054
  return child;
22006
22055
  };
22007
22056
  function syncDaemonStatePath(stateDir, workspaceId) {
22008
- return join49(stateDir, "passengers", `${workspaceId}-sync.json`);
22057
+ return join50(stateDir, "passengers", `${workspaceId}-sync.json`);
22009
22058
  }
22010
22059
  function readPersistedSyncDaemon(stateDir, workspaceId) {
22011
22060
  const path = syncDaemonStatePath(stateDir, workspaceId);
22012
22061
  if (!existsSync46(path)) return void 0;
22013
22062
  try {
22014
- return JSON.parse(readFileSync42(path, "utf8"));
22063
+ return JSON.parse(readFileSync43(path, "utf8"));
22015
22064
  } catch {
22016
22065
  return void 0;
22017
22066
  }
22018
22067
  }
22019
22068
  function writePersistedSyncDaemon(stateDir, state) {
22020
22069
  const path = syncDaemonStatePath(stateDir, state.workspaceId);
22021
- mkdirSync34(join49(stateDir, "passengers"), { recursive: true });
22070
+ mkdirSync34(join50(stateDir, "passengers"), { recursive: true });
22022
22071
  writeFileSync35(path, `${JSON.stringify(state, null, 2)}
22023
22072
  `);
22024
22073
  }
@@ -22039,7 +22088,7 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
22039
22088
  }
22040
22089
  }
22041
22090
  function readAllPersistedSyncWorkspaceIds(stateDir) {
22042
- const dir = join49(stateDir, "passengers");
22091
+ const dir = join50(stateDir, "passengers");
22043
22092
  if (!existsSync46(dir)) return [];
22044
22093
  const suffix = "-sync.json";
22045
22094
  return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
@@ -22537,8 +22586,8 @@ function describeCause(err3) {
22537
22586
  }
22538
22587
 
22539
22588
  // src/daemon.ts
22540
- import { existsSync as existsSync47, mkdirSync as mkdirSync36, readFileSync as readFileSync43, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
22541
- import { join as join50 } from "path";
22589
+ import { existsSync as existsSync47, mkdirSync as mkdirSync36, readFileSync as readFileSync44, readdirSync as readdirSync17, rmSync as rmSync22, statSync as statSync14, writeFileSync as writeFileSync36 } from "fs";
22590
+ import { join as join51 } from "path";
22542
22591
  import { randomBytes as randomBytes2 } from "crypto";
22543
22592
  var DEFAULT_FLOOR_MS = 5e3;
22544
22593
  var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
@@ -22626,7 +22675,7 @@ function latestMtime(path) {
22626
22675
  if (!stat2.isDirectory()) return 0;
22627
22676
  let latest = stat2.mtimeMs;
22628
22677
  for (const entry of readdirSync17(path, { withFileTypes: true })) {
22629
- latest = Math.max(latest, latestMtime(join50(path, entry.name)));
22678
+ latest = Math.max(latest, latestMtime(join51(path, entry.name)));
22630
22679
  }
22631
22680
  return latest;
22632
22681
  }
@@ -22638,7 +22687,7 @@ function makeStaleChecker(paths, opts = {}) {
22638
22687
  return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync47(opts.reloadSentinel);
22639
22688
  }
22640
22689
  function reloadSentinelFor(stateDir) {
22641
- return join50(stateDir, "reload-requested");
22690
+ return join51(stateDir, "reload-requested");
22642
22691
  }
22643
22692
  function readReloadRequestedAt(stateDir) {
22644
22693
  try {
@@ -22648,7 +22697,7 @@ function readReloadRequestedAt(stateDir) {
22648
22697
  }
22649
22698
  }
22650
22699
  function daemonStampFor(stateDir) {
22651
- return join50(stateDir, "daemon.started");
22700
+ return join51(stateDir, "daemon.started");
22652
22701
  }
22653
22702
  function noteDaemonStarted(stateDir, now = Date.now()) {
22654
22703
  mkdirSync36(stateDir, { recursive: true });
@@ -22658,7 +22707,7 @@ function noteDaemonStarted(stateDir, now = Date.now()) {
22658
22707
  }
22659
22708
  function readDaemonStarted(stateDir) {
22660
22709
  try {
22661
- const v = JSON.parse(readFileSync43(daemonStampFor(stateDir), "utf8")).startedAt;
22710
+ const v = JSON.parse(readFileSync44(daemonStampFor(stateDir), "utf8")).startedAt;
22662
22711
  return typeof v === "number" ? v : null;
22663
22712
  } catch {
22664
22713
  return null;
@@ -23235,7 +23284,7 @@ import {
23235
23284
  } from "fs";
23236
23285
  import { createConnection } from "net";
23237
23286
  import { createServer as createServer2 } from "http";
23238
- import { basename as basename14, dirname as dirname31, join as join51 } from "path";
23287
+ import { basename as basename14, dirname as dirname31, join as join52 } from "path";
23239
23288
  import { execFile as execFile3, spawn as spawn12 } from "child_process";
23240
23289
 
23241
23290
  // src/periscope-control.ts
@@ -23464,7 +23513,7 @@ function runCrew(args, onLine, timeoutMs = 20 * 6e4) {
23464
23513
  // src/control-api.ts
23465
23514
  var PAUSABLE = ["dev", "design", "qa"];
23466
23515
  var CONTROL_SOCKET_NAME = "control.sock";
23467
- var controlSocketFor = (stateDir) => join51(stateDir, CONTROL_SOCKET_NAME);
23516
+ var controlSocketFor = (stateDir) => join52(stateDir, CONTROL_SOCKET_NAME);
23468
23517
  var MAX_SOCKET_PATH = 100;
23469
23518
  var MAX_TAIL_BYTES = 1024 * 1024;
23470
23519
  var MAX_WAIT_SECONDS = 30;
@@ -23551,7 +23600,7 @@ function candidates(live, rotated) {
23551
23600
  const out = [live];
23552
23601
  if (!rotated) return out;
23553
23602
  try {
23554
- for (const n of readdirSync18(dirname31(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join51(dirname31(live), n));
23603
+ for (const n of readdirSync18(dirname31(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join52(dirname31(live), n));
23555
23604
  } catch {
23556
23605
  }
23557
23606
  return out;
@@ -23751,7 +23800,7 @@ async function route(deps, method, url, req, abort) {
23751
23800
  if (p.startsWith("/streams/") && get2) {
23752
23801
  const run3 = decodeURIComponent(p.slice("/streams/".length));
23753
23802
  if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
23754
- const file = join51(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23803
+ const file = join52(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23755
23804
  if (!existsSync48(file)) return err2(404, "no such run");
23756
23805
  return tailReply(file, url.searchParams, false, abort);
23757
23806
  }
@@ -23901,19 +23950,19 @@ function crewJson(args, timeoutMs = 6e4) {
23901
23950
 
23902
23951
  // src/periscope-mcp.ts
23903
23952
  import { lstat, mkdir, readdir, readFile, readlink, realpath, rm, stat, writeFile } from "fs/promises";
23904
- import { basename as basename15, dirname as dirname32, isAbsolute as isAbsolute5, join as join53, relative as relative2, resolve as resolve6, sep } from "path";
23953
+ import { basename as basename15, dirname as dirname32, isAbsolute as isAbsolute5, join as join54, relative as relative2, resolve as resolve6, sep } from "path";
23905
23954
  import { z } from "zod";
23906
23955
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23907
23956
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
23908
23957
 
23909
23958
  // src/periscope-endpoints.ts
23910
23959
  import { randomBytes as randomBytes3 } from "crypto";
23911
- import { chmodSync as chmodSync5, existsSync as existsSync49, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync44, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
23912
- import { join as join52 } from "path";
23913
- var secretDir = (stateDir) => join52(stateDir, "periscope-mcp");
23914
- var secretFile = (stateDir, id) => join52(secretDir(stateDir), `${id}.json`);
23915
- var endpointDir = (stateDir) => join52(stateDir, "periscope-endpoints");
23916
- var endpointFile = (stateDir, id) => join52(endpointDir(stateDir), `${id}.json`);
23960
+ import { chmodSync as chmodSync5, existsSync as existsSync49, mkdirSync as mkdirSync37, readdirSync as readdirSync19, readFileSync as readFileSync45, rmSync as rmSync24, writeFileSync as writeFileSync37 } from "fs";
23961
+ import { join as join53 } from "path";
23962
+ var secretDir = (stateDir) => join53(stateDir, "periscope-mcp");
23963
+ var secretFile = (stateDir, id) => join53(secretDir(stateDir), `${id}.json`);
23964
+ var endpointDir = (stateDir) => join53(stateDir, "periscope-endpoints");
23965
+ var endpointFile = (stateDir, id) => join53(endpointDir(stateDir), `${id}.json`);
23917
23966
  function mintPeriscopeSecret(stateDir, ticketRecordId) {
23918
23967
  mkdirSync37(secretDir(stateDir), { recursive: true, mode: 448 });
23919
23968
  const secret = randomBytes3(32).toString("base64url");
@@ -23925,7 +23974,7 @@ function mintPeriscopeSecret(stateDir, ticketRecordId) {
23925
23974
  }
23926
23975
  function readPeriscopeSecret(stateDir, ticketRecordId) {
23927
23976
  try {
23928
- const v = JSON.parse(readFileSync44(secretFile(stateDir, ticketRecordId), "utf8"));
23977
+ const v = JSON.parse(readFileSync45(secretFile(stateDir, ticketRecordId), "utf8"));
23929
23978
  return typeof v.secret === "string" && v.secret ? v.secret : void 0;
23930
23979
  } catch {
23931
23980
  return void 0;
@@ -23936,7 +23985,7 @@ function removePeriscopeSecret(stateDir, ticketRecordId) {
23936
23985
  }
23937
23986
  function readPeriscopeEndpoint(stateDir, ticketRecordId) {
23938
23987
  try {
23939
- return JSON.parse(readFileSync44(endpointFile(stateDir, ticketRecordId), "utf8"));
23988
+ return JSON.parse(readFileSync45(endpointFile(stateDir, ticketRecordId), "utf8"));
23940
23989
  } catch {
23941
23990
  return void 0;
23942
23991
  }
@@ -24054,13 +24103,13 @@ async function resolveInWorktree(root, requested) {
24054
24103
  let abs;
24055
24104
  if (isAbsolute5(requested)) {
24056
24105
  abs = resolve6(requested);
24057
- if (isInside(root, abs)) abs = join53(realRoot, relative2(root, abs));
24106
+ if (isInside(root, abs)) abs = join54(realRoot, relative2(root, abs));
24058
24107
  } else {
24059
24108
  abs = resolve6(realRoot, requested);
24060
24109
  }
24061
24110
  if (!isInside(realRoot, abs)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
24062
24111
  const { real, rest } = await realAncestor(abs);
24063
- const final = rest ? join53(real, rest) : real;
24112
+ const final = rest ? join54(real, rest) : real;
24064
24113
  if (!isInside(realRoot, final)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
24065
24114
  if (relative2(realRoot, final).split(sep).includes(".git")) throw new PathRefusal(".git is not reachable from a periscope");
24066
24115
  return final;
@@ -24128,7 +24177,7 @@ function createPeriscopeMcpServer(d, sc) {
24128
24177
  const entries = (await readdir(abs, { withFileTypes: true })).filter((e) => e.name !== ".git");
24129
24178
  const lines2 = await Promise.all(entries.map(async (e) => {
24130
24179
  if (e.isDirectory()) return `[DIR] ${e.name}`;
24131
- const size = (await lstat(join53(abs, e.name))).size;
24180
+ const size = (await lstat(join54(abs, e.name))).size;
24132
24181
  return `[FILE] ${e.name} (${size} bytes)`;
24133
24182
  }));
24134
24183
  return text(lines2.join("\n") || "(empty)");
@@ -24194,7 +24243,7 @@ function createPeriscopeMcpServer(d, sc) {
24194
24243
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
24195
24244
  }, async ({ path }) => {
24196
24245
  const parent = await inTree(dirname32(path) === "" ? "." : dirname32(path));
24197
- const abs = join53(parent, basename15(path));
24246
+ const abs = join54(parent, basename15(path));
24198
24247
  if (basename15(path) === ".git" || abs === parent) return text("path must name a file", true);
24199
24248
  const st = await lstat(abs).catch(() => void 0);
24200
24249
  if (!st) return text(`${path} does not exist`, true);
@@ -24685,7 +24734,7 @@ if (command === "provision") {
24685
24734
  process.exit(outcome.code);
24686
24735
  }
24687
24736
  if (command === "pool-member") {
24688
- const pool = poolConfigFrom(argv, process.env, () => readFileSync45(0, "utf8"));
24737
+ const pool = poolConfigFrom(argv, process.env, () => readFileSync46(0, "utf8"));
24689
24738
  if (typeof pool === "string") {
24690
24739
  process.stderr.write(`crew pool-member: ${pool}
24691
24740
  `);
@@ -24943,7 +24992,7 @@ if (command === "passenger-sync-daemon") {
24943
24992
  throw e;
24944
24993
  }
24945
24994
  if (command === "credential") {
24946
- const r = runCredential(positional[1], readFileSync45(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
24995
+ const r = runCredential(positional[1], readFileSync46(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
24947
24996
  process.stdout.write(r.out);
24948
24997
  process.stderr.write(r.err);
24949
24998
  process.exit(r.code);
@@ -24955,9 +25004,9 @@ if (command === "passenger-sync-daemon") {
24955
25004
  forges: cfg.ship.forges,
24956
25005
  stateDir: cfg.ship.stateDir,
24957
25006
  configFile: cfg.configFile,
24958
- readFile: (p) => readFileSync45(p, "utf8"),
25007
+ readFile: (p) => readFileSync46(p, "utf8"),
24959
25008
  writeFile: (p, t) => writeFileSync38(p, t),
24960
- stdin: () => readFileSync45(0, "utf8"),
25009
+ stdin: () => readFileSync46(0, "utf8"),
24961
25010
  env: process.env
24962
25011
  }
24963
25012
  );
@@ -25250,7 +25299,7 @@ if (command === "passenger-sync-daemon") {
25250
25299
  }
25251
25300
  if (host === "linux" && hasSystemd) {
25252
25301
  const label = labelFor(CREW_HOME, "run");
25253
- const staleTimer = join54(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25302
+ const staleTimer = join55(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25254
25303
  if (existsSync50(staleTimer)) {
25255
25304
  process.stdout.write(
25256
25305
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
@@ -25322,7 +25371,7 @@ if (command === "passenger-sync-daemon") {
25322
25371
  for (const a of plan.actions) {
25323
25372
  if (a.kind === "enable") {
25324
25373
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
25325
- writeFileSync38(cfg.configFile, planEnable(readFileSync45(cfg.configFile, "utf8"), a.route));
25374
+ writeFileSync38(cfg.configFile, planEnable(readFileSync46(cfg.configFile, "utf8"), a.route));
25326
25375
  enabledSound.add(a.route);
25327
25376
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
25328
25377
  } else {
@@ -26461,7 +26510,7 @@ ${cu.tail.trimEnd()}
26461
26510
  process.exit(1);
26462
26511
  }
26463
26512
  requireArmed("run the daemon loop");
26464
- const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join54(CREW_HOME, "src"), join54(CREW_HOME, "dist"), join54(CREW_HOME, "bin")];
26513
+ const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join55(CREW_HOME, "src"), join55(CREW_HOME, "dist"), join55(CREW_HOME, "bin")];
26465
26514
  const configPaths = daemonConfigPaths(cfg);
26466
26515
  watchPaths.push(...configPaths);
26467
26516
  noteDaemonStarted(cfg.ship.stateDir);
@@ -26766,7 +26815,7 @@ ${cu.tail.trimEnd()}
26766
26815
  `);
26767
26816
  if (!dryRun) {
26768
26817
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
26769
- const raw = existsSync50(resolvedPath) ? JSON.parse(readFileSync45(resolvedPath, "utf8")) : {};
26818
+ const raw = existsSync50(resolvedPath) ? JSON.parse(readFileSync46(resolvedPath, "utf8")) : {};
26770
26819
  raw.agentSkills = result.agentSkills;
26771
26820
  mkdirSync38(dirOf2(resolvedPath), { recursive: true });
26772
26821
  writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
@@ -26792,7 +26841,7 @@ ${cu.tail.trimEnd()}
26792
26841
  throw e;
26793
26842
  }
26794
26843
  const oldName = cfg.ship.name;
26795
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync45(cfg.configFile, "utf8"), newName));
26844
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), newName));
26796
26845
  cfg.ship.name = newName;
26797
26846
  let renameMachineId;
26798
26847
  try {
@@ -26839,7 +26888,7 @@ ${cu.tail.trimEnd()}
26839
26888
  process.exit(2);
26840
26889
  };
26841
26890
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
26842
- const configText = readFileSync45(cfg.configFile, "utf8");
26891
+ const configText = readFileSync46(cfg.configFile, "utf8");
26843
26892
  if (sub === "check") {
26844
26893
  try {
26845
26894
  const routeArg = value("route") ?? positional[2];
@@ -26854,7 +26903,8 @@ ${cu.tail.trimEnd()}
26854
26903
  labels: { ...found.labels, ...o?.labels },
26855
26904
  release: mergeRouteRelease(found.release, o?.release),
26856
26905
  branch: { ...found.branch, ...o?.branch },
26857
- worktrees: { prefix: shipWorktreePrefixFor(found), nameOverride: o?.worktreeName }
26906
+ worktrees: { prefix: shipWorktreePrefixFor(found), nameOverride: o?.worktreeName },
26907
+ mapTokenBudget: cfg.ship.mapTokenBudget
26858
26908
  });
26859
26909
  } catch (e) {
26860
26910
  if (e instanceof ReposError) {
@@ -26879,7 +26929,7 @@ ${cu.tail.trimEnd()}
26879
26929
  }
26880
26930
  }, read: (r) => {
26881
26931
  try {
26882
- return readFileSync45(resolvePath(dir, r), "utf8");
26932
+ return readFileSync46(resolvePath(dir, r), "utf8");
26883
26933
  } catch {
26884
26934
  return void 0;
26885
26935
  }
@@ -27030,7 +27080,7 @@ ${cu.tail.trimEnd()}
27030
27080
  let resolvedRemotes;
27031
27081
  if (existsSync50(resolvedFile)) {
27032
27082
  try {
27033
- resolvedRemotes = JSON.parse(readFileSync45(resolvedFile, "utf8"));
27083
+ resolvedRemotes = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27034
27084
  } catch {
27035
27085
  }
27036
27086
  }
@@ -27051,7 +27101,7 @@ ${cu.tail.trimEnd()}
27051
27101
  isCheckout,
27052
27102
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27053
27103
  try {
27054
- return Object.keys(parseRepoConfig(readFileSync45(resolvePath(d, f), "utf8"), f).hooks).length > 0;
27104
+ return Object.keys(parseRepoConfig(readFileSync46(resolvePath(d, f), "utf8"), f).hooks).length > 0;
27055
27105
  } catch {
27056
27106
  return false;
27057
27107
  }
@@ -27160,7 +27210,7 @@ ${cu.tail.trimEnd()}
27160
27210
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
27161
27211
  `);
27162
27212
  if (existsSync50(resolvedFile)) {
27163
- const raw = JSON.parse(readFileSync45(resolvedFile, "utf8"));
27213
+ const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
27164
27214
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
27165
27215
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
27166
27216
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -27538,7 +27588,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27538
27588
  process.exit(2);
27539
27589
  }
27540
27590
  if (found.shipNameAdopted && !dryRun) {
27541
- writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync45(cfg.configFile, "utf8"), found.shipNameAdopted));
27591
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync46(cfg.configFile, "utf8"), found.shipNameAdopted));
27542
27592
  cfg.ship.name = found.shipNameAdopted;
27543
27593
  }
27544
27594
  if (found.shipRow) {
@@ -27584,7 +27634,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27584
27634
  const hadPreviousFile = existsSync50(resolvedPath);
27585
27635
  if (hadPreviousFile) {
27586
27636
  try {
27587
- previous = JSON.parse(readFileSync45(resolvedPath, "utf8"));
27637
+ previous = JSON.parse(readFileSync46(resolvedPath, "utf8"));
27588
27638
  } catch {
27589
27639
  }
27590
27640
  }
@@ -28458,10 +28508,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28458
28508
  startupSeconds: cfg.ship.intelStartupSeconds,
28459
28509
  prepare: async (path) => {
28460
28510
  for (const f of repoWt.config.worktrees.copy) {
28461
- const src = join54(repoWt.dir, f);
28462
- if (!existsSync50(src) || existsSync50(join54(path, f))) continue;
28463
- mkdirSync38(dirOf2(join54(path, f)), { recursive: true });
28464
- copyFileSync2(src, join54(path, f));
28511
+ const src = join55(repoWt.dir, f);
28512
+ if (!existsSync50(src) || existsSync50(join55(path, f))) continue;
28513
+ mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28514
+ copyFileSync2(src, join55(path, f));
28465
28515
  }
28466
28516
  const setup = repoWt.config.hooks.setup;
28467
28517
  return setup ? runScript(setup, { cwd: path, shell: shellWt }) : null;
@@ -28768,10 +28818,10 @@ ${where.join("\n")}`);
28768
28818
  },
28769
28819
  prepare: async (path) => {
28770
28820
  for (const f of repoP.config.worktrees.copy) {
28771
- const src = join54(repoP.dir, f);
28821
+ const src = join55(repoP.dir, f);
28772
28822
  if (!existsSync50(src)) continue;
28773
- mkdirSync38(dirOf2(join54(path, f)), { recursive: true });
28774
- copyFileSync2(src, join54(path, f));
28823
+ mkdirSync38(dirOf2(join55(path, f)), { recursive: true });
28824
+ copyFileSync2(src, join55(path, f));
28775
28825
  }
28776
28826
  const setup = repoP.config.hooks.setup;
28777
28827
  return setup ? runScript(setup, { cwd: path, shell: shellP, onLine: (l) => {
@@ -29081,7 +29131,7 @@ ${where.join("\n")}`);
29081
29131
  };
29082
29132
  const droppedRoutes = [];
29083
29133
  try {
29084
- const rawCfg = parseYaml2(readFileSync45(cfg.configFile, "utf8"));
29134
+ const rawCfg = parseYaml2(readFileSync46(cfg.configFile, "utf8"));
29085
29135
  for (const r of rawCfg?.routes ?? []) {
29086
29136
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
29087
29137
  try {
@@ -29200,7 +29250,7 @@ ${where.join("\n")}`);
29200
29250
  }
29201
29251
  case "log": {
29202
29252
  try {
29203
- const text2 = readFileSync45(cfg.ship.logFile, "utf8").split("\n");
29253
+ const text2 = readFileSync46(cfg.ship.logFile, "utf8").split("\n");
29204
29254
  process.stdout.write(`${text2.slice(-(Number(value("tail")) || 40)).join("\n")}
29205
29255
  `);
29206
29256
  } catch {
@@ -29223,7 +29273,7 @@ ${where.join("\n")}`);
29223
29273
  };
29224
29274
  if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
29225
29275
  const tracker = new Tracker(route2, cfg.ship);
29226
- const configText = readFileSync45(cfg.configFile, "utf8");
29276
+ const configText = readFileSync46(cfg.configFile, "utf8");
29227
29277
  const cfgBase = dirname33(cfg.configFile);
29228
29278
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
29229
29279
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
@@ -29261,19 +29311,19 @@ ${where.join("\n")}`);
29261
29311
  if (row) {
29262
29312
  await tracker.deleteRepoRow(row.id);
29263
29313
  if (existsSync50(resolvedFile)) {
29264
- const raw = JSON.parse(readFileSync45(resolvedFile, "utf8"));
29314
+ const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29265
29315
  if (raw.repoNames) delete raw.repoNames[row.id];
29266
29316
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
29267
29317
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
29268
29318
  `);
29269
29319
  }
29270
29320
  }
29271
- if (existsSync50(join54(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
29321
+ if (existsSync50(join55(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
29272
29322
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
29273
29323
  break;
29274
29324
  }
29275
29325
  const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
29276
- const fresh = !existsSync50(join54(dir, ".git"));
29326
+ const fresh = !existsSync50(join55(dir, ".git"));
29277
29327
  if (existsSync50(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
29278
29328
  if (dryRun) {
29279
29329
  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`);
@@ -29281,8 +29331,8 @@ ${where.join("\n")}`);
29281
29331
  }
29282
29332
  if (fresh) {
29283
29333
  mkdirSync38(dir, { recursive: true });
29284
- writeFileSync38(join54(dir, "README.md"), SAMPLE_README);
29285
- writeFileSync38(join54(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
29334
+ writeFileSync38(join55(dir, "README.md"), SAMPLE_README);
29335
+ writeFileSync38(join55(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
29286
29336
  const g = (...a) => {
29287
29337
  const r = spawnSync11("git", a, { cwd: dir, encoding: "utf8" });
29288
29338
  if (r.status !== 0) fail2(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
@@ -29301,7 +29351,7 @@ ${where.join("\n")}`);
29301
29351
  });
29302
29352
  repoId = (await tracker.createRepoRow(payload)).id;
29303
29353
  if (existsSync50(resolvedFile)) {
29304
- const raw = JSON.parse(readFileSync45(resolvedFile, "utf8"));
29354
+ const raw = JSON.parse(readFileSync46(resolvedFile, "utf8"));
29305
29355
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29306
29356
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
29307
29357
  writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
@@ -29695,6 +29745,7 @@ watermark: ${state.watermark()}
29695
29745
  base: doctorBaseReport(r, r.config.branch.base, baseHealth),
29696
29746
  hooks: r.config.hooks,
29697
29747
  onPath: (cmd) => !!findOnPath(cmd, doctorSchedulerPath),
29748
+ mapFile: mapFilePath(r.config.hooks.map) ? inspectMapFile(r.dir, mapFilePath(r.config.hooks.map)) : void 0,
29698
29749
  intelSyntax: r.config.hooks.intel && !intelFlavor(r.config.hooks.intel) ? checkHookSyntax(r.config.hooks.intel, r.config.shell) : void 0,
29699
29750
  intelFlavor: intelFlavor(r.config.hooks.intel) ? { name: intelFlavor(r.config.hooks.intel), missing: [
29700
29751
  ...missingPrerequisites(intelFlavor(r.config.hooks.intel), (bin) => !!findOnPath(bin, doctorSchedulerPath)),
@@ -29762,7 +29813,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29762
29813
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run", installContext());
29763
29814
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
29764
29815
  try {
29765
- return readFileSync45(p, "utf8");
29816
+ return readFileSync46(p, "utf8");
29766
29817
  } catch {
29767
29818
  return null;
29768
29819
  }