@tablation/crew 0.121.0 → 0.122.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 +676 -434
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -262,6 +262,9 @@ function intelFlavor(script) {
262
262
  const w = script?.trim();
263
263
  return w && Object.prototype.hasOwnProperty.call(INTEL_FLAVORS, w) ? w : void 0;
264
264
  }
265
+ function wantsCodeBridge(script) {
266
+ return !!script?.trim() && intelFlavor(script) !== "typescript-lsp";
267
+ }
265
268
  var INTEL_MINIMUM_TOOLS = [
266
269
  "find symbols by name",
267
270
  "references to a symbol",
@@ -1309,13 +1312,17 @@ var claudeAdapter = {
1309
1312
  // so the resolved key is never written to an argv string or a config
1310
1313
  // file on disk. Skipped when there's no key to send: a route `crew
1311
1314
  // connect` hasn't resolved yet has nothing to authenticate with.
1312
- ...o.mcp ? ["--mcp-config", JSON.stringify({
1315
+ ...o.mcp || o.code ? ["--mcp-config", JSON.stringify({
1313
1316
  mcpServers: {
1314
- tablation: {
1315
- type: "http",
1316
- url: o.mcp.url,
1317
- headers: { Authorization: `Bearer \${${o.mcp.keyVar}}` }
1318
- }
1317
+ ...o.mcp ? {
1318
+ tablation: {
1319
+ type: "http",
1320
+ url: o.mcp.url,
1321
+ headers: { Authorization: `Bearer \${${o.mcp.keyVar}}` }
1322
+ }
1323
+ } : {},
1324
+ // CREW-2912: the bridge to the worktree's symbol service, attached once `crew worktree` has started it.
1325
+ ...o.code ? { [INTEL_SERVER_NAME]: { type: "stdio", command: o.code.command, args: o.code.args } } : {}
1319
1326
  }
1320
1327
  })] : [],
1321
1328
  // A crew-generated language-server plugin (the `typescript-lsp` intel flavor). Not under oMLX,
@@ -1664,6 +1671,14 @@ var codexAdapter = {
1664
1671
  ...cfg(`mcp_servers.${SERVER}.bearer_token_env_var`, o.mcp.keyVar),
1665
1672
  ...cfg(`mcp_servers.${SERVER}.default_tools_approval_mode`, "approve")
1666
1673
  ] : [],
1674
+ // CREW-2912: the bridge to the worktree's symbol service, scoped to this run like the tracker entry above.
1675
+ ...o.code ? [
1676
+ "-c",
1677
+ `mcp_servers.${INTEL_SERVER_NAME}.command=${tomlString(o.code.command)}`,
1678
+ "-c",
1679
+ `mcp_servers.${INTEL_SERVER_NAME}.args=[${o.code.args.map(tomlString).join(", ")}]`,
1680
+ ...cfg(`mcp_servers.${INTEL_SERVER_NAME}.default_tools_approval_mode`, "approve")
1681
+ ] : [],
1667
1682
  ...spec.args,
1668
1683
  // Prompt on stdin.
1669
1684
  "-"
@@ -2045,7 +2060,8 @@ var geminiAdapter = {
2045
2060
  ];
2046
2061
  return wrapInvocation(spec, { model: o.model, args });
2047
2062
  },
2048
- runEnv: (spec) => ({ ...spec.env }),
2063
+ // CREW-2912: Gemini has no per-run server list, so the bridge is declared in a per-run system settings file the planner wrote.
2064
+ runEnv: (spec, o) => ({ ...spec.env, ...o?.code?.settingsFile ? { GEMINI_CLI_SYSTEM_SETTINGS_PATH: o.code.settingsFile } : {} }),
2049
2065
  provider: () => "google",
2050
2066
  unsetEnv: BILLING_VARS_TO_UNSET2,
2051
2067
  mapStreamLine: mapGeminiLine,
@@ -6189,8 +6205,8 @@ function attentionProblems(items, already) {
6189
6205
  import { createHash as createHash6 } from "crypto";
6190
6206
  import { spawn as spawn5 } from "child_process";
6191
6207
  import { execFileSync as execFileSync8 } from "child_process";
6192
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync25, renameSync as renameSync3, writeFileSync as writeFileSync19, rmSync as rmSync14 } from "fs";
6193
- import { dirname as dirname13, join as join30 } from "path";
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";
6194
6210
  import { tmpdir as tmpdir3 } from "os";
6195
6211
 
6196
6212
  // src/passenger-containers.ts
@@ -6203,18 +6219,18 @@ import { promisify as promisify2 } from "util";
6203
6219
 
6204
6220
  // src/deprovision-host.ts
6205
6221
  import { spawnSync as spawnSync7 } from "child_process";
6206
- import { existsSync as existsSync28, readdirSync as readdirSync11 } from "fs";
6222
+ import { existsSync as existsSync29, readdirSync as readdirSync11 } from "fs";
6207
6223
 
6208
6224
  // src/interrupted.ts
6209
- import { existsSync as existsSync26, readdirSync as readdirSync9, readFileSync as readFileSync24 } from "fs";
6210
- import { join as join28 } from "path";
6225
+ import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync25 } from "fs";
6226
+ import { join as join29 } from "path";
6211
6227
 
6212
6228
  // src/agent.ts
6213
6229
  import { spawnSync as spawnSync6 } from "child_process";
6214
- import { readFileSync as readFileSync23, existsSync as existsSync25, statSync as statSync8, mkdirSync as mkdirSync17, writeFileSync as writeFileSync18, createWriteStream, openSync as openSync4, closeSync as closeSync4, readSync } from "fs";
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";
6215
6231
  import { StringDecoder } from "string_decoder";
6216
6232
  import { finished } from "stream/promises";
6217
- import { join as join27, dirname as dirname12, basename as basename9 } from "path";
6233
+ import { join as join28, dirname as dirname13, basename as basename9 } from "path";
6218
6234
  import { createHash as createHash4 } from "crypto";
6219
6235
 
6220
6236
  // src/state.ts
@@ -7100,6 +7116,143 @@ function renderEnvironment(i) {
7100
7116
  return lines2.join("\n");
7101
7117
  }
7102
7118
 
7119
+ // 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";
7122
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7123
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7124
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7125
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7126
+ import {
7127
+ CallToolRequestSchema,
7128
+ ListToolsRequestSchema
7129
+ } from "@modelcontextprotocol/sdk/types.js";
7130
+ var STATUS_TOOL = "code_status";
7131
+ var STATUS_WAITING = "The symbol service is not available yet: the worktree has not been cut. Run `crew worktree` for the ticket; the symbol tools appear in this server afterwards.";
7132
+ 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
+ function intelDir(stateDir, route2, ticket) {
7134
+ const safe = (s) => s.replace(/[^A-Za-z0-9._-]+/g, "_");
7135
+ return join21(stateDir, "intel", safe(route2), safe(ticket));
7136
+ }
7137
+ var attachFilePath = (stateDir, route2, ticket) => join21(intelDir(stateDir, route2, ticket), "url");
7138
+ function writeAttachFile(file, url) {
7139
+ mkdirSync14(dirname10(file), { recursive: true });
7140
+ writeFileSync15(file, `${url}
7141
+ `);
7142
+ }
7143
+ function readAttachFile(file) {
7144
+ try {
7145
+ const url = readFileSync19(file, "utf8").trim();
7146
+ return /^https?:\/\/\S+$/i.test(url) ? url : void 0;
7147
+ } catch {
7148
+ return void 0;
7149
+ }
7150
+ }
7151
+ var statusTool = {
7152
+ name: STATUS_TOOL,
7153
+ description: "Reports whether this repository's symbol service (find symbols, references, definitions, file outlines) is attached. Before `crew worktree` has been run for the ticket it is not; the symbol tools replace this one once it is.",
7154
+ inputSchema: { type: "object", properties: {} }
7155
+ };
7156
+ var defaultConnect = async (url) => {
7157
+ const client2 = new Client({ name: "crew-intel-bridge", version: "0" }, { capabilities: {} });
7158
+ await client2.connect(new StreamableHTTPClientTransport(new URL(url)));
7159
+ return client2;
7160
+ };
7161
+ function createBridge(o) {
7162
+ const connect2 = o.connectUpstream ?? defaultConnect;
7163
+ const server = new Server(
7164
+ { name: INTEL_SERVER_NAME, version: o.version ?? "0" },
7165
+ { capabilities: { tools: { listChanged: true } } }
7166
+ );
7167
+ let upstream;
7168
+ let lost = false;
7169
+ let connecting = false;
7170
+ let timer;
7171
+ const notify2 = () => {
7172
+ void server.sendToolListChanged().catch(() => {
7173
+ });
7174
+ };
7175
+ const drop = () => {
7176
+ const gone = upstream;
7177
+ upstream = void 0;
7178
+ lost = true;
7179
+ if (gone) void gone.close().catch(() => {
7180
+ });
7181
+ notify2();
7182
+ };
7183
+ const status2 = () => ({ content: [{ type: "text", text: lost ? STATUS_LOST : STATUS_WAITING }] });
7184
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
7185
+ if (!upstream) return { tools: [statusTool] };
7186
+ try {
7187
+ return await upstream.listTools();
7188
+ } catch {
7189
+ drop();
7190
+ return { tools: [statusTool] };
7191
+ }
7192
+ });
7193
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
7194
+ if (!upstream) return status2();
7195
+ try {
7196
+ return await upstream.callTool({ name: req.params.name, arguments: req.params.arguments });
7197
+ } catch {
7198
+ drop();
7199
+ return status2();
7200
+ }
7201
+ });
7202
+ const tryAttach = async () => {
7203
+ if (upstream || connecting) return false;
7204
+ const url = readAttachFile(o.attachFile);
7205
+ if (!url) return false;
7206
+ connecting = true;
7207
+ try {
7208
+ const client2 = await connect2(url);
7209
+ client2.onclose = () => {
7210
+ if (upstream === client2) drop();
7211
+ };
7212
+ upstream = client2;
7213
+ lost = false;
7214
+ notify2();
7215
+ return true;
7216
+ } catch {
7217
+ return false;
7218
+ } finally {
7219
+ connecting = false;
7220
+ }
7221
+ };
7222
+ return {
7223
+ server,
7224
+ attached: () => upstream !== void 0,
7225
+ tryAttach,
7226
+ async start(transport) {
7227
+ await server.connect(transport);
7228
+ if (existsSync20(o.attachFile)) await tryAttach();
7229
+ timer = setInterval(() => {
7230
+ void tryAttach();
7231
+ }, o.pollMs ?? 1e3);
7232
+ timer.unref?.();
7233
+ },
7234
+ async stop() {
7235
+ if (timer) clearInterval(timer);
7236
+ const gone = upstream;
7237
+ upstream = void 0;
7238
+ await gone?.close().catch(() => {
7239
+ });
7240
+ await server.close().catch(() => {
7241
+ });
7242
+ }
7243
+ };
7244
+ }
7245
+ async function runBridge(stateDir, route2, ticket) {
7246
+ const bridge = createBridge({ attachFile: attachFilePath(stateDir, route2, ticket) });
7247
+ const closed = new Promise((resolve8) => {
7248
+ bridge.server.onclose = resolve8;
7249
+ process.stdin.on("end", resolve8);
7250
+ });
7251
+ await bridge.start(new StdioServerTransport());
7252
+ await closed;
7253
+ await bridge.stop();
7254
+ }
7255
+
7103
7256
  // src/contract.ts
