@tablation/crew 0.126.1 → 0.126.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -794,7 +794,7 @@ function shellInitScript(shell) {
794
794
 
795
795
  // src/config.ts
796
796
  import { readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
797
- import { resolve as resolve2, dirname as dirname7, isAbsolute as isAbsolute2, join as join17, basename as basename4 } from "path";
797
+ import { resolve as resolve2, dirname as dirname8, isAbsolute as isAbsolute2, join as join17, basename as basename4 } from "path";
798
798
  import { homedir as homedir6, tmpdir } from "os";
799
799
  import { parse as parse2 } from "yaml";
800
800
 
@@ -2661,7 +2661,7 @@ function explain(ship, required) {
2661
2661
 
2662
2662
  // src/repo-config.ts
2663
2663
  import { readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
2664
- import { join as join16, basename as basename3 } from "path";
2664
+ import { join as join16, basename as basename3, dirname as dirname7 } from "path";
2665
2665
  import { parse } from "yaml";
2666
2666
 
2667
2667
  // src/sandbox-policy.ts
@@ -2863,6 +2863,19 @@ var SPEC_VERSION = 1;
2863
2863
  var REPO_CONFIG_FILENAMES = [".crew.yaml", ".crew.yml", ".crew.json"];
2864
2864
  var RepoConfigError = class extends Error {
2865
2865
  };
2866
+ function versionFileProblem(name, text2) {
2867
+ const remedy = `a version file that is not a JSON object with a string "version" needs hooks.bump instead of release.versionFiles (docs/REPO_SPEC.md)`;
2868
+ let doc;
2869
+ try {
2870
+ doc = JSON.parse(text2);
2871
+ } catch {
2872
+ return `release.versionFiles entry "${name}" is not a JSON document \u2014 ${remedy}`;
2873
+ }
2874
+ if (!doc || typeof doc !== "object" || Array.isArray(doc) || typeof doc.version !== "string") {
2875
+ return `release.versionFiles entry "${name}" has no string "version" field \u2014 ${remedy}`;
2876
+ }
2877
+ return null;
2878
+ }
2866
2879
  var HOOK_NAMES = [
2867
2880
  "test",
2868
2881
  "build",
@@ -3197,6 +3210,22 @@ function parseRepoConfig(text2, file) {
3197
3210
  if (versionFiles.length === 0) {
3198
3211
  throw new RepoConfigError(`${file}: release.versionFiles cannot be empty`);
3199
3212
  }
3213
+ if (versionFilesGiven) {
3214
+ for (const name of versionFiles) {
3215
+ const path = join16(dirname7(file), name);
3216
+ if (!existsSync14(path)) continue;
3217
+ let text3;
3218
+ try {
3219
+ text3 = readFileSync14(path, "utf8");
3220
+ } catch (e) {
3221
+ throw new RepoConfigError(
3222
+ `${file}: release.versionFiles entry "${name}" cannot be read (${e.code ?? "read error"})`
3223
+ );
3224
+ }
3225
+ const problem = versionFileProblem(name, text3);
3226
+ if (problem) throw new RepoConfigError(`${file}: ${problem}`);
3227
+ }
3228
+ }
3200
3229
  const inspectorRaw = raw.periscope?.inspector;
3201
3230
  if (inspectorRaw !== void 0 && inspectorRaw !== "proxy" && inspectorRaw !== "repo") {
3202
3231
  throw new RepoConfigError(`${file}: periscope.inspector must be "proxy" or "repo"`);
@@ -3898,7 +3927,7 @@ function loadConfig(crewHome, configFile, opts = {}) {
3898
3927
  ` + configSearchPath(crewHome).map((c) => ` - ${c}`).join("\n") + `
3899
3928
 
3900
3929
  Create the first of those:
3901
- mkdir -p ${dirname7(defaultConfigPath())} && cp ${join17(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
3930
+ mkdir -p ${dirname8(defaultConfigPath())} && cp ${join17(crewHome, "crew.example.yaml")} ${defaultConfigPath()}`
3902
3931
  );
3903
3932
  }
3904
3933
  let raw;
@@ -3908,7 +3937,7 @@ Create the first of those:
3908
3937
  throw new ConfigError(`${file}: not valid YAML \u2014 ${e.message}`);
3909
3938
  }
3910
3939
  if (!raw || typeof raw !== "object") throw new ConfigError(`${file} is empty or not a mapping`);
3911
- const base = dirname7(file);
3940
+ const base = dirname8(file);
3912
3941
  const missing = new Missing();
3913
3942
  rejectUnknownKeys(raw, /* @__PURE__ */ new Set(["ship", "routes"]), "top-level", file);
3914
3943
  const shipRaw = raw.ship ?? {};
@@ -4275,7 +4304,7 @@ function locateDir(cfg, dir, commonDirOf) {
4275
4304
  const direct = find(target);
4276
4305
  if (direct) return direct;
4277
4306
  const common = commonDirOf?.(target);
4278
- return common ? find(dirname7(resolve2(target, common))) : void 0;
4307
+ return common ? find(dirname8(resolve2(target, common))) : void 0;
4279
4308
  }
4280
4309
  function findRoute(cfg, route2) {
4281
4310
  if (!route2) {
@@ -4717,7 +4746,7 @@ function recoveredFailureHeadlines(o, route2) {
4717
4746
  }
4718
4747
 
4719
4748
  // src/install.ts
4720
- import { dirname as dirname8, join as join18, extname } from "path";
4749
+ import { dirname as dirname9, join as join18, extname } from "path";
4721
4750
  import { homedir as homedir7, userInfo } from "os";
4722
4751
  import { accessSync as accessSync2, constants as constants2, existsSync as existsSync16, mkdirSync as mkdirSync11, readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync5, writeFileSync as writeFileSync12, unlinkSync, realpathSync } from "fs";
4723
4752
  import { spawnSync as spawnSync5 } from "child_process";
@@ -4762,7 +4791,7 @@ function schedulerLogFor(crewLog, job) {
4762
4791
  return `${stem}.${suffix}${ext || ".log"}`;
4763
4792
  }
4764
4793
  function pathFor(ship) {
4765
- const nodeBinDir = dirname8(process.execPath);
4794
+ const nodeBinDir = dirname9(process.execPath);
4766
4795
  const parts = [nodeBinDir];
4767
4796
  if (ship.extraPath) parts.push(ship.extraPath);
4768
4797
  parts.push("/usr/bin", "/bin", "/usr/sbin", "/sbin");
@@ -4784,7 +4813,7 @@ function dockerPathProblem(ship, shellPath = process.env.PATH ?? "") {
4784
4813
  const schedulerPath = pathFor(ship);
4785
4814
  if (findOnPath("docker", schedulerPath)) return void 0;
4786
4815
  const inShell = findOnPath("docker", shellPath);
4787
- return inShell ? `\`docker\` is not on the scheduler PATH (${schedulerPath}) but is at ${inShell} in your shell; add ${dirname8(inShell)} to ship.extraPath in crew.yaml and run \`crew install\`` : `\`docker\` is not on the scheduler PATH (${schedulerPath}) or your shell PATH; install Docker, then add its directory to ship.extraPath in crew.yaml and run \`crew install\``;
4816
+ return inShell ? `\`docker\` is not on the scheduler PATH (${schedulerPath}) but is at ${inShell} in your shell; add ${dirname9(inShell)} to ship.extraPath in crew.yaml and run \`crew install\`` : `\`docker\` is not on the scheduler PATH (${schedulerPath}) or your shell PATH; install Docker, then add its directory to ship.extraPath in crew.yaml and run \`crew install\``;
4788
4817
  }
4789
4818
  var USER_INSTALL = { root: false, user: "" };
4790
4819
  function installContext(env = process.env) {
@@ -5079,7 +5108,7 @@ async function applyInstall(plan, crewHome, dryRun, log) {
5079
5108
  }
5080
5109
  const before = existsSync16(path) ? readFileSync16(path, "utf8") : null;
5081
5110
  if (before !== plan.unitContent[path]) changed2 = true;
5082
- mkdirSync11(dirname8(path), { recursive: true });
5111
+ mkdirSync11(dirname9(path), { recursive: true });
5083
5112
  writeFileSync12(path, plan.unitContent[path]);
5084
5113
  log.emit(`wrote ${path}`);
5085
5114
  }
@@ -5308,7 +5337,7 @@ import {
5308
5337
  readdirSync as readdirSync4,
5309
5338
  rmSync as rmSync9
5310
5339
  } from "fs";
5311
- import { dirname as dirname9, basename as basename5, join as join19 } from "path";
5340
+ import { dirname as dirname10, basename as basename5, join as join19 } from "path";
5312
5341
  var GitError = class extends Error {
5313
5342
  };
5314
5343
  function streamText(x) {
@@ -5688,7 +5717,7 @@ function recoverStrandedReleaseCheckouts(cwd) {
5688
5717
  if (line.startsWith("worktree ")) path = line.slice("worktree ".length);
5689
5718
  else if (line.startsWith("branch refs/heads/crew/release-") && path) {
5690
5719
  gitOk(cwd, ["worktree", "remove", "--force", path]);
5691
- const root = dirname9(path);
5720
+ const root = dirname10(path);
5692
5721
  if (basename5(root).startsWith("crew-release-")) rmSync9(root, { recursive: true, force: true });
5693
5722
  }
5694
5723
  }
@@ -5768,7 +5797,7 @@ function ensureRepoCheckout(dir, remote, opts = {}) {
5768
5797
  `${dir} does not exist and this repo has no discovered remote to clone from \u2014 re-run \`crew connect\`, or check out the repository yourself and point \`repos:\` at it`
5769
5798
  );
5770
5799
  }
5771
- mkdirSync12(dirname9(dir), { recursive: true });
5800
+ mkdirSync12(dirname10(dir), { recursive: true });
5772
5801
  const url = cloneUrlFor(remote, opts);
5773
5802
  try {
5774
5803
  execFileSync5("git", ["clone", url, dir], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
@@ -5793,7 +5822,7 @@ function sleepSync(ms3) {
5793
5822
  }
5794
5823
  function acquireCheckoutLock(dir, timeoutMs = 3e4) {
5795
5824
  const lockPath = checkoutLockPath(dir);
5796
- mkdirSync12(dirname9(lockPath), { recursive: true });
5825
+ mkdirSync12(dirname10(lockPath), { recursive: true });
5797
5826
  const deadline = Date.now() + timeoutMs;
5798
5827
  for (; ; ) {
5799
5828
  try {
@@ -6305,7 +6334,7 @@ import { createHash as createHash6 } from "crypto";
6305
6334
  import { spawn as spawn5 } from "child_process";
6306
6335
  import { execFileSync as execFileSync8 } from "child_process";
6307
6336
  import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync3, writeFileSync as writeFileSync20, rmSync as rmSync15 } from "fs";
6308
- import { dirname as dirname14, join as join32 } from "path";
6337
+ import { dirname as dirname15, join as join32 } from "path";
6309
6338
  import { fileURLToPath } from "url";
6310
6339
  import { tmpdir as tmpdir3 } from "os";
6311
6340
 
@@ -6330,7 +6359,7 @@ import { spawnSync as spawnSync6 } from "child_process";
6330
6359
  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";
6331
6360
  import { StringDecoder } from "string_decoder";
6332
6361
  import { finished } from "stream/promises";
6333
- import { join as join29, dirname as dirname13, basename as basename9 } from "path";
6362
+ import { join as join29, dirname as dirname14, basename as basename9 } from "path";
6334
6363
  import { createHash as createHash4 } from "crypto";
6335
6364
 
6336
6365
  // src/state.ts
@@ -7235,7 +7264,7 @@ function renderEnvironment(i) {
7235
7264
 
7236
7265
  // src/intel-bridge.ts
7237
7266
  import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7238
- import { dirname as dirname10, join as join22 } from "path";
7267
+ import { dirname as dirname11, join as join22 } from "path";
7239
7268
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7240
7269
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7241
7270
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -7255,7 +7284,7 @@ function intelDir(stateDir, route2, ticket) {
7255
7284
  }
7256
7285
  var attachFilePath = (stateDir, route2, ticket) => join22(intelDir(stateDir, route2, ticket), "url");
7257
7286
  function writeAttachFile(file, url) {
7258
- mkdirSync14(dirname10(file), { recursive: true });
7287
+ mkdirSync14(dirname11(file), { recursive: true });
7259
7288
  writeFileSync15(file, `${url}
7260
7289
  `);
7261
7290
  }
@@ -9300,7 +9329,7 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
9300
9329
 
9301
9330
  // src/run-worktree.ts
9302
9331
  import { existsSync as existsSync22 } from "fs";
9303
- import { dirname as dirname11, join as join24 } from "path";
9332
+ import { dirname as dirname12, join as join24 } from "path";
9304
9333
  var describeRunWorktreePlan = (p) => {
9305
9334
  switch (p.action) {
9306
9335
  case "cut-remote":
@@ -9357,7 +9386,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
9357
9386
  if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
9358
9387
  return { action: "recut", path: existing.path, branch, base: baseRef };
9359
9388
  }
9360
- const path = join24(dirname11(dir), worktreeForTicket(dir, cfg, t, role));
9389
+ const path = join24(dirname12(dir), worktreeForTicket(dir, cfg, t, role));
9361
9390
  if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
9362
9391
  return { action: "cut-remote", path, branch: located.branch };
9363
9392
  }
@@ -10631,7 +10660,7 @@ function skillMarkerWarnings(promptsDir, personaPrompts) {
10631
10660
  import { spawn as spawn3, execFile, execFileSync as execFileSync6 } from "child_process";
10632
10661
  import { mkdtempSync, mkdirSync as mkdirSync17, writeFileSync as writeFileSync18, rmSync as rmSync14 } from "fs";
10633
10662
  import { tmpdir as tmpdir2 } from "os";
10634
- import { basename as basename8, dirname as dirname12, join as join28 } from "path";
10663
+ import { basename as basename8, dirname as dirname13, join as join28 } from "path";
10635
10664
  import { promisify } from "util";
10636
10665
 
10637
10666
  // src/run-limit.ts
@@ -10760,7 +10789,7 @@ var SandboxRunner = class {
10760
10789
  throw new Error(`cannot generate the sandbox policy: ${e.message}`);
10761
10790
  }
10762
10791
  policyFile = policyPathFor(plan.streamPath);
10763
- mkdirSync17(dirname12(policyFile), { recursive: true });
10792
+ mkdirSync17(dirname13(policyFile), { recursive: true });
10764
10793
  writeFileSync18(policyFile, generated.yaml, { mode: 384 });
10765
10794
  }
10766
10795
  const create2 = ["sandbox", "create", "--name", name, "--detach"];
@@ -11022,7 +11051,7 @@ function codeBridgeFor(o, harness) {
11022
11051
  "--clone",
11023
11052
  o.ship.sandbox?.workDir ?? DEFAULT_SANDBOX_WORKDIR,
11024
11053
  "--state-dir",
11025
- dirname13(SANDBOX_INTEL_DIR),
11054
+ dirname14(SANDBOX_INTEL_DIR),
11026
11055
  "--ticket",
11027
11056
  number,
11028
11057
  ...seed ? ["--seed-cache", seed] : []
@@ -11036,7 +11065,7 @@ function codeBridgeFor(o, harness) {
11036
11065
  bridge.files = [{ path: bridge.settingsFile, content: text2 }];
11037
11066
  } else {
11038
11067
  const file = join29(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
11039
- mkdirSync18(dirname13(file), { recursive: true });
11068
+ mkdirSync18(dirname14(file), { recursive: true });
11040
11069
  writeFileSync19(file, text2);
11041
11070
  bridge.settingsFile = file;
11042
11071
  }
@@ -11259,7 +11288,7 @@ async function killForDeadline(kill, pid, emit, role, ticket, spawnedAt, limitMi
11259
11288
  var withoutKey = ({ apiKey: _apiKey, ...rest }) => rest;
11260
11289
  function openSink(path) {
11261
11290
  try {
11262
- mkdirSync18(dirname13(path), { recursive: true });
11291
+ mkdirSync18(dirname14(path), { recursive: true });
11263
11292
  return createWriteStream(path, { flags: "a" });
11264
11293
  } catch {
11265
11294
  return void 0;
@@ -11611,7 +11640,7 @@ function spawnAgent(plan, emit, hintMoved) {
11611
11640
  const id = identityOf(plan, adapter.harness);
11612
11641
  let streamFd;
11613
11642
  try {
11614
- mkdirSync18(dirname13(plan.streamPath), { recursive: true });
11643
+ mkdirSync18(dirname14(plan.streamPath), { recursive: true });
11615
11644
  streamFd = openSync4(plan.streamPath, "a");
11616
11645
  } catch (e) {
11617
11646
  void endSink(eventsSink);
@@ -12830,7 +12859,7 @@ var SANDBOX_UID = 1e3;
12830
12859
  var CREW_PREFIX = "/opt/crew";
12831
12860
  var SANDBOX_BRIDGE_PATH = `${CREW_PREFIX}/bin/intel-bridge`;
12832
12861
  function readBridgeBundle() {
12833
- const here = dirname14(fileURLToPath(import.meta.url));
12862
+ const here = dirname15(fileURLToPath(import.meta.url));
12834
12863
  for (const p of [join32(here, "sandbox-intel-bridge.cjs"), join32(here, "..", "dist", "sandbox-intel-bridge.cjs")]) {
12835
12864
  if (existsSync30(p)) return readFileSync27(p, "utf8");
12836
12865
  }
@@ -13055,7 +13084,7 @@ async function buildSandboxImage(plan, deps) {
13055
13084
  try {
13056
13085
  writeFileSync20(join32(ctx, "Dockerfile"), layer.dockerfile);
13057
13086
  for (const [name, content] of Object.entries(layer.files)) {
13058
- mkdirSync19(dirname14(join32(ctx, name)), { recursive: true });
13087
+ mkdirSync19(dirname15(join32(ctx, name)), { recursive: true });
13059
13088
  writeFileSync20(join32(ctx, name), content);
13060
13089
  }
13061
13090
  const code = await deps.run(["build", "--tag", layer.tag, "--file", join32(ctx, "Dockerfile"), ctx], logPath);
@@ -13468,7 +13497,7 @@ function doctorSelftestLines(stored, toolchains) {
13468
13497
  }
13469
13498
 
13470
13499
  // src/cli.ts
13471
- import { resolve as resolve7, dirname as dirname33, join as join55, basename as basename16 } from "path";
13500
+ import { resolve as resolve7, dirname as dirname34, join as join55, basename as basename16 } from "path";
13472
13501
  import { fileURLToPath as fileURLToPath2 } from "url";
13473
13502
  import { hostname as hostname4, homedir as homedir11 } from "os";
13474
13503
  import { spawnSync as spawnSync11 } from "child_process";
@@ -13667,7 +13696,7 @@ function rosterMarkdown(roster, meId) {
13667
13696
 
13668
13697
  // src/connect-resume.ts
13669
13698
  import { mkdirSync as mkdirSync21, readFileSync as readFileSync29, rmSync as rmSync17, writeFileSync as writeFileSync22 } from "fs";
13670
- import { dirname as dirname15, join as join34 } from "path";
13699
+ import { dirname as dirname16, join as join34 } from "path";
13671
13700
  function lastSessionFile(stateDir) {
13672
13701
  return join34(stateDir, "connect-session.json");
13673
13702
  }
@@ -13687,7 +13716,7 @@ function readPointers(stateDir) {
13687
13716
  }
13688
13717
  function writePointers(stateDir, pointers) {
13689
13718
  const file = lastSessionFile(stateDir);
13690
- mkdirSync21(dirname15(file), { recursive: true });
13719
+ mkdirSync21(dirname16(file), { recursive: true });
13691
13720
  writeFileSync22(file, `${JSON.stringify(pointers, null, 2)}
13692
13721
  `, { mode: 384 });
13693
13722
  }
@@ -13774,7 +13803,7 @@ async function signOutSessions(o) {
13774
13803
 
13775
13804
  // src/reset.ts
13776
13805
  import { existsSync as existsSync32, mkdirSync as mkdirSync22, readFileSync as readFileSync30, readdirSync as readdirSync12, rmSync as rmSync18, statSync as statSync10, writeFileSync as writeFileSync23 } from "fs";
13777
- import { dirname as dirname16, join as join35 } from "path";
13806
+ import { dirname as dirname17, join as join35 } from "path";
13778
13807
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13779
13808
  function clonedCheckoutsFile(stateDir) {
13780
13809
  return join35(stateDir, "cloned-checkouts.json");
@@ -13791,7 +13820,7 @@ function recordClonedCheckout(stateDir, dir) {
13791
13820
  const known = readClonedCheckouts(stateDir);
13792
13821
  if (known.includes(dir)) return;
13793
13822
  const file = clonedCheckoutsFile(stateDir);
13794
- mkdirSync22(dirname16(file), { recursive: true });
13823
+ mkdirSync22(dirname17(file), { recursive: true });
13795
13824
  writeFileSync23(file, `${JSON.stringify([...known, dir], null, 2)}
13796
13825
  `);
13797
13826
  }
@@ -13974,7 +14003,7 @@ async function runDeprovision(o, fx, deps) {
13974
14003
  // src/pool-member.ts
13975
14004
  import { mkdirSync as mkdirSync23, readFileSync as readFileSync31, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
13976
14005
  import { homedir as homedir8 } from "os";
13977
- import { dirname as dirname17, join as join36 } from "path";
14006
+ import { dirname as dirname18, join as join36 } from "path";
13978
14007
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
13979
14008
  function backoffMs(attempt) {
13980
14009
  return Math.min(6e4, 5e3 * 2 ** Math.max(0, attempt));
@@ -14218,7 +14247,7 @@ function fileMemberStore(dir) {
14218
14247
  }
14219
14248
  },
14220
14249
  write(f) {
14221
- mkdirSync23(dirname17(file), { recursive: true, mode: 448 });
14250
+ mkdirSync23(dirname18(file), { recursive: true, mode: 448 });
14222
14251
  const tmp = `${file}.tmp`;
14223
14252
  writeFileSync24(tmp, `${JSON.stringify(f)}
14224
14253
  `, { mode: 384 });
@@ -14279,7 +14308,7 @@ import { hostname as hostname3 } from "os";
14279
14308
  import { execFileSync as execFileSync10 } from "child_process";
14280
14309
  import { createHash as createHash7 } from "crypto";
14281
14310
  import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync25 } from "fs";
14282
- import { dirname as dirname18, join as join37 } from "path";
14311
+ import { dirname as dirname19, join as join37 } from "path";
14283
14312
  function machineIdPathFor(stateDir) {
14284
14313
  return join37(stateDir, "machine-id");
14285
14314
  }
@@ -14313,7 +14342,7 @@ function ensureMachineId(stateDir, opts = {}) {
14313
14342
  const raw = (opts.read ?? readRawMachineId)();
14314
14343
  if (!raw) return void 0;
14315
14344
  const hashed = sha256(raw);
14316
- mkdirSync24(dirname18(path), { recursive: true });
14345
+ mkdirSync24(dirname19(path), { recursive: true });
14317
14346
  writeFileSync25(path, `${hashed}
14318
14347
  `, { mode: 384 });
14319
14348
  return hashed;
@@ -16594,7 +16623,7 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16594
16623
 
16595
16624
  // src/repo-refresh.ts
16596
16625
  import { existsSync as existsSync37, mkdirSync as mkdirSync26, readFileSync as readFileSync35, writeFileSync as writeFileSync29 } from "fs";
16597
- import { dirname as dirname20 } from "path";
16626
+ import { dirname as dirname21 } from "path";
16598
16627
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16599
16628
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
16600
16629
  var lastAsked = /* @__PURE__ */ new Map();
@@ -16626,7 +16655,7 @@ function persistRepoRows(stateDir, routeName, rows) {
16626
16655
  if (!existsSync37(path)) return;
16627
16656
  const raw = JSON.parse(readFileSync35(path, "utf8"));
16628
16657
  applyRepoRows(raw, rows);
16629
- mkdirSync26(dirname20(path), { recursive: true });
16658
+ mkdirSync26(dirname21(path), { recursive: true });
16630
16659
  writeFileSync29(path, `${JSON.stringify(raw, null, 2)}
16631
16660
  `);
16632
16661
  }
@@ -17077,7 +17106,7 @@ Ship ${shipName2} found: ${problem}. The release refuses to squash a ref nobody
17077
17106
  var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.record_id === ticket.id && e.event_type === "local_ref_skip" && (e.message ?? "").includes(problem));
17078
17107
 
17079
17108
  // src/connect-wizard.ts
17080
- import { delimiter, dirname as dirname21, join as join41 } from "path";
17109
+ import { delimiter, dirname as dirname22, join as join41 } from "path";
17081
17110
  import { accessSync as accessSync3, constants as constants3, existsSync as existsSync38, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
17082
17111
  import { stringify as stringify3 } from "yaml";
17083
17112
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
@@ -17126,9 +17155,9 @@ async function runWizard(p, env) {
17126
17155
  if (entered && isExec(entered)) agentBin = entered;
17127
17156
  else p.say(entered ? `${entered} is not an executable file.` : "A path is required.");
17128
17157
  }
17129
- const nodeDir = dirname21(env.nodePath);
17158
+ const nodeDir = dirname22(env.nodePath);
17130
17159
  const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
17131
- const baseDirs = [nodeDir, pnpmPath ? dirname21(pnpmPath) : void 0];
17160
+ const baseDirs = [nodeDir, pnpmPath ? dirname22(pnpmPath) : void 0];
17132
17161
  p.say(
17133
17162
  `
17134
17163
  Host Passengers lets this machine run the agent sessions that other people's
@@ -17139,7 +17168,7 @@ Tablation workspaces ask for, in a container. More: ${HOST_PASSENGERS_DOCS_URL}`
17139
17168
  p.say("Docker is not available here, so nothing will be hosted until it is. Your answer is recorded anyway.");
17140
17169
  }
17141
17170
  const dockerPath = hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
17142
- const extraPath = deriveExtraPath([...baseDirs, dockerPath ? dirname21(dockerPath) : void 0]);
17171
+ const extraPath = deriveExtraPath([...baseDirs, dockerPath ? dirname22(dockerPath) : void 0]);
17143
17172
  if (extraPath) {
17144
17173
  p.say(`Hooks will get ${extraPath} on their PATH (where node, pnpm${dockerPath ? " and docker" : ""} live).`);
17145
17174
  }
@@ -17161,7 +17190,7 @@ routes:
17161
17190
  ${routeBlock}`;
17162
17191
  }
17163
17192
  function writeNewConfig(path, text2) {
17164
- mkdirSync27(dirname21(path), { recursive: true });
17193
+ mkdirSync27(dirname22(path), { recursive: true });
17165
17194
  try {
17166
17195
  writeFileSync30(path, text2, { mode: 384, flag: "wx" });
17167
17196
  return true;
@@ -17197,7 +17226,7 @@ Wrote ${o.configPath}.
17197
17226
  // src/connect-json.ts
17198
17227
  import { accessSync as accessSync4, constants as constants4 } from "fs";
17199
17228
  import { homedir as homedir10 } from "os";
17200
- import { dirname as dirname22, join as join42 } from "path";
17229
+ import { dirname as dirname23, join as join42 } from "path";
17201
17230
  import { delimiter as delimiter2 } from "path";
17202
17231
  var CHOICE_EXIT_CODE = 3;
17203
17232
  function createEmitter(json2, write) {
@@ -17263,7 +17292,7 @@ function flagWizardAnswers(f, env) {
17263
17292
  const agentBin = f.agentBin ?? found?.bin;
17264
17293
  const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
17265
17294
  const dockerPath = f.hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
17266
- const extraPath = f.extraPath ?? deriveExtraPath([dirname22(env.nodePath), pnpmPath ? dirname22(pnpmPath) : void 0, dockerPath ? dirname22(dockerPath) : void 0]);
17295
+ const extraPath = f.extraPath ?? deriveExtraPath([dirname23(env.nodePath), pnpmPath ? dirname23(pnpmPath) : void 0, dockerPath ? dirname23(dockerPath) : void 0]);
17267
17296
  return {
17268
17297
  answers: {
17269
17298
  name: f.shipName?.trim() || env.hostname,
@@ -17278,7 +17307,7 @@ function flagWizardAnswers(f, env) {
17278
17307
 
17279
17308
  // src/release-run.ts
17280
17309
  import { readFileSync as readFileSync36, writeFileSync as writeFileSync31, existsSync as existsSync39, copyFileSync, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync3, rmSync as rmSync19 } from "fs";
17281
- import { dirname as dirname23, join as join43 } from "path";
17310
+ import { dirname as dirname24, join as join43 } from "path";
17282
17311
  import { tmpdir as tmpdir5 } from "os";
17283
17312
 
17284
17313
  // src/release.ts
@@ -18025,7 +18054,13 @@ async function bump(o, size) {
18025
18054
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
18026
18055
  return null;
18027
18056
  }
18028
- const current = JSON.parse(readFileSync36(first, "utf8")).version;
18057
+ const firstText = readFileSync36(first, "utf8");
18058
+ const problem = versionFileProblem(o.repo.release.versionFiles[0], firstText);
18059
+ if (problem) {
18060
+ o.emit.error(problem);
18061
+ return null;
18062
+ }
18063
+ const current = JSON.parse(firstText).version;
18029
18064
  if (!current) {
18030
18065
  o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
18031
18066
  return null;
@@ -18164,7 +18199,7 @@ function openReleaseCheckout(o) {
18164
18199
  for (const f of o.repo.worktrees.copy) {
18165
18200
  const src = join43(o.cwd, f);
18166
18201
  if (!existsSync39(src)) continue;
18167
- mkdirSync28(dirname23(join43(dir, f)), { recursive: true });
18202
+ mkdirSync28(dirname24(join43(dir, f)), { recursive: true });
18168
18203
  copyFileSync(src, join43(dir, f));
18169
18204
  }
18170
18205
  return { dir, branch, close };
@@ -19504,7 +19539,7 @@ function nextRoles(decision, ran) {
19504
19539
 
19505
19540
  // src/ship-name.ts
19506
19541
  import { existsSync as existsSync41, mkdirSync as mkdirSync29, readFileSync as readFileSync37, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19507
- import { dirname as dirname24 } from "path";
19542
+ import { dirname as dirname25 } from "path";
19508
19543
  import { parseDocument as parseDocument5 } from "yaml";
19509
19544
  var ShipNameError = class extends Error {
19510
19545
  };
@@ -19583,7 +19618,7 @@ function writeSyncedName(resolvedFile, name) {
19583
19618
  }
19584
19619
  if (raw.shipName === name) return;
19585
19620
  raw.shipName = name;
19586
- mkdirSync29(dirname24(resolvedFile), { recursive: true });
19621
+ mkdirSync29(dirname25(resolvedFile), { recursive: true });
19587
19622
  const tmp = `${resolvedFile}.tmp${process.pid}`;
19588
19623
  writeFileSync32(tmp, `${JSON.stringify(raw, null, 2)}
19589
19624
  `);
@@ -20277,7 +20312,7 @@ function describeCapability(i) {
20277
20312
 
20278
20313
  // src/agents-sync-run.ts
20279
20314
  import { existsSync as existsSync42, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync33 } from "fs";
20280
- import { dirname as dirname25 } from "path";
20315
+ import { dirname as dirname26 } from "path";
20281
20316
  async function runAgentsSync(target, o) {
20282
20317
  const dryRun = o.dryRun ?? false;
20283
20318
  const force = o.force ?? false;
@@ -20314,7 +20349,7 @@ async function runAgentsSync(target, o) {
20314
20349
  raw.agentPersonas = personaResult.agentPersonas;
20315
20350
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
20316
20351
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
20317
- mkdirSync30(dirname25(resolvedPath), { recursive: true });
20352
+ mkdirSync30(dirname26(resolvedPath), { recursive: true });
20318
20353
  writeFileSync33(resolvedPath, `${JSON.stringify(raw, null, 2)}
20319
20354
  `);
20320
20355
  if (target.resolved) {
@@ -20937,7 +20972,7 @@ ${run3.err}` });
20937
20972
  }
20938
20973
 
20939
20974
  // src/sample-ticket.ts
20940
- import { basename as basename12, dirname as dirname26, join as join44 } from "path";
20975
+ import { basename as basename12, dirname as dirname27, join as join44 } from "path";
20941
20976
  var SAMPLE_REPO = "crew-sample";
20942
20977
  var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
20943
20978
  var SAMPLE_MARKER = "sample: true";
@@ -20960,7 +20995,7 @@ has something safe to work on: its test and build hooks always pass, it has no
20960
20995
  remote, and \`crew sample-ticket --remove\` deletes it.
20961
20996
  `;
20962
20997
  function sampleDir(existing, reposBase, workspace) {
20963
- if (existing.length > 0) return join44(dirname26(existing[0]), SAMPLE_REPO);
20998
+ if (existing.length > 0) return join44(dirname27(existing[0]), SAMPLE_REPO);
20964
20999
  return join44(reposBase, workspace, SAMPLE_REPO);
20965
21000
  }
20966
21001
  var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
@@ -21006,7 +21041,7 @@ function planSampleTicket(i) {
21006
21041
 
21007
21042
  // src/worktree-sweep.ts
21008
21043
  import { existsSync as existsSync43, readFileSync as readFileSync40, readdirSync as readdirSync14 } from "fs";
21009
- import { basename as basename13, dirname as dirname27, join as join45, resolve as resolve5 } from "path";
21044
+ import { basename as basename13, dirname as dirname28, join as join45, resolve as resolve5 } from "path";
21010
21045
  function planWorktreeSweep(target, tickets, contract) {
21011
21046
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
21012
21047
  const verified = contract.statuses.verified;
@@ -21070,7 +21105,7 @@ function worktreeTicketNumbers(target) {
21070
21105
  return [...nums];
21071
21106
  }
21072
21107
  function siblingWorktreeDirs(target) {
21073
- const parent = dirname27(target.dir.replace(/[/\\]+$/, ""));
21108
+ const parent = dirname28(target.dir.replace(/[/\\]+$/, ""));
21074
21109
  const stem = `${basename13(target.dir.replace(/[/\\]+$/, ""))}-`;
21075
21110
  const registered = new Set(worktrees(target.dir).map((w) => resolve5(w.path)));
21076
21111
  let names;
@@ -21922,7 +21957,7 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
21922
21957
  // src/ssh-keys.ts
21923
21958
  import { execFileSync as execFileSync13 } from "child_process";
21924
21959
  import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync42, chmodSync as chmodSync3 } from "fs";
21925
- import { dirname as dirname28, join as join49 } from "path";
21960
+ import { dirname as dirname29, join as join49 } from "path";
21926
21961
  var SshKeyError = class extends Error {
21927
21962
  };
21928
21963
  function shipSshKeyPathFor(stateDir) {
@@ -21932,7 +21967,7 @@ function ensureShipSshKeypair(stateDir) {
21932
21967
  const privateKeyPath = shipSshKeyPathFor(stateDir);
21933
21968
  const publicKeyPath = `${privateKeyPath}.pub`;
21934
21969
  if (!existsSync46(privateKeyPath)) {
21935
- mkdirSync32(dirname28(privateKeyPath), { recursive: true });
21970
+ mkdirSync32(dirname29(privateKeyPath), { recursive: true });
21936
21971
  try {
21937
21972
  execFileSync13("ssh-keygen", [
21938
21973
  "-t",
@@ -22124,7 +22159,7 @@ import { join as join50 } from "path";
22124
22159
 
22125
22160
  // src/passenger-mirror.ts
22126
22161
  import { mkdirSync as mkdirSync33 } from "fs";
22127
- import { dirname as dirname29 } from "path";
22162
+ import { dirname as dirname30 } from "path";
22128
22163
  function mirrorRepoOf(spec) {
22129
22164
  const fromOrigin = spec.originUrl ? parseRemote(spec.originUrl) : null;
22130
22165
  if (fromOrigin) return fromOrigin;
@@ -22142,7 +22177,7 @@ async function materializeMirrorCheckouts(specs, cacheFor = (s) => new SandboxCa
22142
22177
  continue;
22143
22178
  }
22144
22179
  try {
22145
- const parent = dirname29(spec.dir);
22180
+ const parent = dirname30(spec.dir);
22146
22181
  mkdirSync33(parent, { recursive: true });
22147
22182
  mkdirSync33(spec.dir, { recursive: true });
22148
22183
  const r = await cacheFor(spec).materialize(repo, spec.base, parent, spec.name);
@@ -22348,7 +22383,7 @@ import { createServer, request as httpRequest } from "http";
22348
22383
  import { request as httpsRequest } from "https";
22349
22384
  import { connect as netConnect } from "net";
22350
22385
  import { connect as tlsConnect } from "tls";
22351
- import { dirname as dirname30, isAbsolute as isAbsolute4 } from "path";
22386
+ import { dirname as dirname31, isAbsolute as isAbsolute4 } from "path";
22352
22387
  import { brotliDecompressSync, gunzipSync, inflateSync } from "zlib";
22353
22388
 
22354
22389
  // src/periscope-inspector.ts
@@ -22462,7 +22497,7 @@ function parsePointedBody(raw) {
22462
22497
  return entry;
22463
22498
  }
22464
22499
  function appendPointed(file, entry, now = /* @__PURE__ */ new Date()) {
22465
- mkdirSync35(dirname30(file), { recursive: true });
22500
+ mkdirSync35(dirname31(file), { recursive: true });
22466
22501
  appendFileSync3(file, `${JSON.stringify({ ...entry, at: now.toISOString() })}
22467
22502
  `);
22468
22503
  }
@@ -23480,7 +23515,7 @@ import {
23480
23515
  } from "fs";
23481
23516
  import { createConnection } from "net";
23482
23517
  import { createServer as createServer2 } from "http";
23483
- import { basename as basename14, dirname as dirname31, join as join52 } from "path";
23518
+ import { basename as basename14, dirname as dirname32, join as join52 } from "path";
23484
23519
  import { execFile as execFile3, spawn as spawn12 } from "child_process";
23485
23520
 
23486
23521
  // src/periscope-control.ts
@@ -23796,7 +23831,7 @@ function candidates(live, rotated) {
23796
23831
  const out = [live];
23797
23832
  if (!rotated) return out;
23798
23833
  try {
23799
- for (const n of readdirSync18(dirname31(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join52(dirname31(live), n));
23834
+ for (const n of readdirSync18(dirname32(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join52(dirname32(live), n));
23800
23835
  } catch {
23801
23836
  }
23802
23837
  return out;
@@ -23956,7 +23991,7 @@ async function tailReply(path, q2, rotated, abort) {
23956
23991
  const deadline = Date.now() + Math.min(wait, MAX_WAIT_SECONDS) * 1e3;
23957
23992
  let r = readTail(path, { after, tail: tail3, rotated });
23958
23993
  while (after && !r.lines.length && !r.reset && Date.now() < deadline && !abort.aborted) {
23959
- await waitForChange(dirname31(path), deadline - Date.now(), abort);
23994
+ await waitForChange(dirname32(path), deadline - Date.now(), abort);
23960
23995
  r = readTail(path, { after: r.cursor, rotated });
23961
23996
  }
23962
23997
  return reply2(200, r);
@@ -24146,7 +24181,7 @@ function crewJson(args, timeoutMs = 6e4) {
24146
24181
 
24147
24182
  // src/periscope-mcp.ts
24148
24183
  import { lstat, mkdir, readdir, readFile, readlink, realpath, rm, stat, writeFile } from "fs/promises";
24149
- import { basename as basename15, dirname as dirname32, isAbsolute as isAbsolute5, join as join54, relative as relative2, resolve as resolve6, sep } from "path";
24184
+ import { basename as basename15, dirname as dirname33, isAbsolute as isAbsolute5, join as join54, relative as relative2, resolve as resolve6, sep } from "path";
24150
24185
  import { z } from "zod";
24151
24186
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24152
24187
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -24284,10 +24319,10 @@ async function realAncestor(p) {
24284
24319
  }
24285
24320
  if (isLink) {
24286
24321
  if (++hops > 40) throw new PathRefusal(`too many symbolic links resolving ${p}`);
24287
- cur = resolve6(dirname32(cur), await readlink(cur));
24322
+ cur = resolve6(dirname33(cur), await readlink(cur));
24288
24323
  continue;
24289
24324
  }
24290
- const parent = dirname32(cur);
24325
+ const parent = dirname33(cur);
24291
24326
  if (parent === cur) throw new PathRefusal(`cannot resolve ${p}`);
24292
24327
  tail3.push(basename15(cur));
24293
24328
  cur = parent;
@@ -24398,7 +24433,7 @@ function createPeriscopeMcpServer(d, sc) {
24398
24433
  if (Buffer.byteLength(content) > MAX_WRITE_BYTES) return text(`content exceeds ${MAX_WRITE_BYTES} bytes`, true);
24399
24434
  const abs = await inTree(path);
24400
24435
  if (abs === await realpath(root)) return text("path must name a file", true);
24401
- await mkdir(dirname32(abs), { recursive: true });
24436
+ await mkdir(dirname33(abs), { recursive: true });
24402
24437
  await inTree(path);
24403
24438
  await writeFile(abs, content);
24404
24439
  return text(`Wrote ${Buffer.byteLength(content)} bytes to ${await rel(abs)}`);
@@ -24438,7 +24473,7 @@ function createPeriscopeMcpServer(d, sc) {
24438
24473
  inputSchema: { path: z.string() },
24439
24474
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
24440
24475
  }, async ({ path }) => {
24441
- const parent = await inTree(dirname32(path) === "" ? "." : dirname32(path));
24476
+ const parent = await inTree(dirname33(path) === "" ? "." : dirname33(path));
24442
24477
  const abs = join54(parent, basename15(path));
24443
24478
  if (basename15(path) === ".git" || abs === parent) return text("path must name a file", true);
24444
24479
  const st = await lstat(abs).catch(() => void 0);
@@ -24560,7 +24595,7 @@ function findPeriscope(sidecars, segment) {
24560
24595
  }
24561
24596
 
24562
24597
  // src/cli.ts
24563
- var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath2(import.meta.url)), "..");
24598
+ var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname34(process.execPath) : resolve7(dirname34(fileURLToPath2(import.meta.url)), "..");
24564
24599
  var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
24565
24600
  var CYCLE_SECONDS2 = 120;
24566
24601
  var STALE_CYCLES = 3;
@@ -27109,7 +27144,7 @@ ${cu.tail.trimEnd()}
27109
27144
  const found = cfg.routes.find((r) => r.route === routeArg);
27110
27145
  if (!found) throw new ReposError(`no route "${routeArg}" in the config (have: ${cfg.routes.map((r) => r.route).join(", ") || "none"})`);
27111
27146
  const named2 = value("route") ? void 0 : positional[3];
27112
- const dir = named2 ? repoDirOf(configText, dirname33(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
27147
+ const dir = named2 ? repoDirOf(configText, dirname34(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
27113
27148
  const target = reposOf(found).find((t) => resolvePath(t.dir) === dir || t.name === named2);
27114
27149
  const o = target ? found.repoOverrides[target.name] : void 0;
27115
27150
  runReposCheck(dir, {
@@ -27133,7 +27168,7 @@ ${cu.tail.trimEnd()}
27133
27168
  if (sub === "hooks") {
27134
27169
  try {
27135
27170
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
27136
- const dir = repoDirOf(configText, dirname33(cfg.configFile), a.route, a.name);
27171
+ const dir = repoDirOf(configText, dirname34(cfg.configFile), a.route, a.name);
27137
27172
  if (!existsSync51(dir)) throw new ReposError(`${dir} does not exist on disk`);
27138
27173
  const files = { exists: (r) => existsSync51(resolvePath(dir, r)), list: (r) => {
27139
27174
  try {
@@ -27300,7 +27335,7 @@ ${cu.tail.trimEnd()}
27300
27335
  }
27301
27336
  const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
27302
27337
  const text2 = configText;
27303
- const base = dirname33(cfg.configFile);
27338
+ const base = dirname34(cfg.configFile);
27304
27339
  const isCheckout = (d) => existsSync51(`${d}/.git`);
27305
27340
  try {
27306
27341
  if (sub === "list") {
@@ -27315,7 +27350,8 @@ ${cu.tail.trimEnd()}
27315
27350
  isCheckout,
27316
27351
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
27317
27352
  try {
27318
- return Object.keys(parseRepoConfig(readFileSync46(resolvePath(d, f), "utf8"), f).hooks).length > 0;
27353
+ const path = resolvePath(d, f);
27354
+ return Object.keys(parseRepoConfig(readFileSync46(path, "utf8"), path).hooks).length > 0;
27319
27355
  } catch {
27320
27356
  return false;
27321
27357
  }
@@ -27978,7 +28014,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27978
28014
  process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
27979
28015
  `);
27980
28016
  } else {
27981
- mkdirSync38(dirname33(resolvedPath), { recursive: true });
28017
+ mkdirSync38(dirname34(resolvedPath), { recursive: true });
27982
28018
  if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
27983
28019
  const tmpPath = `${resolvedPath}.tmp${process.pid}`;
27984
28020
  writeFileSync38(tmpPath, `${JSON.stringify(resolved, null, 2)}
@@ -28027,7 +28063,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28027
28063
  process.stderr.write(`(dry run) would write the minted API key to ${apiKeyFile}
28028
28064
  `);
28029
28065
  } else {
28030
- mkdirSync38(dirname33(apiKeyFile), { recursive: true });
28066
+ mkdirSync38(dirname34(apiKeyFile), { recursive: true });
28031
28067
  writeFileSync38(apiKeyFile, `${apiKeyVar}=${apiKey}
28032
28068
  `, { mode: 384 });
28033
28069
  process.stderr.write(`API key written to ${apiKeyFile}
@@ -29500,7 +29536,7 @@ ${where.join("\n")}`);
29500
29536
  if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
29501
29537
  const tracker = new Tracker(route2, cfg.ship);
29502
29538
  const configText = readFileSync46(cfg.configFile, "utf8");
29503
- const cfgBase = dirname33(cfg.configFile);
29539
+ const cfgBase = dirname34(cfg.configFile);
29504
29540
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
29505
29541
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
29506
29542
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, route2.route);
@@ -24682,6 +24682,19 @@ var SPEC_VERSION = 1;
24682
24682
  var REPO_CONFIG_FILENAMES = [".crew.yaml", ".crew.yml", ".crew.json"];
24683
24683
  var RepoConfigError = class extends Error {
24684
24684
  };
24685
+ function versionFileProblem(name, text) {
24686
+ const remedy = `a version file that is not a JSON object with a string "version" needs hooks.bump instead of release.versionFiles (docs/REPO_SPEC.md)`;
24687
+ let doc;
24688
+ try {
24689
+ doc = JSON.parse(text);
24690
+ } catch {
24691
+ return `release.versionFiles entry "${name}" is not a JSON document \u2014 ${remedy}`;
24692
+ }
24693
+ if (!doc || typeof doc !== "object" || Array.isArray(doc) || typeof doc.version !== "string") {
24694
+ return `release.versionFiles entry "${name}" has no string "version" field \u2014 ${remedy}`;
24695
+ }
24696
+ return null;
24697
+ }
24685
24698
  var HOOK_NAMES = [
24686
24699
  "test",
24687
24700
  "build",
@@ -25015,6 +25028,22 @@ function parseRepoConfig(text, file) {
25015
25028
  if (versionFiles.length === 0) {
25016
25029
  throw new RepoConfigError(`${file}: release.versionFiles cannot be empty`);
25017
25030
  }
25031
+ if (versionFilesGiven) {
25032
+ for (const name of versionFiles) {
25033
+ const path = (0, import_node_path6.join)((0, import_node_path6.dirname)(file), name);
25034
+ if (!(0, import_node_fs5.existsSync)(path)) continue;
25035
+ let text2;
25036
+ try {
25037
+ text2 = (0, import_node_fs5.readFileSync)(path, "utf8");
25038
+ } catch (e) {
25039
+ throw new RepoConfigError(
25040
+ `${file}: release.versionFiles entry "${name}" cannot be read (${e.code ?? "read error"})`
25041
+ );
25042
+ }
25043
+ const problem = versionFileProblem(name, text2);
25044
+ if (problem) throw new RepoConfigError(`${file}: ${problem}`);
25045
+ }
25046
+ }
25018
25047
  const inspectorRaw = raw.periscope?.inspector;
25019
25048
  if (inspectorRaw !== void 0 && inspectorRaw !== "proxy" && inspectorRaw !== "repo") {
25020
25049
  throw new RepoConfigError(`${file}: periscope.inspector must be "proxy" or "repo"`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.126.1",
3
+ "version": "0.126.2",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",