7104
7257
  var DEFAULT_CONTRACT = {
7105
7258
  columns: {
@@ -7329,8 +7482,8 @@ var BeforeFirstEditTracker = class {
7329
7482
  };
7330
7483
 
7331
7484
  // src/agent-log.ts
7332
- import { existsSync as existsSync20, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync19, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "fs";
7333
- import { join as join21 } from "path";
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";
7334
7487
 
7335
7488
  // src/pending-backoff.ts
7336
7489
  var sleepBackoff = (attempts) => Math.min(6e4 * 2 ** Math.max(0, attempts - 1), 30 * 6e4);
@@ -7533,20 +7686,20 @@ async function addRunToTicket(target, t, recordId, run3) {
7533
7686
  };
7534
7687
  }
7535
7688
  }
7536
- var queueDir = (stateDir) => join21(stateDir, "pending-agent-logs");
7689
+ var queueDir = (stateDir) => join22(stateDir, "pending-agent-logs");
7537
7690
  function queueAgentRun(stateDir, name, q2) {
7538
- mkdirSync14(queueDir(stateDir), { recursive: true });
7539
- writeFileSync15(join21(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
7691
+ mkdirSync15(queueDir(stateDir), { recursive: true });
7692
+ writeFileSync16(join22(queueDir(stateDir), `${name}.json`), JSON.stringify(q2));
7540
7693
  }
7541
7694
  async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
7542
7695
  const dir = queueDir(stateDir);
7543
- if (!existsSync20(dir)) return 0;
7696
+ if (!existsSync21(dir)) return 0;
7544
7697
  let sent = 0;
7545
7698
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
7546
- const path = join21(dir, f);
7699
+ const path = join22(dir, f);
7547
7700
  let q2;
7548
7701
  try {
7549
- q2 = JSON.parse(readFileSync19(path, "utf8"));
7702
+ q2 = JSON.parse(readFileSync20(path, "utf8"));
7550
7703
  } catch {
7551
7704
  continue;
7552
7705
  }
@@ -7565,17 +7718,17 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
7565
7718
  attempts: 0,
7566
7719
  nextAttemptAt: (/* @__PURE__ */ new Date()).toISOString()
7567
7720
  });
7568
- rmSync11(path, { force: true });
7721
+ rmSync12(path, { force: true });
7569
7722
  sent += 1;
7570
7723
  log.emit(`sent a queued Agent Log report (${f})`);
7571
7724
  } catch (e) {
7572
7725
  if (!(e instanceof AgentLogUnreachableError)) {
7573
- rmSync11(path, { force: true });
7726
+ rmSync12(path, { force: true });
7574
7727
  log.warn(`dropped a queued Agent Log report (${f}): ${e.message}`);
7575
7728
  continue;
7576
7729
  }
7577
7730
  const attempts = q2.attempts + 1;
7578
- writeFileSync15(path, JSON.stringify({
7731
+ writeFileSync16(path, JSON.stringify({
7579
7732
  ...q2,
7580
7733
  attempts,
7581
7734
  warned: true,
@@ -7586,20 +7739,20 @@ async function replayQueuedAgentRuns(stateDir, route2, keyFor, log, now = /* @__
7586
7739
  }
7587
7740
  return sent;
7588
7741
  }
7589
- var countersDir = (stateDir) => join21(stateDir, "pending-ticket-counters");
7742
+ var countersDir = (stateDir) => join22(stateDir, "pending-ticket-counters");
7590
7743
  function queueTicketCounters(stateDir, name, q2) {
7591
- mkdirSync14(countersDir(stateDir), { recursive: true });
7592
- writeFileSync15(join21(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
7744
+ mkdirSync15(countersDir(stateDir), { recursive: true });
7745
+ writeFileSync16(join22(countersDir(stateDir), `${name}.json`), JSON.stringify(q2));
7593
7746
  }
7594
7747
  async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /* @__PURE__ */ new Date()) {
7595
7748
  const dir = countersDir(stateDir);
7596
- if (!existsSync20(dir)) return 0;
7749
+ if (!existsSync21(dir)) return 0;
7597
7750
  let sent = 0;
7598
7751
  for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
7599
- const path = join21(dir, f);
7752
+ const path = join22(dir, f);
7600
7753
  let q2;
7601
7754
  try {
7602
- q2 = JSON.parse(readFileSync19(path, "utf8"));
7755
+ q2 = JSON.parse(readFileSync20(path, "utf8"));
7603
7756
  } catch {
7604
7757
  continue;
7605
7758
  }
@@ -7608,17 +7761,17 @@ async function replayQueuedTicketCounters(stateDir, route2, keyFor, log, now = /
7608
7761
  const apiKey = await keyFor(q2.role);
7609
7762
  if (!apiKey) continue;
7610
7763
  await writeCounters({ ...q2.target, apiKey }, q2.write);
7611
- rmSync11(path, { force: true });
7764
+ rmSync12(path, { force: true });
7612
7765
  sent += 1;
7613
7766
  log.emit(`added a queued run's spend to ${q2.write.ticketReference ?? q2.write.recordId} (${f})`);
7614
7767
  } catch (e) {
7615
7768
  if (!isRetryable(e)) {
7616
- rmSync11(path, { force: true });
7769
+ rmSync12(path, { force: true });
7617
7770
  log.warn(`dropped a queued ticket counter update (${f}): ${e.message}`);
7618
7771
  continue;
7619
7772
  }
7620
7773
  const attempts = q2.attempts + 1;
7621
- writeFileSync15(path, JSON.stringify({
7774
+ writeFileSync16(path, JSON.stringify({
7622
7775
  ...q2,
7623
7776
  attempts,
7624
7777
  warned: true,
@@ -7821,8 +7974,8 @@ var HandoffTranscriptTracker = class {
7821
7974
  };
7822
7975
 
7823
7976
  // src/pending-handoff.ts
7824
- import { existsSync as existsSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync7, readFileSync as readFileSync20, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "fs";
7825
- import { join as join23 } from "path";
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";
7826
7979
 
7827
7980
  // src/ticket-branch.ts
7828
7981
  import { basename as basename7 } from "path";
@@ -8988,8 +9141,8 @@ function holdWarnings(tickets, ships, contract, humanHolds, now = Date.now()) {
8988
9141
  }
8989
9142
 
8990
9143
  // src/run-worktree.ts
8991
- import { existsSync as existsSync21 } from "fs";
8992
- import { dirname as dirname10, join as join22 } from "path";
9144
+ import { existsSync as existsSync22 } from "fs";
9145
+ import { dirname as dirname11, join as join23 } from "path";
8993
9146
  var describeRunWorktreePlan = (p) => {
8994
9147
  switch (p.action) {
8995
9148
  case "cut-remote":
@@ -9030,7 +9183,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
9030
9183
  const existing = worktreeForNumber(dir, num);
9031
9184
  const located = locateBranchForTicket(dir, cfg, t, role);
9032
9185
  const baseRef = hasRemote ? `${remote}/${base}` : base;
9033
- if (existing && !existing.prunable && existsSync21(existing.path)) {
9186
+ if (existing && !existing.prunable && existsSync22(existing.path)) {
9034
9187
  const branch = existing.branch;
9035
9188
  if (status(existing.path).length > 0) {
9036
9189
  return { action: opts.resumeInterrupted ? "resume-dirty" : "leave-dirty", path: existing.path, branch };
@@ -9046,7 +9199,7 @@ function planRunWorktree(dir, cfg, t, role, opts = {}) {
9046
9199
  if (countCommits(existing.path, `${baseRef}..HEAD`) > 0) return { action: "leave-unpushed", path: existing.path, branch };
9047
9200
  return { action: "recut", path: existing.path, branch, base: baseRef };
9048
9201
  }
9049
- const path = join22(dirname10(dir), worktreeForTicket(dir, cfg, t, role));
9202
+ const path = join23(dirname11(dir), worktreeForTicket(dir, cfg, t, role));
9050
9203
  if (located?.where === "remote" || located && hasRemote && remoteBranchExists(dir, remote, located.branch)) {
9051
9204
  return { action: "cut-remote", path, branch: located.branch };
9052
9205
  }
@@ -9100,7 +9253,7 @@ function dropEmptyRunBranch(dir, cfg, branch) {
9100
9253
  if (remoteConfigured(dir, remote) && remoteBranchExists(dir, remote, branch)) return false;
9101
9254
  if (countCommits(dir, `${baseRef}..${branch}`) !== 0) return false;
9102
9255
  const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
9103
- if (wt && existsSync21(wt.path)) {
9256
+ if (wt && existsSync22(wt.path)) {
9104
9257
  if (status(wt.path).length > 0) return false;
9105
9258
  if (gitOk(dir, ["worktree", "remove", wt.path]) === null) return false;
9106
9259
  }
@@ -9108,7 +9261,7 @@ function dropEmptyRunBranch(dir, cfg, branch) {
9108
9261
  }
9109
9262
  async function finishRunWorktree(dir, cfg, branch) {
9110
9263
  const wt = worktrees(dir).find((w) => w.branch === branch && !w.prunable && w.path !== dir);
9111
- if (!wt || !existsSync21(wt.path)) return { kind: "none" };
9264
+ if (!wt || !existsSync22(wt.path)) return { kind: "none" };
9112
9265
  if (cfg.hooks.handoff) return { kind: "kept-handoff", path: wt.path };
9113
9266
  if (status(wt.path).length > 0) return { kind: "kept-dirty", path: wt.path };
9114
9267
  if (remoteConfigured(dir, cfg.branch.remote) && worktreeHoldsUnpushed(wt.path, cfg.branch.remote)) {
@@ -9120,7 +9273,7 @@ async function finishRunWorktree(dir, cfg, branch) {
9120
9273
  if (pids.length) await killGently(pids);
9121
9274
  }
9122
9275
  gitOk(dir, ["worktree", "remove", wt.path]);
9123
- return existsSync21(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
9276
+ return existsSync22(wt.path) ? { kind: "kept-dirty", path: wt.path } : { kind: "removed", path: wt.path };
9124
9277
  }
9125
9278
 
9126
9279
  // src/handoff-guard.ts
@@ -9503,20 +9656,20 @@ async function guardRunStrays(route2, ship, tracker, role, workingId, since2, lo
9503
9656
 
9504
9657
  // src/pending-handoff.ts
9505
9658
  var PENDING_MAX_AGE_MS = 48 * 36e5;
9506
- var pendingDirFor = (stateDir) => join23(stateDir, "pending-handoffs");
9507
- var stateDirOfStream = (streamPath) => join23(streamPath, "..", "..");
9659
+ var pendingDirFor = (stateDir) => join24(stateDir, "pending-handoffs");
9660
+ var stateDirOfStream = (streamPath) => join24(streamPath, "..", "..");
9508
9661
  function writePendingHandoff(stateDir, rec) {
9509
9662
  const dir = pendingDirFor(stateDir);
9510
- mkdirSync15(dir, { recursive: true });
9511
- writeFileSync16(join23(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
9663
+ mkdirSync16(dir, { recursive: true });
9664
+ writeFileSync17(join24(dir, `${rec.ticketRecordId}.json`), JSON.stringify(rec, null, 2));
9512
9665
  }
9513
9666
  function listPendingHandoffs(stateDir, route2) {
9514
9667
  const dir = pendingDirFor(stateDir);
9515
- if (!existsSync22(dir)) return [];
9668
+ if (!existsSync23(dir)) return [];
9516
9669
  const out = [];
9517
9670
  for (const f of readdirSync7(dir).filter((n) => n.endsWith(".json")).sort()) {
9518
9671
  try {
9519
- const rec = JSON.parse(readFileSync20(join23(dir, f), "utf8"));
9672
+ const rec = JSON.parse(readFileSync21(join24(dir, f), "utf8"));
9520
9673
  if (rec.version === 1 && rec.ticketRecordId && (!route2 || rec.route === route2)) out.push(rec);
9521
9674
  } catch {
9522
9675
  }
@@ -9524,7 +9677,7 @@ function listPendingHandoffs(stateDir, route2) {
9524
9677
  return out;
9525
9678
  }
9526
9679
  function removePendingHandoff(stateDir, ticketRecordId) {
9527
- rmSync12(join23(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
9680
+ rmSync13(join24(pendingDirFor(stateDir), `${ticketRecordId}.json`), { force: true });
9528
9681
  }
9529
9682
  function pendingHandoffFor(stateDir, ticketRecordId) {
9530
9683
  return listPendingHandoffs(stateDir).find((r) => r.ticketRecordId === ticketRecordId);
@@ -9634,8 +9787,8 @@ function expandSkillMarkers(text2, lookup2, opts = {}) {
9634
9787
  }
9635
9788
 
9636
9789
  // src/skills.ts
9637
- import { readFileSync as readFileSync22, readdirSync as readdirSync8, existsSync as existsSync24 } from "fs";
9638
- import { join as join25 } from "path";
9790
+ import { readFileSync as readFileSync23, readdirSync as readdirSync8, existsSync as existsSync25 } from "fs";
9791
+ import { join as join26 } from "path";
9639
9792
  import { parse as parseYaml } from "yaml";
9640
9793
  import { TablationClient as TablationClient3, StaleWriteError as StaleWriteError3, TablationApiError as TablationApiError2 } from "@tablation/client";
9641
9794
 
@@ -9651,8 +9804,8 @@ ${additions}`;
9651
9804
  }
9652
9805
 
9653
9806
  // src/agents.ts
9654
- import { readFileSync as readFileSync21, existsSync as existsSync23 } from "fs";
9655
- import { join as join24 } from "path";
9807
+ import { readFileSync as readFileSync22, existsSync as existsSync24 } from "fs";
9808
+ import { join as join25 } from "path";
9656
9809
  import { TablationClient as TablationClient2, StaleWriteError as StaleWriteError2, TablationApiError } from "@tablation/client";
9657
9810
  var PERSONA_NAME = {
9658
9811
  dev: "Developer",
@@ -9666,12 +9819,12 @@ var CREW_AGENT_COLUMN = "agent_id";
9666
9819
  var AgentsSyncError = class extends Error {
9667
9820
  };
9668
9821
  function personaDefaultPrompt(promptsDir, role) {
9669
- const briefPath = join24(promptsDir, "personas", `lane-${role}.md`);
9670
- if (!existsSync23(briefPath)) throw new AgentsSyncError(`no brief at ${briefPath}`);
9671
- if (SELF_CONTAINED_ROLES.has(role)) return readFileSync21(briefPath, "utf8");
9672
- const commonPath = join24(promptsDir, "personas", "common.md");
9673
- if (!existsSync23(commonPath)) return readFileSync21(briefPath, "utf8");
9674
- return `${readFileSync21(commonPath, "utf8")}${readFileSync21(briefPath, "utf8")}`;
9822
+ const briefPath = join25(promptsDir, "personas", `lane-${role}.md`);
9823
+ 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")}`;
9675
9828
  }
9676
9829
  async function resolveAgentId(route2, role, opts) {
9677
9830
  const cached = route2.resolved?.agentPersonas?.[role]?.agentId;
@@ -10118,7 +10271,7 @@ function describeCrewLink(o) {
10118
10271
  var SkillSyncError = class extends Error {
10119
10272
  };
10120
10273
  function parseSkillFile(path) {
10121
- const text2 = readFileSync22(path, "utf8");
10274
+ const text2 = readFileSync23(path, "utf8");
10122
10275
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(text2);
10123
10276
  if (!match) throw new SkillSyncError(`${path}: missing YAML frontmatter (expected a leading --- block)`);
10124
10277
  const frontmatter = match[1];
@@ -10131,9 +10284,9 @@ function parseSkillFile(path) {
10131
10284
  return { name: meta.name, description: meta.description, prompt: body, file: path };
10132
10285
  }
10133
10286
  function listLocalSkills(promptsDir) {
10134
- const dir = join25(promptsDir, "skills");
10135
- if (!existsSync24(dir)) return [];
10136
- return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join25(dir, f)));
10287
+ const dir = join26(promptsDir, "skills");
10288
+ if (!existsSync25(dir)) return [];
10289
+ return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((f) => parseSkillFile(join26(dir, f)));
10137
10290
  }
10138
10291
  async function syncSkills(route2, opts) {
10139
10292
  if (!route2.resolved) {
@@ -10318,9 +10471,9 @@ function skillMarkerWarnings(promptsDir, personaPrompts) {
10318
10471
 
10319
10472
  // src/runner.ts
10320
10473
  import { spawn as spawn3, execFile, execFileSync as execFileSync6 } from "child_process";
10321
- import { mkdtempSync, mkdirSync as mkdirSync16, writeFileSync as writeFileSync17, rmSync as rmSync13 } from "fs";
10474
+ import { mkdtempSync, mkdirSync as mkdirSync17, writeFileSync as writeFileSync18, rmSync as rmSync14 } from "fs";
10322
10475
  import { tmpdir as tmpdir2 } from "os";
10323
- import { basename as basename8, dirname as dirname11, join as join26 } from "path";
10476
+ import { basename as basename8, dirname as dirname12, join as join27 } from "path";
10324
10477
  import { promisify } from "util";
10325
10478
 
10326
10479
  // src/run-limit.ts
@@ -10449,8 +10602,8 @@ var SandboxRunner = class {
10449
10602
  throw new Error(`cannot generate the sandbox policy: ${e.message}`);
10450
10603
  }
10451
10604
  policyFile = policyPathFor(plan.streamPath);
10452
- mkdirSync16(dirname11(policyFile), { recursive: true });
10453
- writeFileSync17(policyFile, generated.yaml, { mode: 384 });
10605
+ mkdirSync17(dirname12(policyFile), { recursive: true });
10606
+ writeFileSync18(policyFile, generated.yaml, { mode: 384 });
10454
10607
  }
10455
10608
  const create2 = ["sandbox", "create", "--name", name, "--detach"];
10456
10609
  if (policyFile) create2.push("--policy", policyFile);
@@ -10493,13 +10646,13 @@ var SandboxRunner = class {
10493
10646
  const harness = cfg.agentBin ?? basename8(plan.bin);
10494
10647
  const viaUpload = Buffer.byteLength(plan.prompt) > SANDBOX_STDIN_LIMIT;
10495
10648
  if (viaUpload) {
10496
- const tmp = mkdtempSync(join26(tmpdir2(), "crew-prompt-"));
10649
+ const tmp = mkdtempSync(join27(tmpdir2(), "crew-prompt-"));
10497
10650
  try {
10498
- const file = join26(tmp, "prompt.txt");
10499
- writeFileSync17(file, plan.prompt, { mode: 384 });
10651
+ const file = join27(tmp, "prompt.txt");
10652
+ writeFileSync18(file, plan.prompt, { mode: 384 });
10500
10653
  await this.os(["sandbox", "upload", name, file, SANDBOX_PROMPT_PATH]);
10501
10654
  } finally {
10502
- rmSync13(tmp, { recursive: true, force: true });
10655
+ rmSync14(tmp, { recursive: true, force: true });
10503
10656
  }
10504
10657
  }
10505
10658
  const withStubs = `PATH=${shQuote2(SANDBOX_STUB_DIR)}:"$PATH"; export PATH; `;
@@ -10649,7 +10802,7 @@ var ALLOWANCE_REFUSAL_MARKER = "AI_ALLOWANCE_EXCEEDED";
10649
10802
  function streamShowsAllowanceRefusal(streamPath) {
10650
10803
  let raw;
10651
10804
  try {
10652
- raw = readFileSync23(streamPath, "utf8");
10805
+ raw = readFileSync24(streamPath, "utf8");
10653
10806
  } catch {
10654
10807
  return false;
10655
10808
  }
@@ -10672,26 +10825,49 @@ function streamShowsAllowanceRefusal(streamPath) {
10672
10825
  }
10673
10826
  return false;
10674
10827
  }
10675
- var digestPathFor = (stateDir, routeLabel, role, runId) => join27(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10828
+ var digestPathFor = (stateDir, routeLabel, role, runId) => join28(stateDir, `digest-${routeLabel}-${role}${runId ? `-${runId}` : ""}.md`);
10829
+ function codeBridgeFor(o, harness) {
10830
+ if (o.ship.runner === "sandbox" || !o.ticket) return void 0;
10831
+ const number = /(\d+)$/.exec(o.ticket)?.[1];
10832
+ if (!number) return void 0;
10833
+ let intel;
10834
+ try {
10835
+ const repos = reposOf(o.route);
10836
+ const target = repos.find((r) => r.name === o.repo) ?? (repos.length === 1 ? repos[0] : void 0);
10837
+ intel = target ? loadRepoConfig(target.dir)?.hooks?.intel : void 0;
10838
+ } catch {
10839
+ }
10840
+ if (!wantsCodeBridge(intel)) return void 0;
10841
+ const args = [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
10842
+ const bridge = { command: process.execPath, args };
10843
+ if (harness === "gemini") {
10844
+ const file = join28(intelDir(o.stateDir, o.route.route, number), "gemini-settings.json");
10845
+ mkdirSync18(dirname13(file), { recursive: true });
10846
+ writeFileSync19(file, `${JSON.stringify({ mcpServers: { [INTEL_SERVER_NAME]: { command: bridge.command, args } } }, null, 2)}
10847
+ `);
10848
+ bridge.settingsFile = file;
10849
+ }
10850
+ return bridge;
10851
+ }
10676
10852
  function planAgentRun(o) {
10677
- const briefPath = join27(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10678
- if (!existsSync25(briefPath)) {
10853
+ const briefPath = join28(o.route.promptsDir, "personas", `lane-${o.role}.md`);
10854
+ if (!existsSync26(briefPath)) {
10679
10855
  throw new AgentError(`no brief at ${briefPath} \u2014 refusing to run an unscoped session`);
10680
10856
  }
10681
- const commonPath = join27(o.route.promptsDir, "personas", "common.md");
10682
- const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync25(commonPath);
10857
+ const commonPath = join28(o.route.promptsDir, "personas", "common.md");
10858
+ const usesCommon = !SELF_CONTAINED_ROLES.has(o.role) && existsSync26(commonPath);
10683
10859
  const now = o.now ?? (() => Date.now());
10684
10860
  const routeLabel = routeSlug(o.route.route);
10685
10861
  const ownDigest = o.runId ? digestPathFor(o.stateDir, routeLabel, o.role, o.runId) : void 0;
10686
- const digestPath2 = ownDigest && existsSync25(ownDigest) ? ownDigest : digestPathFor(o.stateDir, routeLabel, o.role);
10862
+ const digestPath2 = ownDigest && existsSync26(ownDigest) ? ownDigest : digestPathFor(o.stateDir, routeLabel, o.role);
10687
10863
  let digest;
10688
10864
  let ageSeconds;
10689
- if (existsSync25(digestPath2)) {
10865
+ if (existsSync26(digestPath2)) {
10690
10866
  ageSeconds = Math.floor((now() - statSync8(digestPath2).mtimeMs) / 1e3);
10691
- if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync23(digestPath2, "utf8");
10867
+ if (ageSeconds <= DIGEST_MAX_AGE_SECONDS) digest = readFileSync24(digestPath2, "utf8");
10692
10868
  }
10693
- const common = usesCommon ? readFileSync23(commonPath, "utf8") : "";
10694
- const brief = readFileSync23(briefPath, "utf8");
10869
+ const common = usesCommon ? readFileSync24(commonPath, "utf8") : "";
10870
+ const brief = readFileSync24(briefPath, "utf8");
10695
10871
  const effectiveCommon = o.divergedPrompt ? "" : common;
10696
10872
  const effectiveBrief = appendAdditions(o.divergedPrompt ?? brief, o.personaAdditions);
10697
10873
  const localSkills = new Map(listLocalSkills(o.route.promptsDir).map((s) => [s.name, s.prompt]));
@@ -10714,7 +10890,7 @@ function planAgentRun(o) {
10714
10890
  digest
10715
10891
  });
10716
10892
  const promptSha = createHash4("sha256").update(expanded.text).digest("hex");
10717
- const streamsDir = join27(o.stateDir, "streams");
10893
+ const streamsDir = join28(o.stateDir, "streams");
10718
10894
  const base = `${routeLabel}-${o.role}-${o.cycle}${o.runId ? `-${o.runId}` : ""}`;
10719
10895
  const seat = resolveSeat(o.ship, o.route, o.role, o.agentPersona ?? (o.agentModel ? { model: o.agentModel } : {}), o.repo);
10720
10896
  const { model } = seat;
@@ -10729,7 +10905,9 @@ function planAgentRun(o) {
10729
10905
  toolchain = { ...toolchain, inference: { baseUrl: inferenceBaseUrl(o.route.baseUrl, o.route.resolved.workspaceId), token: o.apiKey } };
10730
10906
  }
10731
10907
  const adapter = adapterFor(toolchain.harness);
10908
+ const code = codeBridgeFor(o, adapter.harness);
10732
10909
  const invocation = adapter.invocation(toolchain, {
10910
+ code,
10733
10911
  role: o.role,
10734
10912
  model,
10735
10913
  pluginDirs: adapter.harness === "claude" ? lspPluginDirsFor(o.route, o.repo, o.stateDir) : void 0,
@@ -10788,7 +10966,7 @@ function planAgentRun(o) {
10788
10966
  unsetEnv: adapter.unsetEnv,
10789
10967
  setEnv: {
10790
10968
  ...o.apiKey ? { [API_KEY_VAR]: o.apiKey } : {},
10791
- ...adapter.runEnv(toolchain),
10969
+ ...adapter.runEnv(toolchain, { code }),
10792
10970
  // The operator's global SessionStart hook (pair-context-hook.sh) injects
10793
10971
  // the interactive Pair persona's prompt for any session it can't tell
10794
10972
  // apart from a person's own terminal. Every headless lane run this
@@ -10805,8 +10983,8 @@ function planAgentRun(o) {
10805
10983
  digestAttached: digest !== void 0,
10806
10984
  digestAgeSeconds: ageSeconds,
10807
10985
  model,
10808
- streamPath: join27(streamsDir, `${base}.jsonl`),
10809
- eventsPath: join27(streamsDir, `${base}.events.jsonl`),
10986
+ streamPath: join28(streamsDir, `${base}.jsonl`),
10987
+ eventsPath: join28(streamsDir, `${base}.events.jsonl`),
10810
10988
  ticket: o.ticket,
10811
10989
  ticketRecordId: o.ticketRecordId ?? null,
10812
10990
  repo: o.repo ?? null,
@@ -10837,7 +11015,7 @@ function describePlan(p) {
10837
11015
  var pidPathFor = (streamPath) => `${streamPath}.pid`;
10838
11016
  function readRunSidecar(streamPath) {
10839
11017
  try {
10840
- const sc = JSON.parse(readFileSync23(pidPathFor(streamPath), "utf8"));
11018
+ const sc = JSON.parse(readFileSync24(pidPathFor(streamPath), "utf8"));
10841
11019
  return typeof sc.pid === "number" && typeof sc.spawnedAt === "string" && typeof sc.role === "string" ? sc : null;
10842
11020
  } catch {
10843
11021
  return null;
@@ -10884,7 +11062,7 @@ async function killForDeadline(kill, pid, emit, role, ticket, spawnedAt, limitMi
10884
11062
  var withoutKey = ({ apiKey: _apiKey, ...rest }) => rest;
10885
11063
  function openSink(path) {
10886
11064
  try {
10887
- mkdirSync17(dirname12(path), { recursive: true });
11065
+ mkdirSync18(dirname13(path), { recursive: true });
10888
11066
  return createWriteStream(path, { flags: "a" });
10889
11067
  } catch {
10890
11068
  return void 0;
@@ -11070,7 +11248,7 @@ var RunObserver = class {
11070
11248
  };
11071
11249
  function writeRunMeta(id, startedAt, liveTicket, extra) {
11072
11250
  try {
11073
- writeFileSync18(`${id.streamPath}.meta.json`, JSON.stringify({
11251
+ writeFileSync19(`${id.streamPath}.meta.json`, JSON.stringify({
11074
11252
  route: id.route,
11075
11253
  role: id.role,
11076
11254
  hint: id.ticket,
@@ -11236,7 +11414,7 @@ function spawnAgent(plan, emit, hintMoved) {
11236
11414
  const id = identityOf(plan, adapter.harness);
11237
11415
  let streamFd;
11238
11416
  try {
11239
- mkdirSync17(dirname12(plan.streamPath), { recursive: true });
11417
+ mkdirSync18(dirname13(plan.streamPath), { recursive: true });
11240
11418
  streamFd = openSync4(plan.streamPath, "a");
11241
11419
  } catch (e) {
11242
11420
  void endSink(eventsSink);
@@ -11294,7 +11472,7 @@ function spawnAgent(plan, emit, hintMoved) {
11294
11472
  promptSha: plan.promptSha
11295
11473
  };
11296
11474
  try {
11297
- writeFileSync18(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
11475
+ writeFileSync19(pidPathFor(plan.streamPath), JSON.stringify(sidecar));
11298
11476
  } catch {
11299
11477
  }
11300
11478
  }
@@ -11408,21 +11586,21 @@ var pidAlive = (pid) => {
11408
11586
  };
11409
11587
  function runFinished(streamPath) {
11410
11588
  const path = `${streamPath}.meta.json`;
11411
- if (!existsSync26(path)) return false;
11589
+ if (!existsSync27(path)) return false;
11412
11590
  try {
11413
- return JSON.parse(readFileSync24(path, "utf8")).running !== true;
11591
+ return JSON.parse(readFileSync25(path, "utf8")).running !== true;
11414
11592
  } catch {
11415
11593
  return true;
11416
11594
  }
11417
11595
  }
11418
11596
  function orphanedStreams(o) {
11419
- const dir = join28(o.stateDir, "streams");
11420
- if (!existsSync26(dir)) return [];
11597
+ const dir = join29(o.stateDir, "streams");
11598
+ if (!existsSync27(dir)) return [];
11421
11599
  const ownerAlive = o.isOwnerAlive ?? pidAlive;
11422
11600
  const out = [];
11423
11601
  for (const name of readdirSync9(dir)) {
11424
11602
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11425
- const streamPath = join28(dir, name);
11603
+ const streamPath = join29(dir, name);
11426
11604
  if (runFinished(streamPath)) continue;
11427
11605
  const sidecar = readRunSidecar(streamPath);
11428
11606
  if (!sidecar || sidecar.route !== o.routeLabel) continue;
@@ -11475,15 +11653,15 @@ async function reconcileRuns(o) {
11475
11653
  }
11476
11654
 
11477
11655
  // src/live-runs.ts
11478
- import { existsSync as existsSync27, readdirSync as readdirSync10, statSync as statSync9 } from "fs";
11479
- import { join as join29 } from "path";
11656
+ import { existsSync as existsSync28, readdirSync as readdirSync10, statSync as statSync9 } from "fs";
11657
+ import { join as join30 } from "path";
11480
11658
  function liveRuns(stateDir, isAlive = runIsAlive) {
11481
- const dir = join29(stateDir, "streams");
11482
- if (!existsSync27(dir)) return [];
11659
+ const dir = join30(stateDir, "streams");
11660
+ if (!existsSync28(dir)) return [];
11483
11661
  const out = [];
11484
11662
  for (const name of readdirSync10(dir)) {
11485
11663
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11486
- const streamPath = join29(dir, name);
11664
+ const streamPath = join30(dir, name);
11487
11665
  if (runFinished(streamPath)) continue;
11488
11666
  const sidecar = readRunSidecar(streamPath);
11489
11667
  if (!sidecar || !isAlive(sidecar)) continue;
@@ -11493,12 +11671,12 @@ function liveRuns(stateDir, isAlive = runIsAlive) {
11493
11671
  }
11494
11672
  function runActivity(stateDir, isAlive = runIsAlive) {
11495
11673
  const activeRuns = liveRuns(stateDir, isAlive).length;
11496
- const dir = join29(stateDir, "streams");
11674
+ const dir = join30(stateDir, "streams");
11497
11675
  let newest = 0;
11498
- if (existsSync27(dir)) {
11676
+ if (existsSync28(dir)) {
11499
11677
  for (const name of readdirSync10(dir)) {
11500
11678
  if (!name.endsWith(".jsonl") || name.endsWith(".events.jsonl")) continue;
11501
- const streamPath = join29(dir, name);
11679
+ const streamPath = join30(dir, name);
11502
11680
  if (!runFinished(streamPath)) continue;
11503
11681
  try {
11504
11682
  newest = Math.max(newest, statSync9(`${streamPath}.meta.json`).mtimeMs);
@@ -11869,7 +12047,7 @@ function realDeprovisionDeps(cfg, emit, crewHome, getStore) {
11869
12047
  if (host === "windows") return [];
11870
12048
  const ctx = installContext();
11871
12049
  const systemd = detectSystemd();
11872
- return ["run", "release", "passengers"].flatMap((job) => planInstall(cfg.ship, crewHome, host, systemd, job, ctx).unitPaths).filter((p) => existsSync28(p));
12050
+ return ["run", "release", "passengers"].flatMap((job) => planInstall(cfg.ship, crewHome, host, systemd, job, ctx).unitPaths).filter((p) => existsSync29(p));
11873
12051
  };
11874
12052
  return {
11875
12053
  liveRuns: () => liveRuns(stateDir).map((r) => ({ role: r.role, route: r.route, pid: r.pid, ticketRecordId: r.ticketRecordId ?? void 0, streamPath: r.streamPath })),
@@ -11930,7 +12108,7 @@ function realDeprovisionDeps(cfg, emit, crewHome, getStore) {
11930
12108
  },
11931
12109
  remaining() {
11932
12110
  const left = [];
11933
- if (existsSync28(cfg.configFile)) left.push(`config ${cfg.configFile}`);
12111
+ if (existsSync29(cfg.configFile)) left.push(`config ${cfg.configFile}`);
11934
12112
  for (const h of forgeHosts()) if (hasForgeToken(cfg.ship.forges, h, stateDir)) left.push(`forge token for ${h}`);
11935
12113
  try {
11936
12114
  for (const k of readdirSync11(keysDir(stateDir))) left.push(`seat key ${k}`);
@@ -12538,19 +12716,19 @@ function planSandbox(repo, decl, warm = {}) {
12538
12716
  const layer = repoLayer(repo, decl, crew, warm);
12539
12717
  return { repo, tag: layer.tag, claude: SANDBOX_CLAUDE_VERSION, layers: [base, crew, layer] };
12540
12718
  }
12541
- var sandboxStatePath = (stateDir) => join30(stateDir, "sandbox-images.json");
12719
+ var sandboxStatePath = (stateDir) => join31(stateDir, "sandbox-images.json");
12542
12720
  function readSandboxState(stateDir) {
12543
12721
  try {
12544
- const v = JSON.parse(readFileSync25(sandboxStatePath(stateDir), "utf8"));
12722
+ const v = JSON.parse(readFileSync26(sandboxStatePath(stateDir), "utf8"));
12545
12723
  return v && typeof v === "object" && !Array.isArray(v) ? v : {};
12546
12724
  } catch {
12547
12725
  return {};
12548
12726
  }
12549
12727
  }
12550
12728
  function writeSandboxState(stateDir, state) {
12551
- mkdirSync18(stateDir, { recursive: true });
12729
+ mkdirSync19(stateDir, { recursive: true });
12552
12730
  const file = sandboxStatePath(stateDir);
12553
- writeFileSync19(`${file}.tmp`, `${JSON.stringify(state, null, 2)}
12731
+ writeFileSync20(`${file}.tmp`, `${JSON.stringify(state, null, 2)}
12554
12732
  `);
12555
12733
  renameSync3(`${file}.tmp`, file);
12556
12734
  }
@@ -12570,7 +12748,7 @@ var spawnBuildRunner = (engine = "docker") => (args, logPath) => new Promise((re
12570
12748
  child.stdout.on("data", (d) => chunks.push(d));
12571
12749
  child.stderr.on("data", (d) => chunks.push(d));
12572
12750
  const done = (code, extra = "") => {
12573
- writeFileSync19(logPath, `$ ${engine} ${args.join(" ")}
12751
+ writeFileSync20(logPath, `$ ${engine} ${args.join(" ")}
12574
12752
  ${Buffer.concat(chunks).toString("utf8")}${extra}`, { flag: "a" });
12575
12753
  resolve8(code);
12576
12754
  };
@@ -12592,7 +12770,7 @@ var engineRemoveImage = (engine = "docker", exec = realExec2) => (tag) => {
12592
12770
  } catch {
12593
12771
  }
12594
12772
  };
12595
- var sandboxLogDir = (stateDir) => join30(stateDir, "sandbox-logs");
12773
+ var sandboxLogDir = (stateDir) => join31(stateDir, "sandbox-logs");
12596
12774
  async function buildSandboxImage(plan, deps) {
12597
12775
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
12598
12776
  const state = readSandboxState(deps.stateDir);
@@ -12601,20 +12779,20 @@ async function buildSandboxImage(plan, deps) {
12601
12779
  return { kind: "reused", tag: plan.tag };
12602
12780
  }
12603
12781
  const logDir = sandboxLogDir(deps.stateDir);
12604
- mkdirSync18(logDir, { recursive: true });
12605
- const logPath = join30(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
12782
+ mkdirSync19(logDir, { recursive: true });
12783
+ const logPath = join31(logDir, `${slug(plan.repo)}-${now().toISOString().replace(/[:.]/g, "-")}.log`);
12606
12784
  const builtLayers = [];
12607
12785
  for (const layer of plan.layers) {
12608
12786
  if (deps.hasImage(layer.tag)) continue;
12609
- const ctx = join30(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
12610
- mkdirSync18(ctx, { recursive: true });
12787
+ const ctx = join31(tmpdir3(), `crew-sandbox-${hash([layer.tag, process.pid, Date.now()])}`);
12788
+ mkdirSync19(ctx, { recursive: true });
12611
12789
  try {
12612
- writeFileSync19(join30(ctx, "Dockerfile"), layer.dockerfile);
12790
+ writeFileSync20(join31(ctx, "Dockerfile"), layer.dockerfile);
12613
12791
  for (const [name, content] of Object.entries(layer.files)) {
12614
- mkdirSync18(dirname13(join30(ctx, name)), { recursive: true });
12615
- writeFileSync19(join30(ctx, name), content);
12792
+ mkdirSync19(dirname14(join31(ctx, name)), { recursive: true });
12793
+ writeFileSync20(join31(ctx, name), content);
12616
12794
  }
12617
- const code = await deps.run(["build", "--tag", layer.tag, "--file", join30(ctx, "Dockerfile"), ctx], logPath);
12795
+ const code = await deps.run(["build", "--tag", layer.tag, "--file", join31(ctx, "Dockerfile"), ctx], logPath);
12618
12796
  if (code !== 0) {
12619
12797
  state[plan.repo] = { ...entry, failure: { tag: plan.tag, at: now().toISOString(), logPath } };
12620
12798
  writeSandboxState(deps.stateDir, state);
@@ -12622,7 +12800,7 @@ async function buildSandboxImage(plan, deps) {
12622
12800
  }
12623
12801
  builtLayers.push(layer.name);
12624
12802
  } finally {
12625
- rmSync14(ctx, { recursive: true, force: true });
12803
+ rmSync15(ctx, { recursive: true, force: true });
12626
12804
  }
12627
12805
  }
12628
12806
  const tags = [{ tag: plan.tag, builtAt: now().toISOString(), claude: plan.claude }, ...entry.tags.filter((t) => t.tag !== plan.tag)];
@@ -12803,9 +12981,9 @@ function managedRuntimeWarnings(kind, ship) {
12803
12981
 
12804
12982
  // src/selftest.ts
12805
12983
  import { spawn as spawn6 } from "child_process";
12806
- import { existsSync as existsSync29, mkdirSync as mkdirSync19, mkdtempSync as mkdtempSync2, readFileSync as readFileSync26, rmSync as rmSync15, writeFileSync as writeFileSync20 } from "fs";
12984
+ import { existsSync as existsSync30, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync2, readFileSync as readFileSync27, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
12807
12985
  import { tmpdir as tmpdir4 } from "os";
12808
- import { join as join31 } from "path";
12986
+ import { join as join32 } from "path";
12809
12987
  var SELFTEST_TIMEOUT_MS = 12e4;
12810
12988
  var KEY_VAR = "CREW_API_KEY";
12811
12989
  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: ";
@@ -12966,7 +13144,7 @@ async function runSelftest(i, deps = realDeps) {
12966
13144
  ...adapter.runEnv(spec),
12967
13145
  [KEY_VAR]: i.apiKey
12968
13146
  };
12969
- const cwd = mkdtempSync2(join31(tmpdir4(), "crew-selftest-"));
13147
+ const cwd = mkdtempSync2(join32(tmpdir4(), "crew-selftest-"));
12970
13148
  try {
12971
13149
  const run3 = await deps.run({
12972
13150
  bin: invocation.bin,
@@ -12988,7 +13166,7 @@ async function runSelftest(i, deps = realDeps) {
12988
13166
  return finish({ ok: false, failure, detail, ...usage2 });
12989
13167
  } finally {
12990
13168
  try {
12991
- rmSync15(cwd, { recursive: true, force: true });
13169
+ rmSync16(cwd, { recursive: true, force: true });
12992
13170
  } catch {
12993
13171
  }
12994
13172
  }
@@ -12999,11 +13177,11 @@ function formatResult(r) {
12999
13177
  const secs = `${(r.ms / 1e3).toFixed(1)}s`;
13000
13178
  return r.ok ? `${r.toolchain}: PASS ${r.detail} (${secs}${tok})` : `${r.toolchain}: FAIL ${r.failure} \u2014 ${r.detail} (${secs})`;
13001
13179
  }
13002
- var fileFor2 = (stateDir) => join31(stateDir, "selftest.json");
13180
+ var fileFor2 = (stateDir) => join32(stateDir, "selftest.json");
13003
13181
  function readSelftest(stateDir) {
13004
13182
  try {
13005
13183
  const p = fileFor2(stateDir);
13006
- return existsSync29(p) ? JSON.parse(readFileSync26(p, "utf8")) : {};
13184
+ return existsSync30(p) ? JSON.parse(readFileSync27(p, "utf8")) : {};
13007
13185
  } catch {
13008
13186
  return {};
13009
13187
  }
@@ -13011,8 +13189,8 @@ function readSelftest(stateDir) {
13011
13189
  function recordSelftest(stateDir, results) {
13012
13190
  const all = readSelftest(stateDir);
13013
13191
  for (const r of results) all[r.toolchain] = r;
13014
- mkdirSync19(stateDir, { recursive: true });
13015
- writeFileSync20(fileFor2(stateDir), `${JSON.stringify(all, null, 2)}
13192
+ mkdirSync20(stateDir, { recursive: true });
13193
+ writeFileSync21(fileFor2(stateDir), `${JSON.stringify(all, null, 2)}
13016
13194
  `);
13017
13195
  }
13018
13196
  function doctorSelftestLines(stored, toolchains) {
@@ -13024,7 +13202,7 @@ function doctorSelftestLines(stored, toolchains) {
13024
13202
  }
13025
13203
 
13026
13204
  // src/cli.ts
13027
- import { resolve as resolve7, dirname as dirname32, join as join53, basename as basename16 } from "path";
13205
+ import { resolve as resolve7, dirname as dirname33, join as join54, basename as basename16 } from "path";
13028
13206
  import { fileURLToPath } from "url";
13029
13207
  import { hostname as hostname4, homedir as homedir11 } from "os";
13030
13208
  import { spawnSync as spawnSync11 } from "child_process";
@@ -13032,6 +13210,7 @@ import { parse as parseYaml2 } from "yaml";
13032
13210
 
13033
13211
  // src/argv.ts
13034
13212
  var VALUE_FLAGS = /* @__PURE__ */ new Set([
13213
+ "state-dir",
13035
13214
  "agent-bin",
13036
13215
  "area",
13037
13216
  "base-url",
@@ -13221,14 +13400,14 @@ function rosterMarkdown(roster, meId) {
13221
13400
  }
13222
13401
 
13223
13402
  // src/connect-resume.ts
13224
- import { mkdirSync as mkdirSync20, readFileSync as readFileSync27, rmSync as rmSync16, writeFileSync as writeFileSync21 } from "fs";
13225
- import { dirname as dirname14, join as join32 } from "path";
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";
13226
13405
  function lastSessionFile(stateDir) {
13227
- return join32(stateDir, "connect-session.json");
13406
+ return join33(stateDir, "connect-session.json");
13228
13407
  }
13229
13408
  function readPointers(stateDir) {
13230
13409
  try {
13231
- const parsed = JSON.parse(readFileSync27(lastSessionFile(stateDir), "utf8"));
13410
+ const parsed = JSON.parse(readFileSync28(lastSessionFile(stateDir), "utf8"));
13232
13411
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
13233
13412
  const out = {};
13234
13413
  for (const [host, v] of Object.entries(parsed)) {
@@ -13242,8 +13421,8 @@ function readPointers(stateDir) {
13242
13421
  }
13243
13422
  function writePointers(stateDir, pointers) {
13244
13423
  const file = lastSessionFile(stateDir);
13245
- mkdirSync20(dirname14(file), { recursive: true });
13246
- writeFileSync21(file, `${JSON.stringify(pointers, null, 2)}
13424
+ mkdirSync21(dirname15(file), { recursive: true });
13425
+ writeFileSync22(file, `${JSON.stringify(pointers, null, 2)}
13247
13426
  `, { mode: 384 });
13248
13427
  }
13249
13428
  function rememberSession(stateDir, host, workspaceSlug) {
@@ -13260,7 +13439,7 @@ function forgetWorkspace(stateDir, host, workspaceSlug) {
13260
13439
  const { [host]: _old, ...rest } = all;
13261
13440
  const next = left.length ? { ...rest, [host]: left } : rest;
13262
13441
  if (Object.keys(next).length) writePointers(stateDir, next);
13263
- else rmSync16(lastSessionFile(stateDir), { force: true });
13442
+ else rmSync17(lastSessionFile(stateDir), { force: true });
13264
13443
  } catch {
13265
13444
  }
13266
13445
  }
@@ -13300,7 +13479,7 @@ function forgetSession(stateDir, host) {
13300
13479
  try {
13301
13480
  const { [host]: _gone, ...rest } = readPointers(stateDir);
13302
13481
  if (Object.keys(rest).length) writePointers(stateDir, rest);
13303
- else rmSync16(lastSessionFile(stateDir), { force: true });
13482
+ else rmSync17(lastSessionFile(stateDir), { force: true });
13304
13483
  } catch {
13305
13484
  }
13306
13485
  }
@@ -13328,15 +13507,15 @@ async function signOutSessions(o) {
13328
13507
  }
13329
13508
 
13330
13509
  // src/reset.ts
13331
- import { existsSync as existsSync30, mkdirSync as mkdirSync21, readFileSync as readFileSync28, readdirSync as readdirSync12, rmSync as rmSync17, statSync as statSync10, writeFileSync as writeFileSync22 } from "fs";
13332
- import { dirname as dirname15, join as join33 } from "path";
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";
13333
13512
  import { hostFromUrl as hostFromUrl3 } from "@tablation/client";
13334
13513
  function clonedCheckoutsFile(stateDir) {
13335
- return join33(stateDir, "cloned-checkouts.json");
13514
+ return join34(stateDir, "cloned-checkouts.json");
13336
13515
  }
13337
13516
  function readClonedCheckouts(stateDir) {
13338
13517
  try {
13339
- const parsed = JSON.parse(readFileSync28(clonedCheckoutsFile(stateDir), "utf8"));
13518
+ const parsed = JSON.parse(readFileSync29(clonedCheckoutsFile(stateDir), "utf8"));
13340
13519
  return Array.isArray(parsed) ? parsed.filter((d) => typeof d === "string") : [];
13341
13520
  } catch {
13342
13521
  return [];
@@ -13346,8 +13525,8 @@ function recordClonedCheckout(stateDir, dir) {
13346
13525
  const known = readClonedCheckouts(stateDir);
13347
13526
  if (known.includes(dir)) return;
13348
13527
  const file = clonedCheckoutsFile(stateDir);
13349
- mkdirSync21(dirname15(file), { recursive: true });
13350
- writeFileSync22(file, `${JSON.stringify([...known, dir], null, 2)}
13528
+ mkdirSync22(dirname16(file), { recursive: true });
13529
+ writeFileSync23(file, `${JSON.stringify([...known, dir], null, 2)}
13351
13530
  `);
13352
13531
  }
13353
13532
  var mark = (dryRun) => dryRun ? "would-remove" : "removed";
@@ -13401,8 +13580,8 @@ async function runReset(o, fx) {
13401
13580
  }
13402
13581
  if (o.beforeState) steps.push(...await o.beforeState());
13403
13582
  steps.push(...o.wipeState ? wipeStateDir(o.stateDir, o.dryRun) : removeState(o.stateDir, o.dryRun));
13404
- if (existsSync30(o.configFile)) {
13405
- if (!o.dryRun) rmSync17(o.configFile, { force: true });
13583
+ if (existsSync31(o.configFile)) {
13584
+ if (!o.dryRun) rmSync18(o.configFile, { force: true });
13406
13585
  steps.push({ step: "config", target: o.configFile, action: mark(o.dryRun) });
13407
13586
  } else {
13408
13587
  steps.push({ step: "config", target: o.configFile, action: "absent" });
@@ -13410,7 +13589,7 @@ async function runReset(o, fx) {
13410
13589
  return steps;
13411
13590
  }
13412
13591
  function removeCheckout(dir, dryRun, fx) {
13413
- if (!existsSync30(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13592
+ if (!existsSync31(dir)) return { step: "checkout", target: dir, action: "absent", detail: "already gone" };
13414
13593
  const { dirty, linkedWorktrees } = fx.inspectCheckout(dir);
13415
13594
  if (dirty.length) {
13416
13595
  return { step: "checkout", target: dir, action: "kept", detail: `has uncommitted changes (${dirty.length} path${dirty.length === 1 ? "" : "s"}: ${dirty.slice(0, 5).join(", ")}${dirty.length > 5 ? ", \u2026" : ""})` };
@@ -13418,21 +13597,21 @@ function removeCheckout(dir, dryRun, fx) {
13418
13597
  if (linkedWorktrees.length) {
13419
13598
  return { step: "checkout", target: dir, action: "kept", detail: `has linked worktrees (${linkedWorktrees.join(", ")}); remove them first` };
13420
13599
  }
13421
- if (!dryRun) rmSync17(dir, { recursive: true, force: true });
13600
+ if (!dryRun) rmSync18(dir, { recursive: true, force: true });
13422
13601
  return { step: "checkout", target: dir, action: mark(dryRun) };
13423
13602
  }
13424
13603
  function removeState(stateDir, dryRun) {
13425
13604
  const out = [];
13426
- const targets = [join33(stateDir, "resolved"), join33(stateDir, "keys"), clonedCheckoutsFile(stateDir), join33(stateDir, "connect-session.json"), join33(stateDir, "reload-requested"), join33(stateDir, "daemon.started")];
13605
+ const targets = [join34(stateDir, "resolved"), join34(stateDir, "keys"), clonedCheckoutsFile(stateDir), join34(stateDir, "connect-session.json"), join34(stateDir, "reload-requested"), join34(stateDir, "daemon.started")];
13427
13606
  try {
13428
13607
  for (const name of readdirSync12(stateDir)) {
13429
- if (name.startsWith(".") && statSync10(join33(stateDir, name)).isFile()) targets.push(join33(stateDir, name));
13608
+ if (name.startsWith(".") && statSync10(join34(stateDir, name)).isFile()) targets.push(join34(stateDir, name));
13430
13609
  }
13431
13610
  } catch {
13432
13611
  }
13433
13612
  for (const t of targets) {
13434
- if (!existsSync30(t)) continue;
13435
- if (!dryRun) rmSync17(t, { recursive: true, force: true });
13613
+ if (!existsSync31(t)) continue;
13614
+ if (!dryRun) rmSync18(t, { recursive: true, force: true });
13436
13615
  out.push({ step: "state", target: t, action: mark(dryRun) });
13437
13616
  }
13438
13617
  if (!out.length) out.push({ step: "state", target: stateDir, action: "absent", detail: "nothing to remove" });
@@ -13446,7 +13625,7 @@ function wipeStateDir(stateDir, dryRun) {
13446
13625
  return [{ step: "state", target: stateDir, action: "absent", detail: "no state directory" }];
13447
13626
  }
13448
13627
  if (!names.length) return [{ step: "state", target: stateDir, action: "absent", detail: "already empty" }];
13449
- if (!dryRun) for (const n of names) rmSync17(join33(stateDir, n), { recursive: true, force: true });
13628
+ if (!dryRun) for (const n of names) rmSync18(join34(stateDir, n), { recursive: true, force: true });
13450
13629
  return [{ step: "state", target: stateDir, action: mark(dryRun), detail: `${names.length} entr${names.length === 1 ? "y" : "ies"}` }];
13451
13630
  }
13452
13631
  function describeResetStep(s) {
@@ -13527,9 +13706,9 @@ async function runDeprovision(o, fx, deps) {
13527
13706
  }
13528
13707
 
13529
13708
  // src/pool-member.ts
13530
- import { mkdirSync as mkdirSync22, readFileSync as readFileSync29, renameSync as renameSync4, writeFileSync as writeFileSync23 } from "fs";
13709
+ import { mkdirSync as mkdirSync23, readFileSync as readFileSync30, renameSync as renameSync4, writeFileSync as writeFileSync24 } from "fs";
13531
13710
  import { homedir as homedir8 } from "os";
13532
- import { dirname as dirname16, join as join34 } from "path";
13711
+ import { dirname as dirname17, join as join35 } from "path";
13533
13712
  var POOL_EXIT = { ok: 0, failed: 1, credential: 2 };
13534
13713
  function backoffMs(attempt) {
13535
13714
  return Math.min(6e4, 5e3 * 2 ** Math.max(0, attempt));
@@ -13760,22 +13939,22 @@ async function runPoolMember(deps) {
13760
13939
  }
13761
13940
  }
13762
13941
  function defaultMemberDir(env = process.env) {
13763
- return env.CREW_POOL_DIR ?? join34(env.XDG_STATE_HOME ?? join34(homedir8(), ".local", "state"), "crew-pool-member");
13942
+ return env.CREW_POOL_DIR ?? join35(env.XDG_STATE_HOME ?? join35(homedir8(), ".local", "state"), "crew-pool-member");
13764
13943
  }
13765
13944
  function fileMemberStore(dir) {
13766
- const file = join34(dir, "member.json");
13945
+ const file = join35(dir, "member.json");
13767
13946
  return {
13768
13947
  read() {
13769
13948
  try {
13770
- return JSON.parse(readFileSync29(file, "utf8"));
13949
+ return JSON.parse(readFileSync30(file, "utf8"));
13771
13950
  } catch {
13772
13951
  return {};
13773
13952
  }
13774
13953
  },
13775
13954
  write(f) {
13776
- mkdirSync22(dirname16(file), { recursive: true, mode: 448 });
13955
+ mkdirSync23(dirname17(file), { recursive: true, mode: 448 });
13777
13956
  const tmp = `${file}.tmp`;
13778
- writeFileSync23(tmp, `${JSON.stringify(f)}
13957
+ writeFileSync24(tmp, `${JSON.stringify(f)}
13779
13958
  `, { mode: 384 });
13780
13959
  renameSync4(tmp, file);
13781
13960
  }
@@ -13827,16 +14006,16 @@ function hostArchitecture(arch = process.arch) {
13827
14006
 
13828
14007
  // src/pool-member-host.ts
13829
14008
  import "child_process";
13830
- import { existsSync as existsSync34 } from "fs";
14009
+ import { existsSync as existsSync35 } from "fs";
13831
14010
  import { hostname as hostname3 } from "os";
13832
14011
 
13833
14012
  // src/machine-id.ts
13834
14013
  import { execFileSync as execFileSync10 } from "child_process";
13835
14014
  import { createHash as createHash7 } from "crypto";
13836
- import { existsSync as existsSync31, mkdirSync as mkdirSync23, readFileSync as readFileSync30, writeFileSync as writeFileSync24 } from "fs";
13837
- import { dirname as dirname17, join as join35 } from "path";
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";
13838
14017
  function machineIdPathFor(stateDir) {
13839
- return join35(stateDir, "machine-id");
14018
+ return join36(stateDir, "machine-id");
13840
14019
  }
13841
14020
  var sha256 = (s) => createHash7("sha256").update(s).digest("hex");
13842
14021
  function readRawMachineId(platform2 = process.platform) {
@@ -13849,8 +14028,8 @@ function readRawMachineId(platform2 = process.platform) {
13849
14028
  return /"IOPlatformUUID"\s*=\s*"([^"]+)"/.exec(out)?.[1];
13850
14029
  }
13851
14030
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
13852
- if (existsSync31(p)) {
13853
- const id = readFileSync30(p, "utf8").trim();
14031
+ if (existsSync32(p)) {
14032
+ const id = readFileSync31(p, "utf8").trim();
13854
14033
  if (id) return id;
13855
14034
  }
13856
14035
  }
@@ -13861,15 +14040,15 @@ function readRawMachineId(platform2 = process.platform) {
13861
14040
  function ensureMachineId(stateDir, opts = {}) {
13862
14041
  if (opts.override?.trim()) return sha256(opts.override.trim());
13863
14042
  const path = machineIdPathFor(stateDir);
13864
- if (existsSync31(path)) {
13865
- const cached = readFileSync30(path, "utf8").trim();
14043
+ if (existsSync32(path)) {
14044
+ const cached = readFileSync31(path, "utf8").trim();
13866
14045
  if (/^[0-9a-f]{64}$/.test(cached)) return cached;
13867
14046
  }
13868
14047
  const raw = (opts.read ?? readRawMachineId)();
13869
14048
  if (!raw) return void 0;
13870
14049
  const hashed = sha256(raw);
13871
- mkdirSync23(dirname17(path), { recursive: true });
13872
- writeFileSync24(path, `${hashed}
14050
+ mkdirSync24(dirname18(path), { recursive: true });
14051
+ writeFileSync25(path, `${hashed}
13873
14052
  `, { mode: 384 });
13874
14053
  return hashed;
13875
14054
  }
@@ -13882,7 +14061,7 @@ function matchShipRows(ships, shipName2, machineId) {
13882
14061
 
13883
14062
  // src/provision.ts
13884
14063
  import { spawnSync as spawnSync8 } from "child_process";
13885
- import { existsSync as existsSync32, readFileSync as readFileSync31, writeFileSync as writeFileSync25 } from "fs";
14064
+ import { existsSync as existsSync33, readFileSync as readFileSync32, writeFileSync as writeFileSync26 } from "fs";
13886
14065
  import { hostname as hostname2 } from "os";
13887
14066
  import { parseDocument as parseDocument3, isMap as isMap2, isScalar } from "yaml";
13888
14067
 
@@ -13891,7 +14070,7 @@ import { parseDocument as parseDocument2 } from "yaml";
13891
14070
 
13892
14071
  // src/repos-cmd.ts
13893
14072
  import { homedir as homedir9 } from "os";
13894
- import { basename as basename10, join as join36, resolve as resolve4 } from "path";
14073
+ import { basename as basename10, join as join37, resolve as resolve4 } from "path";
13895
14074
  import { isMap, isSeq, parseDocument, stringify as stringify2 } from "yaml";
13896
14075
  var ReposError = class extends Error {
13897
14076
  };
@@ -14169,7 +14348,7 @@ function planRepoClone(i) {
14169
14348
  }
14170
14349
  const ship = doc.get("ship", true);
14171
14350
  const basePath = String(m.get("reposBasePath") ?? (isMap(ship) ? ship.get("reposBasePath") : void 0) ?? "~/Crew");
14172
- const target = i.dir ? expandDir(i.dir, i.base) : join36(expandDir(basePath, i.base), i.route.split("/")[0], name);
14351
+ const target = i.dir ? expandDir(i.dir, i.base) : join37(expandDir(basePath, i.base), i.route.split("/")[0], name);
14173
14352
  const add = planRepoAdd({ text: i.text, base: i.base, route: i.route, path: target, origin: remote, resolved: i.resolved, name, isCheckout: i.isCheckout });
14174
14353
  const forgeHost = i.resolved?.repoForgeHosts?.[id];
14175
14354
  return { ...add, remote, target, ...forgeHost ? { forgeHost } : {} };
@@ -14476,7 +14655,7 @@ function realProvisionDeps(crewHome) {
14476
14655
  };
14477
14656
  const configFile = () => {
14478
14657
  const f = findConfigFile(crewHome);
14479
- return existsSync32(f) ? f : null;
14658
+ return existsSync33(f) ? f : null;
14480
14659
  };
14481
14660
  return {
14482
14661
  crew: spawnCrew,
@@ -14484,13 +14663,13 @@ function realProvisionDeps(crewHome) {
14484
14663
  enableRoute(route2) {
14485
14664
  const file = configFile();
14486
14665
  if (!file) return false;
14487
- const text2 = readFileSync31(file, "utf8");
14666
+ const text2 = readFileSync32(file, "utf8");
14488
14667
  const doc = parseDocument3(text2);
14489
14668
  const routes = doc.get("routes");
14490
14669
  const entry = routes?.items?.find((r) => r.get("route") === route2);
14491
14670
  if (!entry) throw new Error(`route ${route2} is not in ${file}`);
14492
14671
  if (entry.get("enabled") === true) return false;
14493
- writeFileSync25(file, planEnable(text2, route2));
14672
+ writeFileSync26(file, planEnable(text2, route2));
14494
14673
  return true;
14495
14674
  },
14496
14675
  installState() {
@@ -14510,14 +14689,14 @@ function realProvisionDeps(crewHome) {
14510
14689
  continue;
14511
14690
  }
14512
14691
  for (const p of plan.unitPaths) {
14513
- if (!existsSync32(p) || readFileSync31(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14692
+ if (!existsSync33(p) || readFileSync32(p, "utf8") !== plan.unitContent[p]) unitsStale = true;
14514
14693
  }
14515
14694
  }
14516
14695
  const label = labelFor(crewHome, "run");
14517
14696
  let running = false;
14518
14697
  let lingering = true;
14519
14698
  if (mechanism === "systemd") {
14520
- const system = ctx.root || existsSync32(systemUnitPathFor(label));
14699
+ const system = ctx.root || existsSync33(systemUnitPathFor(label));
14521
14700
  const ctl = system ? ["systemctl"] : ["systemctl", "--user"];
14522
14701
  running = spawnSync8(ctl[0], [...ctl.slice(1), "is-active", "--quiet", `${label}.service`]).status === 0;
14523
14702
  if (!system) {
@@ -14534,18 +14713,18 @@ function realProvisionDeps(crewHome) {
14534
14713
  setShipSetting(key, value2) {
14535
14714
  const file = configFile();
14536
14715
  if (!file) return false;
14537
- const doc = parseDocument3(readFileSync31(file, "utf8"));
14716
+ const doc = parseDocument3(readFileSync32(file, "utf8"));
14538
14717
  if (doc.getIn(["ship", key]) === value2) return false;
14539
14718
  doc.setIn(["ship", key], value2);
14540
- writeFileSync25(file, doc.toString());
14719
+ writeFileSync26(file, doc.toString());
14541
14720
  return true;
14542
14721
  },
14543
14722
  applyManagedDefaults() {
14544
14723
  const file = configFile();
14545
14724
  if (!file) return [];
14546
- const doc = parseDocument3(readFileSync31(file, "utf8"));
14725
+ const doc = parseDocument3(readFileSync32(file, "utf8"));
14547
14726
  const wrote = applyManagedDefaultsToDoc(doc);
14548
- if (wrote.length) writeFileSync25(file, doc.toString());
14727
+ if (wrote.length) writeFileSync26(file, doc.toString());
14549
14728
  return wrote;
14550
14729
  },
14551
14730
  storedToken(host) {
@@ -14562,9 +14741,9 @@ host=${host}
14562
14741
 
14563
14742
  // src/tunnel.ts
14564
14743
  import { spawn as spawn7 } from "child_process";
14565
- import { existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync32, writeFileSync as writeFileSync26, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14744
+ import { existsSync as existsSync34, mkdirSync as mkdirSync25, readFileSync as readFileSync33, writeFileSync as writeFileSync27, unlinkSync as unlinkSync3, readdirSync as readdirSync13 } from "fs";
14566
14745
  import { randomInt } from "crypto";
14567
- import { join as join37 } from "path";
14746
+ import { join as join38 } from "path";
14568
14747
  var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
14569
14748
  var defaultSpawn = (cmd, args) => spawn7(cmd, args, DEFAULT_SPAWN_OPTIONS);
14570
14749
  var SLUG_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -14636,21 +14815,21 @@ function startTunnel(opts) {
14636
14815
  };
14637
14816
  }
14638
14817
  function tunnelStatePath(stateDir, workspaceId) {
14639
- return join37(stateDir, "passengers", `${workspaceId}.json`);
14818
+ return join38(stateDir, "passengers", `${workspaceId}.json`);
14640
14819
  }
14641
14820
  function readPersistedTunnel(stateDir, workspaceId) {
14642
14821
  const path = tunnelStatePath(stateDir, workspaceId);
14643
- if (!existsSync33(path)) return void 0;
14822
+ if (!existsSync34(path)) return void 0;
14644
14823
  try {
14645
- return JSON.parse(readFileSync32(path, "utf8"));
14824
+ return JSON.parse(readFileSync33(path, "utf8"));
14646
14825
  } catch {
14647
14826
  return void 0;
14648
14827
  }
14649
14828
  }
14650
14829
  function writePersistedTunnel(stateDir, state) {
14651
14830
  const path = tunnelStatePath(stateDir, state.workspaceId);
14652
- mkdirSync24(join37(stateDir, "passengers"), { recursive: true });
14653
- writeFileSync26(path, `${JSON.stringify(state, null, 2)}
14831
+ mkdirSync25(join38(stateDir, "passengers"), { recursive: true });
14832
+ writeFileSync27(path, `${JSON.stringify(state, null, 2)}
14654
14833
  `);
14655
14834
  }
14656
14835
  function updatePersistedTunnel(stateDir, workspaceId, patch2) {
@@ -14752,8 +14931,8 @@ function syncPassengerTunnels(plans, relayHost, relayPort, privateKeyPath, state
14752
14931
  }
14753
14932
  }
14754
14933
  function readAllPersistedWorkspaceIds(stateDir) {
14755
- const dir = join37(stateDir, "passengers");
14756
- if (!existsSync33(dir)) return [];
14934
+ const dir = join38(stateDir, "passengers");
14935
+ if (!existsSync34(dir)) return [];
14757
14936
  return readdirSync13(dir).filter((f) => f.endsWith(".json") && !f.endsWith("-sync.json")).map((f) => f.slice(0, -".json".length));
14758
14937
  }
14759
14938
 
@@ -14778,7 +14957,7 @@ function realPoolMemberDeps(o) {
14778
14957
  crewVersion: crewVersion(o.crewHome),
14779
14958
  hostname: hostname3()
14780
14959
  },
14781
- hasShipConfig: () => existsSync34(findConfigFile(o.crewHome)),
14960
+ hasShipConfig: () => existsSync35(findConfigFile(o.crewHome)),
14782
14961
  async provision(lease, instanceClass, shipKind) {
14783
14962
  const hosts = lease.forges.map((f) => f.host.toLowerCase());
14784
14963
  const lines2 = [];
@@ -15654,8 +15833,8 @@ ${waiting.map((t) => `| ${displayKey2(t)} | ${t.status} | ${blockers(t, i)} |`).
15654
15833
  }
15655
15834
 
15656
15835
  // src/poll.ts
15657
- import { writeFileSync as writeFileSync27 } from "fs";
15658
- import { join as join38 } from "path";
15836
+ import { writeFileSync as writeFileSync28 } from "fs";
15837
+ import { join as join39 } from "path";
15659
15838
 
15660
15839
  // src/stalled.ts
15661
15840
  var STALLED_AFTER_CYCLES = 30;
@@ -15962,7 +16141,7 @@ async function decideCycle(o) {
15962
16141
  }
15963
16142
  return decision;
15964
16143
  }
15965
- var digestPath = (stateDir, route2, role, runId) => join38(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
16144
+ var digestPath = (stateDir, route2, role, runId) => join39(stateDir, `digest-${route2}-${role}${runId ? `-${runId}` : ""}.md`);
15966
16145
  function otherRunsRemoved(tickets, own) {
15967
16146
  if (!own) return tickets;
15968
16147
  return tickets.filter((t) => t.id === own.ticket.id || !own.inFlight(t.id));
@@ -16040,7 +16219,7 @@ function writeDigest(o, d, role, stateDir, runId, own) {
16040
16219
 
16041
16220
  ${body}` : body;
16042
16221
  const path = digestPath(stateDir, routeSlug(o.route.route), role, runId);
16043
- writeFileSync27(path, text2);
16222
+ writeFileSync28(path, text2);
16044
16223
  o.emit.emit(`queue digest written for ${role} (${Buffer.byteLength(text2)} bytes)`, {
16045
16224
  data: { role, bytes: Buffer.byteLength(text2) }
16046
16225
  });
@@ -16148,8 +16327,8 @@ async function resolveOne(tracker, t, seat, approvedStatus, buildingStatus, affi
16148
16327
  }
16149
16328
 
16150
16329
  // src/repo-refresh.ts
16151
- import { existsSync as existsSync35, mkdirSync as mkdirSync25, readFileSync as readFileSync33, writeFileSync as writeFileSync28 } from "fs";
16152
- import { dirname as dirname19 } from "path";
16330
+ import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync34, writeFileSync as writeFileSync29 } from "fs";
16331
+ import { dirname as dirname20 } from "path";
16153
16332
  var REFRESH_COOLDOWN_MS = 2 * 6e4;
16154
16333
  var UNSERVABLE_PASSES_BEFORE_ATTENTION = 3;
16155
16334
  var lastAsked = /* @__PURE__ */ new Map();
@@ -16178,11 +16357,11 @@ function applyRepoRows(resolved, rows) {
16178
16357
  }
16179
16358
  function persistRepoRows(stateDir, routeName, rows) {
16180
16359
  const path = resolvedPathFor(stateDir, routeName);
16181
- if (!existsSync35(path)) return;
16182
- const raw = JSON.parse(readFileSync33(path, "utf8"));
16360
+ if (!existsSync36(path)) return;
16361
+ const raw = JSON.parse(readFileSync34(path, "utf8"));
16183
16362
  applyRepoRows(raw, rows);
16184
- mkdirSync25(dirname19(path), { recursive: true });
16185
- writeFileSync28(path, `${JSON.stringify(raw, null, 2)}
16363
+ mkdirSync26(dirname20(path), { recursive: true });
16364
+ writeFileSync29(path, `${JSON.stringify(raw, null, 2)}
16186
16365
  `);
16187
16366
  }
16188
16367
  async function refreshUnknownRepos(o) {
@@ -16632,13 +16811,13 @@ Ship ${shipName2} found: ${problem}. The release refuses to squash a ref nobody
16632
16811
  var localRefSkipReported = (ticket, problem, events) => events.some((e) => e.record_id === ticket.id && e.event_type === "local_ref_skip" && (e.message ?? "").includes(problem));
16633
16812
 
16634
16813
  // src/connect-wizard.ts
16635
- import { delimiter, dirname as dirname20, join as join39 } from "path";
16636
- import { accessSync as accessSync3, constants as constants3, existsSync as existsSync36, mkdirSync as mkdirSync26, writeFileSync as writeFileSync29 } from "fs";
16814
+ import { delimiter, dirname as dirname21, join as join40 } from "path";
16815
+ import { accessSync as accessSync3, constants as constants3, existsSync as existsSync37, mkdirSync as mkdirSync27, writeFileSync as writeFileSync30 } from "fs";
16637
16816
  import { stringify as stringify3 } from "yaml";
16638
16817
  var HOST_PASSENGERS_DOCS_URL = "https://crew.tablation.dev/host-passengers";
16639
16818
  function shouldRunWizard(opts) {
16640
16819
  if (!opts.isTTY) return false;
16641
- const exists = opts.configExists ?? existsSync36;
16820
+ const exists = opts.configExists ?? existsSync37;
16642
16821
  return !configSearchPath(opts.crewHome).some((p) => exists(p));
16643
16822
  }
16644
16823
  function firstRunConfigPath() {
@@ -16655,7 +16834,7 @@ function defaultIsExecutable(p) {
16655
16834
  function findOnPath2(name, pathEnv, isExecutable = defaultIsExecutable) {
16656
16835
  for (const dir of pathEnv.split(delimiter)) {
16657
16836
  if (!dir) continue;
16658
- const candidate = join39(dir, name);
16837
+ const candidate = join40(dir, name);
16659
16838
  if (isExecutable(candidate)) return candidate;
16660
16839
  }
16661
16840
  return void 0;
@@ -16681,9 +16860,9 @@ async function runWizard(p, env) {
16681
16860
  if (entered && isExec(entered)) agentBin = entered;
16682
16861
  else p.say(entered ? `${entered} is not an executable file.` : "A path is required.");
16683
16862
  }
16684
- const nodeDir = dirname20(env.nodePath);
16863
+ const nodeDir = dirname21(env.nodePath);
16685
16864
  const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
16686
- const baseDirs = [nodeDir, pnpmPath ? dirname20(pnpmPath) : void 0];
16865
+ const baseDirs = [nodeDir, pnpmPath ? dirname21(pnpmPath) : void 0];
16687
16866
  p.say(
16688
16867
  `
16689
16868
  Host Passengers lets this machine run the agent sessions that other people's
@@ -16694,7 +16873,7 @@ Tablation workspaces ask for, in a container. More: ${HOST_PASSENGERS_DOCS_URL}`
16694
16873
  p.say("Docker is not available here, so nothing will be hosted until it is. Your answer is recorded anyway.");
16695
16874
  }
16696
16875
  const dockerPath = hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
16697
- const extraPath = deriveExtraPath([...baseDirs, dockerPath ? dirname20(dockerPath) : void 0]);
16876
+ const extraPath = deriveExtraPath([...baseDirs, dockerPath ? dirname21(dockerPath) : void 0]);
16698
16877
  if (extraPath) {
16699
16878
  p.say(`Hooks will get ${extraPath} on their PATH (where node, pnpm${dockerPath ? " and docker" : ""} live).`);
16700
16879
  }
@@ -16716,9 +16895,9 @@ routes:
16716
16895
  ${routeBlock}`;
16717
16896
  }
16718
16897
  function writeNewConfig(path, text2) {
16719
- mkdirSync26(dirname20(path), { recursive: true });
16898
+ mkdirSync27(dirname21(path), { recursive: true });
16720
16899
  try {
16721
- writeFileSync29(path, text2, { mode: 384, flag: "wx" });
16900
+ writeFileSync30(path, text2, { mode: 384, flag: "wx" });
16722
16901
  return true;
16723
16902
  } catch (e) {
16724
16903
  if (e.code === "EEXIST") return false;
@@ -16752,7 +16931,7 @@ Wrote ${o.configPath}.
16752
16931
  // src/connect-json.ts
16753
16932
  import { accessSync as accessSync4, constants as constants4 } from "fs";
16754
16933
  import { homedir as homedir10 } from "os";
16755
- import { dirname as dirname21, join as join40 } from "path";
16934
+ import { dirname as dirname22, join as join41 } from "path";
16756
16935
  import { delimiter as delimiter2 } from "path";
16757
16936
  var CHOICE_EXIT_CODE = 3;
16758
16937
  function createEmitter(json2, write) {
@@ -16791,12 +16970,12 @@ function agentBinSearchDirs(home, pathEnv, extraPath) {
16791
16970
  const dirs = [
16792
16971
  ...pathEnv.split(delimiter2),
16793
16972
  ...(extraPath ?? "").split(delimiter2),
16794
- join40(home, ".local/bin"),
16795
- join40(home, ".claude/local"),
16973
+ join41(home, ".local/bin"),
16974
+ join41(home, ".claude/local"),
16796
16975
  "/opt/homebrew/bin",
16797
16976
  "/usr/local/bin",
16798
- join40(home, ".npm-global/bin"),
16799
- join40(home, ".volta/bin")
16977
+ join41(home, ".npm-global/bin"),
16978
+ join41(home, ".volta/bin")
16800
16979
  ];
16801
16980
  return [...new Set(dirs.filter(Boolean))];
16802
16981
  }
@@ -16818,7 +16997,7 @@ function flagWizardAnswers(f, env) {
16818
16997
  const agentBin = f.agentBin ?? found?.bin;
16819
16998
  const pnpmPath = findOnPath2("pnpm", env.pathEnv, isExec);
16820
16999
  const dockerPath = f.hostPassengers ? findOnPath2("docker", env.pathEnv, isExec) : void 0;
16821
- const extraPath = f.extraPath ?? deriveExtraPath([dirname21(env.nodePath), pnpmPath ? dirname21(pnpmPath) : void 0, dockerPath ? dirname21(dockerPath) : void 0]);
17000
+ const extraPath = f.extraPath ?? deriveExtraPath([dirname22(env.nodePath), pnpmPath ? dirname22(pnpmPath) : void 0, dockerPath ? dirname22(dockerPath) : void 0]);
16822
17001
  return {
16823
17002
  answers: {
16824
17003
  name: f.shipName?.trim() || env.hostname,
@@ -16832,8 +17011,8 @@ function flagWizardAnswers(f, env) {
16832
17011
  }
16833
17012
 
16834
17013
  // src/release-run.ts
16835
- import { readFileSync as readFileSync34, writeFileSync as writeFileSync30, existsSync as existsSync37, copyFileSync, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync3, rmSync as rmSync18 } from "fs";
16836
- import { dirname as dirname22, join as join41 } from "path";
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";
16837
17016
  import { tmpdir as tmpdir5 } from "os";
16838
17017
 
16839
17018
  // src/release.ts
@@ -17574,13 +17753,13 @@ async function bump(o, size) {
17574
17753
  }
17575
17754
  return printed;
17576
17755
  }
17577
- const files = o.repo.release.versionFiles.map((f) => join41(o.cwd, f));
17756
+ const files = o.repo.release.versionFiles.map((f) => join42(o.cwd, f));
17578
17757
  const first = files[0];
17579
- if (!existsSync37(first)) {
17758
+ if (!existsSync38(first)) {
17580
17759
  o.emit.error(`no ${o.repo.release.versionFiles[0]} to read a version from`);
17581
17760
  return null;
17582
17761
  }
17583
- const current = JSON.parse(readFileSync34(first, "utf8")).version;
17762
+ const current = JSON.parse(readFileSync35(first, "utf8")).version;
17584
17763
  if (!current) {
17585
17764
  o.emit.error(`no version field in ${o.repo.release.versionFiles[0]}`);
17586
17765
  return null;
@@ -17593,27 +17772,27 @@ async function bump(o, size) {
17593
17772
  return version;
17594
17773
  }
17595
17774
  for (const f of files) {
17596
- const src = readFileSync34(f, "utf8");
17775
+ const src = readFileSync35(f, "utf8");
17597
17776
  const out = src.replace(/("version"\s*:\s*")[^"]*(")/, `$1${version}$2`);
17598
17777
  if (out === src) {
17599
17778
  o.emit.error(`no version field in ${f}`);
17600
17779
  return null;
17601
17780
  }
17602
- writeFileSync30(f, out);
17781
+ writeFileSync31(f, out);
17603
17782
  }
17604
17783
  return version;
17605
17784
  }
17606
17785
  function writeChangelog(o, version, entries) {
17607
17786
  const file = o.repo.release.changelog;
17608
17787
  if (!file || entries.length === 0) return;
17609
- const path = join41(o.cwd, file);
17788
+ const path = join42(o.cwd, file);
17610
17789
  const section = renderChangelogSection(version, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), entries);
17611
17790
  if (o.dryRun) {
17612
17791
  o.emit.emit(`would prepend ${entries.length} entr(ies) to ${file}`);
17613
17792
  return;
17614
17793
  }
17615
- const existing = existsSync37(path) ? readFileSync34(path, "utf8") : "# Changelog\n";
17616
- writeFileSync30(path, insertChangelogSection(existing, section));
17794
+ const existing = existsSync38(path) ? readFileSync35(path, "utf8") : "# Changelog\n";
17795
+ writeFileSync31(path, insertChangelogSection(existing, section));
17617
17796
  }
17618
17797
  async function confirm(o, expected) {
17619
17798
  if (!o.repo.hooks.released) return void 0;
@@ -17696,7 +17875,7 @@ function closeReleaseCheckout(cwd, root, dir, branch, run3 = gitOk) {
17696
17875
  run3(cwd, ["worktree", "remove", "--force", dir]);
17697
17876
  } finally {
17698
17877
  try {
17699
- rmSync18(root, { recursive: true, force: true });
17878
+ rmSync19(root, { recursive: true, force: true });
17700
17879
  } finally {
17701
17880
  run3(cwd, ["worktree", "prune"]);
17702
17881
  run3(cwd, ["branch", "-D", branch]);
@@ -17706,8 +17885,8 @@ function closeReleaseCheckout(cwd, root, dir, branch, run3 = gitOk) {
17706
17885
  function openReleaseCheckout(o) {
17707
17886
  const { remote, base } = o.repo.branch;
17708
17887
  const branch = `crew/release-${Date.now()}`;
17709
- const root = mkdtempSync3(join41(tmpdir5(), "crew-release-"));
17710
- const dir = join41(root, "checkout");
17888
+ const root = mkdtempSync3(join42(tmpdir5(), "crew-release-"));
17889
+ const dir = join42(root, "checkout");
17711
17890
  o.emit.emit(`release checkout: ${dir}`, { step: "release" });
17712
17891
  const close = () => closeReleaseCheckout(o.cwd, root, dir, branch);
17713
17892
  try {
@@ -17717,10 +17896,10 @@ function openReleaseCheckout(o) {
17717
17896
  throw e;
17718
17897
  }
17719
17898
  for (const f of o.repo.worktrees.copy) {
17720
- const src = join41(o.cwd, f);
17721
- if (!existsSync37(src)) continue;
17722
- mkdirSync27(dirname22(join41(dir, f)), { recursive: true });
17723
- copyFileSync(src, join41(dir, f));
17899
+ const src = join42(o.cwd, f);
17900
+ if (!existsSync38(src)) continue;
17901
+ mkdirSync28(dirname23(join42(dir, f)), { recursive: true });
17902
+ copyFileSync(src, join42(dir, f));
17724
17903
  }
17725
17904
  return { dir, branch, close };
17726
17905
  }
@@ -18742,7 +18921,7 @@ function missingReleaserWarning(repo, release, ships, now = Date.now()) {
18742
18921
  }
18743
18922
 
18744
18923
  // src/watch.ts
18745
- import { watch as watchFile, existsSync as existsSync38, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
18924
+ import { watch as watchFile, existsSync as existsSync39, statSync as statSync11, openSync as openSync5, readSync as readSync2, closeSync as closeSync5 } from "fs";
18746
18925
  var E = "\x1B[";
18747
18926
  var dim = (s) => `${E}2m${s}${E}0m`;
18748
18927
  var bold = (s) => `${E}1m${s}${E}0m`;
@@ -18843,7 +19022,7 @@ function renderHeaders(states, now = Date.now()) {
18843
19022
  return states.map((s) => renderHeader(s, now)).join("");
18844
19023
  }
18845
19024
  function readFrom(file, from) {
18846
- if (!existsSync38(file)) return { events: [], offset: from };
19025
+ if (!existsSync39(file)) return { events: [], offset: from };
18847
19026
  const size = statSync11(file).size;
18848
19027
  if (size < from) return readFrom(file, 0);
18849
19028
  if (size === from) return { events: [], offset: from };
@@ -19086,8 +19265,8 @@ function nextRoles(decision, ran) {
19086
19265
  }
19087
19266
 
19088
19267
  // src/ship-name.ts
19089
- import { existsSync as existsSync39, mkdirSync as mkdirSync28, readFileSync as readFileSync35, renameSync as renameSync5, writeFileSync as writeFileSync31 } from "fs";
19090
- import { dirname as dirname23 } from "path";
19268
+ import { existsSync as existsSync40, mkdirSync as mkdirSync29, readFileSync as readFileSync36, renameSync as renameSync5, writeFileSync as writeFileSync32 } from "fs";
19269
+ import { dirname as dirname24 } from "path";
19091
19270
  import { parseDocument as parseDocument5 } from "yaml";
19092
19271
  var ShipNameError = class extends Error {
19093
19272
  };
@@ -19148,34 +19327,34 @@ async function syncRouteName(i) {
19148
19327
  return { status: "ok", ...base, action, name: i.local };
19149
19328
  }
19150
19329
  function readSyncedName(resolvedFile) {
19151
- if (!existsSync39(resolvedFile)) return void 0;
19330
+ if (!existsSync40(resolvedFile)) return void 0;
19152
19331
  try {
19153
- const v = JSON.parse(readFileSync35(resolvedFile, "utf8")).shipName;
19332
+ const v = JSON.parse(readFileSync36(resolvedFile, "utf8")).shipName;
19154
19333
  return typeof v === "string" ? v : void 0;
19155
19334
  } catch {
19156
19335
  return void 0;
19157
19336
  }
19158
19337
  }
19159
19338
  function writeSyncedName(resolvedFile, name) {
19160
- if (!existsSync39(resolvedFile)) return;
19339
+ if (!existsSync40(resolvedFile)) return;
19161
19340
  let raw;
19162
19341
  try {
19163
- raw = JSON.parse(readFileSync35(resolvedFile, "utf8"));
19342
+ raw = JSON.parse(readFileSync36(resolvedFile, "utf8"));
19164
19343
  } catch {
19165
19344
  return;
19166
19345
  }
19167
19346
  if (raw.shipName === name) return;
19168
19347
  raw.shipName = name;
19169
- mkdirSync28(dirname23(resolvedFile), { recursive: true });
19348
+ mkdirSync29(dirname24(resolvedFile), { recursive: true });
19170
19349
  const tmp = `${resolvedFile}.tmp${process.pid}`;
19171
- writeFileSync31(tmp, `${JSON.stringify(raw, null, 2)}
19350
+ writeFileSync32(tmp, `${JSON.stringify(raw, null, 2)}
19172
19351
  `);
19173
19352
  renameSync5(tmp, resolvedFile);
19174
19353
  }
19175
19354
  async function reconcileShipName(i) {
19176
19355
  let local = i.ship.name;
19177
19356
  try {
19178
- local = shipNameOfYaml(readFileSync35(i.configFile, "utf8")) ?? local;
19357
+ local = shipNameOfYaml(readFileSync36(i.configFile, "utf8")) ?? local;
19179
19358
  } catch {
19180
19359
  }
19181
19360
  const resolvedFile = resolvedPathFor(i.stateDir, i.route);
@@ -19199,8 +19378,8 @@ async function reconcileShipName(i) {
19199
19378
  }
19200
19379
  if (result.status !== "ok") return result;
19201
19380
  if (result.action === "adopt" || result.action === "row-wins") {
19202
- const text2 = readFileSync35(i.configFile, "utf8");
19203
- writeFileSync31(i.configFile, setShipNameInYaml(text2, result.name));
19381
+ const text2 = readFileSync36(i.configFile, "utf8");
19382
+ writeFileSync32(i.configFile, setShipNameInYaml(text2, result.name));
19204
19383
  i.ship.name = result.name;
19205
19384
  if (result.action === "row-wins") {
19206
19385
  i.emit?.warn(
@@ -19859,8 +20038,8 @@ function describeCapability(i) {
19859
20038
  }
19860
20039
 
19861
20040
  // src/agents-sync-run.ts
19862
- import { existsSync as existsSync40, mkdirSync as mkdirSync29, readFileSync as readFileSync36, writeFileSync as writeFileSync32 } from "fs";
19863
- import { dirname as dirname24 } from "path";
20041
+ import { existsSync as existsSync41, mkdirSync as mkdirSync30, readFileSync as readFileSync37, writeFileSync as writeFileSync33 } from "fs";
20042
+ import { dirname as dirname25 } from "path";
19864
20043
  async function runAgentsSync(target, o) {
19865
20044
  const dryRun = o.dryRun ?? false;
19866
20045
  const force = o.force ?? false;
@@ -19893,12 +20072,12 @@ async function runAgentsSync(target, o) {
19893
20072
  for (const s of skillResult?.outcomes ?? []) o.say(describeSkillSyncOutcome(s));
19894
20073
  if (!dryRun) {
19895
20074
  const resolvedPath = resolvedPathFor(o.stateDir, target.route);
19896
- const raw = existsSync40(resolvedPath) ? JSON.parse(readFileSync36(resolvedPath, "utf8")) : {};
20075
+ const raw = existsSync41(resolvedPath) ? JSON.parse(readFileSync37(resolvedPath, "utf8")) : {};
19897
20076
  raw.agentPersonas = personaResult.agentPersonas;
19898
20077
  if (skillResult) raw.agentSkills = skillResult.agentSkills;
19899
20078
  if (personaResult.releaseAgentId) raw.releaseAgentId = personaResult.releaseAgentId;
19900
- mkdirSync29(dirname24(resolvedPath), { recursive: true });
19901
- writeFileSync32(resolvedPath, `${JSON.stringify(raw, null, 2)}
20079
+ mkdirSync30(dirname25(resolvedPath), { recursive: true });
20080
+ writeFileSync33(resolvedPath, `${JSON.stringify(raw, null, 2)}
19902
20081
  `);
19903
20082
  if (target.resolved) {
19904
20083
  target.resolved.agentPersonas = personaResult.agentPersonas;
@@ -20015,7 +20194,7 @@ async function showLogEntry(route2, entryId, opts = {}) {
20015
20194
 
20016
20195
  // src/repos-check.ts
20017
20196
  import { spawnSync as spawnSync10 } from "child_process";
20018
- import { readFileSync as readFileSync37 } from "fs";
20197
+ import { readFileSync as readFileSync38 } from "fs";
20019
20198
  import { basename as basename11 } from "path";
20020
20199
  var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
20021
20200
  function checkHookSyntax(script, shell) {
@@ -20056,7 +20235,7 @@ function checkRepoSpec(dir, opts = {}) {
20056
20235
  }
20057
20236
  let parsed;
20058
20237
  try {
20059
- parsed = parseRepoConfig(readFileSync37(file, "utf8"), file);
20238
+ parsed = parseRepoConfig(readFileSync38(file, "utf8"), file);
20060
20239
  } catch (e) {
20061
20240
  if (!(e instanceof RepoConfigError)) throw e;
20062
20241
  return { ...empty, file, error: e.message, exitCode: 1 };
@@ -20513,7 +20692,7 @@ ${run3.err}` });
20513
20692
  }
20514
20693
 
20515
20694
  // src/sample-ticket.ts
20516
- import { basename as basename12, dirname as dirname25, join as join42 } from "path";
20695
+ import { basename as basename12, dirname as dirname26, join as join43 } from "path";
20517
20696
  var SAMPLE_REPO = "crew-sample";
20518
20697
  var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
20519
20698
  var SAMPLE_MARKER = "sample: true";
@@ -20536,8 +20715,8 @@ has something safe to work on: its test and build hooks always pass, it has no
20536
20715
  remote, and \`crew sample-ticket --remove\` deletes it.
20537
20716
  `;
20538
20717
  function sampleDir(existing, reposBase, workspace) {
20539
- if (existing.length > 0) return join42(dirname25(existing[0]), SAMPLE_REPO);
20540
- return join42(reposBase, workspace, SAMPLE_REPO);
20718
+ if (existing.length > 0) return join43(dirname26(existing[0]), SAMPLE_REPO);
20719
+ return join43(reposBase, workspace, SAMPLE_REPO);
20541
20720
  }
20542
20721
  var SAMPLE_TITLE = "Sample: add a documented `greet` function with a test";
20543
20722
  function sampleDescription() {
@@ -20581,8 +20760,8 @@ function planSampleTicket(i) {
20581
20760
  }
20582
20761
 
20583
20762
  // src/worktree-sweep.ts
20584
- import { existsSync as existsSync41, readFileSync as readFileSync38, readdirSync as readdirSync14 } from "fs";
20585
- import { basename as basename13, dirname as dirname26, join as join43, resolve as resolve5 } from "path";
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";
20586
20765
  function planWorktreeSweep(target, tickets, contract) {
20587
20766
  const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
20588
20767
  const verified = contract.statuses.verified;
@@ -20646,7 +20825,7 @@ function worktreeTicketNumbers(target) {
20646
20825
  return [...nums];
20647
20826
  }
20648
20827
  function siblingWorktreeDirs(target) {
20649
- const parent = dirname26(target.dir.replace(/[/\\]+$/, ""));
20828
+ const parent = dirname27(target.dir.replace(/[/\\]+$/, ""));
20650
20829
  const stem = `${basename13(target.dir.replace(/[/\\]+$/, ""))}-`;
20651
20830
  const registered = new Set(worktrees(target.dir).map((w) => resolve5(w.path)));
20652
20831
  let names;
@@ -20659,7 +20838,7 @@ function siblingWorktreeDirs(target) {
20659
20838
  for (const name of names) {
20660
20839
  if (!name.startsWith(stem)) continue;
20661
20840
  const m = DIR_NUMBER_SUFFIXED.exec(name);
20662
- const path = join43(parent, name);
20841
+ const path = join44(parent, name);
20663
20842
  if (m && !registered.has(resolve5(path))) out.push({ path, number: m[1] });
20664
20843
  }
20665
20844
  return out;
@@ -20694,7 +20873,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20694
20873
  let removed = 0;
20695
20874
  let keptBranches = 0;
20696
20875
  for (const a of actions) {
20697
- if (!dryRun && base && !a.keepBranch && existsSync41(a.path) && isHandBackMerge(a.path, base)) {
20876
+ if (!dryRun && base && !a.keepBranch && existsSync42(a.path) && isHandBackMerge(a.path, base)) {
20698
20877
  if (gitOk(a.path, ["merge", "--abort"]) !== null) {
20699
20878
  log.emit(
20700
20879
  `abandoned the hand-back merge of ${base} in ${a.path}: ${a.ticket.issue_id} deployed without it`,
@@ -20702,7 +20881,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20702
20881
  );
20703
20882
  }
20704
20883
  }
20705
- if (!dryRun && existsSync41(a.path)) {
20884
+ if (!dryRun && existsSync42(a.path)) {
20706
20885
  let dirty = false;
20707
20886
  try {
20708
20887
  dirty = status(a.path).length > 0;
@@ -20713,7 +20892,7 @@ async function applyWorktreeSweep(cwd, actions, dryRun, log, remote = "origin",
20713
20892
  continue;
20714
20893
  }
20715
20894
  }
20716
- if (!dryRun && a.requirePushed && a.branch && existsSync41(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20895
+ if (!dryRun && a.requirePushed && a.branch && existsSync42(a.path) && remoteConfigured(cwd, remote) && worktreeHoldsUnpushed(a.path, remote)) {
20717
20896
  refuse2(a, "unpushed commits", `${a.branch} has commits that are not on ${remote}`);
20718
20897
  continue;
20719
20898
  }
@@ -20762,7 +20941,7 @@ function isHandBackMerge(path, base) {
20762
20941
  if (!msgPath) return false;
20763
20942
  let first;
20764
20943
  try {
20765
- first = readFileSync38(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
20944
+ first = readFileSync39(resolve5(path, msgPath), "utf8").split("\n")[0] ?? "";
20766
20945
  } catch {
20767
20946
  return false;
20768
20947
  }
@@ -20867,14 +21046,14 @@ function applyRemoteBranchCleanup(cwd, remote, branches2, dryRun, log) {
20867
21046
  }
20868
21047
 
20869
21048
  // src/stream-sweep.ts
20870
- import { existsSync as existsSync42, readdirSync as readdirSync15, rmSync as rmSync19, statSync as statSync12 } from "fs";
20871
- import { join as join44 } from "path";
21049
+ import { existsSync as existsSync43, readdirSync as readdirSync15, rmSync as rmSync20, statSync as statSync12 } from "fs";
21050
+ import { join as join45 } from "path";
20872
21051
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
20873
21052
  function planStreamSweep(stateDir, retentionDays, now = Date.now()) {
20874
- const dir = join44(stateDir, "streams");
20875
- if (!existsSync42(dir)) return [];
21053
+ const dir = join45(stateDir, "streams");
21054
+ if (!existsSync43(dir)) return [];
20876
21055
  const cutoff = now - retentionDays * DAY_MS2;
20877
- return readdirSync15(dir).map((name) => join44(dir, name)).filter((path) => {
21056
+ return readdirSync15(dir).map((name) => join45(dir, name)).filter((path) => {
20878
21057
  try {
20879
21058
  return statSync12(path).mtimeMs < cutoff;
20880
21059
  } catch {
@@ -20890,7 +21069,7 @@ function applyStreamSweep(paths, dryRun, log) {
20890
21069
  continue;
20891
21070
  }
20892
21071
  try {
20893
- rmSync19(path, { force: true });
21072
+ rmSync20(path, { force: true });
20894
21073
  removed++;
20895
21074
  log.emit(`removed stream artifact ${path}`, { step: "worktree" });
20896
21075
  } catch (e) {
@@ -20900,9 +21079,9 @@ function applyStreamSweep(paths, dryRun, log) {
20900
21079
  return removed;
20901
21080
  }
20902
21081
  function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
20903
- if (!existsSync42(stateDir)) return [];
21082
+ if (!existsSync43(stateDir)) return [];
20904
21083
  const cutoff = now - retentionDays * DAY_MS2;
20905
- return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join44(stateDir, name)).filter((path) => {
21084
+ return readdirSync15(stateDir).filter((name) => ROTATED_EVENTS_RE.test(name)).map((name) => join45(stateDir, name)).filter((path) => {
20906
21085
  try {
20907
21086
  return statSync12(path).mtimeMs < cutoff;
20908
21087
  } catch {
@@ -20912,12 +21091,12 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
20912
21091
  }
20913
21092
 
20914
21093
  // src/cli.ts
20915
- import { realpathSync as realpathSync3, readFileSync as readFileSync44, existsSync as existsSync49, mkdirSync as mkdirSync37, writeFileSync as writeFileSync37, rmSync as rmSync24, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync6, statSync as statSync16, readdirSync as readdirSync20 } from "fs";
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";
20916
21095
  import { createInterface } from "readline/promises";
20917
21096
 
20918
21097
  // src/periscope-cmd.ts
20919
- import { existsSync as existsSync43, mkdirSync as mkdirSync30, readFileSync as readFileSync39, rmSync as rmSync20, writeFileSync as writeFileSync33 } from "fs";
20920
- import { join as join45 } from "path";
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";
20921
21100
  var PeriscopeError = class extends Error {
20922
21101
  };
20923
21102
  var LivePeriscopeError = class extends PeriscopeError {
@@ -20927,22 +21106,22 @@ var LivePeriscopeError = class extends PeriscopeError {
20927
21106
  this.data = { refused: "live_periscope_exists", ...live };
20928
21107
  }
20929
21108
  };
20930
- var priorDir = (stateDir) => join45(stateDir, "periscope-priors");
20931
- var priorPath = (stateDir, ticketRecordId) => join45(priorDir(stateDir), `${ticketRecordId}.json`);
21109
+ var priorDir = (stateDir) => join46(stateDir, "periscope-priors");
21110
+ var priorPath = (stateDir, ticketRecordId) => join46(priorDir(stateDir), `${ticketRecordId}.json`);
20932
21111
  function removePeriscopePrior(stateDir, ticketRecordId) {
20933
- rmSync20(priorPath(stateDir, ticketRecordId), { force: true });
21112
+ rmSync21(priorPath(stateDir, ticketRecordId), { force: true });
20934
21113
  }
20935
21114
  function writePeriscopePrior(stateDir, ticketRecordId, prior) {
20936
21115
  try {
20937
- mkdirSync30(priorDir(stateDir), { recursive: true });
20938
- writeFileSync33(priorPath(stateDir, ticketRecordId), JSON.stringify(prior));
21116
+ mkdirSync31(priorDir(stateDir), { recursive: true });
21117
+ writeFileSync34(priorPath(stateDir, ticketRecordId), JSON.stringify(prior));
20939
21118
  } catch {
20940
21119
  }
20941
21120
  }
20942
21121
  function readPeriscopePrior(stateDir, ticketRecordId) {
20943
21122
  try {
20944
21123
  const p = priorPath(stateDir, ticketRecordId);
20945
- return existsSync43(p) ? JSON.parse(readFileSync39(p, "utf8")) : void 0;
21124
+ return existsSync44(p) ? JSON.parse(readFileSync40(p, "utf8")) : void 0;
20946
21125
  } catch {
20947
21126
  return void 0;
20948
21127
  }
@@ -21396,7 +21575,7 @@ ${tail2(preview.output)}`);
21396
21575
  // src/periscope-idle.ts
21397
21576
  import { execFileSync as execFileSync12 } from "child_process";
21398
21577
  import { statSync as statSync13 } from "fs";
21399
- import { join as join46 } from "path";
21578
+ import { join as join47 } from "path";
21400
21579
  var MAX_SCANNED_FILES = 2e4;
21401
21580
  function newestWorktreeMtime(root) {
21402
21581
  const a = newestTrackedMtime(root);
@@ -21421,7 +21600,7 @@ function newestTrackedMtime(root) {
21421
21600
  if (!rel) continue;
21422
21601
  if (++scanned > MAX_SCANNED_FILES) break;
21423
21602
  try {
21424
- const m = statSync13(join46(root, rel)).mtimeMs;
21603
+ const m = statSync13(join47(root, rel)).mtimeMs;
21425
21604
  if (newest === null || m > newest) newest = m;
21426
21605
  } catch {
21427
21606
  }
@@ -21497,18 +21676,18 @@ import { dirname as dirOf2, resolve as resolvePath } from "path";
21497
21676
 
21498
21677
  // src/ssh-keys.ts
21499
21678
  import { execFileSync as execFileSync13 } from "child_process";
21500
- import { existsSync as existsSync44, mkdirSync as mkdirSync31, readFileSync as readFileSync40, chmodSync as chmodSync3 } from "fs";
21501
- import { dirname as dirname27, join as join47 } from "path";
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";
21502
21681
  var SshKeyError = class extends Error {
21503
21682
  };
21504
21683
  function shipSshKeyPathFor(stateDir) {
21505
- return join47(stateDir, "ssh", "id_ed25519");
21684
+ return join48(stateDir, "ssh", "id_ed25519");
21506
21685
  }
21507
21686
  function ensureShipSshKeypair(stateDir) {
21508
21687
  const privateKeyPath = shipSshKeyPathFor(stateDir);
21509
21688
  const publicKeyPath = `${privateKeyPath}.pub`;
21510
- if (!existsSync44(privateKeyPath)) {
21511
- mkdirSync31(dirname27(privateKeyPath), { recursive: true });
21689
+ if (!existsSync45(privateKeyPath)) {
21690
+ mkdirSync32(dirname28(privateKeyPath), { recursive: true });
21512
21691
  try {
21513
21692
  execFileSync13("ssh-keygen", [
21514
21693
  "-t",
@@ -21528,10 +21707,10 @@ function ensureShipSshKeypair(stateDir) {
21528
21707
  }
21529
21708
  chmodSync3(privateKeyPath, 384);
21530
21709
  }
21531
- if (!existsSync44(publicKeyPath)) {
21710
+ if (!existsSync45(publicKeyPath)) {
21532
21711
  throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
21533
21712
  }
21534
- const publicKey = readFileSync40(publicKeyPath, "utf8").trim();
21713
+ const publicKey = readFileSync41(publicKeyPath, "utf8").trim();
21535
21714
  return { privateKeyPath, publicKeyPath, publicKey };
21536
21715
  }
21537
21716
  function sshKeygenAvailable() {
@@ -21695,12 +21874,12 @@ async function unregisterEndpoint(workspaceId, info, d) {
21695
21874
  import { createServer as createHttpServer } from "http";
21696
21875
  import { spawn as spawn9 } from "child_process";
21697
21876
  import { randomBytes } from "crypto";
21698
- import { existsSync as existsSync45, mkdirSync as mkdirSync33, readFileSync as readFileSync41, writeFileSync as writeFileSync34, unlinkSync as unlinkSync4, readdirSync as readdirSync16 } from "fs";
21699
- import { join as join48 } from "path";
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";
21700
21879
 
21701
21880
  // src/passenger-mirror.ts
21702
- import { mkdirSync as mkdirSync32 } from "fs";
21703
- import { dirname as dirname28 } from "path";
21881
+ import { mkdirSync as mkdirSync33 } from "fs";
21882
+ import { dirname as dirname29 } from "path";
21704
21883
  function mirrorRepoOf(spec) {
21705
21884
  const fromOrigin = spec.originUrl ? parseRemote(spec.originUrl) : null;
21706
21885
  if (fromOrigin) return fromOrigin;
@@ -21718,9 +21897,9 @@ async function materializeMirrorCheckouts(specs, cacheFor = (s) => new SandboxCa
21718
21897
  continue;
21719
21898
  }
21720
21899
  try {
21721
- const parent = dirname28(spec.dir);
21722
- mkdirSync32(parent, { recursive: true });
21723
- mkdirSync32(spec.dir, { recursive: true });
21900
+ const parent = dirname29(spec.dir);
21901
+ mkdirSync33(parent, { recursive: true });
21902
+ mkdirSync33(spec.dir, { recursive: true });
21724
21903
  const r = await cacheFor(spec).materialize(repo, spec.base, parent, spec.name);
21725
21904
  out.push({ ...base, action: r.changed ? "updated" : "unchanged", detail: r.tip });
21726
21905
  } catch (e) {
@@ -21826,21 +22005,21 @@ var defaultSpawn2 = (cmd, args, env) => {
21826
22005
  return child;
21827
22006
  };
21828
22007
  function syncDaemonStatePath(stateDir, workspaceId) {
21829
- return join48(stateDir, "passengers", `${workspaceId}-sync.json`);
22008
+ return join49(stateDir, "passengers", `${workspaceId}-sync.json`);
21830
22009
  }
21831
22010
  function readPersistedSyncDaemon(stateDir, workspaceId) {
21832
22011
  const path = syncDaemonStatePath(stateDir, workspaceId);
21833
- if (!existsSync45(path)) return void 0;
22012
+ if (!existsSync46(path)) return void 0;
21834
22013
  try {
21835
- return JSON.parse(readFileSync41(path, "utf8"));
22014
+ return JSON.parse(readFileSync42(path, "utf8"));
21836
22015
  } catch {
21837
22016
  return void 0;
21838
22017
  }
21839
22018
  }
21840
22019
  function writePersistedSyncDaemon(stateDir, state) {
21841
22020
  const path = syncDaemonStatePath(stateDir, state.workspaceId);
21842
- mkdirSync33(join48(stateDir, "passengers"), { recursive: true });
21843
- writeFileSync34(path, `${JSON.stringify(state, null, 2)}
22021
+ mkdirSync34(join49(stateDir, "passengers"), { recursive: true });
22022
+ writeFileSync35(path, `${JSON.stringify(state, null, 2)}
21844
22023
  `);
21845
22024
  }
21846
22025
  function removePersistedSyncDaemon(stateDir, workspaceId) {
@@ -21860,8 +22039,8 @@ function waitForExit(pid, alive, timeoutMs = 5e3) {
21860
22039
  }
21861
22040
  }
21862
22041
  function readAllPersistedSyncWorkspaceIds(stateDir) {
21863
- const dir = join48(stateDir, "passengers");
21864
- if (!existsSync45(dir)) return [];
22042
+ const dir = join49(stateDir, "passengers");
22043
+ if (!existsSync46(dir)) return [];
21865
22044
  const suffix = "-sync.json";
21866
22045
  return readdirSync16(dir).filter((f) => f.endsWith(suffix)).map((f) => f.slice(0, -suffix.length));
21867
22046
  }
@@ -21919,12 +22098,12 @@ function syncPassengerSyncDaemons(plans, cfg, stateDir, deps = {}) {
21919
22098
 
21920
22099
  // src/periscope-proxy.ts
21921
22100
  import { spawn as spawn10 } from "child_process";
21922
- import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync34 } from "fs";
22101
+ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync35 } from "fs";
21923
22102
  import { createServer, request as httpRequest } from "http";
21924
22103
  import { request as httpsRequest } from "https";
21925
22104
  import { connect as netConnect } from "net";
21926
22105
  import { connect as tlsConnect } from "tls";
21927
- import { dirname as dirname29, isAbsolute as isAbsolute4 } from "path";
22106
+ import { dirname as dirname30, isAbsolute as isAbsolute4 } from "path";
21928
22107
  import { brotliDecompressSync, gunzipSync, inflateSync } from "zlib";
21929
22108
 
21930
22109
  // src/periscope-inspector.ts
@@ -22038,7 +22217,7 @@ function parsePointedBody(raw) {
22038
22217
  return entry;
22039
22218
  }
22040
22219
  function appendPointed(file, entry, now = /* @__PURE__ */ new Date()) {
22041
- mkdirSync34(dirname29(file), { recursive: true });
22220
+ mkdirSync35(dirname30(file), { recursive: true });
22042
22221
  appendFileSync3(file, `${JSON.stringify({ ...entry, at: now.toISOString() })}
22043
22222
  `);
22044
22223
  }
@@ -22299,6 +22478,33 @@ async function launchProxy(invocation, worktree, previewUrl, number, o) {
22299
22478
  throw new Error(`the proxy did not start listening on ${o.host}:${port}`);
22300
22479
  }
22301
22480
 
22481
+ // src/intel-worktree.ts
22482
+ async function startWorktreeIntel(d) {
22483
+ const file = attachFilePath(d.stateDir, d.route, d.number);
22484
+ const probe = d.probe ?? probeIntel;
22485
+ let outcome;
22486
+ try {
22487
+ const known = readAttachFile(file);
22488
+ outcome = known ? await probe({ runHook: async () => ({ code: 0, output: known }) }, {}, 3) : { state: "absent" };
22489
+ if (outcome.state !== "ready") {
22490
+ const prepared = await d.prepare(d.worktree);
22491
+ if (prepared && prepared.code !== 0) {
22492
+ outcome = { state: "failed", reason: `setup exited ${prepared.code}` };
22493
+ } else {
22494
+ const env = { CREW_TICKET: d.ticketTag, CREW_BRANCH: d.branch };
22495
+ const ports = await d.runHook("ports", d.worktree, env);
22496
+ if (ports) Object.assign(env, parseEnvLines(ports.output));
22497
+ outcome = await probe({ runHook: (e) => d.runHook("intel", d.worktree, e) }, env, d.startupSeconds);
22498
+ }
22499
+ }
22500
+ if (outcome.state === "ready") writeAttachFile(file, outcome.url);
22501
+ } catch (e) {
22502
+ outcome = { state: "failed", reason: e.message.slice(0, 200) };
22503
+ }
22504
+ const tail3 = outcome.state === "ready" ? " The session's `code` server attaches to it within a second; until it lists these tools it still shows only `code_status`." : "";
22505
+ return `${codeToolsParagraph(outcome)}${tail3}`;
22506
+ }
22507
+
22302
22508
  // src/outage.ts
22303
22509
  var TRANSIENT_BACKOFF_MS = [15e3, 3e4, 6e4];
22304
22510
  var OUTAGE_ESCALATE_AFTER = 5;
@@ -22331,8 +22537,8 @@ function describeCause(err3) {
22331
22537
  }
22332
22538
 
22333
22539
  // src/daemon.ts
22334
- import { existsSync as existsSync46, mkdirSync as mkdirSync35, readFileSync as readFileSync42, readdirSync as readdirSync17, rmSync as rmSync21, statSync as statSync14, writeFileSync as writeFileSync35 } from "fs";
22335
- import { join as join49 } from "path";
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";
22336
22542
  import { randomBytes as randomBytes2 } from "crypto";
22337
22543
  var DEFAULT_FLOOR_MS = 5e3;
22338
22544
  var DEFAULT_CEILING_MS = INTERVAL_SECONDS * 1e3;
@@ -22420,7 +22626,7 @@ function latestMtime(path) {
22420
22626
  if (!stat2.isDirectory()) return 0;
22421
22627
  let latest = stat2.mtimeMs;
22422
22628
  for (const entry of readdirSync17(path, { withFileTypes: true })) {
22423
- latest = Math.max(latest, latestMtime(join49(path, entry.name)));
22629
+ latest = Math.max(latest, latestMtime(join50(path, entry.name)));
22424
22630
  }
22425
22631
  return latest;
22426
22632
  }
@@ -22429,10 +22635,10 @@ function computeTreeSignature(paths) {
22429
22635
  }
22430
22636
  function makeStaleChecker(paths, opts = {}) {
22431
22637
  const initial = computeTreeSignature(paths);
22432
- return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync46(opts.reloadSentinel);
22638
+ return () => computeTreeSignature(paths) !== initial || opts.reloadSentinel !== void 0 && existsSync47(opts.reloadSentinel);
22433
22639
  }
22434
22640
  function reloadSentinelFor(stateDir) {
22435
- return join49(stateDir, "reload-requested");
22641
+ return join50(stateDir, "reload-requested");
22436
22642
  }
22437
22643
  function readReloadRequestedAt(stateDir) {
22438
22644
  try {
@@ -22442,17 +22648,17 @@ function readReloadRequestedAt(stateDir) {
22442
22648
  }
22443
22649
  }
22444
22650
  function daemonStampFor(stateDir) {
22445
- return join49(stateDir, "daemon.started");
22651
+ return join50(stateDir, "daemon.started");
22446
22652
  }
22447
22653
  function noteDaemonStarted(stateDir, now = Date.now()) {
22448
- mkdirSync35(stateDir, { recursive: true });
22449
- rmSync21(reloadSentinelFor(stateDir), { force: true });
22450
- writeFileSync35(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
22654
+ mkdirSync36(stateDir, { recursive: true });
22655
+ rmSync22(reloadSentinelFor(stateDir), { force: true });
22656
+ writeFileSync36(daemonStampFor(stateDir), `${JSON.stringify({ pid: process.pid, startedAt: now })}
22451
22657
  `);
22452
22658
  }
22453
22659
  function readDaemonStarted(stateDir) {
22454
22660
  try {
22455
- const v = JSON.parse(readFileSync42(daemonStampFor(stateDir), "utf8")).startedAt;
22661
+ const v = JSON.parse(readFileSync43(daemonStampFor(stateDir), "utf8")).startedAt;
22456
22662
  return typeof v === "number" ? v : null;
22457
22663
  } catch {
22458
22664
  return null;
@@ -23018,18 +23224,18 @@ import { createHash as createHash10 } from "crypto";
23018
23224
  import {
23019
23225
  chmodSync as chmodSync4,
23020
23226
  closeSync as closeSync6,
23021
- existsSync as existsSync47,
23227
+ existsSync as existsSync48,
23022
23228
  fstatSync,
23023
23229
  openSync as openSync6,
23024
23230
  readSync as readSync3,
23025
23231
  readdirSync as readdirSync18,
23026
- rmSync as rmSync22,
23232
+ rmSync as rmSync23,
23027
23233
  statSync as statSync15,
23028
23234
  watch
23029
23235
  } from "fs";
23030
23236
  import { createConnection } from "net";
23031
23237
  import { createServer as createServer2 } from "http";
23032
- import { basename as basename14, dirname as dirname30, join as join50 } from "path";
23238
+ import { basename as basename14, dirname as dirname31, join as join51 } from "path";
23033
23239
  import { execFile as execFile3, spawn as spawn12 } from "child_process";
23034
23240
 
23035
23241
  // src/periscope-control.ts
@@ -23258,7 +23464,7 @@ function runCrew(args, onLine, timeoutMs = 20 * 6e4) {
23258
23464
  // src/control-api.ts
23259
23465
  var PAUSABLE = ["dev", "design", "qa"];
23260
23466
  var CONTROL_SOCKET_NAME = "control.sock";
23261
- var controlSocketFor = (stateDir) => join50(stateDir, CONTROL_SOCKET_NAME);
23467
+ var controlSocketFor = (stateDir) => join51(stateDir, CONTROL_SOCKET_NAME);
23262
23468
  var MAX_SOCKET_PATH = 100;
23263
23469
  var MAX_TAIL_BYTES = 1024 * 1024;
23264
23470
  var MAX_WAIT_SECONDS = 30;
@@ -23345,7 +23551,7 @@ function candidates(live, rotated) {
23345
23551
  const out = [live];
23346
23552
  if (!rotated) return out;
23347
23553
  try {
23348
- for (const n of readdirSync18(dirname30(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join50(dirname30(live), n));
23554
+ for (const n of readdirSync18(dirname31(live))) if (ROTATED_EVENTS_RE.test(n)) out.push(join51(dirname31(live), n));
23349
23555
  } catch {
23350
23556
  }
23351
23557
  return out;
@@ -23389,7 +23595,7 @@ function lastLines2(path, n) {
23389
23595
  }
23390
23596
  function readTail(live, opts) {
23391
23597
  const max = opts.maxBytes ?? MAX_TAIL_BYTES;
23392
- if (!existsSync47(live)) {
23598
+ if (!existsSync48(live)) {
23393
23599
  return { lines: [], cursor: opts.after ?? encodeCursor({ dev: 0, ino: 0, off: 0 }), reset: false };
23394
23600
  }
23395
23601
  const cur = opts.after ? decodeCursor(opts.after) : null;
@@ -23505,7 +23711,7 @@ async function tailReply(path, q2, rotated, abort) {
23505
23711
  const deadline = Date.now() + Math.min(wait, MAX_WAIT_SECONDS) * 1e3;
23506
23712
  let r = readTail(path, { after, tail: tail3, rotated });
23507
23713
  while (after && !r.lines.length && !r.reset && Date.now() < deadline && !abort.aborted) {
23508
- await waitForChange(dirname30(path), deadline - Date.now(), abort);
23714
+ await waitForChange(dirname31(path), deadline - Date.now(), abort);
23509
23715
  r = readTail(path, { after: r.cursor, rotated });
23510
23716
  }
23511
23717
  return reply2(200, r);
@@ -23545,8 +23751,8 @@ async function route(deps, method, url, req, abort) {
23545
23751
  if (p.startsWith("/streams/") && get2) {
23546
23752
  const run3 = decodeURIComponent(p.slice("/streams/".length));
23547
23753
  if (!RUN_NAME_RE.test(run3) || run3.includes("..") || /\.(pid|meta\.json|events\.jsonl)$/.test(run3)) return err2(400, "not a run name");
23548
- const file = join50(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23549
- if (!existsSync47(file)) return err2(404, "no such run");
23754
+ const file = join51(deps.stateDir, "streams", run3.endsWith(".jsonl") ? run3 : `${run3}.jsonl`);
23755
+ if (!existsSync48(file)) return err2(404, "no such run");
23550
23756
  return tailReply(file, url.searchParams, false, abort);
23551
23757
  }
23552
23758
  if ((p === "/pause" || p === "/resume") && post3) {
@@ -23590,7 +23796,7 @@ function send(res, r) {
23590
23796
  res.end(body);
23591
23797
  }
23592
23798
  async function socketIsStale(path) {
23593
- if (!existsSync47(path)) return false;
23799
+ if (!existsSync48(path)) return false;
23594
23800
  return new Promise((resolve8) => {
23595
23801
  const c = createConnection(path);
23596
23802
  c.once("connect", () => {
@@ -23608,8 +23814,8 @@ async function startControlServer(deps) {
23608
23814
  log(`control API not started: socket path is too long for a unix socket (${path})`);
23609
23815
  return null;
23610
23816
  }
23611
- if (await socketIsStale(path)) rmSync22(path, { force: true });
23612
- else if (existsSync47(path)) {
23817
+ if (await socketIsStale(path)) rmSync23(path, { force: true });
23818
+ else if (existsSync48(path)) {
23613
23819
  log(`control API not started: ${path} is in use by another process`);
23614
23820
  return null;
23615
23821
  }
@@ -23655,7 +23861,7 @@ async function startControlServer(deps) {
23655
23861
  path,
23656
23862
  close: () => new Promise((done) => {
23657
23863
  server.close(() => {
23658
- rmSync22(path, { force: true });
23864
+ rmSync23(path, { force: true });
23659
23865
  done();
23660
23866
  });
23661
23867
  })
@@ -23695,57 +23901,57 @@ function crewJson(args, timeoutMs = 6e4) {
23695
23901
 
23696
23902
  // src/periscope-mcp.ts
23697
23903
  import { lstat, mkdir, readdir, readFile, readlink, realpath, rm, stat, writeFile } from "fs/promises";
23698
- import { basename as basename15, dirname as dirname31, isAbsolute as isAbsolute5, join as join52, relative as relative2, resolve as resolve6, sep } from "path";
23904
+ import { basename as basename15, dirname as dirname32, isAbsolute as isAbsolute5, join as join53, relative as relative2, resolve as resolve6, sep } from "path";
23699
23905
  import { z } from "zod";
23700
23906
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23701
23907
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
23702
23908
 
23703
23909
  // src/periscope-endpoints.ts
23704
23910
  import { randomBytes as randomBytes3 } from "crypto";
23705
- import { chmodSync as chmodSync5, existsSync as existsSync48, mkdirSync as mkdirSync36, readdirSync as readdirSync19, readFileSync as readFileSync43, rmSync as rmSync23, writeFileSync as writeFileSync36 } from "fs";
23706
- import { join as join51 } from "path";
23707
- var secretDir = (stateDir) => join51(stateDir, "periscope-mcp");
23708
- var secretFile = (stateDir, id) => join51(secretDir(stateDir), `${id}.json`);
23709
- var endpointDir = (stateDir) => join51(stateDir, "periscope-endpoints");
23710
- var endpointFile = (stateDir, id) => join51(endpointDir(stateDir), `${id}.json`);
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`);
23711
23917
  function mintPeriscopeSecret(stateDir, ticketRecordId) {
23712
- mkdirSync36(secretDir(stateDir), { recursive: true, mode: 448 });
23918
+ mkdirSync37(secretDir(stateDir), { recursive: true, mode: 448 });
23713
23919
  const secret = randomBytes3(32).toString("base64url");
23714
23920
  const file = secretFile(stateDir, ticketRecordId);
23715
- writeFileSync36(file, `${JSON.stringify({ secret })}
23921
+ writeFileSync37(file, `${JSON.stringify({ secret })}
23716
23922
  `, { mode: 384 });
23717
23923
  chmodSync5(file, 384);
23718
23924
  return secret;
23719
23925
  }
23720
23926
  function readPeriscopeSecret(stateDir, ticketRecordId) {
23721
23927
  try {
23722
- const v = JSON.parse(readFileSync43(secretFile(stateDir, ticketRecordId), "utf8"));
23928
+ const v = JSON.parse(readFileSync44(secretFile(stateDir, ticketRecordId), "utf8"));
23723
23929
  return typeof v.secret === "string" && v.secret ? v.secret : void 0;
23724
23930
  } catch {
23725
23931
  return void 0;
23726
23932
  }
23727
23933
  }
23728
23934
  function removePeriscopeSecret(stateDir, ticketRecordId) {
23729
- rmSync23(secretFile(stateDir, ticketRecordId), { force: true });
23935
+ rmSync24(secretFile(stateDir, ticketRecordId), { force: true });
23730
23936
  }
23731
23937
  function readPeriscopeEndpoint(stateDir, ticketRecordId) {
23732
23938
  try {
23733
- return JSON.parse(readFileSync43(endpointFile(stateDir, ticketRecordId), "utf8"));
23939
+ return JSON.parse(readFileSync44(endpointFile(stateDir, ticketRecordId), "utf8"));
23734
23940
  } catch {
23735
23941
  return void 0;
23736
23942
  }
23737
23943
  }
23738
23944
  function writePeriscopeEndpoint(stateDir, rec) {
23739
- mkdirSync36(endpointDir(stateDir), { recursive: true });
23740
- writeFileSync36(endpointFile(stateDir, rec.ticketRecordId), `${JSON.stringify(rec, null, 2)}
23945
+ mkdirSync37(endpointDir(stateDir), { recursive: true });
23946
+ writeFileSync37(endpointFile(stateDir, rec.ticketRecordId), `${JSON.stringify(rec, null, 2)}
23741
23947
  `);
23742
23948
  }
23743
23949
  function removePeriscopeEndpointRecord(stateDir, ticketRecordId) {
23744
- rmSync23(endpointFile(stateDir, ticketRecordId), { force: true });
23950
+ rmSync24(endpointFile(stateDir, ticketRecordId), { force: true });
23745
23951
  }
23746
23952
  function listPeriscopeEndpoints(stateDir) {
23747
23953
  const dir = endpointDir(stateDir);
23748
- if (!existsSync48(dir)) return [];
23954
+ if (!existsSync49(dir)) return [];
23749
23955
  const out = [];
23750
23956
  for (const name of readdirSync19(dir)) {
23751
23957
  if (!name.endsWith(".json")) continue;
@@ -23833,10 +24039,10 @@ async function realAncestor(p) {
23833
24039
  }
23834
24040
  if (isLink) {
23835
24041
  if (++hops > 40) throw new PathRefusal(`too many symbolic links resolving ${p}`);
23836
- cur = resolve6(dirname31(cur), await readlink(cur));
24042
+ cur = resolve6(dirname32(cur), await readlink(cur));
23837
24043
  continue;
23838
24044
  }
23839
- const parent = dirname31(cur);
24045
+ const parent = dirname32(cur);
23840
24046
  if (parent === cur) throw new PathRefusal(`cannot resolve ${p}`);
23841
24047
  tail3.push(basename15(cur));
23842
24048
  cur = parent;
@@ -23848,13 +24054,13 @@ async function resolveInWorktree(root, requested) {
23848
24054
  let abs;
23849
24055
  if (isAbsolute5(requested)) {
23850
24056
  abs = resolve6(requested);
23851
- if (isInside(root, abs)) abs = join52(realRoot, relative2(root, abs));
24057
+ if (isInside(root, abs)) abs = join53(realRoot, relative2(root, abs));
23852
24058
  } else {
23853
24059
  abs = resolve6(realRoot, requested);
23854
24060
  }
23855
24061
  if (!isInside(realRoot, abs)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
23856
24062
  const { real, rest } = await realAncestor(abs);
23857
- const final = rest ? join52(real, rest) : real;
24063
+ const final = rest ? join53(real, rest) : real;
23858
24064
  if (!isInside(realRoot, final)) throw new PathRefusal(`${requested} is outside this periscope's worktree`);
23859
24065
  if (relative2(realRoot, final).split(sep).includes(".git")) throw new PathRefusal(".git is not reachable from a periscope");
23860
24066
  return final;
@@ -23922,7 +24128,7 @@ function createPeriscopeMcpServer(d, sc) {
23922
24128
  const entries = (await readdir(abs, { withFileTypes: true })).filter((e) => e.name !== ".git");
23923
24129
  const lines2 = await Promise.all(entries.map(async (e) => {
23924
24130
  if (e.isDirectory()) return `[DIR] ${e.name}`;
23925
- const size = (await lstat(join52(abs, e.name))).size;
24131
+ const size = (await lstat(join53(abs, e.name))).size;
23926
24132
  return `[FILE] ${e.name} (${size} bytes)`;
23927
24133
  }));
23928
24134
  return text(lines2.join("\n") || "(empty)");
@@ -23947,7 +24153,7 @@ function createPeriscopeMcpServer(d, sc) {
23947
24153
  if (Buffer.byteLength(content) > MAX_WRITE_BYTES) return text(`content exceeds ${MAX_WRITE_BYTES} bytes`, true);
23948
24154
  const abs = await inTree(path);
23949
24155
  if (abs === await realpath(root)) return text("path must name a file", true);
23950
- await mkdir(dirname31(abs), { recursive: true });
24156
+ await mkdir(dirname32(abs), { recursive: true });
23951
24157
  await inTree(path);
23952
24158
  await writeFile(abs, content);
23953
24159
  return text(`Wrote ${Buffer.byteLength(content)} bytes to ${await rel(abs)}`);
@@ -23987,8 +24193,8 @@ function createPeriscopeMcpServer(d, sc) {
23987
24193
  inputSchema: { path: z.string() },
23988
24194
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
23989
24195
  }, async ({ path }) => {
23990
- const parent = await inTree(dirname31(path) === "" ? "." : dirname31(path));
23991
- const abs = join52(parent, basename15(path));
24196
+ const parent = await inTree(dirname32(path) === "" ? "." : dirname32(path));
24197
+ const abs = join53(parent, basename15(path));
23992
24198
  if (basename15(path) === ".git" || abs === parent) return text("path must name a file", true);
23993
24199
  const st = await lstat(abs).catch(() => void 0);
23994
24200
  if (!st) return text(`${path} does not exist`, true);
@@ -24109,7 +24315,7 @@ function findPeriscope(sidecars, segment) {
24109
24315
  }
24110
24316
 
24111
24317
  // src/cli.ts
24112
- var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname32(process.execPath) : resolve7(dirname32(fileURLToPath(import.meta.url)), "..");
24318
+ var CREW_HOME = isCompiledBinary(import.meta.url) ? dirname33(process.execPath) : resolve7(dirname33(fileURLToPath(import.meta.url)), "..");
24113
24319
  var RELEASE_LOCK_TTL_MS = 20 * 60 * 1e3;
24114
24320
  var CYCLE_SECONDS2 = 120;
24115
24321
  var STALE_CYCLES = 3;
@@ -24367,6 +24573,7 @@ if (!command) usage(argv.length ? `unknown option "${argv[0]}"` : void 0);
24367
24573
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
24368
24574
  "passenger-sync-daemon",
24369
24575
  "periscope-proxy",
24576
+ "intel",
24370
24577
  "agents",
24371
24578
  "connect",
24372
24579
  "credential",
@@ -24478,7 +24685,7 @@ if (command === "provision") {
24478
24685
  process.exit(outcome.code);
24479
24686
  }
24480
24687
  if (command === "pool-member") {
24481
- const pool = poolConfigFrom(argv, process.env, () => readFileSync44(0, "utf8"));
24688
+ const pool = poolConfigFrom(argv, process.env, () => readFileSync45(0, "utf8"));
24482
24689
  if (typeof pool === "string") {
24483
24690
  process.stderr.write(`crew pool-member: ${pool}
24484
24691
  `);
@@ -24512,6 +24719,14 @@ if (command === "passenger-sync-daemon") {
24512
24719
  process.exit(2);
24513
24720
  }
24514
24721
  runProxyFromEnv(dropFile);
24722
+ } else if (command === "intel") {
24723
+ const [, sub, bridgeRoute, bridgeTicket] = positional;
24724
+ const stateDir = value("state-dir");
24725
+ if (sub !== "bridge" || !bridgeRoute || !bridgeTicket || !stateDir) {
24726
+ process.stderr.write("usage: crew intel bridge <route> <ticket number> --state-dir DIR (registered by a lane run, not run by hand)\n");
24727
+ process.exit(2);
24728
+ }
24729
+ await runBridge(stateDir, bridgeRoute, bridgeTicket);
24515
24730
  } else {
24516
24731
  let requireArmed = function(what) {
24517
24732
  if (dryRun || route2.enabled) return;
@@ -24713,7 +24928,7 @@ if (command === "passenger-sync-daemon") {
24713
24928
  } else {
24714
24929
  const toleratesNoRoutes = command === "credential" || command === "forge" || command === "repos" || command === "ship" || command === "disconnect" && flag("reset") || command === "deprovision" || command === "connect" && flag("sign-out");
24715
24930
  const configless = command === "connect" && flag("sign-out") || command === "disconnect" && flag("reset") || command === "deprovision";
24716
- if (configless && !existsSync49(findConfigFile(CREW_HOME))) {
24931
+ if (configless && !existsSync50(findConfigFile(CREW_HOME))) {
24717
24932
  cfg = loadConfig(CREW_HOME, firstRunConfigPath(), { text: "ship: {}\n", allowNoRoutes: true });
24718
24933
  } else {
24719
24934
  cfg = loadConfig(CREW_HOME, void 0, toleratesNoRoutes ? { allowNoRoutes: true } : {});
@@ -24728,7 +24943,7 @@ if (command === "passenger-sync-daemon") {
24728
24943
  throw e;
24729
24944
  }
24730
24945
  if (command === "credential") {
24731
- const r = runCredential(positional[1], readFileSync44(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
24946
+ const r = runCredential(positional[1], readFileSync45(0, "utf8"), cfg.ship.forges, cfg.ship.stateDir);
24732
24947
  process.stdout.write(r.out);
24733
24948
  process.stderr.write(r.err);
24734
24949
  process.exit(r.code);
@@ -24740,9 +24955,9 @@ if (command === "passenger-sync-daemon") {
24740
24955
  forges: cfg.ship.forges,
24741
24956
  stateDir: cfg.ship.stateDir,
24742
24957
  configFile: cfg.configFile,
24743
- readFile: (p) => readFileSync44(p, "utf8"),
24744
- writeFile: (p, t) => writeFileSync37(p, t),
24745
- stdin: () => readFileSync44(0, "utf8"),
24958
+ readFile: (p) => readFileSync45(p, "utf8"),
24959
+ writeFile: (p, t) => writeFileSync38(p, t),
24960
+ stdin: () => readFileSync45(0, "utf8"),
24746
24961
  env: process.env
24747
24962
  }
24748
24963
  );
@@ -25035,8 +25250,8 @@ if (command === "passenger-sync-daemon") {
25035
25250
  }
25036
25251
  if (host === "linux" && hasSystemd) {
25037
25252
  const label = labelFor(CREW_HOME, "run");
25038
- const staleTimer = join53(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25039
- if (existsSync49(staleTimer)) {
25253
+ const staleTimer = join54(homedir11(), ".config", "systemd", "user", `${label}.timer`);
25254
+ if (existsSync50(staleTimer)) {
25040
25255
  process.stdout.write(
25041
25256
  `removing stale run timer from before the persistent-service cutover: ${staleTimer}${dryRun ? " (dry run)" : ""}
25042
25257
  `
@@ -25063,7 +25278,7 @@ if (command === "passenger-sync-daemon") {
25063
25278
  const tracker = new Tracker(c, cfg.ship);
25064
25279
  problems.push(...validateContract(tracker.contract).map((p) => `contract: ${p}`));
25065
25280
  for (const r of await resolvedRepos(c)) {
25066
- if (!existsSync49(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
25281
+ if (!existsSync50(r.dir)) problems.push(`repo ${r.name}: ${r.dir} is missing on disk`);
25067
25282
  if (!satisfies(host, r.config.platform)) problems.push(`repo ${r.name}: requires ${r.config.platform}, this host is ${host}`);
25068
25283
  readiness.push(...validateEffective(r.config).map((p) => `repo ${r.name}: ${p}`));
25069
25284
  }
@@ -25090,7 +25305,7 @@ if (command === "passenger-sync-daemon") {
25090
25305
  }
25091
25306
  async function doctorFix(o) {
25092
25307
  const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
25093
- const unitExists = (p) => existsSync49(p);
25308
+ const unitExists = (p) => existsSync50(p);
25094
25309
  const crontab = o.host === "linux" ? spawnSync11("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
25095
25310
  const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run", installContext()), unitExists, crontab) : true;
25096
25311
  const plan = planFix({ routes, schedulerAbsent, host: o.host });
@@ -25107,7 +25322,7 @@ if (command === "passenger-sync-daemon") {
25107
25322
  for (const a of plan.actions) {
25108
25323
  if (a.kind === "enable") {
25109
25324
  if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
25110
- writeFileSync37(cfg.configFile, planEnable(readFileSync44(cfg.configFile, "utf8"), a.route));
25325
+ writeFileSync38(cfg.configFile, planEnable(readFileSync45(cfg.configFile, "utf8"), a.route));
25111
25326
  enabledSound.add(a.route);
25112
25327
  changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
25113
25328
  } else {
@@ -26246,7 +26461,7 @@ ${cu.tail.trimEnd()}
26246
26461
  process.exit(1);
26247
26462
  }
26248
26463
  requireArmed("run the daemon loop");
26249
- const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join53(CREW_HOME, "src"), join53(CREW_HOME, "dist"), join53(CREW_HOME, "bin")];
26464
+ const watchPaths = isCompiledBinary(import.meta.url) ? [process.execPath] : [join54(CREW_HOME, "src"), join54(CREW_HOME, "dist"), join54(CREW_HOME, "bin")];
26250
26465
  const configPaths = daemonConfigPaths(cfg);
26251
26466
  watchPaths.push(...configPaths);
26252
26467
  noteDaemonStarted(cfg.ship.stateDir);
@@ -26551,10 +26766,10 @@ ${cu.tail.trimEnd()}
26551
26766
  `);
26552
26767
  if (!dryRun) {
26553
26768
  const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
26554
- const raw = existsSync49(resolvedPath) ? JSON.parse(readFileSync44(resolvedPath, "utf8")) : {};
26769
+ const raw = existsSync50(resolvedPath) ? JSON.parse(readFileSync45(resolvedPath, "utf8")) : {};
26555
26770
  raw.agentSkills = result.agentSkills;
26556
- mkdirSync37(dirOf2(resolvedPath), { recursive: true });
26557
- writeFileSync37(resolvedPath, `${JSON.stringify(raw, null, 2)}
26771
+ mkdirSync38(dirOf2(resolvedPath), { recursive: true });
26772
+ writeFileSync38(resolvedPath, `${JSON.stringify(raw, null, 2)}
26558
26773
  `);
26559
26774
  }
26560
26775
  if (result.outcomes.some((o) => o.action === "diverged")) process.exitCode = 1;
@@ -26577,7 +26792,7 @@ ${cu.tail.trimEnd()}
26577
26792
  throw e;
26578
26793
  }
26579
26794
  const oldName = cfg.ship.name;
26580
- writeFileSync37(cfg.configFile, setShipNameInYaml(readFileSync44(cfg.configFile, "utf8"), newName));
26795
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync45(cfg.configFile, "utf8"), newName));
26581
26796
  cfg.ship.name = newName;
26582
26797
  let renameMachineId;
26583
26798
  try {
@@ -26624,14 +26839,14 @@ ${cu.tail.trimEnd()}
26624
26839
  process.exit(2);
26625
26840
  };
26626
26841
  if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
26627
- const configText = readFileSync44(cfg.configFile, "utf8");
26842
+ const configText = readFileSync45(cfg.configFile, "utf8");
26628
26843
  if (sub === "check") {
26629
26844
  try {
26630
26845
  const routeArg = value("route") ?? positional[2];
26631
26846
  const found = cfg.routes.find((r) => r.route === routeArg);
26632
26847
  if (!found) throw new ReposError(`no route "${routeArg}" in the config (have: ${cfg.routes.map((r) => r.route).join(", ") || "none"})`);
26633
26848
  const named2 = value("route") ? void 0 : positional[3];
26634
- const dir = named2 ? repoDirOf(configText, dirname32(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
26849
+ const dir = named2 ? repoDirOf(configText, dirname33(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
26635
26850
  const target = reposOf(found).find((t) => resolvePath(t.dir) === dir || t.name === named2);
26636
26851
  const o = target ? found.repoOverrides[target.name] : void 0;
26637
26852
  runReposCheck(dir, {
@@ -26654,9 +26869,9 @@ ${cu.tail.trimEnd()}
26654
26869
  if (sub === "hooks") {
26655
26870
  try {
26656
26871
  const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
26657
- const dir = repoDirOf(configText, dirname32(cfg.configFile), a.route, a.name);
26658
- if (!existsSync49(dir)) throw new ReposError(`${dir} does not exist on disk`);
26659
- const files = { exists: (r) => existsSync49(resolvePath(dir, r)), list: (r) => {
26872
+ const dir = repoDirOf(configText, dirname33(cfg.configFile), a.route, a.name);
26873
+ if (!existsSync50(dir)) throw new ReposError(`${dir} does not exist on disk`);
26874
+ const files = { exists: (r) => existsSync50(resolvePath(dir, r)), list: (r) => {
26660
26875
  try {
26661
26876
  return readdirSync20(resolvePath(dir, r));
26662
26877
  } catch {
@@ -26664,7 +26879,7 @@ ${cu.tail.trimEnd()}
26664
26879
  }
26665
26880
  }, read: (r) => {
26666
26881
  try {
26667
- return readFileSync44(resolvePath(dir, r), "utf8");
26882
+ return readFileSync45(resolvePath(dir, r), "utf8");
26668
26883
  } catch {
26669
26884
  return void 0;
26670
26885
  }
@@ -26706,19 +26921,19 @@ ${cu.tail.trimEnd()}
26706
26921
  let cleared;
26707
26922
  if (clear2) {
26708
26923
  const plan = clearRouteHooks(configText, a.route, a.name);
26709
- if (plan.removed.length > 0) writeFileSync37(cfg.configFile, plan.text);
26924
+ if (plan.removed.length > 0) writeFileSync38(cfg.configFile, plan.text);
26710
26925
  cleared = { file: cfg.configFile, removed: plan.removed };
26711
26926
  }
26712
26927
  if (write && suggestion.source === null && recipeIds.length === 0 && hookSpecs.length === 0) throw new ReposError(`nothing to write: ${suggestion.notes[0]}`);
26713
26928
  if (write === "route") {
26714
26929
  const plan = planRouteHooks(configText, a.route, a.name, picked.hooks, { replace: flag("replace") });
26715
- writeFileSync37(cfg.configFile, plan.text);
26930
+ writeFileSync38(cfg.configFile, plan.text);
26716
26931
  written = { target: "route", file: cfg.configFile, hooks: plan.written, kept: plan.kept, replaced: plan.replaced };
26717
26932
  } else if (write === "repo") {
26718
26933
  const have = existingRepoConfig(files);
26719
26934
  if (have) throw new ReposError(`${resolvePath(dir, have)} already exists; not overwriting it`);
26720
26935
  const file = resolvePath(dir, ".crew.yaml");
26721
- writeFileSync37(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
26936
+ writeFileSync38(file, starterRepoConfig(picked.hooks, picked.release), { flag: "wx" });
26722
26937
  written = { target: "repo", file, hooks: Object.keys(picked.hooks), kept: [], replaced: [] };
26723
26938
  }
26724
26939
  if (flag("json")) {
@@ -26774,7 +26989,7 @@ ${cu.tail.trimEnd()}
26774
26989
  if (sub === "list" && !routeName) usage2();
26775
26990
  if (sub === "add") {
26776
26991
  try {
26777
- const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync49(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
26992
+ const a = parseAddArgs(positional.slice(2), routeNamesOf(configText), (p) => existsSync50(resolvePath(p.replace(/^~(?=\/|$)/, homedir11()))));
26778
26993
  routeName = a.route;
26779
26994
  addPath = a.path;
26780
26995
  addName = a.name;
@@ -26804,7 +27019,7 @@ ${cu.tail.trimEnd()}
26804
27019
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, routeName);
26805
27020
  if (sub === "list" || sub === "add") {
26806
27021
  const liveRoute = cfg.routes.find((r) => r.route === routeName);
26807
- if (liveRoute?.resolved?.reposModelId && existsSync49(resolvedFile) && !dryRun) {
27022
+ if (liveRoute?.resolved?.reposModelId && existsSync50(resolvedFile) && !dryRun) {
26808
27023
  try {
26809
27024
  await hydrateApiKeys([liveRoute]);
26810
27025
  persistRepoRows(cfg.ship.stateDir, liveRoute.route, await new Tracker(liveRoute, cfg.ship).repoRowsForMap());
@@ -26813,16 +27028,16 @@ ${cu.tail.trimEnd()}
26813
27028
  }
26814
27029
  }
26815
27030
  let resolvedRemotes;
26816
- if (existsSync49(resolvedFile)) {
27031
+ if (existsSync50(resolvedFile)) {
26817
27032
  try {
26818
- resolvedRemotes = JSON.parse(readFileSync44(resolvedFile, "utf8"));
27033
+ resolvedRemotes = JSON.parse(readFileSync45(resolvedFile, "utf8"));
26819
27034
  } catch {
26820
27035
  }
26821
27036
  }
26822
27037
  const originOf = (dir) => gitOk(dir, ["remote", "get-url", "origin"]) ?? void 0;
26823
27038
  const text2 = configText;
26824
- const base = dirname32(cfg.configFile);
26825
- const isCheckout = (d) => existsSync49(`${d}/.git`);
27039
+ const base = dirname33(cfg.configFile);
27040
+ const isCheckout = (d) => existsSync50(`${d}/.git`);
26826
27041
  try {
26827
27042
  if (sub === "list") {
26828
27043
  if (flag("json")) {
@@ -26832,11 +27047,11 @@ ${cu.tail.trimEnd()}
26832
27047
  base,
26833
27048
  resolved: resolvedRemotes,
26834
27049
  originOf,
26835
- exists: (d) => existsSync49(d),
27050
+ exists: (d) => existsSync50(d),
26836
27051
  isCheckout,
26837
27052
  repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
26838
27053
  try {
26839
- return Object.keys(parseRepoConfig(readFileSync44(resolvePath(d, f), "utf8"), f).hooks).length > 0;
27054
+ return Object.keys(parseRepoConfig(readFileSync45(resolvePath(d, f), "utf8"), f).hooks).length > 0;
26840
27055
  } catch {
26841
27056
  return false;
26842
27057
  }
@@ -26853,7 +27068,7 @@ ${cu.tail.trimEnd()}
26853
27068
  for (const r of repos) {
26854
27069
  const origin2 = originOf(r.dir);
26855
27070
  const row = matchRepoName(origin2, resolvedRemotes);
26856
- const note = !existsSync49(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
27071
+ const note = !existsSync50(r.dir) ? "missing on disk" : row ? `Repos row: ${row}` : `no Repos row matches ${normalizeRemote(origin2) ?? "this checkout (no origin remote)"}`;
26857
27072
  process.stdout.write(`${r.name} ${r.dir} ${note}
26858
27073
  `);
26859
27074
  }
@@ -26865,9 +27080,9 @@ ${cu.tail.trimEnd()}
26865
27080
  ` : `${human}
26866
27081
  `);
26867
27082
  const plan2 = planRepoClone({ text: text2, base, route: routeName, name: addName, resolved: resolvedRemotes, dir: value("dir"), isCheckout });
26868
- if (existsSync49(plan2.target) && (existsSync49(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
27083
+ if (existsSync50(plan2.target) && (existsSync50(`${plan2.target}/.git`) || readdirSync20(plan2.target).length > 0)) {
26869
27084
  throw new ReposError(
26870
- existsSync49(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
27085
+ existsSync50(`${plan2.target}/.git`) ? `${plan2.target} is already a checkout \u2014 attach it with \`crew repos add ${routeName} ${plan2.name} ${plan2.target}\`` : `${plan2.target} exists and is not empty \u2014 pass --dir to clone somewhere else`
26871
27086
  );
26872
27087
  }
26873
27088
  if (dryRun) {
@@ -26885,14 +27100,14 @@ ${cu.tail.trimEnd()}
26885
27100
  throw new ReposError(`${e.message}${authHint}
26886
27101
  crew.yaml was not changed`);
26887
27102
  }
26888
- writeFileSync37(cfg.configFile, plan2.text);
27103
+ writeFileSync38(cfg.configFile, plan2.text);
26889
27104
  if (plan2.droppedDir) process.stderr.write(`crew repos: dropped dir: ${plan2.droppedDir} from route ${routeName} \u2014 it is not a checkout (the clone base); checkouts now live under repos:
26890
27105
  `);
26891
27106
  say("attached", { route: routeName, name: plan2.name, dir: plan2.target, mode: plan2.mode }, `cloned and attached ${plan2.target} to ${routeName} as ${plan2.mode === "dir" ? "dir" : `repos.${plan2.name}`} in ${cfg.configFile}`);
26892
27107
  break;
26893
27108
  }
26894
27109
  const target = resolvePath(addPath.replace(/^~(?=\/|$)/, homedir11()));
26895
- const top = existsSync49(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
27110
+ const top = existsSync50(target) ? gitOk(target, ["rev-parse", "--show-toplevel"]) : null;
26896
27111
  if (!top) throw new ReposError(`${target} is not a git checkout`);
26897
27112
  const origin = originOf(top);
26898
27113
  const plan = planRepoAdd({
@@ -26944,15 +27159,15 @@ ${cu.tail.trimEnd()}
26944
27159
  const created = await rowTracker.createRepoRow(rowPayload);
26945
27160
  process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
26946
27161
  `);
26947
- if (existsSync49(resolvedFile)) {
26948
- const raw = JSON.parse(readFileSync44(resolvedFile, "utf8"));
27162
+ if (existsSync50(resolvedFile)) {
27163
+ const raw = JSON.parse(readFileSync45(resolvedFile, "utf8"));
26949
27164
  raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
26950
27165
  raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
26951
- writeFileSync37(resolvedFile, `${JSON.stringify(raw, null, 2)}
27166
+ writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
26952
27167
  `);
26953
27168
  }
26954
27169
  }
26955
- writeFileSync37(cfg.configFile, plan.text);
27170
+ writeFileSync38(cfg.configFile, plan.text);
26956
27171
  process.stdout.write(`added ${top} to ${routeName} as ${plan.mode === "dir" ? "dir" : `repos.${plan.name}`} in ${cfg.configFile}
26957
27172
  `);
26958
27173
  } catch (e) {
@@ -27323,7 +27538,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27323
27538
  process.exit(2);
27324
27539
  }
27325
27540
  if (found.shipNameAdopted && !dryRun) {
27326
- writeFileSync37(cfg.configFile, setShipNameInYaml(readFileSync44(cfg.configFile, "utf8"), found.shipNameAdopted));
27541
+ writeFileSync38(cfg.configFile, setShipNameInYaml(readFileSync45(cfg.configFile, "utf8"), found.shipNameAdopted));
27327
27542
  cfg.ship.name = found.shipNameAdopted;
27328
27543
  }
27329
27544
  if (found.shipRow) {
@@ -27366,10 +27581,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27366
27581
  }
27367
27582
  }
27368
27583
  let previous;
27369
- const hadPreviousFile = existsSync49(resolvedPath);
27584
+ const hadPreviousFile = existsSync50(resolvedPath);
27370
27585
  if (hadPreviousFile) {
27371
27586
  try {
27372
- previous = JSON.parse(readFileSync44(resolvedPath, "utf8"));
27587
+ previous = JSON.parse(readFileSync45(resolvedPath, "utf8"));
27373
27588
  } catch {
27374
27589
  }
27375
27590
  }
@@ -27499,10 +27714,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27499
27714
  process.stderr.write(`(dry run) would write resolved ids to ${resolvedPath}
27500
27715
  `);
27501
27716
  } else {
27502
- mkdirSync37(dirname32(resolvedPath), { recursive: true });
27717
+ mkdirSync38(dirname33(resolvedPath), { recursive: true });
27503
27718
  if (hadPreviousFile) copyFileSync2(resolvedPath, `${resolvedPath}.bak`);
27504
27719
  const tmpPath = `${resolvedPath}.tmp${process.pid}`;
27505
- writeFileSync37(tmpPath, `${JSON.stringify(resolved, null, 2)}
27720
+ writeFileSync38(tmpPath, `${JSON.stringify(resolved, null, 2)}
27506
27721
  `);
27507
27722
  renameSync6(tmpPath, resolvedPath);
27508
27723
  process.stderr.write(`Resolved ids written to ${resolvedPath}
@@ -27548,8 +27763,8 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
27548
27763
  process.stderr.write(`(dry run) would write the minted API key to ${apiKeyFile}
27549
27764
  `);
27550
27765
  } else {
27551
- mkdirSync37(dirname32(apiKeyFile), { recursive: true });
27552
- writeFileSync37(apiKeyFile, `${apiKeyVar}=${apiKey}
27766
+ mkdirSync38(dirname33(apiKeyFile), { recursive: true });
27767
+ writeFileSync38(apiKeyFile, `${apiKeyVar}=${apiKey}
27553
27768
  `, { mode: 384 });
27554
27769
  process.stderr.write(`API key written to ${apiKeyFile}
27555
27770
  `);
@@ -28231,6 +28446,33 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28231
28446
  }
28232
28447
  const pathWt = applyRunWorktree(repoWt.dir, repoWt.config, planWt);
28233
28448
  emit.emit(describeRunWorktreePlan(planWt));
28449
+ if (pathWt && planIsUsable(planWt) && repoWt.config.hooks.intel) {
28450
+ const shellWt = resolveShell(repoWt.config.shell ?? cfg.ship.shell);
28451
+ const paragraph = await startWorktreeIntel({
28452
+ worktree: pathWt,
28453
+ route: routeWt.route,
28454
+ number: String(nWt),
28455
+ stateDir: cfg.ship.stateDir,
28456
+ ticketTag: String(ticketWt.issue_tag ?? ticketWt.issue_id ?? `ISSUE-${nWt}`),
28457
+ branch: planWt.branch ?? "",
28458
+ startupSeconds: cfg.ship.intelStartupSeconds,
28459
+ prepare: async (path) => {
28460
+ 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));
28465
+ }
28466
+ const setup = repoWt.config.hooks.setup;
28467
+ return setup ? runScript(setup, { cwd: path, shell: shellWt }) : null;
28468
+ },
28469
+ runHook: async (name, cwd, env) => {
28470
+ const script = repoWt.config.hooks[name];
28471
+ return script ? runScript(script, { cwd, env, shell: shellWt, timeoutMs: name === "intel" ? 6e4 : void 0 }) : null;
28472
+ }
28473
+ });
28474
+ emit.emit(paragraph);
28475
+ }
28234
28476
  if (pathWt) process.stdout.write(`${pathWt}
28235
28477
  `);
28236
28478
  if (!planIsUsable(planWt)) process.exitCode = 1;
@@ -28430,7 +28672,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
28430
28672
  const shellR = hit && resolveShell(hit.repo.config.shell ?? cfg.ship.shell);
28431
28673
  const resumed = await runPeriscopeResume({
28432
28674
  find: async () => hit && { row: hit.row, tag: hit.tag },
28433
- worktreeExists: (p) => existsSync49(p),
28675
+ worktreeExists: (p) => existsSync50(p),
28434
28676
  prepareDropFile,
28435
28677
  startProxy: (wt, url, n) => launchProxy(crewInvocation(CREW_HOME), wt, url, Number(n), { host: hit.row.host || "localhost", inject: hit.repo.config.periscope?.inspector !== "repo" }),
28436
28678
  caps: periscopeCaps(cfg.ship),
@@ -28526,10 +28768,10 @@ ${where.join("\n")}`);
28526
28768
  },
28527
28769
  prepare: async (path) => {
28528
28770
  for (const f of repoP.config.worktrees.copy) {
28529
- const src = join53(repoP.dir, f);
28530
- if (!existsSync49(src)) continue;
28531
- mkdirSync37(dirOf2(join53(path, f)), { recursive: true });
28532
- copyFileSync2(src, join53(path, f));
28771
+ const src = join54(repoP.dir, f);
28772
+ if (!existsSync50(src)) continue;
28773
+ mkdirSync38(dirOf2(join54(path, f)), { recursive: true });
28774
+ copyFileSync2(src, join54(path, f));
28533
28775
  }
28534
28776
  const setup = repoP.config.hooks.setup;
28535
28777
  return setup ? runScript(setup, { cwd: path, shell: shellP, onLine: (l) => {
@@ -28839,7 +29081,7 @@ ${where.join("\n")}`);
28839
29081
  };
28840
29082
  const droppedRoutes = [];
28841
29083
  try {
28842
- const rawCfg = parseYaml2(readFileSync44(cfg.configFile, "utf8"));
29084
+ const rawCfg = parseYaml2(readFileSync45(cfg.configFile, "utf8"));
28843
29085
  for (const r of rawCfg?.routes ?? []) {
28844
29086
  if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
28845
29087
  try {
@@ -28892,8 +29134,8 @@ ${where.join("\n")}`);
28892
29134
  process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
28893
29135
  break;
28894
29136
  }
28895
- mkdirSync37(cfg.ship.stateDir, { recursive: true });
28896
- writeFileSync37(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
29137
+ mkdirSync38(cfg.ship.stateDir, { recursive: true });
29138
+ writeFileSync38(sentinel, `${(/* @__PURE__ */ new Date()).toISOString()}
28897
29139
  `);
28898
29140
  process.stdout.write(
28899
29141
  "crew reload: requested \u2014 the daemon relaunches with the new config once its running agents end (it starts no new work meanwhile); if it is busy, `crew reload --now` forces the relaunch and interrupts them\n"
@@ -28958,7 +29200,7 @@ ${where.join("\n")}`);
28958
29200
  }
28959
29201
  case "log": {
28960
29202
  try {
28961
- const text2 = readFileSync44(cfg.ship.logFile, "utf8").split("\n");
29203
+ const text2 = readFileSync45(cfg.ship.logFile, "utf8").split("\n");
28962
29204
  process.stdout.write(`${text2.slice(-(Number(value("tail")) || 40)).join("\n")}
28963
29205
  `);
28964
29206
  } catch {
@@ -28981,8 +29223,8 @@ ${where.join("\n")}`);
28981
29223
  };
28982
29224
  if (!route2.resolved) fail2(`route ${route2.route} is not connected \u2014 run \`crew connect\` first`);
28983
29225
  const tracker = new Tracker(route2, cfg.ship);
28984
- const configText = readFileSync44(cfg.configFile, "utf8");
28985
- const cfgBase = dirname32(cfg.configFile);
29226
+ const configText = readFileSync45(cfg.configFile, "utf8");
29227
+ const cfgBase = dirname33(cfg.configFile);
28986
29228
  const configured2 = configuredRepos(configText, route2.route, cfgBase);
28987
29229
  const dir = configured2.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured2.map((r) => r.dir), route2.reposBasePath, route2.route.split("/")[0]);
28988
29230
  const resolvedFile = resolvedPathFor(cfg.ship.stateDir, route2.route);
@@ -29010,7 +29252,7 @@ ${where.join("\n")}`);
29010
29252
  }
29011
29253
  if (configured2.some((r) => r.dir === dir)) {
29012
29254
  try {
29013
- writeFileSync37(cfg.configFile, planRepoRemove(configText, cfgBase, route2.route, dir).text);
29255
+ writeFileSync38(cfg.configFile, planRepoRemove(configText, cfgBase, route2.route, dir).text);
29014
29256
  } catch (e) {
29015
29257
  if (e instanceof ReposError) fail2(e.message);
29016
29258
  throw e;
@@ -29018,29 +29260,29 @@ ${where.join("\n")}`);
29018
29260
  }
29019
29261
  if (row) {
29020
29262
  await tracker.deleteRepoRow(row.id);
29021
- if (existsSync49(resolvedFile)) {
29022
- const raw = JSON.parse(readFileSync44(resolvedFile, "utf8"));
29263
+ if (existsSync50(resolvedFile)) {
29264
+ const raw = JSON.parse(readFileSync45(resolvedFile, "utf8"));
29023
29265
  if (raw.repoNames) delete raw.repoNames[row.id];
29024
29266
  if (raw.repoRemotes) delete raw.repoRemotes[row.id];
29025
- writeFileSync37(resolvedFile, `${JSON.stringify(raw, null, 2)}
29267
+ writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
29026
29268
  `);
29027
29269
  }
29028
29270
  }
29029
- if (existsSync49(join53(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync24(dir, { recursive: true, force: true });
29271
+ if (existsSync50(join54(dir, ".git")) && basename16(dir) === SAMPLE_REPO) rmSync25(dir, { recursive: true, force: true });
29030
29272
  say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
29031
29273
  break;
29032
29274
  }
29033
29275
  const projectId = route2.resolved.areaId ?? resolveRepoProject(await tracker.projectRows(), value("project"));
29034
- const fresh = !existsSync49(join53(dir, ".git"));
29035
- if (existsSync49(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
29276
+ const fresh = !existsSync50(join54(dir, ".git"));
29277
+ if (existsSync50(dir) && fresh && readdirSync20(dir).length > 0) fail2(`${dir} exists and is not a git checkout \u2014 move it aside`);
29036
29278
  if (dryRun) {
29037
29279
  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`);
29038
29280
  break;
29039
29281
  }
29040
29282
  if (fresh) {
29041
- mkdirSync37(dir, { recursive: true });
29042
- writeFileSync37(join53(dir, "README.md"), SAMPLE_README);
29043
- writeFileSync37(join53(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
29283
+ mkdirSync38(dir, { recursive: true });
29284
+ writeFileSync38(join54(dir, "README.md"), SAMPLE_README);
29285
+ writeFileSync38(join54(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
29044
29286
  const g = (...a) => {
29045
29287
  const r = spawnSync11("git", a, { cwd: dir, encoding: "utf8" });
29046
29288
  if (r.status !== 0) fail2(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
@@ -29058,17 +29300,17 @@ ${where.join("\n")}`);
29058
29300
  spec: { platform: "unix", releaseMode: "integrate", ciProvider: "none" }
29059
29301
  });
29060
29302
  repoId = (await tracker.createRepoRow(payload)).id;
29061
- if (existsSync49(resolvedFile)) {
29062
- const raw = JSON.parse(readFileSync44(resolvedFile, "utf8"));
29303
+ if (existsSync50(resolvedFile)) {
29304
+ const raw = JSON.parse(readFileSync45(resolvedFile, "utf8"));
29063
29305
  raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
29064
29306
  raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
29065
- writeFileSync37(resolvedFile, `${JSON.stringify(raw, null, 2)}
29307
+ writeFileSync38(resolvedFile, `${JSON.stringify(raw, null, 2)}
29066
29308
  `);
29067
29309
  }
29068
29310
  }
29069
29311
  if (!configured2.some((r) => r.dir === dir)) {
29070
29312
  try {
29071
- writeFileSync37(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route2.route, path: dir, name: SAMPLE_REPO }).text);
29313
+ writeFileSync38(cfg.configFile, planRepoAdd({ text: configText, base: cfgBase, route: route2.route, path: dir, name: SAMPLE_REPO }).text);
29072
29314
  } catch (e) {
29073
29315
  if (e instanceof ReposError) fail2(e.message);
29074
29316
  throw e;
@@ -29414,7 +29656,7 @@ watermark: ${state.watermark()}
29414
29656
  }
29415
29657
  for (const r of repos) {
29416
29658
  const remote = r.config.branch.remote;
29417
- if (existsSync49(r.dir)) {
29659
+ if (existsSync50(r.dir)) {
29418
29660
  pushAuth.push({
29419
29661
  route: c.route,
29420
29662
  repo: r.name,
@@ -29509,7 +29751,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29509
29751
  }
29510
29752
  try {
29511
29753
  const evFile = eventFileFor(cfg.ship.stateDir);
29512
- const evSize = existsSync49(evFile) ? statSync16(evFile).size : 0;
29754
+ const evSize = existsSync50(evFile) ? statSync16(evFile).size : 0;
29513
29755
  process.stdout.write(
29514
29756
  evSize > cfg.ship.eventsRotateBytes * 4 ? `events log: WARN ${evFile} is ${Math.round(evSize / 1048576)} MB (> 4x eventsRotateBytes); run \`crew reap\` to rotate it
29515
29757
  ` : "events log: ok\n"
@@ -29520,7 +29762,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
29520
29762
  const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run", installContext());
29521
29763
  const abandons = unitAbandonsAgents(unitPlan, (p) => {
29522
29764
  try {
29523
- return readFileSync44(p, "utf8");
29765
+ return readFileSync45(p, "utf8");
29524
29766
  } catch {
29525
29767
  return null;
29526
29768
  }