squadrant 0.16.3 → 0.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1313,6 +1313,9 @@ function stampAttempt(rec, patch, now) {
1313
1313
  attempts.push(last);
1314
1314
  return { ...rec, attempts };
1315
1315
  }
1316
+ function isStickyAttention(state) {
1317
+ return state === "blocked" || state === "review";
1318
+ }
1316
1319
  function nextPendingTool(current, ev, now) {
1317
1320
  if (ev.note === "agent.hook.PreToolUse")
1318
1321
  return { name: ev.tool ?? "tool", since: now };
@@ -1341,7 +1344,7 @@ function reduce(rec, ev, now) {
1341
1344
  };
1342
1345
  case "task.progress": {
1343
1346
  const pendingTool = nextPendingTool(rec.pendingTool, ev, now);
1344
- if (rec.state === "blocked")
1347
+ if (isStickyAttention(rec.state))
1345
1348
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type, pendingTool };
1346
1349
  const b = { ...base, pendingTool };
1347
1350
  if (rec.state === "awaiting-input" || rec.state === "stalled")
@@ -1349,7 +1352,7 @@ function reduce(rec, ev, now) {
1349
1352
  return stampAttempt(b, {}, now);
1350
1353
  }
1351
1354
  case "heartbeat":
1352
- if (rec.state === "blocked")
1355
+ if (isStickyAttention(rec.state))
1353
1356
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
1354
1357
  if (rec.state === "awaiting-input")
1355
1358
  return { ...base, state: "working" };
@@ -1358,7 +1361,12 @@ function reduce(rec, ev, now) {
1358
1361
  if (rec.state === "blocked")
1359
1362
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
1360
1363
  return { ...base, state: "blocked", question: ev.question, pendingTool: void 0 };
1364
+ case "task.review":
1365
+ return { ...base, state: "review", reviewNote: ev.message, pendingTool: void 0 };
1361
1366
  case "task.done":
1367
+ if (rec.state === "review" && ev.source !== "approve") {
1368
+ return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
1369
+ }
1362
1370
  return { ...base, state: "done", resultRef: ev.resultRef, parseWarning: ev.parseWarning };
1363
1371
  case "task.failed":
1364
1372
  return { ...base, state: "failed", error: ev.error, exitCode: ev.exitCode };
@@ -1371,7 +1379,7 @@ function reduce(rec, ev, now) {
1371
1379
  case "task.turn.started":
1372
1380
  return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0 };
1373
1381
  case "task.turn.completed":
1374
- if (rec.state === "blocked")
1382
+ if (isStickyAttention(rec.state))
1375
1383
  return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
1376
1384
  if (rec.pendingTool)
1377
1385
  return stampAttempt(base, {}, now);
@@ -1454,6 +1462,10 @@ function formatMessage(rec, event) {
1454
1462
  }
1455
1463
  case "blocked":
1456
1464
  return `CREW BLOCKED ${tag}: ${(rec.question ?? "(no question)").trim()}`;
1465
+ case "review": {
1466
+ const note = (rec.reviewNote ?? "").trim();
1467
+ return `CREW REVIEW ${tag}: ${note || "ready for review"} \u2014 run 'squadrant diff ${rec.project} ${rec.name ?? rec.id}' then 'squadrant crew approve' or send feedback.`;
1468
+ }
1457
1469
  case "failed":
1458
1470
  return `CREW FAILED ${tag}: ${(rec.error ?? "(no error)").trim()}`;
1459
1471
  case "stalled": {
@@ -1829,14 +1841,15 @@ var init_reduce = __esm({
1829
1841
  DEFAULT_TASK_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
1830
1842
  TERMINAL_RECORD_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
1831
1843
  TERMINAL_RECORD_KEEP_PER_PROJECT = 20;
1832
- ATTENTION_STATES = /* @__PURE__ */ new Set(["done", "blocked", "failed", "stalled", "awaiting-input"]);
1833
- REAPABLE_SURFACE_STATES = /* @__PURE__ */ new Set(["working", "stalled", "awaiting-input", "blocked"]);
1844
+ ATTENTION_STATES = /* @__PURE__ */ new Set(["done", "blocked", "review", "failed", "stalled", "awaiting-input"]);
1845
+ REAPABLE_SURFACE_STATES = /* @__PURE__ */ new Set(["working", "stalled", "awaiting-input", "blocked", "review"]);
1834
1846
  IDLE_DEBOUNCE_MS = 12e3;
1835
1847
  KNOWN_EVENT_TYPES = /* @__PURE__ */ new Set([
1836
1848
  "task.started",
1837
1849
  "task.progress",
1838
1850
  "heartbeat",
1839
1851
  "task.blocked",
1852
+ "task.review",
1840
1853
  "task.done",
1841
1854
  "task.failed",
1842
1855
  "task.session",
@@ -2332,13 +2345,14 @@ function projectHealth(input) {
2332
2345
  const { project, now, captainName, captainStopped, commandPresent, crews } = input;
2333
2346
  const out = [];
2334
2347
  const captainState = input.captainState ?? (captainStopped === true ? "stopped" : captainStopped === false ? "alive" : "unknown");
2348
+ const deferral = input.captainDeferral;
2335
2349
  out.push({
2336
2350
  kind: "captain",
2337
2351
  project,
2338
2352
  ref: captainName,
2339
2353
  state: captainState,
2340
2354
  lastSeenMs: null,
2341
- detail: captainState === "stopped" ? "captain workspace closed \u2014 crews reaped; delivery paused" : captainState === "gone" ? "captain process died (crash) \u2014 crews reaped" : void 0
2355
+ detail: captainState === "stopped" ? "captain workspace closed \u2014 crews reaped; delivery paused" : captainState === "gone" ? "captain process died (crash) \u2014 crews reaped" : deferral?.stuck ? `\u26A0\uFE0F delivery stuck (${deferral.maxDeferCount}+ retries) \u2014 draft/ghost text blocking captain pane; input never touched, delivers automatically once cleared` : void 0
2342
2356
  });
2343
2357
  if (commandPresent !== null) {
2344
2358
  out.push({
@@ -3005,6 +3019,8 @@ function buildContext(opts) {
3005
3019
  opencodeBridge: null,
3006
3020
  cmuxEventsBridge: null,
3007
3021
  telegramBridge: void 0,
3022
+ notifyFault: opts.notifyFault ?? (() => {
3023
+ }),
3008
3024
  lifecycleSources: opts.lifecycleSources ?? [],
3009
3025
  broadcast: () => {
3010
3026
  },
@@ -3345,7 +3361,7 @@ var init_captain_delivery = __esm({
3345
3361
  const seq = entry.seq;
3346
3362
  const deferCount = this.deferCounts.get(seq) ?? 0;
3347
3363
  const stable = (this.stableCounts.get(seq) ?? 0) >= this.opts.stableProbePolls;
3348
- const probe = stable || deferCount >= this.opts.maxDefers;
3364
+ const probe = stable;
3349
3365
  try {
3350
3366
  await send(msg, probe ? { probe: true } : void 0);
3351
3367
  this.deferCounts.delete(seq);
@@ -3469,7 +3485,9 @@ async function runLivenessTick(deps) {
3469
3485
  }
3470
3486
  }
3471
3487
  function createDelivery(ctx, daemonCmux) {
3472
- const { stateRoot, store, log, livenessRegistry, isPidAlive, opts } = ctx;
3488
+ const { stateRoot, store, log, livenessRegistry, isPidAlive, opts, telegramBridge } = ctx;
3489
+ const notifyFault = ctx.notifyFault ?? (() => {
3490
+ });
3473
3491
  const defaultNotify = async (args) => {
3474
3492
  try {
3475
3493
  await appendToMailbox({
@@ -3492,6 +3510,7 @@ function createDelivery(ctx, daemonCmux) {
3492
3510
  const cfg = loadConfig();
3493
3511
  const deliveries = /* @__PURE__ */ new Map();
3494
3512
  const deliveryStats = (project) => deliveries.get(project)?.stats();
3513
+ const stuckNotified = /* @__PURE__ */ new Set();
3495
3514
  const sessionStartMs = Date.now();
3496
3515
  let delivering = false;
3497
3516
  const deliveryCore = async () => {
@@ -3563,6 +3582,18 @@ function createDelivery(ctx, daemonCmux) {
3563
3582
  break;
3564
3583
  }
3565
3584
  }
3585
+ const stuck = d.stats().stuck;
3586
+ if (stuck && !stuckNotified.has(project)) {
3587
+ stuckNotified.add(project);
3588
+ const { maxDeferCount } = d.stats();
3589
+ log(`delivery stuck project=${project} deferCount=${maxDeferCount}`);
3590
+ const text = `\u26A0\uFE0F DELIVERY STUCK: an in-progress draft (or ghost text) in your input box has blocked pending notification(s) for ${maxDeferCount}+ retries. Your input is never touched \u2014 this keeps retrying safely and will deliver automatically once you submit or clear it.`;
3591
+ appendCaptainMessage({ stateRoot, project, text, source: "daemon" }).catch((e) => log(`delivery stuck alert failed project=${project}: ${e.message}`));
3592
+ Promise.resolve(notifyFault(project, text)).catch((e) => log(`delivery stuck fault-notify failed project=${project}: ${e.message}`));
3593
+ telegramBridge?.pushRaw(project, text);
3594
+ } else if (!stuck && stuckNotified.has(project)) {
3595
+ stuckNotified.delete(project);
3596
+ }
3566
3597
  }
3567
3598
  };
3568
3599
  const deliveryTick = async () => {
@@ -3839,7 +3870,12 @@ function startDaemon(ctx, opts, pkgVersion) {
3839
3870
  captainStopped: null,
3840
3871
  captainState: deriveCaptainState(capEntry),
3841
3872
  commandPresent: null,
3842
- crews: store.list(project)
3873
+ crews: store.list(project),
3874
+ // #579/#484 Gap 3: surface the same deferral stats already exposed to
3875
+ // the snapshot (line ~135 below) on the health row too, so `squadrant
3876
+ // doctor` / `squadrant status --detailed` show a stuck delivery with
3877
+ // zero configuration.
3878
+ captainDeferral: deliveryStats(project)
3843
3879
  }));
3844
3880
  }
3845
3881
  return out;
@@ -4286,8 +4322,12 @@ var init_commands = __esm({
4286
4322
  build: (a) => a[0] ? ok("crews", ["crew", "list", a[0]]) : usage("crews", "usage: /crews <project>")
4287
4323
  },
4288
4324
  launch: {
4325
+ // --headless (#586, same reason as #520 on the boot-if-down path): runCommand
4326
+ // execs this argv from the daemon, which has no CMUX_WORKSPACE_ID and no
4327
+ // terminal — a plain `launch` would open the cmux GUI app and exit 0 before
4328
+ // the workspace is ever launched.
4289
4329
  usage: "/launch <project>",
4290
- build: (a) => a[0] ? ok("launch", ["launch", a[0]]) : usage("launch", "usage: /launch <project>")
4330
+ build: (a) => a[0] ? ok("launch", ["launch", a[0], "--headless"]) : usage("launch", "usage: /launch <project>")
4291
4331
  },
4292
4332
  effort: {
4293
4333
  usage: "/effort [max|balance|low]",
@@ -4461,6 +4501,9 @@ ${ev.message}` : "");
4461
4501
  case "task.blocked":
4462
4502
  return `\u{1F6A7} [${project}] CREW BLOCKED \xB7 ${ev.id}
4463
4503
  ${ev.question}`;
4504
+ case "task.review":
4505
+ return `\u{1F440} [${project}] CREW REVIEW \xB7 ${ev.id}` + (ev.message ? `
4506
+ ${ev.message}` : "");
4464
4507
  case "task.idle":
4465
4508
  return `\u{1F4A4} [${project}] CREW IDLE \xB7 ${ev.id}`;
4466
4509
  case "task.failed":
@@ -4693,6 +4736,7 @@ var init_tiers = __esm({
4693
4736
  ALERTS = /* @__PURE__ */ new Set([
4694
4737
  ...DONE_ONLY,
4695
4738
  "task.blocked",
4739
+ "task.review",
4696
4740
  "task.approval.requested",
4697
4741
  "task.input.requested",
4698
4742
  "task.timeout"
@@ -4740,6 +4784,14 @@ function createTelegramBridge(opts) {
4740
4784
  s.offset = next;
4741
4785
  saveState(stateRoot, s);
4742
4786
  }
4787
+ async function sendToTopic(project, text) {
4788
+ let threadId = loadState(stateRoot).topics[topicKey(project)];
4789
+ if (threadId === void 0) {
4790
+ threadId = await client.createForumTopic(cfg.supergroupId, topicName(project));
4791
+ setTopic(stateRoot, project, threadId);
4792
+ }
4793
+ await client.sendMessage(cfg.supergroupId, threadId, text);
4794
+ }
4743
4795
  async function deliverOutbound(project, ev) {
4744
4796
  const resolved = resolveNotify(cfg.notify, loadProjectOverride(project, configRoot));
4745
4797
  const live = loadState(stateRoot).notify[project];
@@ -4748,12 +4800,10 @@ function createTelegramBridge(opts) {
4748
4800
  return;
4749
4801
  if (!tierIncludes(resolved.crew, ev.type))
4750
4802
  return;
4751
- let threadId = loadState(stateRoot).topics[topicKey(project)];
4752
- if (threadId === void 0) {
4753
- threadId = await client.createForumTopic(cfg.supergroupId, topicName(project));
4754
- setTopic(stateRoot, project, threadId);
4755
- }
4756
- await client.sendMessage(cfg.supergroupId, threadId, formatLifecycle(project, ev));
4803
+ await sendToTopic(project, formatLifecycle(project, ev));
4804
+ }
4805
+ async function deliverRawOutbound(project, text) {
4806
+ await sendToTopic(project, text);
4757
4807
  }
4758
4808
  function resolveLiveNotify(project) {
4759
4809
  const resolved = resolveNotify(cfg.notify, loadProjectOverride(project, configRoot));
@@ -5061,6 +5111,11 @@ function createTelegramBridge(opts) {
5061
5111
  log(`telegram outbound failed project=${project}: ${e.message}`);
5062
5112
  });
5063
5113
  },
5114
+ pushRaw(project, text) {
5115
+ void deliverRawOutbound(project, text).catch((e) => {
5116
+ log(`telegram raw push failed project=${project}: ${e.message}`);
5117
+ });
5118
+ },
5064
5119
  health() {
5065
5120
  return { polling: running, lastSuccessfulPollAt, lastError, lastErrorAt };
5066
5121
  }
@@ -5898,7 +5953,7 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
5898
5953
  if (task) {
5899
5954
  if (TERMINAL_STATES.has(task.state)) {
5900
5955
  await deps.emitEvent(project, { type: "task.reopened", id: task.id });
5901
- } else if (task.state === "blocked" || task.state === "awaiting-input") {
5956
+ } else if (task.state === "blocked" || task.state === "awaiting-input" || task.state === "review") {
5902
5957
  await deps.emitEvent(project, { type: "task.started", id: task.id });
5903
5958
  }
5904
5959
  }
@@ -6243,6 +6298,18 @@ function cmux(args) {
6243
6298
  );
6244
6299
  });
6245
6300
  }
6301
+ function cmuxStdin(args, input) {
6302
+ return new Promise((resolve3, reject) => {
6303
+ const child = execFile2(resolveCmuxBin(), args, { encoding: "utf-8", timeout: CMUX_TIMEOUT, env: { ...process.env, CMUX_QUIET: "1" } }, (err, stdout) => {
6304
+ if (err) {
6305
+ reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
6306
+ return;
6307
+ }
6308
+ resolve3(stdout.trim());
6309
+ });
6310
+ child.stdin.end(input);
6311
+ });
6312
+ }
6246
6313
  function parseList(output) {
6247
6314
  let parsed;
6248
6315
  try {
@@ -6642,6 +6709,37 @@ function createCmuxDriver() {
6642
6709
  }
6643
6710
  throw new DeferDelivery(draft);
6644
6711
  },
6712
+ async showDiff(opts) {
6713
+ const source = opts.source ?? "branch";
6714
+ const args = ["diff"];
6715
+ if (source === "staged") {
6716
+ args.push("--staged");
6717
+ } else if (source === "unstaged") {
6718
+ args.push("--unstaged");
6719
+ } else {
6720
+ args.push("--branch", "--base", opts.base);
6721
+ if (opts.lastTurn)
6722
+ args.push("--last-turn");
6723
+ }
6724
+ args.push("--cwd", opts.cwd, "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split");
6725
+ if (opts.title)
6726
+ args.push("--title", opts.title);
6727
+ if (opts.focus === false)
6728
+ args.push("--no-focus");
6729
+ else
6730
+ args.push("--focus", "true");
6731
+ await cmux(args);
6732
+ },
6733
+ async showPatch(opts) {
6734
+ const args = ["diff", "-", "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split"];
6735
+ if (opts.title)
6736
+ args.push("--title", opts.title);
6737
+ if (opts.focus === false)
6738
+ args.push("--no-focus");
6739
+ else
6740
+ args.push("--focus", "true");
6741
+ await cmuxStdin(args, opts.patch);
6742
+ },
6645
6743
  async listSurfaces(workspaceId) {
6646
6744
  let output;
6647
6745
  try {
@@ -6738,7 +6836,8 @@ var init_runtimes = __esm({
6738
6836
  });
6739
6837
 
6740
6838
  // packages/workspaces/dist/notifiers/cmux.js
6741
- import { execFileSync as execFileSync6, execSync as execSync2 } from "child_process";
6839
+ import { execFile as execFileCb, execSync as execSync2 } from "child_process";
6840
+ import { promisify as promisify2 } from "util";
6742
6841
  function createCmuxNotifier(_scope) {
6743
6842
  return {
6744
6843
  name: "cmux",
@@ -6755,13 +6854,15 @@ function createCmuxNotifier(_scope) {
6755
6854
  }
6756
6855
  },
6757
6856
  async notify(message) {
6758
- execFileSync6("squadrant", ["runtime", "send", "--command", message], { encoding: "utf-8", timeout: CMUX_TIMEOUT });
6857
+ await execFile3("squadrant", ["runtime", "send", "--command", message], { encoding: "utf-8", timeout: CMUX_TIMEOUT });
6759
6858
  }
6760
6859
  };
6761
6860
  }
6861
+ var execFile3;
6762
6862
  var init_cmux2 = __esm({
6763
6863
  "packages/workspaces/dist/notifiers/cmux.js"() {
6764
6864
  init_cmux();
6865
+ execFile3 = promisify2(execFileCb);
6765
6866
  }
6766
6867
  });
6767
6868
 
@@ -9907,7 +10008,7 @@ var init_require_daemon = __esm({
9907
10008
  // packages/cli/src/index.ts
9908
10009
  init_dist();
9909
10010
  init_dist2();
9910
- import { Command as Command31 } from "commander";
10011
+ import { Command as Command32 } from "commander";
9911
10012
  import { readFileSync as readFileSync15, existsSync as existsSync12, writeFileSync as writeFileSync11 } from "fs";
9912
10013
  import { fileURLToPath as fileURLToPath6 } from "url";
9913
10014
  import { dirname as dirname9, join as join29 } from "path";
@@ -10799,9 +10900,18 @@ function progressBar(completed, total) {
10799
10900
  }
10800
10901
  function captainIndicator(state) {
10801
10902
  if (state === "alive" || state === "stale") return chalk6.green("\u25CF");
10903
+ if (state === "stopped") return chalk6.magenta("\u23FB");
10802
10904
  if (state === void 0 || state === "unknown") return chalk6.dim("?");
10803
10905
  return chalk6.dim("\u25CB");
10804
10906
  }
10907
+ function formatProjectRow(name, captainName, fm, statusMdState, captainState) {
10908
+ const sessionIndicator = captainIndicator(captainState);
10909
+ const captainDisplay = `${captainName.padEnd(11)} ${sessionIndicator}`;
10910
+ const crew = statusMdState === "ok" ? String(fm.active_crew ?? 0).padEnd(6) : chalk6.dim("?").padEnd(6);
10911
+ const progress = statusMdState === "ok" ? progressBar(fm.tasks_completed ?? 0, fm.tasks_total ?? 0).padEnd(25) : statusMdState === "unreadable" ? chalk6.red("status.md unreadable").padEnd(25) : chalk6.dim("no notes").padEnd(25);
10912
+ const updated = statusMdState === "ok" ? timeAgo(fm.last_updated) : chalk6.dim("\u2014");
10913
+ return ` ${name.padEnd(18)} ${captainDisplay} ${crew} ${progress} ${updated}`;
10914
+ }
10805
10915
  var statusCommand = new Command4("status").description("Show status of all projects from spoke vault status files").option("--detailed", "also show live per-component service health from the daemon (#77)").action(async (opts) => {
10806
10916
  const config = loadConfig();
10807
10917
  const projects = Object.entries(config.projects);
@@ -10826,28 +10936,19 @@ var statusCommand = new Command4("status").description("Show status of all proje
10826
10936
  console.log(chalk6.dim(" " + "\u2500".repeat(85)));
10827
10937
  for (const [name, project] of projects) {
10828
10938
  const workspace = registry.forProject(name, config);
10829
- if (!await workspace.exists("status.md")) {
10830
- console.log(` ${name.padEnd(18)} ${chalk6.dim("no status.md")}`);
10831
- continue;
10832
- }
10833
10939
  let fm = {};
10834
- try {
10835
- const raw = await workspace.read("status.md");
10836
- fm = matter2(raw).data;
10837
- } catch {
10838
- console.log(` ${name.padEnd(18)} ${chalk6.red("error reading status.md")}`);
10839
- continue;
10940
+ let statusMdState = "missing";
10941
+ if (await workspace.exists("status.md")) {
10942
+ try {
10943
+ const raw = await workspace.read("status.md");
10944
+ fm = matter2(raw).data;
10945
+ statusMdState = "ok";
10946
+ } catch {
10947
+ statusMdState = "unreadable";
10948
+ }
10840
10949
  }
10841
- const sessionIndicator = captainIndicator(captainStateByProject.get(name));
10842
- const captainDisplay = `${project.captainName.padEnd(11)} ${sessionIndicator}`;
10843
- const crew = String(fm.active_crew ?? 0).padEnd(6);
10844
- const progress = progressBar(
10845
- fm.tasks_completed ?? 0,
10846
- fm.tasks_total ?? 0
10847
- ).padEnd(25);
10848
- const updated = timeAgo(fm.last_updated);
10849
10950
  console.log(
10850
- ` ${name.padEnd(18)} ${captainDisplay} ${crew} ${progress} ${updated}`
10951
+ formatProjectRow(name, project.captainName, fm, statusMdState, captainStateByProject.get(name))
10851
10952
  );
10852
10953
  }
10853
10954
  console.log("");
@@ -10938,6 +11039,7 @@ init_dist4();
10938
11039
  import { Command as Command8 } from "commander";
10939
11040
  import { createConnection as createConnection3 } from "net";
10940
11041
  import { randomUUID as randomUUID4 } from "crypto";
11042
+ import { execFileSync as execFileSync6 } from "child_process";
10941
11043
  import { homedir as homedir16 } from "os";
10942
11044
  import { join as join21 } from "path";
10943
11045
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "fs";
@@ -11337,6 +11439,8 @@ function buildSignalRequest(signal, o) {
11337
11439
  };
11338
11440
  } else if (signal === "blocked") {
11339
11441
  event = { type: "task.blocked", id: taskId, reason: "crew signaled blocked", question: o.question ?? "" };
11442
+ } else if (signal === "review") {
11443
+ event = { type: "task.review", id: taskId, ...o.message !== void 0 ? { message: o.message } : {} };
11340
11444
  } else {
11341
11445
  event = { type: "task.failed", id: taskId, error: o.error ?? "crew signaled failed" };
11342
11446
  }
@@ -11365,6 +11469,49 @@ async function runCrewSignal(signal, o, deps) {
11365
11469
  const req = buildSignalRequest(signal, { ...o, writeResult: o.writeResult ?? defaultWriteResult });
11366
11470
  await deps.call(req);
11367
11471
  }
11472
+ function resolveApproveTarget(tasks, crew) {
11473
+ const matches = tasks.filter((t) => t.name === crew);
11474
+ if (matches.length === 0) return null;
11475
+ return matches.reduce((a, b) => (b.createdAt ?? 0) > (a.createdAt ?? 0) ? b : a);
11476
+ }
11477
+ function defaultPushBranch(cwd, branch) {
11478
+ execFileSync6("git", ["-C", cwd, "push", "-u", "origin", branch], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
11479
+ }
11480
+ function defaultCreatePr(cwd, o) {
11481
+ return execFileSync6(
11482
+ "gh",
11483
+ ["pr", "create", "--base", o.base, "--head", o.branch, "--title", o.title, "--body", o.body],
11484
+ { cwd, encoding: "utf-8" }
11485
+ ).trim();
11486
+ }
11487
+ async function runCrewApprove(project, crew, deps) {
11488
+ const config = loadConfig();
11489
+ const proj = config.projects[project];
11490
+ if (!proj) throw new Error(`Project '${project}' not found. Run 'squadrant projects list'.`);
11491
+ const tasks = await deps.call({ kind: "list", project });
11492
+ const task = resolveApproveTarget(tasks, crew);
11493
+ if (!task) throw new Error(`Crew '${crew}' not found for ${project}. Run 'squadrant crew list ${project}'.`);
11494
+ if (task.state !== "review") {
11495
+ throw new Error(
11496
+ `Crew '${crew}' is not awaiting review (state=${task.state}). Only a crew that signaled 'review' can be approved.`
11497
+ );
11498
+ }
11499
+ const cwd = task.cwd ?? proj.path;
11500
+ const base = resolveWorktreeBase(proj.path);
11501
+ const branch = crewBranch(crew);
11502
+ const title = (task.task ?? branch).split(/\r?\n/)[0].trim().slice(0, 100);
11503
+ const body = (task.reviewNote ?? task.task ?? "").trim();
11504
+ const pushBranch = deps.pushBranch ?? defaultPushBranch;
11505
+ const createPr = deps.createPr ?? defaultCreatePr;
11506
+ pushBranch(cwd, branch);
11507
+ const prUrl = createPr(cwd, { base, branch, title, body });
11508
+ await deps.call({
11509
+ kind: "event",
11510
+ project,
11511
+ event: { type: "task.done", id: task.id, resultRef: "", message: `Approved \u2014 PR opened: ${prUrl}`, source: "approve" }
11512
+ });
11513
+ return prUrl;
11514
+ }
11368
11515
  function addControlPlaneCrewCommands(crew) {
11369
11516
  crew.command("dispatch <project> <task>").description("Dispatch a crew task via the control-plane daemon").requiredOption("--provider <p>", "claude|opencode|codex (gemini: experimental, headless not supported)").option("--mode <m>", "headless|interactive", "interactive").option("--cwd <dir>", "working dir for the crew (project/worktree); required for codex to edit code").action(async (project, task, opts) => {
11370
11517
  const req = buildDispatchRequest({ project, task, provider: opts.provider, mode: opts.mode, cwd: opts.cwd });
@@ -11439,9 +11586,9 @@ function addControlPlaneCrewCommands(crew) {
11439
11586
  }
11440
11587
  process.exit(0);
11441
11588
  });
11442
- crew.command("signal <state>").description("Emit explicit terminal signal from a crew session: done|blocked|failed (reads SQUADRANT_CREW_* env, or --task-id/--project for codex)").option("--message <m>", "Summary written to resultRef (done)").option("--question <q>", "Question to surface to captain (blocked)").option("--error <e>", "Error message (failed)").option("--task-id <id>", "Explicit task id (codex; overrides SQUADRANT_CREW_TASK_ID env)").option("--project <p>", "Explicit project (codex; overrides SQUADRANT_CREW_PROJECT env)").action(async (state, opts) => {
11443
- if (state !== "done" && state !== "blocked" && state !== "failed") {
11444
- process.stderr.write(`unknown signal '${state}' (expected: done|blocked|failed)
11589
+ crew.command("signal <state>").description("Emit explicit terminal/review signal from a crew session: done|blocked|failed|review (reads SQUADRANT_CREW_* env, or --task-id/--project for codex)").option("--message <m>", "Summary written to resultRef (done), or review summary (review)").option("--question <q>", "Question to surface to captain (blocked)").option("--error <e>", "Error message (failed)").option("--task-id <id>", "Explicit task id (codex; overrides SQUADRANT_CREW_TASK_ID env)").option("--project <p>", "Explicit project (codex; overrides SQUADRANT_CREW_PROJECT env)").action(async (state, opts) => {
11590
+ if (state !== "done" && state !== "blocked" && state !== "failed" && state !== "review") {
11591
+ process.stderr.write(`unknown signal '${state}' (expected: done|blocked|failed|review)
11445
11592
  `);
11446
11593
  process.exit(2);
11447
11594
  }
@@ -11457,6 +11604,17 @@ function addControlPlaneCrewCommands(crew) {
11457
11604
  process.exit(0);
11458
11605
  } catch (e) {
11459
11606
  process.stderr.write(`${e.message}
11607
+ `);
11608
+ process.exit(1);
11609
+ }
11610
+ });
11611
+ crew.command("approve <project> <crew>").description("Approve a crew's reviewed work: push crew/<name> + open a PR, then terminalize DONE (#599)").action(async (project, crewName) => {
11612
+ try {
11613
+ const prUrl = await runCrewApprove(project, crewName, { call: squadrantdCall });
11614
+ process.stdout.write(`\u2714 Approved ${crewBranch(crewName)} \u2014 pushed + PR opened: ${prUrl}
11615
+ `);
11616
+ } catch (e) {
11617
+ process.stderr.write(`${e.message}
11460
11618
  `);
11461
11619
  process.exit(1);
11462
11620
  }
@@ -11614,6 +11772,199 @@ crewCommand.command("close").description("Shutdown a crew session (closes its ta
11614
11772
  }
11615
11773
  });
11616
11774
 
11775
+ // packages/cli/src/commands/diff.ts
11776
+ init_dist();
11777
+ init_dist3();
11778
+ import { Command as Command10 } from "commander";
11779
+ import chalk10 from "chalk";
11780
+ import { execFileSync as execFileSync7 } from "child_process";
11781
+ import readline2 from "readline";
11782
+ function resolveDiffTarget(tasks, crew, projectPath) {
11783
+ const matches = tasks.filter((t) => t.name === crew);
11784
+ if (matches.length === 0) return null;
11785
+ const task = matches.reduce((a, b) => (b.createdAt ?? 0) > (a.createdAt ?? 0) ? b : a);
11786
+ const cwd = task.cwd ?? projectPath;
11787
+ return { cwd, isShared: cwd === projectPath };
11788
+ }
11789
+ function resolveDiffSources(opts) {
11790
+ if (opts.working) return ["unstaged", "staged"];
11791
+ if (opts.staged) return ["staged"];
11792
+ if (opts.unstaged) return ["unstaged"];
11793
+ return [];
11794
+ }
11795
+ function resolveDiffMode(crew, opts) {
11796
+ const requestedModes = [crew !== void 0, opts.pr !== void 0, opts.base !== void 0 || opts.head !== void 0 || opts.against !== void 0];
11797
+ if (requestedModes.filter(Boolean).length > 1) {
11798
+ throw new Error(
11799
+ "squadrant diff: a crew argument, --pr, and --base/--head/--against are mutually exclusive."
11800
+ );
11801
+ }
11802
+ if (opts.against !== void 0 && (opts.base !== void 0 || opts.head !== void 0)) {
11803
+ throw new Error("squadrant diff: --against cannot be combined with --base/--head.");
11804
+ }
11805
+ if (crew !== void 0) return { mode: "crew", crew };
11806
+ if (opts.pr !== void 0) return { mode: "pr", pr: opts.pr };
11807
+ if (opts.against !== void 0) return { mode: "refs", base: opts.against, head: "HEAD" };
11808
+ if (opts.base !== void 0 || opts.head !== void 0) {
11809
+ if (opts.base === void 0 || opts.head === void 0) {
11810
+ throw new Error(
11811
+ "squadrant diff: --base and --head must be used together (or use --against <ref> to diff against HEAD)."
11812
+ );
11813
+ }
11814
+ return { mode: "refs", base: opts.base, head: opts.head };
11815
+ }
11816
+ return { mode: "pick" };
11817
+ }
11818
+ function buildCrewDiffStats(tasks, projectPath, base, getStat) {
11819
+ const live = /* @__PURE__ */ new Map();
11820
+ for (const t of tasks) {
11821
+ if (!t.name || TERMINAL_STATES.has(t.state)) continue;
11822
+ const prev = live.get(t.name);
11823
+ if (!prev || (t.createdAt ?? 0) > (prev.createdAt ?? 0)) live.set(t.name, t);
11824
+ }
11825
+ return [...live.values()].map((t) => {
11826
+ const cwd = t.cwd ?? projectPath;
11827
+ return { name: t.name, cwd, stat: getStat(cwd, base).trim() };
11828
+ });
11829
+ }
11830
+ function parseCrewPick(raw, stats) {
11831
+ const trimmed = raw.trim();
11832
+ const idx = Number(trimmed);
11833
+ if (Number.isInteger(idx) && idx >= 1 && idx <= stats.length) {
11834
+ return stats[idx - 1].name;
11835
+ }
11836
+ const byName = stats.find((s) => s.name === trimmed);
11837
+ if (byName) return byName.name;
11838
+ throw new Error(`Invalid selection '${raw}'. Enter a number 1-${stats.length} or a crew name.`);
11839
+ }
11840
+ function promptLine2(question) {
11841
+ return new Promise((resolve3) => {
11842
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
11843
+ rl.question(question, (answer) => {
11844
+ rl.close();
11845
+ resolve3(answer);
11846
+ });
11847
+ });
11848
+ }
11849
+ function getPrDiff(projectPath, pr) {
11850
+ try {
11851
+ return execFileSync7("gh", ["pr", "diff", pr], { cwd: projectPath, encoding: "utf-8" });
11852
+ } catch (e) {
11853
+ throw new Error(`Could not fetch PR #${pr} diff (gh pr diff failed): ${e.message}`);
11854
+ }
11855
+ }
11856
+ function getRefsDiff(projectPath, base, head) {
11857
+ try {
11858
+ return execFileSync7("git", ["-C", projectPath, "diff", `${base}...${head}`], { encoding: "utf-8" });
11859
+ } catch (e) {
11860
+ throw new Error(`Could not diff ${base}...${head}: ${e.message}`);
11861
+ }
11862
+ }
11863
+ async function openCrewDiff(project, proj, crew, opts, runtime, workspaceId) {
11864
+ const tasks = await squadrantdCall({ kind: "list", project });
11865
+ const target = resolveDiffTarget(tasks, crew, proj.path);
11866
+ if (!target) {
11867
+ throw new Error(`Crew '${crew}' not found for ${project}. Run 'squadrant crew list ${project}'.`);
11868
+ }
11869
+ const base = resolveWorktreeBase(proj.path);
11870
+ const branchLabel = crewBranch(crew);
11871
+ if (!runtime.showDiff) {
11872
+ throw new Error(`Runtime '${runtime.name}' has no native diff viewer yet \u2014 Phase 1 supports cmux only.`);
11873
+ }
11874
+ const sources = resolveDiffSources(opts);
11875
+ if (sources.length > 0) {
11876
+ let opened = 0;
11877
+ for (const source of sources) {
11878
+ const statArgs = source === "staged" ? ["diff", "--stat", "--cached"] : ["diff", "--stat"];
11879
+ const stat2 = execFileSync7("git", ["-C", target.cwd, ...statArgs], { encoding: "utf-8" }).trim();
11880
+ if (!stat2) continue;
11881
+ await runtime.showDiff({
11882
+ workspaceId,
11883
+ cwd: target.cwd,
11884
+ base,
11885
+ title: `${branchLabel} \u2014 ${source}`,
11886
+ layout: opts.layout,
11887
+ focus: opts.focus,
11888
+ source
11889
+ });
11890
+ opened++;
11891
+ }
11892
+ if (opened === 0) {
11893
+ const label = sources.length > 1 ? "staged or unstaged" : sources[0];
11894
+ console.log(`No ${label} changes on ${branchLabel}.`);
11895
+ } else {
11896
+ console.log(chalk10.dim(`Opened ${opened} working-tree diff(s) (${sources.join(", ")}) for ${branchLabel}.`));
11897
+ }
11898
+ return;
11899
+ }
11900
+ const diffStat = execFileSync7(
11901
+ "git",
11902
+ ["-C", target.cwd, "diff", "--stat", `${base}...HEAD`],
11903
+ { encoding: "utf-8" }
11904
+ ).trim();
11905
+ if (!diffStat) {
11906
+ console.log(`No changes on ${branchLabel} vs ${base}.`);
11907
+ return;
11908
+ }
11909
+ await runtime.showDiff({
11910
+ workspaceId,
11911
+ cwd: target.cwd,
11912
+ base,
11913
+ title: `${branchLabel} vs ${base}`,
11914
+ layout: opts.layout,
11915
+ focus: opts.focus,
11916
+ lastTurn: opts.lastTurn,
11917
+ source: "branch"
11918
+ });
11919
+ console.log(chalk10.dim(`Opened ${branchLabel} vs ${base} in cmux diff.`));
11920
+ }
11921
+ async function runDiff(project, crewArg, opts) {
11922
+ const config = loadConfig();
11923
+ const proj = config.projects[project];
11924
+ if (!proj) {
11925
+ throw new Error(`Project '${project}' not found. Run 'squadrant projects list'.`);
11926
+ }
11927
+ const mode = resolveDiffMode(crewArg, opts);
11928
+ const { runtime, workspaceId } = await resolveCaptainWorkspace(project);
11929
+ if (mode.mode === "pr" || mode.mode === "refs") {
11930
+ if (!runtime.showPatch) {
11931
+ throw new Error(`Runtime '${runtime.name}' has no native patch viewer yet \u2014 Phase 1 supports cmux only.`);
11932
+ }
11933
+ const title = mode.mode === "pr" ? `PR #${mode.pr}` : `${mode.base}...${mode.head}`;
11934
+ const patch = mode.mode === "pr" ? getPrDiff(proj.path, mode.pr) : getRefsDiff(proj.path, mode.base, mode.head);
11935
+ if (!patch.trim()) {
11936
+ console.log(`No changes in ${title}.`);
11937
+ return;
11938
+ }
11939
+ await runtime.showPatch({ workspaceId, patch, title, layout: opts.layout, focus: opts.focus });
11940
+ console.log(chalk10.dim(`Opened ${title} in cmux diff.`));
11941
+ return;
11942
+ }
11943
+ let crew;
11944
+ if (mode.mode === "pick") {
11945
+ const tasks = await squadrantdCall({ kind: "list", project });
11946
+ const base = resolveWorktreeBase(proj.path);
11947
+ const stats = buildCrewDiffStats(tasks, proj.path, base, (cwd, b) => {
11948
+ try {
11949
+ return execFileSync7("git", ["-C", cwd, "diff", "--stat", `${b}...HEAD`], { encoding: "utf-8" });
11950
+ } catch {
11951
+ return "";
11952
+ }
11953
+ });
11954
+ if (stats.length === 0) {
11955
+ throw new Error(`No live crews for ${project}. Run 'squadrant crew list ${project}' to check, or 'squadrant crew spawn' one.`);
11956
+ }
11957
+ console.log(`Live crews for ${project} (vs ${base}):`);
11958
+ stats.forEach((s, i) => console.log(` ${i + 1}. ${s.name} \u2014 ${s.stat || "no changes"}`));
11959
+ const raw = await promptLine2("Pick a crew to diff (number or name): ");
11960
+ crew = parseCrewPick(raw, stats);
11961
+ } else {
11962
+ crew = mode.crew;
11963
+ }
11964
+ await openCrewDiff(project, proj, crew, opts, runtime, workspaceId);
11965
+ }
11966
+ var diffCommand = new Command10("diff").description("Open a crew's branch diff, a PR, or a ref comparison in cmux's native diff viewer \u2014 no VSCode required (#596/#604)").argument("<project>", "Project name (must be registered)").argument("[crew]", "Crew name (e.g. crew-1); omit with no other flags to pick from live crews").option("--pr <n>", "Review a PR: wraps `gh pr diff <n>` (mutually exclusive with crew/--base/--head)").option("--base <ref>", "Base ref for a --head ref comparison (merge-base diff)").option("--head <ref>", "Head ref for a --base ref comparison").option("--against <ref>", "Alias: diff <ref>...HEAD (mutually exclusive with --base/--head)").option("--layout <mode>", "split (default) or unified", "split").option("--last-turn", "diff only changes since the crew's last agent turn", false).option("--no-focus", "open the diff pane without stealing focus").option("--staged", "show only staged (index) changes \u2014 VSCode's 'Staged Changes' panel (#599)", false).option("--unstaged", "show only unstaged working-tree changes \u2014 VSCode's 'Changes' panel (#599)", false).option("--working", "show both staged and unstaged changes (mid-task working-tree review, #599)", false).action(runDiff);
11967
+
11617
11968
  // packages/cli/src/commands/side.ts
11618
11969
  init_dist();
11619
11970
  init_dist3();
@@ -11621,11 +11972,11 @@ init_dist4();
11621
11972
  init_dist3();
11622
11973
  init_dist();
11623
11974
  init_dist2();
11624
- import { Command as Command10 } from "commander";
11975
+ import { Command as Command11 } from "commander";
11625
11976
  import fs20 from "fs";
11626
11977
  import path22 from "path";
11627
11978
  import os12 from "os";
11628
- import chalk10 from "chalk";
11979
+ import chalk11 from "chalk";
11629
11980
  var TEMPLATES_DIR3 = path22.join(os12.homedir(), ".config", "squadrant", "templates");
11630
11981
  async function runSideSpawn2(input) {
11631
11982
  const config = loadConfig();
@@ -11687,7 +12038,7 @@ async function runSideClose2(project, name) {
11687
12038
  config.defaults.worktreeDir ?? ".worktrees"
11688
12039
  );
11689
12040
  }
11690
- var sideCommand = new Command10("side").description(
12041
+ var sideCommand = new Command11("side").description(
11691
12042
  "Spawn and manage side-sessions (research/debug) \u2014 fresh-context tabs off the daemon lifecycle"
11692
12043
  );
11693
12044
  sideCommand.command("spawn").description(
@@ -11712,9 +12063,9 @@ sideCommand.command("spawn").description(
11712
12063
  direction: opts.direction,
11713
12064
  agent: opts.agent
11714
12065
  });
11715
- console.log(chalk10.green(`\u2714 Side session '${pane.title}' spawned (${pane.surfaceId})`));
12066
+ console.log(chalk11.green(`\u2714 Side session '${pane.title}' spawned (${pane.surfaceId})`));
11716
12067
  } catch (err) {
11717
- console.error(chalk10.red(err.message));
12068
+ console.error(chalk11.red(err.message));
11718
12069
  process.exit(1);
11719
12070
  }
11720
12071
  }
@@ -11723,14 +12074,14 @@ sideCommand.command("list").description("List live side-sessions for a project")
11723
12074
  try {
11724
12075
  const sessions = await runSideList2(project);
11725
12076
  if (sessions.length === 0) {
11726
- console.log(chalk10.yellow(`No live side-sessions for ${project}.`));
12077
+ console.log(chalk11.yellow(`No live side-sessions for ${project}.`));
11727
12078
  return;
11728
12079
  }
11729
12080
  for (const s of sessions) {
11730
12081
  console.log(` ${s.name} (${s.surfaceId})`);
11731
12082
  }
11732
12083
  } catch (err) {
11733
- console.error(chalk10.red(err.message));
12084
+ console.error(chalk11.red(err.message));
11734
12085
  process.exit(1);
11735
12086
  }
11736
12087
  });
@@ -11743,9 +12094,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
11743
12094
  label: "message"
11744
12095
  });
11745
12096
  await runSideSend2(project, name, resolvedMessage);
11746
- console.log(chalk10.green(`\u2714 Sent to ${project}:${name}`));
12097
+ console.log(chalk11.green(`\u2714 Sent to ${project}:${name}`));
11747
12098
  } catch (err) {
11748
- console.error(chalk10.red(err.message));
12099
+ console.error(chalk11.red(err.message));
11749
12100
  process.exit(1);
11750
12101
  }
11751
12102
  }
@@ -11753,9 +12104,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
11753
12104
  sideCommand.command("close").description("Close a side-session (closes its tab)").argument("<project>", "Project name").argument("<name>", "Session name").action(async (project, name) => {
11754
12105
  try {
11755
12106
  await runSideClose2(project, name);
11756
- console.log(chalk10.green(`\u2714 Closed ${project}:${name}`));
12107
+ console.log(chalk11.green(`\u2714 Closed ${project}:${name}`));
11757
12108
  } catch (err) {
11758
- console.error(chalk10.red(err.message));
12109
+ console.error(chalk11.red(err.message));
11759
12110
  process.exit(1);
11760
12111
  }
11761
12112
  });
@@ -11763,15 +12114,15 @@ sideCommand.command("close").description("Close a side-session (closes its tab)"
11763
12114
  // packages/cli/src/commands/dashboard.ts
11764
12115
  init_dist();
11765
12116
  init_dist3();
11766
- import { Command as Command11 } from "commander";
12117
+ import { Command as Command12 } from "commander";
11767
12118
  import { execSync as execSync10 } from "child_process";
11768
12119
  import { homedir as homedir18 } from "os";
11769
12120
  import { join as join23 } from "path";
11770
- import chalk12 from "chalk";
12121
+ import chalk13 from "chalk";
11771
12122
 
11772
12123
  // packages/web/dist/read-status.js
11773
12124
  function deriveState(tasks) {
11774
- if (tasks.some((t) => t.state === "blocked" || t.state === "awaiting-input"))
12125
+ if (tasks.some((t) => t.state === "blocked" || t.state === "awaiting-input" || t.state === "review"))
11775
12126
  return "blocked";
11776
12127
  if (tasks.some((t) => t.state === "failed" || t.state === "stalled"))
11777
12128
  return "errored";
@@ -11786,14 +12137,14 @@ function deriveRowState(tasks, captainState) {
11786
12137
  }
11787
12138
  function buildExcerpt(tasks) {
11788
12139
  const working = tasks.filter((t) => t.state === "working").length;
11789
- const blocked = tasks.filter((t) => t.state === "blocked" || t.state === "awaiting-input").length;
12140
+ const blocked = tasks.filter((t) => t.state === "blocked" || t.state === "awaiting-input" || t.state === "review").length;
11790
12141
  const parts = [];
11791
12142
  if (working > 0)
11792
12143
  parts.push(`${working} working`);
11793
12144
  if (blocked > 0)
11794
12145
  parts.push(`${blocked} blocked`);
11795
12146
  const summary = parts.length > 0 ? parts.join(", ") : "idle";
11796
- const active = tasks.filter((t) => ["working", "blocked", "awaiting-input", "submitted"].includes(t.state));
12147
+ const active = tasks.filter((t) => ["working", "blocked", "awaiting-input", "review", "submitted"].includes(t.state));
11797
12148
  const titles = active.slice(0, 3).map((t) => {
11798
12149
  const firstLine2 = t.task ? t.task.split("\n")[0] : "";
11799
12150
  return t.name ?? (firstLine2 || t.id.slice(0, 8));
@@ -11844,14 +12195,14 @@ async function readAllStatuses(deps) {
11844
12195
  }
11845
12196
 
11846
12197
  // packages/web/dist/render.js
11847
- import chalk11 from "chalk";
12198
+ import chalk12 from "chalk";
11848
12199
  var ICON = {
11849
- idle: chalk11.green,
11850
- busy: chalk11.cyan,
11851
- blocked: chalk11.yellow,
11852
- errored: chalk11.red,
11853
- offline: chalk11.dim,
11854
- unknown: chalk11.gray
12200
+ idle: chalk12.green,
12201
+ busy: chalk12.cyan,
12202
+ blocked: chalk12.yellow,
12203
+ errored: chalk12.red,
12204
+ offline: chalk12.dim,
12205
+ unknown: chalk12.gray
11855
12206
  };
11856
12207
  var ICON_CHAR = {
11857
12208
  idle: "\u25CF",
@@ -11894,10 +12245,10 @@ function renderDashboard(rows, opts) {
11894
12245
  const width = opts.width ?? 100;
11895
12246
  const lines = [];
11896
12247
  lines.push("");
11897
- lines.push(" " + chalk11.bold("\u{1F4CA} Squadrant Dashboard") + " " + chalk11.dim(opts.now));
12248
+ lines.push(" " + chalk12.bold("\u{1F4CA} Squadrant Dashboard") + " " + chalk12.dim(opts.now));
11898
12249
  lines.push("");
11899
12250
  if (rows.length === 0) {
11900
- lines.push(" " + chalk11.yellow("No projects registered. Add one with: squadrant projects add <name> <path>"));
12251
+ lines.push(" " + chalk12.yellow("No projects registered. Add one with: squadrant projects add <name> <path>"));
11901
12252
  lines.push("");
11902
12253
  return lines.join("\n");
11903
12254
  }
@@ -11908,14 +12259,14 @@ function renderDashboard(rows, opts) {
11908
12259
  const excerptW = Math.max(20, width - FIXED);
11909
12260
  for (const r of rows) {
11910
12261
  const icon = ICON[r.state](ICON_CHAR[r.state]);
11911
- const name = chalk11.cyan(pad(r.project, NAME_W));
12262
+ const name = chalk12.cyan(pad(r.project, NAME_W));
11912
12263
  const state = ICON[r.state](pad(r.state, STATE_W));
11913
12264
  const age = pad(formatAge(r.lastChecked, opts.now), AGE_W);
11914
- const excerpt = chalk11.dim(truncate(firstLine(r.excerpt), excerptW));
12265
+ const excerpt = chalk12.dim(truncate(firstLine(r.excerpt), excerptW));
11915
12266
  lines.push(` ${icon} ${name} ${state} ${age} \u2502 ${excerpt}`);
11916
12267
  }
11917
12268
  lines.push("");
11918
- lines.push(chalk11.dim(" Refreshes every 10s \xB7 Ctrl+C to exit"));
12269
+ lines.push(chalk12.dim(" Refreshes every 10s \xB7 Ctrl+C to exit"));
11919
12270
  lines.push("");
11920
12271
  return lines.join("\n");
11921
12272
  }
@@ -11982,7 +12333,7 @@ init_dist();
11982
12333
  import { join as join22 } from "path";
11983
12334
  import { homedir as homedir17 } from "os";
11984
12335
  import { existsSync as existsSync11, readFileSync as readFileSync14 } from "fs";
11985
- import { execFile as execFile3 } from "child_process";
12336
+ import { execFile as execFile4 } from "child_process";
11986
12337
  var DEFAULT_TIMEOUT_MS = 2e3;
11987
12338
  var AGENT_CLIS = ["claude", "codex", "gemini", "opencode"];
11988
12339
  function withTimeout2(p, ms) {
@@ -12099,7 +12450,7 @@ function defaultProbeRunners() {
12099
12450
  return {
12100
12451
  probeCmuxBin: () => new Promise((resolve3) => {
12101
12452
  try {
12102
- execFile3(resolveCmuxBin(), ["--version"], { timeout: 1500 }, (err) => resolve3(!err));
12453
+ execFile4(resolveCmuxBin(), ["--version"], { timeout: 1500 }, (err) => resolve3(!err));
12103
12454
  } catch {
12104
12455
  resolve3(false);
12105
12456
  }
@@ -13038,10 +13389,10 @@ async function runDashboardWeb(input) {
13038
13389
  sockPath: SOCK3,
13039
13390
  runners: defaultProbeRunners()
13040
13391
  });
13041
- console.log(chalk12.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
13042
- console.log(chalk12.dim(` polling the daemon every ${input.interval}s \xB7 localhost only \xB7 read-only \xB7 Ctrl-C to stop`));
13392
+ console.log(chalk13.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
13393
+ console.log(chalk13.dim(` polling the daemon every ${input.interval}s \xB7 localhost only \xB7 read-only \xB7 Ctrl-C to stop`));
13043
13394
  }
13044
- var dashboardCommand = new Command11("dashboard").description("Live status grid of all projects (derived from daemon task state)").option("--once", "Print one snapshot and exit (used by --pane's refresh loop)").option("--pane", "Open a refreshing sidebar pane in the current cmux workspace").option("--web", "Serve the live system-health web dashboard on 127.0.0.1 (HTTP + SSE)").option("--port <port>", "Port for --web (default 7878)", (v) => parseInt(v, 10), 7878).option("--direction <dir>", "Pane split direction (right|left|up|down)", "right").option("--interval <seconds>", "Daemon poll interval for --web (default 5); refresh interval for --pane (default 10)", (v) => parseInt(v, 10)).action(async (opts) => {
13395
+ var dashboardCommand = new Command12("dashboard").description("Live status grid of all projects (derived from daemon task state)").option("--once", "Print one snapshot and exit (used by --pane's refresh loop)").option("--pane", "Open a refreshing sidebar pane in the current cmux workspace").option("--web", "Serve the live system-health web dashboard on 127.0.0.1 (HTTP + SSE)").option("--port <port>", "Port for --web (default 7878)", (v) => parseInt(v, 10), 7878).option("--direction <dir>", "Pane split direction (right|left|up|down)", "right").option("--interval <seconds>", "Daemon poll interval for --web (default 5); refresh interval for --pane (default 10)", (v) => parseInt(v, 10)).action(async (opts) => {
13045
13396
  try {
13046
13397
  if (opts.web) {
13047
13398
  await runDashboardWeb({ port: opts.port, interval: opts.interval ?? 5 });
@@ -13049,12 +13400,12 @@ var dashboardCommand = new Command11("dashboard").description("Live status grid
13049
13400
  }
13050
13401
  if (opts.pane) {
13051
13402
  const pane = await runDashboardPane({ direction: opts.direction, interval: opts.interval ?? 10 });
13052
- console.log(chalk12.green(`\u2714 Dashboard pane opened in ${pane.workspaceId} ${pane.surfaceId}`));
13403
+ console.log(chalk13.green(`\u2714 Dashboard pane opened in ${pane.workspaceId} ${pane.surfaceId}`));
13053
13404
  return;
13054
13405
  }
13055
13406
  await runDashboardOnce();
13056
13407
  } catch (err) {
13057
- console.error(chalk12.red(err.message));
13408
+ console.error(chalk13.red(err.message));
13058
13409
  process.exit(1);
13059
13410
  }
13060
13411
  });
@@ -13065,12 +13416,12 @@ dashboardCommand.command("sync-hub").description("Mirror each spoke status.md in
13065
13416
  return;
13066
13417
  }
13067
13418
  if (results.length === 0) {
13068
- console.log(chalk12.dim("\n No mirrors written (no projects with usable status.md, or hubVault unset).\n"));
13419
+ console.log(chalk13.dim("\n No mirrors written (no projects with usable status.md, or hubVault unset).\n"));
13069
13420
  return;
13070
13421
  }
13071
- console.log(chalk12.bold("\n \u{1F4CA} Hub mirror sync\n"));
13422
+ console.log(chalk13.bold("\n \u{1F4CA} Hub mirror sync\n"));
13072
13423
  for (const r of results) {
13073
- console.log(` ${chalk12.green("\u2714")} ${chalk12.cyan(r.project.padEnd(16))} \u2192 ${chalk12.dim(r.hubPath)}`);
13424
+ console.log(` ${chalk13.green("\u2714")} ${chalk13.cyan(r.project.padEnd(16))} \u2192 ${chalk13.dim(r.hubPath)}`);
13074
13425
  }
13075
13426
  console.log("");
13076
13427
  });
@@ -13080,12 +13431,12 @@ init_dist();
13080
13431
  init_dist4();
13081
13432
  init_dist3();
13082
13433
  init_dist2();
13083
- import { Command as Command12 } from "commander";
13434
+ import { Command as Command13 } from "commander";
13084
13435
  import { execSync as execSync11 } from "child_process";
13085
13436
  import fs22 from "fs";
13086
13437
  import path24 from "path";
13087
13438
  import os13 from "os";
13088
- import chalk13 from "chalk";
13439
+ import chalk14 from "chalk";
13089
13440
 
13090
13441
  // packages/cli/src/commands/launch-interactive.ts
13091
13442
  import checkbox, { Separator } from "@inquirer/checkbox";
@@ -13166,16 +13517,16 @@ var TEMPLATES_DIR4 = path24.join(os13.homedir(), ".config", "squadrant", "templa
13166
13517
  var SESSIONS_PATH2 = path24.join(os13.homedir(), ".config", "squadrant", "sessions.json");
13167
13518
  function ensureCmuxReady(headless) {
13168
13519
  if (headless || isInsideCmux()) return;
13169
- console.log(chalk13.yellow("\n Not running inside cmux. Opening cmux app...\n"));
13520
+ console.log(chalk14.yellow("\n Not running inside cmux. Opening cmux app...\n"));
13170
13521
  execSync11(`open "${CMUX_APP}"`, { stdio: "inherit" });
13171
- console.log(chalk13.bold(" Run `squadrant launch` from inside a cmux workspace.\n"));
13522
+ console.log(chalk14.bold(" Run `squadrant launch` from inside a cmux workspace.\n"));
13172
13523
  process.exit(0);
13173
13524
  }
13174
- var launchCommand = new Command12("launch").description(
13525
+ var launchCommand = new Command13("launch").description(
13175
13526
  "Launch a project captain (with project arg) or all captains (--all). Use `squadrant command` for one-shot Command tasks."
13176
13527
  ).argument("[project]", "Project name to launch captain for").option("--fresh", "Start a new session instead of resuming the last one").option("--keep", "Resume the latest session even on a new day / after a template change").option("--all", "Launch all captain workspaces").option("--headless", "Skip the interactive cmux-app requirement (used by the daemon to boot captains without a terminal)").action(async (project, opts) => {
13177
13528
  if (opts.fresh && opts.keep) {
13178
- console.error(chalk13.red("\n \u2718 --fresh and --keep are mutually exclusive\n"));
13529
+ console.error(chalk14.red("\n \u2718 --fresh and --keep are mutually exclusive\n"));
13179
13530
  process.exit(1);
13180
13531
  }
13181
13532
  const config = loadConfig();
@@ -13223,29 +13574,29 @@ var launchCommand = new Command12("launch").description(
13223
13574
  return null;
13224
13575
  }
13225
13576
  },
13226
- onFreshReason: (reason) => console.log(chalk13.cyan(` \u21BB ${reason}`)),
13227
- onStoppingStale: (name) => console.log(chalk13.yellow(` Closing stale workspace '${name}' for fresh start`)),
13228
- onAlreadyExists: (name) => console.log(chalk13.yellow(` Workspace '${name}' already exists \u2014 switching to it`)),
13229
- onCreated: (name) => console.log(chalk13.green(` \u2714 Workspace '${name}' created`))
13577
+ onFreshReason: (reason) => console.log(chalk14.cyan(` \u21BB ${reason}`)),
13578
+ onStoppingStale: (name) => console.log(chalk14.yellow(` Closing stale workspace '${name}' for fresh start`)),
13579
+ onAlreadyExists: (name) => console.log(chalk14.yellow(` Workspace '${name}' already exists \u2014 switching to it`)),
13580
+ onCreated: (name) => console.log(chalk14.green(` \u2714 Workspace '${name}' created`))
13230
13581
  });
13231
13582
  } catch (err) {
13232
- console.error(chalk13.red(` \u2718 Failed: ${err.message}`));
13583
+ console.error(chalk14.red(` \u2718 Failed: ${err.message}`));
13233
13584
  hadFailure = true;
13234
13585
  }
13235
13586
  }
13236
13587
  if (opts.all) {
13237
13588
  const hubPath = resolveHome(config.hubVault);
13238
13589
  fs22.mkdirSync(hubPath, { recursive: true });
13239
- console.log(chalk13.bold("\nLaunching all captain workspaces\n"));
13590
+ console.log(chalk14.bold("\nLaunching all captain workspaces\n"));
13240
13591
  for (const [name, proj] of Object.entries(config.projects)) {
13241
13592
  const projPath = resolveHome(proj.path);
13242
13593
  const spokePath = resolveHome(proj.spokeVault);
13243
13594
  if (!fs22.existsSync(spokePath)) {
13244
13595
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
13245
13596
  await ensureSpokeLayout(spokeDriver);
13246
- console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13597
+ console.log(chalk14.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13247
13598
  }
13248
- console.log(chalk13.bold(`
13599
+ console.log(chalk14.bold(`
13249
13600
  Captain: ${proj.captainName} (${name})`));
13250
13601
  await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
13251
13602
  }
@@ -13253,7 +13604,7 @@ var launchCommand = new Command12("launch").description(
13253
13604
  } else if (!project) {
13254
13605
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
13255
13606
  console.error(
13256
- chalk13.red(
13607
+ chalk14.red(
13257
13608
  "\n \u2718 Specify a project name, or pass --all to launch every captain.\n For one-shot Command tasks, use `squadrant command --task <briefing|learnings-review|wiki-aggregate>`.\n"
13258
13609
  )
13259
13610
  );
@@ -13268,10 +13619,10 @@ var launchCommand = new Command12("launch").description(
13268
13619
  }));
13269
13620
  const selected = await selectCaptainsInteractive(entries);
13270
13621
  if (selected.length === 0) {
13271
- console.log(chalk13.yellow("\n No captains selected.\n"));
13622
+ console.log(chalk14.yellow("\n No captains selected.\n"));
13272
13623
  return;
13273
13624
  }
13274
- console.log(chalk13.bold(`
13625
+ console.log(chalk14.bold(`
13275
13626
  Launching ${selected.length} captain workspace(s) in parallel
13276
13627
  `));
13277
13628
  await Promise.all(selected.map(async (name) => {
@@ -13281,9 +13632,9 @@ Launching ${selected.length} captain workspace(s) in parallel
13281
13632
  if (!fs22.existsSync(spokePath)) {
13282
13633
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
13283
13634
  await ensureSpokeLayout(spokeDriver);
13284
- console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13635
+ console.log(chalk14.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13285
13636
  }
13286
- console.log(chalk13.bold(`
13637
+ console.log(chalk14.bold(`
13287
13638
  Captain: ${proj.captainName} (${name})`));
13288
13639
  await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
13289
13640
  }));
@@ -13291,7 +13642,7 @@ Launching ${selected.length} captain workspace(s) in parallel
13291
13642
  } else {
13292
13643
  if (!config.projects[project]) {
13293
13644
  console.error(
13294
- chalk13.red(
13645
+ chalk14.red(
13295
13646
  `
13296
13647
  \u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
13297
13648
  `
@@ -13305,10 +13656,10 @@ Launching ${selected.length} captain workspace(s) in parallel
13305
13656
  if (!fs22.existsSync(spokePath)) {
13306
13657
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
13307
13658
  await ensureSpokeLayout(spokeDriver);
13308
- console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13659
+ console.log(chalk14.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13309
13660
  }
13310
13661
  console.log(
13311
- chalk13.bold(
13662
+ chalk14.bold(
13312
13663
  `
13313
13664
  Launching captain workspace for '${project}' (${proj.captainName})
13314
13665
  `
@@ -13322,8 +13673,8 @@ Launching captain workspace for '${project}' (${proj.captainName})
13322
13673
  // packages/cli/src/commands/shutdown.ts
13323
13674
  init_dist();
13324
13675
  init_dist3();
13325
- import { Command as Command13 } from "commander";
13326
- import chalk14 from "chalk";
13676
+ import { Command as Command14 } from "commander";
13677
+ import chalk15 from "chalk";
13327
13678
  init_dist();
13328
13679
  function nameVariants(name) {
13329
13680
  const stripped = name.replace(/^⚓\s+/, "").trim();
@@ -13336,23 +13687,23 @@ async function closeMatching(runtime, variants, label) {
13336
13687
  const failed = [];
13337
13688
  if (matches.length === 0) {
13338
13689
  console.log(
13339
- chalk14.yellow(` \u26A0 Workspace '${label}' not found \u2014 already closed?`)
13690
+ chalk15.yellow(` \u26A0 Workspace '${label}' not found \u2014 already closed?`)
13340
13691
  );
13341
13692
  return { closed, failed };
13342
13693
  }
13343
13694
  for (const ws of matches) {
13344
13695
  try {
13345
13696
  await runtime.stop(ws.id);
13346
- console.log(chalk14.green(` \u2714 Closed: ${ws.name}`));
13697
+ console.log(chalk15.green(` \u2714 Closed: ${ws.name}`));
13347
13698
  closed.push(ws.name);
13348
13699
  } catch {
13349
- console.log(chalk14.red(` \u2718 Failed to close: ${ws.name}`));
13700
+ console.log(chalk15.red(` \u2718 Failed to close: ${ws.name}`));
13350
13701
  failed.push(ws.name);
13351
13702
  }
13352
13703
  }
13353
13704
  return { closed, failed };
13354
13705
  }
13355
- var shutdownCommand = new Command13("shutdown").description(
13706
+ var shutdownCommand = new Command14("shutdown").description(
13356
13707
  "Shutdown command + all captain workspaces (no args) or one captain workspace"
13357
13708
  ).argument("[project]", "Project name to shut down captain for").action(async (project) => {
13358
13709
  const config = loadConfig();
@@ -13368,11 +13719,11 @@ var shutdownCommand = new Command13("shutdown").description(
13368
13719
  const allVariants = /* @__PURE__ */ new Set([...captainVariants, ...commandVariants]);
13369
13720
  const squadrantWorkspaces = workspaces.filter((w) => allVariants.has(w.name));
13370
13721
  if (squadrantWorkspaces.length === 0) {
13371
- console.log(chalk14.yellow("\nNo squadrant workspaces found to close.\n"));
13722
+ console.log(chalk15.yellow("\nNo squadrant workspaces found to close.\n"));
13372
13723
  return;
13373
13724
  }
13374
13725
  console.log(
13375
- chalk14.bold(
13726
+ chalk15.bold(
13376
13727
  `
13377
13728
  Shutting down ${squadrantWorkspaces.length} workspace(s)...
13378
13729
  `
@@ -13392,9 +13743,9 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
13392
13743
  for (const ws of squadrantWorkspaces) {
13393
13744
  try {
13394
13745
  await globalRuntime.stop(ws.id);
13395
- console.log(chalk14.green(` \u2714 Closed: ${ws.name}`));
13746
+ console.log(chalk15.green(` \u2714 Closed: ${ws.name}`));
13396
13747
  } catch {
13397
- console.log(chalk14.red(` \u2718 Failed to close: ${ws.name}`));
13748
+ console.log(chalk15.red(` \u2718 Failed to close: ${ws.name}`));
13398
13749
  }
13399
13750
  }
13400
13751
  console.log("");
@@ -13402,7 +13753,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
13402
13753
  }
13403
13754
  if (!config.projects[project]) {
13404
13755
  console.error(
13405
- chalk14.red(
13756
+ chalk15.red(
13406
13757
  `
13407
13758
  \u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
13408
13759
  `
@@ -13413,7 +13764,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
13413
13764
  const captainName = config.projects[project].captainName;
13414
13765
  const runtime = runtimes.forProject(project, config);
13415
13766
  console.log(
13416
- chalk14.bold(`
13767
+ chalk15.bold(`
13417
13768
  Shutting down captain workspace for '${project}'...
13418
13769
  `)
13419
13770
  );
@@ -13437,13 +13788,13 @@ Shutting down captain workspace for '${project}'...
13437
13788
 
13438
13789
  // packages/cli/src/commands/feedback.ts
13439
13790
  init_dist();
13440
- import { Command as Command14 } from "commander";
13791
+ import { Command as Command15 } from "commander";
13441
13792
  import fs23 from "fs";
13442
13793
  import os14 from "os";
13443
13794
  import path25 from "path";
13444
13795
  import { fileURLToPath as fileURLToPath3 } from "url";
13445
13796
  import { execSync as execSync12 } from "child_process";
13446
- import chalk15 from "chalk";
13797
+ import chalk16 from "chalk";
13447
13798
  var REPO_URL = "https://github.com/tu11aa/squadrant";
13448
13799
  function readPkgVersion() {
13449
13800
  try {
@@ -13491,21 +13842,21 @@ function buildIssueUrl(metrics, squadrantVersion) {
13491
13842
  });
13492
13843
  return `${REPO_URL}/issues/new?${params.toString()}`;
13493
13844
  }
13494
- var feedbackCommand = new Command14("feedback").description("Open a pre-filled GitHub issue for feedback or bug reports").action(() => {
13845
+ var feedbackCommand = new Command15("feedback").description("Open a pre-filled GitHub issue for feedback or bug reports").action(() => {
13495
13846
  const config = loadConfig();
13496
13847
  const metricsPath = config.metrics?.path || path25.join(os14.homedir(), ".config", "squadrant", "metrics.json");
13497
13848
  const metrics = readMetrics(metricsPath);
13498
13849
  const version = readStamp(config) ?? readPkgVersion();
13499
13850
  const issueUrl = buildIssueUrl(metrics, version);
13500
- console.log(chalk15.bold("\nOpening feedback issue in browser...\n"));
13501
- console.log(chalk15.dim(` URL: ${issueUrl.substring(0, 80)}...
13851
+ console.log(chalk16.bold("\nOpening feedback issue in browser...\n"));
13852
+ console.log(chalk16.dim(` URL: ${issueUrl.substring(0, 80)}...
13502
13853
  `));
13503
13854
  try {
13504
13855
  execSync12(`open "${issueUrl}"`, { stdio: "ignore" });
13505
- console.log(chalk15.green(" \u2714 Browser opened\n"));
13856
+ console.log(chalk16.green(" \u2714 Browser opened\n"));
13506
13857
  } catch {
13507
- console.log(chalk15.yellow(" \u26A0 Could not open browser automatically."));
13508
- console.log(` Open manually: ${chalk15.cyan(issueUrl)}
13858
+ console.log(chalk16.yellow(" \u26A0 Could not open browser automatically."));
13859
+ console.log(` Open manually: ${chalk16.cyan(issueUrl)}
13509
13860
  `);
13510
13861
  }
13511
13862
  });
@@ -13514,10 +13865,10 @@ var feedbackCommand = new Command14("feedback").description("Open a pre-filled G
13514
13865
  init_dist();
13515
13866
  init_dist();
13516
13867
  init_dist3();
13517
- import { Command as Command15 } from "commander";
13868
+ import { Command as Command16 } from "commander";
13518
13869
  import fs24 from "fs";
13519
13870
  import path26 from "path";
13520
- import chalk16 from "chalk";
13871
+ import chalk17 from "chalk";
13521
13872
  import matter3 from "gray-matter";
13522
13873
  function getDateStr(yesterday) {
13523
13874
  return iso(daysAgo(yesterday ? 1 : 0));
@@ -13547,7 +13898,7 @@ function formatStandup(standups, dateStr, raw) {
13547
13898
  const lines = [];
13548
13899
  const header = `Standup \u2014 ${dateStr}`;
13549
13900
  if (!raw) {
13550
- lines.push(chalk16.bold(`
13901
+ lines.push(chalk17.bold(`
13551
13902
  ${header}
13552
13903
  `));
13553
13904
  } else {
@@ -13560,12 +13911,12 @@ ${header}
13560
13911
  const tasksTotal = s.status.tasks_total ?? 0;
13561
13912
  const tasksInProgress = s.status.tasks_in_progress ?? 0;
13562
13913
  if (!raw) {
13563
- lines.push(chalk16.cyan.bold(`## ${s.name}`));
13914
+ lines.push(chalk17.cyan.bold(`## ${s.name}`));
13564
13915
  } else {
13565
13916
  lines.push(`## ${s.name}`);
13566
13917
  }
13567
13918
  if (s.gitCommits.length > 0 || tasksDone > 0) {
13568
- lines.push(!raw ? chalk16.green("Done:") : "**Done:**");
13919
+ lines.push(!raw ? chalk17.green("Done:") : "**Done:**");
13569
13920
  for (const commit of s.gitCommits) {
13570
13921
  lines.push(` - ${commit}`);
13571
13922
  }
@@ -13574,7 +13925,7 @@ ${header}
13574
13925
  }
13575
13926
  }
13576
13927
  if (tasksInProgress > 0) {
13577
- lines.push(!raw ? chalk16.yellow("In Progress:") : "**In Progress:**");
13928
+ lines.push(!raw ? chalk17.yellow("In Progress:") : "**In Progress:**");
13578
13929
  lines.push(` - ${tasksInProgress} task(s) active`);
13579
13930
  }
13580
13931
  if (s.dailyLog) {
@@ -13584,7 +13935,7 @@ ${header}
13584
13935
  if (match) {
13585
13936
  const items = match[1].trim().split("\n").filter((l) => l.trim().startsWith("-"));
13586
13937
  if (items.length > 0 && section === "Tomorrow") {
13587
- lines.push(!raw ? chalk16.blue("Next:") : "**Next:**");
13938
+ lines.push(!raw ? chalk17.blue("Next:") : "**Next:**");
13588
13939
  for (const item of items) lines.push(` ${item.trim()}`);
13589
13940
  }
13590
13941
  }
@@ -13592,20 +13943,20 @@ ${header}
13592
13943
  }
13593
13944
  if (s.blockers.length > 0) {
13594
13945
  hasBlockers = true;
13595
- lines.push(!raw ? chalk16.red("Blocked:") : "**Blocked:**");
13946
+ lines.push(!raw ? chalk17.red("Blocked:") : "**Blocked:**");
13596
13947
  for (const b of s.blockers) {
13597
13948
  lines.push(` - ${b}`);
13598
13949
  }
13599
13950
  }
13600
13951
  if (s.gitCommits.length === 0 && tasksDone === 0 && !s.dailyLog) {
13601
- lines.push(!raw ? chalk16.dim(" (no activity)") : " (no activity)");
13952
+ lines.push(!raw ? chalk17.dim(" (no activity)") : " (no activity)");
13602
13953
  }
13603
13954
  lines.push("");
13604
13955
  }
13605
13956
  const totalCommits = standups.reduce((sum, s) => sum + s.gitCommits.length, 0);
13606
13957
  const totalDone = standups.reduce((sum, s) => sum + (s.status.tasks_completed ?? 0), 0);
13607
13958
  if (!raw) {
13608
- lines.push(chalk16.dim(`--- ${totalCommits} commits, ${totalDone} tasks done${hasBlockers ? ", HAS BLOCKERS" : ""} ---
13959
+ lines.push(chalk17.dim(`--- ${totalCommits} commits, ${totalDone} tasks done${hasBlockers ? ", HAS BLOCKERS" : ""} ---
13609
13960
  `));
13610
13961
  } else {
13611
13962
  lines.push(`---
@@ -13614,12 +13965,12 @@ ${header}
13614
13965
  }
13615
13966
  return lines.join("\n");
13616
13967
  }
13617
- var standupCommand = new Command15("standup").description("Generate daily standup report from spoke vault data and git logs (zero tokens)").option("-p, --project <name>", "Show standup for a specific project only").option("-a, --all", "Show all projects (default)").option("-y, --yesterday", "Show yesterday's standup instead of today").option("-r, --raw", "Output raw markdown (for pasting into Slack/chat)").action(async (opts) => {
13968
+ var standupCommand = new Command16("standup").description("Generate daily standup report from spoke vault data and git logs (zero tokens)").option("-p, --project <name>", "Show standup for a specific project only").option("-a, --all", "Show all projects (default)").option("-y, --yesterday", "Show yesterday's standup instead of today").option("-r, --raw", "Output raw markdown (for pasting into Slack/chat)").action(async (opts) => {
13618
13969
  const config = loadConfig();
13619
13970
  const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
13620
13971
  const projects = Object.entries(config.projects);
13621
13972
  if (projects.length === 0) {
13622
- console.log(chalk16.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
13973
+ console.log(chalk17.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
13623
13974
  return;
13624
13975
  }
13625
13976
  const dateStr = getDateStr(!!opts.yesterday);
@@ -13628,7 +13979,7 @@ var standupCommand = new Command15("standup").description("Generate daily standu
13628
13979
  if (opts.project) {
13629
13980
  const match = projects.find(([name]) => name === opts.project);
13630
13981
  if (!match) {
13631
- console.error(chalk16.red(`Project "${opts.project}" not found.`));
13982
+ console.error(chalk17.red(`Project "${opts.project}" not found.`));
13632
13983
  process.exit(1);
13633
13984
  }
13634
13985
  targets = [match];
@@ -13646,10 +13997,10 @@ var standupCommand = new Command15("standup").description("Generate daily standu
13646
13997
  init_dist();
13647
13998
  init_dist();
13648
13999
  init_dist3();
13649
- import { Command as Command16 } from "commander";
14000
+ import { Command as Command17 } from "commander";
13650
14001
  import fs25 from "fs";
13651
14002
  import path27 from "path";
13652
- import chalk17 from "chalk";
14003
+ import chalk18 from "chalk";
13653
14004
  import matter4 from "gray-matter";
13654
14005
  function readStatus(spokeVault) {
13655
14006
  const statusFile = path27.join(spokeVault, "status.md");
@@ -13719,7 +14070,7 @@ function formatRetro(retros, fromStr, toStr, raw) {
13719
14070
  const lines = [];
13720
14071
  const header = `Retro \u2014 ${fromStr} \u2192 ${toStr}`;
13721
14072
  lines.push(raw ? `# ${header}
13722
- ` : chalk17.bold(`
14073
+ ` : chalk18.bold(`
13723
14074
  ${header}
13724
14075
  `));
13725
14076
  let totalCommits = 0;
@@ -13729,39 +14080,39 @@ ${header}
13729
14080
  totalCommits += r.commits.length;
13730
14081
  totalPRs += r.mergedPRs.length;
13731
14082
  totalShipped += r.shipped.length;
13732
- lines.push(raw ? `## ${r.name}` : chalk17.cyan.bold(`## ${r.name}`));
13733
- renderList(lines, r.shipped, raw, "Shipped", chalk17.green);
14083
+ lines.push(raw ? `## ${r.name}` : chalk18.cyan.bold(`## ${r.name}`));
14084
+ renderList(lines, r.shipped, raw, "Shipped", chalk18.green);
13734
14085
  if (r.mergedPRs.length > 0) {
13735
- lines.push(raw ? `**PRs merged:**` : chalk17.green("PRs merged:"));
14086
+ lines.push(raw ? `**PRs merged:**` : chalk18.green("PRs merged:"));
13736
14087
  for (const pr of r.mergedPRs) lines.push(` - ${pr}`);
13737
14088
  }
13738
- renderList(lines, r.inProgress, raw, "In Progress", chalk17.yellow);
13739
- renderList(lines, r.blocked, raw, "Blocked", chalk17.red);
13740
- renderList(lines, r.decisions, raw, "Key Decisions", chalk17.magenta);
14089
+ renderList(lines, r.inProgress, raw, "In Progress", chalk18.yellow);
14090
+ renderList(lines, r.blocked, raw, "Blocked", chalk18.red);
14091
+ renderList(lines, r.decisions, raw, "Key Decisions", chalk18.magenta);
13741
14092
  const metricBits = [
13742
14093
  `${r.commits.length} commits`,
13743
14094
  `${r.mergedPRs.length} PRs merged`,
13744
14095
  `${r.shipped.length} shipped`
13745
14096
  ];
13746
- lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` : chalk17.dim(` ${metricBits.join(" \xB7 ")}`));
14097
+ lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` : chalk18.dim(` ${metricBits.join(" \xB7 ")}`));
13747
14098
  if (r.shipped.length === 0 && r.commits.length === 0 && r.mergedPRs.length === 0 && r.inProgress.length === 0 && r.blocked.length === 0) {
13748
- lines.push(raw ? "_(no activity in this window)_" : chalk17.dim(" (no activity in this window)"));
14099
+ lines.push(raw ? "_(no activity in this window)_" : chalk18.dim(" (no activity in this window)"));
13749
14100
  }
13750
14101
  lines.push("");
13751
14102
  }
13752
14103
  const summary = `${totalShipped} items shipped \xB7 ${totalCommits} commits \xB7 ${totalPRs} PRs merged`;
13753
14104
  lines.push(raw ? `---
13754
14105
  *${summary}*
13755
- ` : chalk17.dim(`--- ${summary} ---
14106
+ ` : chalk18.dim(`--- ${summary} ---
13756
14107
  `));
13757
14108
  return lines.join("\n");
13758
14109
  }
13759
- var retroCommand = new Command16("retro").description("Generate a retro (weekly/sprint summary) from daily logs and git (zero tokens)").option("-w, --week", "Trailing 7 days (default)").option("-s, --sprint [days]", "Custom window of N days (default 14 if N omitted)").option("-p, --project <name>", "Retro for a single project").option("-a, --all", "All projects (default)").option("-r, --raw", "Raw markdown output (for pasting into Slack/Obsidian)").action(async (opts) => {
14110
+ var retroCommand = new Command17("retro").description("Generate a retro (weekly/sprint summary) from daily logs and git (zero tokens)").option("-w, --week", "Trailing 7 days (default)").option("-s, --sprint [days]", "Custom window of N days (default 14 if N omitted)").option("-p, --project <name>", "Retro for a single project").option("-a, --all", "All projects (default)").option("-r, --raw", "Raw markdown output (for pasting into Slack/Obsidian)").action(async (opts) => {
13760
14111
  const config = loadConfig();
13761
14112
  const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
13762
14113
  const projects = Object.entries(config.projects);
13763
14114
  if (projects.length === 0) {
13764
- console.log(chalk17.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
14115
+ console.log(chalk18.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
13765
14116
  return;
13766
14117
  }
13767
14118
  let windowDays = 7;
@@ -13778,7 +14129,7 @@ var retroCommand = new Command16("retro").description("Generate a retro (weekly/
13778
14129
  if (opts.project) {
13779
14130
  const match = projects.find(([name]) => name === opts.project);
13780
14131
  if (!match) {
13781
- console.error(chalk17.red(`Project "${opts.project}" not found.`));
14132
+ console.error(chalk18.red(`Project "${opts.project}" not found.`));
13782
14133
  process.exit(1);
13783
14134
  }
13784
14135
  targets = [match];
@@ -13794,8 +14145,8 @@ var retroCommand = new Command16("retro").description("Generate a retro (weekly/
13794
14145
  // packages/cli/src/commands/runtime.ts
13795
14146
  init_dist();
13796
14147
  init_dist3();
13797
- import { Command as Command17 } from "commander";
13798
- import chalk18 from "chalk";
14148
+ import { Command as Command18 } from "commander";
14149
+ import chalk19 from "chalk";
13799
14150
  function buildRegistry() {
13800
14151
  return new RuntimeRegistry({
13801
14152
  cmux: createCmuxDriver()
@@ -13827,7 +14178,7 @@ async function needRef(resolved) {
13827
14178
  }
13828
14179
  return ref.id;
13829
14180
  }
13830
- var runtimeCommand = new Command17("runtime").description("Interact with the runtime layer (workspaces). Bridges bash scripts to the RuntimeDriver.");
14181
+ var runtimeCommand = new Command18("runtime").description("Interact with the runtime layer (workspaces). Bridges bash scripts to the RuntimeDriver.");
13831
14182
  runtimeCommand.command("status").description("Print 'running' or 'stopped' for a target; exit 0 if running, 1 if not").argument("[target]", "Project name").option("--command", "Target the command workspace instead of a project captain").action(async (target, opts) => {
13832
14183
  const config = loadConfig();
13833
14184
  const registry = buildRegistry();
@@ -13842,7 +14193,7 @@ runtimeCommand.command("status").description("Print 'running' or 'stopped' for a
13842
14193
  process.exit(1);
13843
14194
  }
13844
14195
  } catch (err) {
13845
- console.error(chalk18.red(err.message));
14196
+ console.error(chalk19.red(err.message));
13846
14197
  process.exit(2);
13847
14198
  }
13848
14199
  });
@@ -13889,9 +14240,9 @@ async function runRuntimeSend(arg1, arg2, opts, confirmOpts) {
13889
14240
  runtimeCommand.command("send").description("Send a message to a target workspace AND commit with Enter. With --command, the first positional is the message.").argument("<arg1>", "Project name, or the message when --command is used").argument("[arg2]", "Message (when target is a project). Omit when using --command.").option("--command", "Target the command workspace").action(async (arg1, arg2, opts) => {
13890
14241
  try {
13891
14242
  await runRuntimeSend(arg1, arg2, opts);
13892
- console.log(chalk18.green("\u2714 Delivered (confirmed)"));
14243
+ console.log(chalk19.green("\u2714 Delivered (confirmed)"));
13893
14244
  } catch (err) {
13894
- console.error(chalk18.red(err.message));
14245
+ console.error(chalk19.red(err.message));
13895
14246
  process.exit(1);
13896
14247
  }
13897
14248
  });
@@ -13917,7 +14268,7 @@ runtimeCommand.command("read-screen").description("Print a terminal snapshot of
13917
14268
  const screen = await resolved.driver.readScreen(ref);
13918
14269
  process.stdout.write(screen);
13919
14270
  } catch (err) {
13920
- console.error(chalk18.red(err.message));
14271
+ console.error(chalk19.red(err.message));
13921
14272
  process.exit(1);
13922
14273
  }
13923
14274
  });
@@ -13928,13 +14279,13 @@ runtimeCommand.command("stop").description("Stop a target workspace").argument("
13928
14279
  const resolved = resolveTarget(registry, config, target, !!opts.command);
13929
14280
  const ref = await resolved.driver.status(resolved.workspaceName);
13930
14281
  if (!ref) {
13931
- console.log(chalk18.yellow(`Workspace '${resolved.workspaceName}' already stopped`));
14282
+ console.log(chalk19.yellow(`Workspace '${resolved.workspaceName}' already stopped`));
13932
14283
  return;
13933
14284
  }
13934
14285
  await resolved.driver.stop(ref.id);
13935
- console.log(chalk18.green(`\u2714 Stopped ${resolved.workspaceName}`));
14286
+ console.log(chalk19.green(`\u2714 Stopped ${resolved.workspaceName}`));
13936
14287
  } catch (err) {
13937
- console.error(chalk18.red(err.message));
14288
+ console.error(chalk19.red(err.message));
13938
14289
  process.exit(1);
13939
14290
  }
13940
14291
  });
@@ -13942,8 +14293,8 @@ runtimeCommand.command("stop").description("Stop a target workspace").argument("
13942
14293
  // packages/cli/src/commands/workspace.ts
13943
14294
  init_dist();
13944
14295
  init_dist3();
13945
- import { Command as Command18 } from "commander";
13946
- import chalk19 from "chalk";
14296
+ import { Command as Command19 } from "commander";
14297
+ import chalk20 from "chalk";
13947
14298
  function buildRegistry2() {
13948
14299
  return new WorkspaceRegistry({
13949
14300
  obsidian: createObsidianDriver
@@ -13961,7 +14312,7 @@ async function readStdin() {
13961
14312
  }
13962
14313
  return Buffer.concat(chunks).toString("utf-8");
13963
14314
  }
13964
- var workspaceCommand = new Command18("workspace").description("Interact with the workspace layer (vault storage). Bridges bash scripts to the WorkspaceDriver.");
14315
+ var workspaceCommand = new Command19("workspace").description("Interact with the workspace layer (vault storage). Bridges bash scripts to the WorkspaceDriver.");
13965
14316
  function resolveTargetAndPath(arg1, arg2, useHub) {
13966
14317
  if (useHub) {
13967
14318
  if (arg2 !== void 0) {
@@ -13983,7 +14334,7 @@ workspaceCommand.command("read").description("Print the contents of a scope-rela
13983
14334
  const content = await driver.read(path30);
13984
14335
  process.stdout.write(content);
13985
14336
  } catch (err) {
13986
- console.error(chalk19.red(err.message));
14337
+ console.error(chalk20.red(err.message));
13987
14338
  process.exit(1);
13988
14339
  }
13989
14340
  });
@@ -14013,7 +14364,7 @@ workspaceCommand.command("write").description("Write content to a scope-relative
14013
14364
  const payload = rawContent === "-" ? await readStdin() : rawContent;
14014
14365
  await driver.write(path30, payload);
14015
14366
  } catch (err) {
14016
- console.error(chalk19.red(err.message));
14367
+ console.error(chalk20.red(err.message));
14017
14368
  process.exit(1);
14018
14369
  }
14019
14370
  });
@@ -14026,7 +14377,7 @@ workspaceCommand.command("list").description("List entries in a scope-relative d
14026
14377
  const entries = await driver.list(path30);
14027
14378
  for (const entry of entries) console.log(entry);
14028
14379
  } catch (err) {
14029
- console.error(chalk19.red(err.message));
14380
+ console.error(chalk20.red(err.message));
14030
14381
  process.exit(1);
14031
14382
  }
14032
14383
  });
@@ -14039,7 +14390,7 @@ workspaceCommand.command("exists").description("Exit 0 if path exists, 1 if not"
14039
14390
  const ok2 = await driver.exists(path30);
14040
14391
  process.exit(ok2 ? 0 : 1);
14041
14392
  } catch (err) {
14042
- console.error(chalk19.red(err.message));
14393
+ console.error(chalk20.red(err.message));
14043
14394
  process.exit(2);
14044
14395
  }
14045
14396
  });
@@ -14051,7 +14402,7 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
14051
14402
  const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
14052
14403
  await driver.mkdir(path30);
14053
14404
  } catch (err) {
14054
- console.error(chalk19.red(err.message));
14405
+ console.error(chalk20.red(err.message));
14055
14406
  process.exit(1);
14056
14407
  }
14057
14408
  });
@@ -14059,8 +14410,8 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
14059
14410
  // packages/cli/src/commands/notify.ts
14060
14411
  init_dist();
14061
14412
  init_dist3();
14062
- import { Command as Command19 } from "commander";
14063
- import chalk20 from "chalk";
14413
+ import { Command as Command20 } from "commander";
14414
+ import chalk21 from "chalk";
14064
14415
  async function readStdin2() {
14065
14416
  const chunks = [];
14066
14417
  for await (const chunk of process.stdin) {
@@ -14068,7 +14419,7 @@ async function readStdin2() {
14068
14419
  }
14069
14420
  return Buffer.concat(chunks).toString("utf-8");
14070
14421
  }
14071
- var notifyCommand = new Command19("notify").description("Send a message to the user via the configured notifier").argument("<message>", "Message to send (use '-' to read from stdin)").action(async (message) => {
14422
+ var notifyCommand = new Command20("notify").description("Send a message to the user via the configured notifier").argument("<message>", "Message to send (use '-' to read from stdin)").action(async (message) => {
14072
14423
  const config = loadConfig();
14073
14424
  const registry = new NotifierRegistry({ cmux: createCmuxNotifier });
14074
14425
  try {
@@ -14076,7 +14427,7 @@ var notifyCommand = new Command19("notify").description("Send a message to the u
14076
14427
  if (!payload) throw new Error("Empty message");
14077
14428
  await registry.get(config).notify(payload);
14078
14429
  } catch (err) {
14079
- console.error(chalk20.red(err.message));
14430
+ console.error(chalk21.red(err.message));
14080
14431
  process.exit(1);
14081
14432
  }
14082
14433
  });
@@ -14086,8 +14437,8 @@ init_dist();
14086
14437
  init_dist4();
14087
14438
  init_dist3();
14088
14439
  init_dist();
14089
- import { Command as Command20 } from "commander";
14090
- import chalk21 from "chalk";
14440
+ import { Command as Command21 } from "commander";
14441
+ import chalk22 from "chalk";
14091
14442
  import fs26 from "fs";
14092
14443
  import path28 from "path";
14093
14444
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -14134,17 +14485,17 @@ async function runEmit(opts) {
14134
14485
  for (const dest of emitter.destinations(scope, projectRoot)) {
14135
14486
  const result = await emitter.emit(source, dest, { dryRun: opts.dryRun });
14136
14487
  if (opts.dryRun) {
14137
- console.log(chalk21.cyan(`[${emitter.name}] ${dest.path}`));
14488
+ console.log(chalk22.cyan(`[${emitter.name}] ${dest.path}`));
14138
14489
  console.log(result.diff ?? "(no diff)");
14139
14490
  } else if (result.written) {
14140
14491
  console.log(
14141
- chalk21.green(
14492
+ chalk22.green(
14142
14493
  `\u2714 ${emitter.name} \u2192 ${dest.path} (${result.bytesWritten} bytes)`
14143
14494
  )
14144
14495
  );
14145
14496
  emittedCount.written++;
14146
14497
  } else {
14147
- console.log(chalk21.gray(`- ${emitter.name} \u2192 ${dest.path} (skipped)`));
14498
+ console.log(chalk22.gray(`- ${emitter.name} \u2192 ${dest.path} (skipped)`));
14148
14499
  emittedCount.skipped++;
14149
14500
  }
14150
14501
  }
@@ -14167,14 +14518,14 @@ async function runEmit(opts) {
14167
14518
  `Unknown project '${projectName}'. Available: ${Object.keys(cfg.projects).join(", ") || "(none)"}`
14168
14519
  );
14169
14520
  }
14170
- console.error(chalk21.yellow(`\u26A0 unknown project: ${projectName}`));
14521
+ console.error(chalk22.yellow(`\u26A0 unknown project: ${projectName}`));
14171
14522
  continue;
14172
14523
  }
14173
14524
  const source = await readProjectLevelSource(
14174
14525
  createObsidianDriver({ root: proj.path })
14175
14526
  );
14176
14527
  if (!source) {
14177
- console.log(chalk21.gray(`- ${projectName}: no AGENTS.md, skipping`));
14528
+ console.log(chalk22.gray(`- ${projectName}: no AGENTS.md, skipping`));
14178
14529
  continue;
14179
14530
  }
14180
14531
  for (const name of targets) {
@@ -14184,21 +14535,21 @@ async function runEmit(opts) {
14184
14535
  }
14185
14536
  if (!opts.dryRun) {
14186
14537
  console.log(
14187
- chalk21.bold(
14538
+ chalk22.bold(
14188
14539
  `
14189
14540
  Projection complete \u2014 ${emittedCount.written} written, ${emittedCount.skipped} skipped.`
14190
14541
  )
14191
14542
  );
14192
14543
  }
14193
14544
  }
14194
- var projectionCommand = new Command20("projection").description(
14545
+ var projectionCommand = new Command21("projection").description(
14195
14546
  "Project squadrant instructions and skills to supported agent formats"
14196
14547
  );
14197
14548
  projectionCommand.command("emit").description("Emit projections to disk").option("--scope <scope>", "user or project", parseScope).option("--project <name>", "managed project name").option("--target <name>", "single target (cursor, codex, gemini, opencode)").option("--all", "emit user-level + every managed project").action(async (opts) => {
14198
14549
  try {
14199
14550
  await runEmit({ ...opts, dryRun: false });
14200
14551
  } catch (err) {
14201
- console.error(chalk21.red(err.message));
14552
+ console.error(chalk22.red(err.message));
14202
14553
  process.exit(1);
14203
14554
  }
14204
14555
  });
@@ -14206,7 +14557,7 @@ projectionCommand.command("diff").description("Preview changes without writing")
14206
14557
  try {
14207
14558
  await runEmit({ ...opts, dryRun: true });
14208
14559
  } catch (err) {
14209
- console.error(chalk21.red(err.message));
14560
+ console.error(chalk22.red(err.message));
14210
14561
  process.exit(1);
14211
14562
  }
14212
14563
  });
@@ -14216,7 +14567,7 @@ projectionCommand.command("list").description("List registered projection target
14216
14567
  const emitter = registry.get(name);
14217
14568
  const userDests = emitter.destinations("user").map((d) => d.path);
14218
14569
  const projectDests = emitter.destinations("project", "<project>").map((d) => d.path);
14219
- console.log(chalk21.bold(name));
14570
+ console.log(chalk22.bold(name));
14220
14571
  console.log(` user: ${userDests.join(", ") || "(none)"}`);
14221
14572
  console.log(` project: ${projectDests.join(", ") || "(none)"}`);
14222
14573
  }
@@ -14224,9 +14575,9 @@ projectionCommand.command("list").description("List registered projection target
14224
14575
 
14225
14576
  // packages/cli/src/commands/codex-chat-smoke.ts
14226
14577
  init_dist4();
14227
- import { Command as Command21 } from "commander";
14578
+ import { Command as Command22 } from "commander";
14228
14579
  import { resolve as resolve2 } from "path";
14229
- var codexChatSmokeCommand = new Command21("codex-chat-smoke").description("Phase 1 gate: prove the codex app-server JSON-RPC path works end-to-end.").option("--cwd <dir>", "working dir for the codex thread", process.cwd()).option("--model <m>", "model id (optional)").option(
14580
+ var codexChatSmokeCommand = new Command22("codex-chat-smoke").description("Phase 1 gate: prove the codex app-server JSON-RPC path works end-to-end.").option("--cwd <dir>", "working dir for the codex thread", process.cwd()).option("--model <m>", "model id (optional)").option(
14230
14581
  "--approval",
14231
14582
  "include the approval round-trip (Phase 1 PASS requires this)",
14232
14583
  false
@@ -14295,11 +14646,11 @@ init_dist();
14295
14646
  init_dist();
14296
14647
  init_dist();
14297
14648
  init_dist2();
14298
- import { Command as Command22 } from "commander";
14649
+ import { Command as Command23 } from "commander";
14299
14650
  import fs27 from "fs";
14300
14651
  import { fileURLToPath as fileURLToPath5 } from "url";
14301
14652
  import { dirname as dirname6, join as join25 } from "path";
14302
- import chalk22 from "chalk";
14653
+ import chalk23 from "chalk";
14303
14654
  function runConfigCheck(opts) {
14304
14655
  const raw = JSON.parse(fs27.readFileSync(opts.configPath, "utf-8"));
14305
14656
  const def = getDefaultConfig();
@@ -14354,14 +14705,14 @@ function runConfigSet(key, value, configPath = DEFAULT_CONFIG_PATH) {
14354
14705
  }
14355
14706
  function printRestartOutcome(outcome) {
14356
14707
  if (outcome === "skipped-not-running") {
14357
- console.log(chalk22.dim("(daemon not running \u2014 change applies on next start)"));
14708
+ console.log(chalk23.dim("(daemon not running \u2014 change applies on next start)"));
14358
14709
  } else if (outcome === "skipped-opt-out") {
14359
- console.log(chalk22.dim("(run 'squadrant heal daemon' to apply)"));
14710
+ console.log(chalk23.dim("(run 'squadrant heal daemon' to apply)"));
14360
14711
  }
14361
14712
  }
14362
14713
  function runConfigSetAction(opts) {
14363
14714
  runConfigSet(opts.key, opts.value, opts.configPath);
14364
- console.log(chalk22.green(`\u2714 set ${opts.key} = ${opts.value}`));
14715
+ console.log(chalk23.green(`\u2714 set ${opts.key} = ${opts.value}`));
14365
14716
  if (isDaemonCachedKey(opts.key)) {
14366
14717
  const doRestart = opts.doRestart ?? restartDaemonIfRunning;
14367
14718
  const outcome = doRestart({ reason: `config ${opts.key}`, noRestart: opts.noRestart });
@@ -14369,9 +14720,9 @@ function runConfigSetAction(opts) {
14369
14720
  }
14370
14721
  }
14371
14722
  var SEV_COLOR = {
14372
- info: chalk22.green,
14373
- advisory: chalk22.yellow,
14374
- warn: chalk22.red
14723
+ info: chalk23.green,
14724
+ advisory: chalk23.yellow,
14725
+ warn: chalk23.red
14375
14726
  };
14376
14727
  var KIND_GLYPH = {
14377
14728
  missing: "+",
@@ -14383,14 +14734,14 @@ function printItems(items) {
14383
14734
  for (const i of items) {
14384
14735
  const color = SEV_COLOR[i.severity] ?? ((s) => s);
14385
14736
  const detail = i.note ? ` (${i.note})` : i.suggested !== void 0 ? ` \u2192 ${JSON.stringify(i.suggested)}` : "";
14386
- console.log(" " + color(`${KIND_GLYPH[i.kind]} ${i.kind}: ${i.path}`) + chalk22.dim(detail));
14737
+ console.log(" " + color(`${KIND_GLYPH[i.kind]} ${i.kind}: ${i.path}`) + chalk23.dim(detail));
14387
14738
  }
14388
14739
  }
14389
- var configCommand = new Command22("config").description("Inspect and reconcile squadrant config");
14740
+ var configCommand = new Command23("config").description("Inspect and reconcile squadrant config");
14390
14741
  configCommand.command("check").description("Detect config drift vs the current default schema").option("--fix", "Apply the safe tier (add missing, remove deprecated)", false).option("--accept", "Stamp the current version without changing config (dismiss advisories)", false).option("--json", "Output drift items as JSON", false).action((opts) => {
14391
14742
  const pkgVersion = readPkgVersion2();
14392
14743
  if (!fs27.existsSync(DEFAULT_CONFIG_PATH)) {
14393
- console.log(chalk22.yellow("No config found \u2014 run `squadrant init` first."));
14744
+ console.log(chalk23.yellow("No config found \u2014 run `squadrant init` first."));
14394
14745
  return;
14395
14746
  }
14396
14747
  const res = runConfigCheck({ configPath: DEFAULT_CONFIG_PATH, pkgVersion, fix: opts.fix, accept: opts.accept });
@@ -14399,21 +14750,21 @@ configCommand.command("check").description("Detect config drift vs the current d
14399
14750
  return;
14400
14751
  }
14401
14752
  if (res.items.length === 0) {
14402
- console.log(chalk22.green("\u2714 Config is in sync with the current schema."));
14753
+ console.log(chalk23.green("\u2714 Config is in sync with the current schema."));
14403
14754
  return;
14404
14755
  }
14405
- console.log(chalk22.bold("\nConfig drift:\n"));
14756
+ console.log(chalk23.bold("\nConfig drift:\n"));
14406
14757
  printItems(res.items);
14407
14758
  if (opts.fix && res.applied.length) {
14408
- console.log(chalk22.green(`
14759
+ console.log(chalk23.green(`
14409
14760
  \u2714 Applied ${res.applied.length} safe item(s): ${res.applied.join(", ")}`));
14410
14761
  }
14411
14762
  const judgment = res.remaining.filter((i) => i.kind === "changed-default" || i.kind === "invalid");
14412
14763
  if (judgment.length) {
14413
- console.log(chalk22.yellow(`
14764
+ console.log(chalk23.yellow(`
14414
14765
  ${judgment.length} item(s) need review \u2014 run the config-doctor skill, or \`squadrant config check --accept\` to keep your values.`));
14415
14766
  } else if (res.stamped) {
14416
- console.log(chalk22.green("\n\u2714 Config reconciled and stamped."));
14767
+ console.log(chalk23.green("\n\u2714 Config reconciled and stamped."));
14417
14768
  }
14418
14769
  });
14419
14770
  configCommand.command("get").description("Read a config value by dotted key (e.g. defaults.effort)").argument("<key>", "dotted config key").action((key) => {
@@ -14421,7 +14772,7 @@ configCommand.command("get").description("Read a config value by dotted key (e.g
14421
14772
  const value = runConfigGet(key);
14422
14773
  console.log(typeof value === "string" ? value : JSON.stringify(value));
14423
14774
  } catch (e) {
14424
- console.error(chalk22.red(e.message));
14775
+ console.error(chalk23.red(e.message));
14425
14776
  process.exit(1);
14426
14777
  }
14427
14778
  });
@@ -14429,7 +14780,7 @@ configCommand.command("set").description("Write a config value by dotted key (e.
14429
14780
  try {
14430
14781
  runConfigSetAction({ key, value, noRestart: opts.restart === false });
14431
14782
  } catch (e) {
14432
- console.error(chalk22.red(e.message));
14783
+ console.error(chalk23.red(e.message));
14433
14784
  process.exit(1);
14434
14785
  }
14435
14786
  });
@@ -14439,8 +14790,8 @@ function readPkgVersion2() {
14439
14790
  }
14440
14791
 
14441
14792
  // packages/cli/src/commands/heal.ts
14442
- import { Command as Command23 } from "commander";
14443
- import chalk23 from "chalk";
14793
+ import { Command as Command24 } from "commander";
14794
+ import chalk24 from "chalk";
14444
14795
  init_dist2();
14445
14796
  init_dist2();
14446
14797
  function buildHealStatus(components) {
@@ -14481,15 +14832,15 @@ async function runHealStatus(opts) {
14481
14832
  return result.healthy ? 0 : 2;
14482
14833
  }
14483
14834
  if (result.healthy) {
14484
- stdout.write(chalk23.green("\u2714 all components healthy\n"));
14835
+ stdout.write(chalk24.green("\u2714 all components healthy\n"));
14485
14836
  return 0;
14486
14837
  }
14487
- stdout.write(chalk23.bold("Unhealthy components:\n\n"));
14838
+ stdout.write(chalk24.bold("Unhealthy components:\n\n"));
14488
14839
  for (const c of result.components) {
14489
14840
  if (c.healCmd) {
14490
- stdout.write(` ${chalk23.red("\u2718")} ${c.kind.padEnd(8)} ${c.ref.padEnd(16)} ${chalk23.red(c.state.padEnd(8))} ${c.project}
14841
+ stdout.write(` ${chalk24.red("\u2718")} ${c.kind.padEnd(8)} ${c.ref.padEnd(16)} ${chalk24.red(c.state.padEnd(8))} ${c.project}
14491
14842
  `);
14492
- stdout.write(` heal: ${chalk23.cyan(c.healCmd)}
14843
+ stdout.write(` heal: ${chalk24.cyan(c.healCmd)}
14493
14844
  `);
14494
14845
  }
14495
14846
  }
@@ -14500,7 +14851,7 @@ async function runHealDaemon(opts) {
14500
14851
  stdout.write("restarting squadrantd via launchd kickstart...\n");
14501
14852
  try {
14502
14853
  opts.ensureDaemon();
14503
- stdout.write(chalk23.green("\u2714 daemon kickstart complete\n"));
14854
+ stdout.write(chalk24.green("\u2714 daemon kickstart complete\n"));
14504
14855
  return 0;
14505
14856
  } catch (e) {
14506
14857
  stderr.write(`heal daemon failed: ${e.message}
@@ -14508,8 +14859,8 @@ async function runHealDaemon(opts) {
14508
14859
  return 1;
14509
14860
  }
14510
14861
  }
14511
- var healCommand = new Command23("heal").description("Targeted, idempotent remediation for squadrant components (daemon, health)").addHelpText("after", "\nDeferred: 'squadrant heal crew <id>' (re-attach) \u2014 see issue #100.").addCommand(
14512
- new Command23("status").description("Dry-run: print unhealthy components and the exact heal command for each").option("-p, --project <project>", "scope to one project").option("--json", "output machine-readable JSON (exit 0=healthy, 1=error, 2=unhealthy)").action(async (opts) => {
14862
+ var healCommand = new Command24("heal").description("Targeted, idempotent remediation for squadrant components (daemon, health)").addHelpText("after", "\nDeferred: 'squadrant heal crew <id>' (re-attach) \u2014 see issue #100.").addCommand(
14863
+ new Command24("status").description("Dry-run: print unhealthy components and the exact heal command for each").option("-p, --project <project>", "scope to one project").option("--json", "output machine-readable JSON (exit 0=healthy, 1=error, 2=unhealthy)").action(async (opts) => {
14513
14864
  const code = await runHealStatus({
14514
14865
  project: opts.project,
14515
14866
  json: opts.json ?? false,
@@ -14520,7 +14871,7 @@ var healCommand = new Command23("heal").description("Targeted, idempotent remedi
14520
14871
  process.exit(code);
14521
14872
  })
14522
14873
  ).addCommand(
14523
- new Command23("daemon").description("Restart squadrantd via the idempotent launchd kickstart path").action(async () => {
14874
+ new Command24("daemon").description("Restart squadrantd via the idempotent launchd kickstart path").action(async () => {
14524
14875
  const code = await runHealDaemon({
14525
14876
  ensureDaemon: () => restartDaemonIfRunning({ reason: "heal", isRunning: () => true }),
14526
14877
  stdout: process.stdout,
@@ -14531,15 +14882,15 @@ var healCommand = new Command23("heal").description("Targeted, idempotent remedi
14531
14882
  );
14532
14883
 
14533
14884
  // packages/cli/src/commands/group.ts
14534
- import { Command as Command25 } from "commander";
14535
- import chalk25 from "chalk";
14885
+ import { Command as Command26 } from "commander";
14886
+ import chalk26 from "chalk";
14536
14887
 
14537
14888
  // packages/cli/src/commands/dispatch.ts
14538
14889
  init_dist();
14539
14890
  init_dist2();
14540
- import { Command as Command24 } from "commander";
14891
+ import { Command as Command25 } from "commander";
14541
14892
  import { execSync as execSync13 } from "child_process";
14542
- import chalk24 from "chalk";
14893
+ import chalk25 from "chalk";
14543
14894
  async function runDispatch(toProject, task, opts) {
14544
14895
  const fromProject = resolveCurrentProject(loadConfig());
14545
14896
  if (!fromProject) {
@@ -14564,21 +14915,21 @@ async function runDispatch(toProject, task, opts) {
14564
14915
  async function dispatchAction(toProject, task, opts) {
14565
14916
  try {
14566
14917
  const result = await runDispatch(toProject, task, opts);
14567
- console.log(chalk24.green(`\u2714 Dispatched to '${toProject}' (task ${result.id.slice(0, 8)})`));
14568
- console.log(chalk24.dim(` originProject: ${result.originProject ?? "none"}`));
14569
- console.log(chalk24.dim(" You will be notified when the task settles (done/blocked/failed)."));
14918
+ console.log(chalk25.green(`\u2714 Dispatched to '${toProject}' (task ${result.id.slice(0, 8)})`));
14919
+ console.log(chalk25.dim(` originProject: ${result.originProject ?? "none"}`));
14920
+ console.log(chalk25.dim(" You will be notified when the task settles (done/blocked/failed)."));
14570
14921
  } catch (e) {
14571
- console.error(chalk24.red(`\u2718 ${e.message}`));
14922
+ console.error(chalk25.red(`\u2718 ${e.message}`));
14572
14923
  process.exit(1);
14573
14924
  }
14574
14925
  }
14575
- var dispatchCommand = new Command24("dispatch").description("Dispatch a task to any registered project (tracked, reports back on settle)").argument("<project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (same-group only; default: 120)", (v) => parseInt(v, 10) * 1e3).action(dispatchAction);
14926
+ var dispatchCommand = new Command25("dispatch").description("Dispatch a task to any registered project (tracked, reports back on settle)").argument("<project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (same-group only; default: 120)", (v) => parseInt(v, 10) * 1e3).action(dispatchAction);
14576
14927
 
14577
14928
  // packages/cli/src/commands/group.ts
14578
14929
  init_dist2();
14579
- var groupCommand = new Command25("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
14580
- new Command25("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (v) => parseInt(v, 10) * 1e3).action(async (toProject, task, opts) => {
14581
- console.error(chalk25.yellow(
14930
+ var groupCommand = new Command26("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
14931
+ new Command26("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (v) => parseInt(v, 10) * 1e3).action(async (toProject, task, opts) => {
14932
+ console.error(chalk26.yellow(
14582
14933
  `\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
14583
14934
  ));
14584
14935
  await dispatchAction(toProject, task, opts);
@@ -14589,8 +14940,8 @@ var groupCommand = new Command25("group").description("Cross-project intra-group
14589
14940
  init_dist();
14590
14941
  init_dist2();
14591
14942
  import { join as join26, dirname as dirname7 } from "path";
14592
- import { Command as Command26 } from "commander";
14593
- import chalk26 from "chalk";
14943
+ import { Command as Command27 } from "commander";
14944
+ import chalk27 from "chalk";
14594
14945
  init_require_daemon();
14595
14946
  async function runPing(project, message) {
14596
14947
  const config = loadConfig();
@@ -14606,20 +14957,20 @@ async function runPing(project, message) {
14606
14957
  source: "cli"
14607
14958
  });
14608
14959
  }
14609
- var pingCommand = new Command26("ping").description("Fire-and-forget: deliver a message into a registered project's captain pane (no tracked task, no report-back)").argument("<project>", "Target project name (must be registered)").argument("<message>", "Message to deliver").action(async (project, message) => {
14960
+ var pingCommand = new Command27("ping").description("Fire-and-forget: deliver a message into a registered project's captain pane (no tracked task, no report-back)").argument("<project>", "Target project name (must be registered)").argument("<message>", "Message to deliver").action(async (project, message) => {
14610
14961
  try {
14611
14962
  await runPing(project, message);
14612
- console.log(chalk26.green(`\u2714 Pinged '${project}'`));
14963
+ console.log(chalk27.green(`\u2714 Pinged '${project}'`));
14613
14964
  } catch (err) {
14614
- console.error(chalk26.red(err.message));
14965
+ console.error(chalk27.red(err.message));
14615
14966
  process.exit(1);
14616
14967
  }
14617
14968
  });
14618
14969
 
14619
14970
  // packages/cli/src/commands/cmux.ts
14620
14971
  init_dist();
14621
- import { Command as Command27 } from "commander";
14622
- import chalk27 from "chalk";
14972
+ import { Command as Command28 } from "commander";
14973
+ import chalk28 from "chalk";
14623
14974
  async function runCmuxAutoconfig(opts) {
14624
14975
  const { json, stdout, stderr } = opts;
14625
14976
  let r;
@@ -14638,19 +14989,19 @@ async function runCmuxAutoconfig(opts) {
14638
14989
  stdout.write(`wrote cmux automation config \u2192 ${r.configPath}
14639
14990
  `);
14640
14991
  } else {
14641
- stdout.write(chalk27.dim(`cmux automation config already in place (${r.configPath})
14992
+ stdout.write(chalk28.dim(`cmux automation config already in place (${r.configPath})
14642
14993
  `));
14643
14994
  }
14644
14995
  if (r.verdict === "reachable") {
14645
- stdout.write(chalk27.green("\u2714 daemon-direct delivery is reachable \u2014 cmux control socket accepts the daemon\n"));
14996
+ stdout.write(chalk28.green("\u2714 daemon-direct delivery is reachable \u2014 cmux control socket accepts the daemon\n"));
14646
14997
  return 0;
14647
14998
  }
14648
14999
  if (r.needsRestart) {
14649
15000
  stdout.write(
14650
- chalk27.yellow("\u26A0 cmux is still on the old socket mode \u2014 restart cmux to enable daemon-direct delivery.\n")
15001
+ chalk28.yellow("\u26A0 cmux is still on the old socket mode \u2014 restart cmux to enable daemon-direct delivery.\n")
14651
15002
  );
14652
15003
  if (r.promptedThisRun) {
14653
- stdout.write(chalk27.dim(" (one-time prompt \u2014 you won't be nagged again)\n"));
15004
+ stdout.write(chalk28.dim(" (one-time prompt \u2014 you won't be nagged again)\n"));
14654
15005
  }
14655
15006
  return 2;
14656
15007
  }
@@ -14659,8 +15010,8 @@ async function runCmuxAutoconfig(opts) {
14659
15010
  );
14660
15011
  return 1;
14661
15012
  }
14662
- var cmuxCommand = new Command27("cmux").description("cmux integration helpers").addCommand(
14663
- new Command27("autoconfig").description(
15013
+ var cmuxCommand = new Command28("cmux").description("cmux integration helpers").addCommand(
15014
+ new Command28("autoconfig").description(
14664
15015
  "Write the cmux automation socket config and probe whether daemon-direct\ndelivery is reachable. Idempotent; prompts once if a cmux restart is needed."
14665
15016
  ).option("--json", "output machine-readable JSON (exit 0=reachable, 1=unknown, 2=restart-needed)").action(async (opts) => {
14666
15017
  const code = await runCmuxAutoconfig({
@@ -14677,8 +15028,8 @@ init_dist();
14677
15028
  init_dist2();
14678
15029
  import fs28 from "fs";
14679
15030
  import path29 from "path";
14680
- import { Command as Command28 } from "commander";
14681
- import chalk28 from "chalk";
15031
+ import { Command as Command29 } from "commander";
15032
+ import chalk29 from "chalk";
14682
15033
  var VALID_EFFORTS = ["max", "balance", "low"];
14683
15034
  var EFFORT_MEANING = {
14684
15035
  max: "tokens are plentiful \u2014 bias crew spawns toward claude/opus",
@@ -14743,29 +15094,29 @@ async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(
14743
15094
  }
14744
15095
  }
14745
15096
  }
14746
- var effortCommand = new Command28("effort").description("Get or set the crew tokenomics dial (max | balance | low) \u2014 global by default, or per-project with --project").argument("[value]", "effort level to set: max | balance | low").option("--project <name>", "target a specific project (get: show its resolved effort; set: write a per-project override)").action(async (value, options) => {
15097
+ var effortCommand = new Command29("effort").description("Get or set the crew tokenomics dial (max | balance | low) \u2014 global by default, or per-project with --project").argument("[value]", "effort level to set: max | balance | low").option("--project <name>", "target a specific project (get: show its resolved effort; set: write a per-project override)").action(async (value, options) => {
14747
15098
  if (value === void 0) {
14748
15099
  let result;
14749
15100
  try {
14750
15101
  result = runEffortGet(void 0, options.project);
14751
15102
  } catch (err) {
14752
- console.error(chalk28.red(err.message));
15103
+ console.error(chalk29.red(err.message));
14753
15104
  process.exit(1);
14754
15105
  }
14755
15106
  const label = options.project ? `${options.project} project` : "global";
14756
- console.log(chalk28.bold(`Current effort (${label}):`), chalk28.cyan(result.effort));
14757
- console.log(chalk28.dim(EFFORT_MEANING[result.effort]));
15107
+ console.log(chalk29.bold(`Current effort (${label}):`), chalk29.cyan(result.effort));
15108
+ console.log(chalk29.dim(EFFORT_MEANING[result.effort]));
14758
15109
  return;
14759
15110
  }
14760
15111
  try {
14761
15112
  runEffortSet(value, void 0, options.project);
14762
15113
  } catch (err) {
14763
- console.error(chalk28.red(err.message));
15114
+ console.error(chalk29.red(err.message));
14764
15115
  process.exit(1);
14765
15116
  }
14766
15117
  const effort = value;
14767
- console.log(chalk28.green(`\u2714 effort \u2192 ${effort} (${effortScopeLabel(options.project)})`));
14768
- console.log(chalk28.dim(EFFORT_MEANING[effort]));
15118
+ console.log(chalk29.green(`\u2714 effort \u2192 ${effort} (${effortScopeLabel(options.project)})`));
15119
+ console.log(chalk29.dim(EFFORT_MEANING[effort]));
14769
15120
  try {
14770
15121
  const { createCmuxDriver: createCmuxDriver2, RuntimeRegistry: RuntimeRegistry2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports3));
14771
15122
  const config = loadConfig();
@@ -14775,7 +15126,7 @@ var effortCommand = new Command28("effort").description("Get or set the crew tok
14775
15126
  const append = (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" });
14776
15127
  await notifyCaptainsOfEffort(effort, config, driver, process.cwd(), append, options.project);
14777
15128
  } catch {
14778
- console.log(chalk28.dim("(no running captain detected \u2014 change applies on next launch)"));
15129
+ console.log(chalk29.dim("(no running captain detected \u2014 change applies on next launch)"));
14779
15130
  }
14780
15131
  });
14781
15132
 
@@ -14784,8 +15135,8 @@ init_dist();
14784
15135
  init_dist2();
14785
15136
  import { join as join27, dirname as dirname8 } from "path";
14786
15137
  import { emitKeypressEvents } from "readline";
14787
- import { Command as Command29 } from "commander";
14788
- import chalk29 from "chalk";
15138
+ import { Command as Command30 } from "commander";
15139
+ import chalk30 from "chalk";
14789
15140
  function defaultStateRoot() {
14790
15141
  return join27(dirname8(DEFAULT_CONFIG_PATH), "state");
14791
15142
  }
@@ -14832,11 +15183,11 @@ async function questionYesNo(prompt) {
14832
15183
  });
14833
15184
  });
14834
15185
  }
14835
- var telegramCommand = new Command29("telegram").description("Two-way Telegram integration: push crew events to a topic and reply into the captain pane");
15186
+ var telegramCommand = new Command30("telegram").description("Two-way Telegram integration: push crew events to a topic and reply into the captain pane");
14836
15187
  telegramCommand.command("status").description("Show Telegram config and linked projects").action(() => {
14837
15188
  const { tokenSet, supergroupId, links } = runTelegramStatus({ config: loadConfig(), stateRoot: defaultStateRoot() });
14838
- console.log(`token: ${tokenSet ? chalk29.green("set") : chalk29.yellow("unset")}`);
14839
- console.log(`supergroup: ${supergroupId ?? chalk29.yellow("unset")}`);
15189
+ console.log(`token: ${tokenSet ? chalk30.green("set") : chalk30.yellow("unset")}`);
15190
+ console.log(`supergroup: ${supergroupId ?? chalk30.yellow("unset")}`);
14840
15191
  if (links.length === 0) {
14841
15192
  console.log("no projects linked");
14842
15193
  return;
@@ -14846,32 +15197,32 @@ telegramCommand.command("status").description("Show Telegram config and linked p
14846
15197
  telegramCommand.command("link").argument("<project>", "project to bind to a Telegram topic").description("Create (or reuse) a forum topic for a project and bind it").action(async (project) => {
14847
15198
  const cfg = loadConfig().telegram;
14848
15199
  if (!cfg) {
14849
- console.error(chalk29.red("telegram config absent \u2014 add a `telegram` block to ~/.config/squadrant/config.json"));
15200
+ console.error(chalk30.red("telegram config absent \u2014 add a `telegram` block to ~/.config/squadrant/config.json"));
14850
15201
  process.exit(1);
14851
15202
  }
14852
15203
  const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
14853
15204
  if (!token) {
14854
- console.error(chalk29.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
15205
+ console.error(chalk30.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
14855
15206
  process.exit(1);
14856
15207
  }
14857
15208
  const client = createTelegramClient({ token });
14858
15209
  const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
14859
- console.log(chalk29.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
15210
+ console.log(chalk30.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
14860
15211
  });
14861
15212
  telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").option("--reset-token", "force re-entry of the bot token even if one already exists").option("--redetect", "force group re-detection even when a supergroup is already configured").option("--user-id <id>", "allowlist user-id \u2014 enables remote control on a re-run without getUpdates detection", (v) => parseInt(v, 10)).action(async (opts) => {
14862
15213
  if (!process.stdin.isTTY) {
14863
- console.error(chalk29.red("setup requires a TTY \u2014 pipe input is not supported"));
15214
+ console.error(chalk30.red("setup requires a TTY \u2014 pipe input is not supported"));
14864
15215
  process.exit(1);
14865
15216
  }
14866
15217
  console.log();
14867
- console.log(chalk29.bold("Telegram setup") + " \u2014 connect squadrant to a Telegram bot for notifications + remote control");
15218
+ console.log(chalk30.bold("Telegram setup") + " \u2014 connect squadrant to a Telegram bot for notifications + remote control");
14868
15219
  console.log();
14869
15220
  console.log("Before you start you need:");
14870
15221
  console.log(" 1. A bot token from @BotFather (send /newbot)");
14871
15222
  console.log(" 2. A forum supergroup with the bot added as an admin (Topics enabled)");
14872
15223
  console.log(" 3. Bot privacy mode set to OFF (@BotFather \u2192 /setprivacy \u2192 Disable)");
14873
15224
  console.log();
14874
- console.log(chalk29.bold("Step 1/3 \u2014 Bot token"));
15225
+ console.log(chalk30.bold("Step 1/3 \u2014 Bot token"));
14875
15226
  const existingCfg = loadConfig().telegram;
14876
15227
  const existingToken = existingCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
14877
15228
  const decision = resolveSetupToken(existingToken, { resetToken: opts.resetToken ?? false });
@@ -14883,67 +15234,67 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
14883
15234
  try {
14884
15235
  botUser = await client.getMe();
14885
15236
  token = existingToken;
14886
- console.log(chalk29.green(`Using existing bot token (@${botUser.username})`));
15237
+ console.log(chalk30.green(`Using existing bot token (@${botUser.username})`));
14887
15238
  console.log();
14888
15239
  } catch {
14889
- console.log(chalk29.yellow("Existing token is invalid \u2014 please enter a new one."));
15240
+ console.log(chalk30.yellow("Existing token is invalid \u2014 please enter a new one."));
14890
15241
  console.log("Paste your bot token then press Enter (input is hidden):");
14891
15242
  token = await questionMasked();
14892
15243
  if (!token) {
14893
- console.error(chalk29.red("token required"));
15244
+ console.error(chalk30.red("token required"));
14894
15245
  process.exit(1);
14895
15246
  }
14896
15247
  client = createTelegramClient({ token });
14897
15248
  try {
14898
15249
  botUser = await client.getMe();
14899
15250
  } catch (e) {
14900
- console.error(chalk29.red(`token rejected: ${e.message}`));
15251
+ console.error(chalk30.red(`token rejected: ${e.message}`));
14901
15252
  process.exit(1);
14902
15253
  }
14903
- console.log(chalk29.green(`Connected as @${botUser.username}`));
15254
+ console.log(chalk30.green(`Connected as @${botUser.username}`));
14904
15255
  console.log();
14905
15256
  }
14906
15257
  } else {
14907
15258
  console.log("Paste your bot token then press Enter (input is hidden):");
14908
15259
  token = await questionMasked();
14909
15260
  if (!token) {
14910
- console.error(chalk29.red("token required"));
15261
+ console.error(chalk30.red("token required"));
14911
15262
  process.exit(1);
14912
15263
  }
14913
15264
  client = createTelegramClient({ token });
14914
15265
  try {
14915
15266
  botUser = await client.getMe();
14916
15267
  } catch (e) {
14917
- console.error(chalk29.red(`token rejected: ${e.message}`));
15268
+ console.error(chalk30.red(`token rejected: ${e.message}`));
14918
15269
  process.exit(1);
14919
15270
  }
14920
- console.log(chalk29.green(`Connected as @${botUser.username}`));
15271
+ console.log(chalk30.green(`Connected as @${botUser.username}`));
14921
15272
  console.log();
14922
15273
  }
14923
- console.log(chalk29.bold("Step 2/3 \u2014 Supergroup"));
15274
+ console.log(chalk30.bold("Step 2/3 \u2014 Supergroup"));
14924
15275
  const groupDecision = resolveSetupGroup(existingCfg?.supergroupId, { redetect: opts.redetect ?? false });
14925
15276
  let supergroupId;
14926
15277
  let detectedUserId;
14927
15278
  if (groupDecision === "reuse") {
14928
15279
  supergroupId = existingCfg.supergroupId;
14929
- console.log(chalk29.green(`Using existing group: ${supergroupId}`));
15280
+ console.log(chalk30.green(`Using existing group: ${supergroupId}`));
14930
15281
  console.log();
14931
15282
  } else {
14932
15283
  console.log("Add the bot to your forum supergroup, then send any message in it.");
14933
- console.log(chalk29.dim("Waiting for a message (up to 60s)\u2026"));
15284
+ console.log(chalk30.dim("Waiting for a message (up to 60s)\u2026"));
14934
15285
  try {
14935
15286
  ({ supergroupId, userId: detectedUserId } = await detectGroupAndUser(client, { timeoutMs: 6e4 }));
14936
15287
  } catch {
14937
- console.error(chalk29.red("Timed out \u2014 no supergroup message received within 60s."));
14938
- console.error(chalk29.yellow("Check: bot is an admin in the group \xB7 privacy mode is OFF \xB7 Topics enabled"));
15288
+ console.error(chalk30.red("Timed out \u2014 no supergroup message received within 60s."));
15289
+ console.error(chalk30.yellow("Check: bot is an admin in the group \xB7 privacy mode is OFF \xB7 Topics enabled"));
14939
15290
  process.exit(1);
14940
15291
  }
14941
- console.log(chalk29.green(`Found group: ${supergroupId}`));
15292
+ console.log(chalk30.green(`Found group: ${supergroupId}`));
14942
15293
  console.log();
14943
15294
  }
14944
- console.log(chalk29.bold("Step 3/3 \u2014 Remote control + Save"));
14945
- console.log(chalk29.dim("Remote control enables auto-launching captains and the General command channel"));
14946
- console.log(chalk29.dim("from your phone \u2014 gated to your Telegram user-id only (fail-closed)."));
15295
+ console.log(chalk30.bold("Step 3/3 \u2014 Remote control + Save"));
15296
+ console.log(chalk30.dim("Remote control enables auto-launching captains and the General command channel"));
15297
+ console.log(chalk30.dim("from your phone \u2014 gated to your Telegram user-id only (fail-closed)."));
14947
15298
  const finalUserId = resolveSetupUserId(opts.userId, detectedUserId, defaultStateRoot());
14948
15299
  let users;
14949
15300
  let remoteControl;
@@ -14957,32 +15308,32 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
14957
15308
  remoteControl = true;
14958
15309
  }
14959
15310
  } else if (groupDecision === "detect") {
14960
- console.log(chalk29.yellow("Could not read your user-id from that message \u2014 skipping remote control."));
14961
- console.log(chalk29.yellow("Re-run with --user-id <id> to enable, or edit telegram.users in config manually."));
15311
+ console.log(chalk30.yellow("Could not read your user-id from that message \u2014 skipping remote control."));
15312
+ console.log(chalk30.yellow("Re-run with --user-id <id> to enable, or edit telegram.users in config manually."));
14962
15313
  printedRemoteControlState = true;
14963
15314
  } else {
14964
15315
  const existingUsers = existingCfg?.users;
14965
15316
  if (existingUsers && existingUsers.length > 0) {
14966
- console.log(chalk29.dim(`Remote control: already configured (user-id ${existingUsers[0]}). Use --user-id to update.`));
15317
+ console.log(chalk30.dim(`Remote control: already configured (user-id ${existingUsers[0]}). Use --user-id to update.`));
14967
15318
  } else {
14968
- console.log(chalk29.dim("Remote control: off. Re-run with --user-id <id> to enable."));
15319
+ console.log(chalk30.dim("Remote control: off. Re-run with --user-id <id> to enable."));
14969
15320
  }
14970
15321
  printedRemoteControlState = true;
14971
15322
  }
14972
15323
  writeTelegramConfig(DEFAULT_CONFIG_PATH, { token, supergroupId, users, remoteControl });
14973
- console.log(chalk29.green(`Wrote telegram config \u2014 token: ${maskToken(token)} group: ${supergroupId}`));
15324
+ console.log(chalk30.green(`Wrote telegram config \u2014 token: ${maskToken(token)} group: ${supergroupId}`));
14974
15325
  if (!printedRemoteControlState) {
14975
15326
  if (remoteControl) {
14976
- console.log(chalk29.green(`Remote control: ON (allowlisted user-id ${users[0]})`));
15327
+ console.log(chalk30.green(`Remote control: ON (allowlisted user-id ${users[0]})`));
14977
15328
  } else {
14978
- console.log(chalk29.dim("Remote control: off (default). Re-run with --user-id <id> to enable."));
15329
+ console.log(chalk30.dim("Remote control: off (default). Re-run with --user-id <id> to enable."));
14979
15330
  }
14980
15331
  }
14981
15332
  try {
14982
15333
  await runRegisterCommands({ client });
14983
- console.log(chalk29.dim("Registered the /command menu."));
15334
+ console.log(chalk30.dim("Registered the /command menu."));
14984
15335
  } catch (e) {
14985
- console.log(chalk29.yellow(`command-menu registration skipped: ${e.message}`));
15336
+ console.log(chalk30.yellow(`command-menu registration skipped: ${e.message}`));
14986
15337
  }
14987
15338
  const topics = loadState(defaultStateRoot()).topics;
14988
15339
  const topicEntries = Object.entries(topics);
@@ -14991,28 +15342,28 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
14991
15342
  const project = key.slice(0, key.indexOf("::"));
14992
15343
  return `${project}\u2192${id}`;
14993
15344
  }).join(", ");
14994
- console.log(chalk29.dim(`Existing topics: ${summary} (already created \u2014 not recreated)`));
15345
+ console.log(chalk30.dim(`Existing topics: ${summary} (already created \u2014 not recreated)`));
14995
15346
  } else {
14996
- console.log(chalk29.dim("No project topics yet \u2014 they're created on first delivery or via: squadrant telegram link <project>"));
15347
+ console.log(chalk30.dim("No project topics yet \u2014 they're created on first delivery or via: squadrant telegram link <project>"));
14997
15348
  }
14998
15349
  runTelegramPostSetup({});
14999
15350
  console.log();
15000
- console.log(`Next: ${chalk29.cyan("squadrant telegram link <project>")}`);
15351
+ console.log(`Next: ${chalk30.cyan("squadrant telegram link <project>")}`);
15001
15352
  });
15002
15353
  telegramCommand.command("register-commands").description("Register (or re-register) the bot's / command menu with Telegram").action(async () => {
15003
15354
  const cfg = loadConfig().telegram;
15004
15355
  if (!cfg) {
15005
- console.error(chalk29.red("telegram config absent \u2014 run: squadrant telegram setup"));
15356
+ console.error(chalk30.red("telegram config absent \u2014 run: squadrant telegram setup"));
15006
15357
  process.exit(1);
15007
15358
  }
15008
15359
  const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
15009
15360
  if (!token) {
15010
- console.error(chalk29.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
15361
+ console.error(chalk30.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
15011
15362
  process.exit(1);
15012
15363
  }
15013
15364
  const client = createTelegramClient({ token });
15014
15365
  await runRegisterCommands({ client });
15015
- console.log(chalk29.green(`registered ${BOT_COMMANDS.length} bot commands`));
15366
+ console.log(chalk30.green(`registered ${BOT_COMMANDS.length} bot commands`));
15016
15367
  });
15017
15368
  telegramCommand.command("notify").argument("[project]", "project to toggle").argument("[state]", "on | off | crew | cap").argument("[value]", "tier for crew (all|alert_only|done_only|none) or on|off for cap").option("--status", "list notification state for all projects").description("Live on|off (state), or crew <tier> / cap <on|off> preference (per-project config)").action(async (project, state, value, opts) => {
15018
15369
  const stateRoot = defaultStateRoot();
@@ -15023,7 +15374,7 @@ telegramCommand.command("notify").argument("[project]", "project to toggle").arg
15023
15374
  return;
15024
15375
  }
15025
15376
  for (const r of rows) {
15026
- console.log(` ${r.project}: ${r.active ? chalk29.green("on") : chalk29.dim("off (muted)")}`);
15377
+ console.log(` ${r.project}: ${r.active ? chalk30.green("on") : chalk30.dim("off (muted)")}`);
15027
15378
  }
15028
15379
  return;
15029
15380
  }
@@ -15032,53 +15383,53 @@ telegramCommand.command("notify").argument("[project]", "project to toggle").arg
15032
15383
  const token = tgCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
15033
15384
  if (state === "crew" || state === "cap") {
15034
15385
  if (value === void 0) {
15035
- console.error(chalk29.red(`usage: squadrant telegram notify <project> ${state} <value>`));
15386
+ console.error(chalk30.red(`usage: squadrant telegram notify <project> ${state} <value>`));
15036
15387
  process.exit(1);
15037
15388
  }
15038
15389
  const resolved2 = resolveNotify(globalNotify, loadProjectOverride(project));
15039
15390
  const before2 = { ...resolved2, active: isNotifyActive(stateRoot, project) };
15040
15391
  const res = runTelegramNotifyPref({ project, dimension: state, value });
15041
15392
  if (!res.ok) {
15042
- console.error(chalk29.red(res.message));
15393
+ console.error(chalk30.red(res.message));
15043
15394
  process.exit(1);
15044
15395
  }
15045
- console.log(chalk29.green(`${project} ${state} = ${value}`));
15396
+ console.log(chalk30.green(`${project} ${state} = ${value}`));
15046
15397
  const after2 = state === "crew" ? { ...before2, crew: value } : { ...before2, cap: value === "on" };
15047
15398
  if (tgCfg && token) {
15048
15399
  const client = createTelegramClient({ token });
15049
15400
  const sent = await runNotifyConfirmation({ project, before: before2, after: after2, cfg: tgCfg, client, stateRoot });
15050
- if (sent) console.log(chalk29.dim(`\u2192 notified ${project} topic`));
15401
+ if (sent) console.log(chalk30.dim(`\u2192 notified ${project} topic`));
15051
15402
  }
15052
15403
  return;
15053
15404
  }
15054
15405
  if (state !== "on" && state !== "off") {
15055
- console.error(chalk29.red("usage: squadrant telegram notify <project> <on|off|crew <tier>|cap <on|off>>"));
15406
+ console.error(chalk30.red("usage: squadrant telegram notify <project> <on|off|crew <tier>|cap <on|off>>"));
15056
15407
  process.exit(1);
15057
15408
  }
15058
15409
  const resolved = resolveNotify(globalNotify, loadProjectOverride(project));
15059
15410
  const before = { ...resolved, active: isNotifyActive(stateRoot, project) };
15060
15411
  const after = { ...before, active: state === "on" };
15061
15412
  runTelegramNotifySet({ project, active: state === "on", stateRoot });
15062
- console.log(chalk29.green(`${project} notifications ${state === "on" ? "ON" : "OFF"}`));
15413
+ console.log(chalk30.green(`${project} notifications ${state === "on" ? "ON" : "OFF"}`));
15063
15414
  if (tgCfg && token) {
15064
15415
  const client = createTelegramClient({ token });
15065
15416
  const sent = await runNotifyConfirmation({ project, before, after, cfg: tgCfg, client, stateRoot });
15066
- if (sent) console.log(chalk29.dim(`\u2192 notified ${project} topic`));
15417
+ if (sent) console.log(chalk30.dim(`\u2192 notified ${project} topic`));
15067
15418
  }
15068
15419
  });
15069
15420
  telegramCommand.command("send").argument("<project>", "project whose topic receives the message").argument("[message...]", "message text (omit to read from stdin)").description("Send a message to a project's linked Telegram topic").action(async (project, messageParts) => {
15070
15421
  const cfg = loadConfig().telegram;
15071
15422
  if (!cfg) {
15072
- console.error(chalk29.red("telegram config absent \u2014 add a `telegram` block to ~/.config/squadrant/config.json"));
15423
+ console.error(chalk30.red("telegram config absent \u2014 add a `telegram` block to ~/.config/squadrant/config.json"));
15073
15424
  process.exit(1);
15074
15425
  }
15075
15426
  const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
15076
15427
  if (!token) {
15077
- console.error(chalk29.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
15428
+ console.error(chalk30.red("no botToken in config and TELEGRAM_BOT_TOKEN is unset"));
15078
15429
  process.exit(1);
15079
15430
  }
15080
15431
  if (!capAllowed(project, cfg.notify)) {
15081
- console.log(chalk29.dim(`${project}: captain messages muted (cap=off) \u2014 not sent`));
15432
+ console.log(chalk30.dim(`${project}: captain messages muted (cap=off) \u2014 not sent`));
15082
15433
  return;
15083
15434
  }
15084
15435
  let message;
@@ -15091,19 +15442,19 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
15091
15442
  for await (const line of rl) lines.push(line);
15092
15443
  message = lines.join("\n").trimEnd();
15093
15444
  if (!message) {
15094
- console.error(chalk29.red("no message provided (stdin was empty)"));
15445
+ console.error(chalk30.red("no message provided (stdin was empty)"));
15095
15446
  process.exit(1);
15096
15447
  }
15097
15448
  } else {
15098
- console.error(chalk29.red("message required \u2014 pass as argument or pipe via stdin"));
15449
+ console.error(chalk30.red("message required \u2014 pass as argument or pipe via stdin"));
15099
15450
  process.exit(1);
15100
15451
  }
15101
15452
  const client = createTelegramClient({ token });
15102
15453
  try {
15103
15454
  const { chatId, topicId } = await runTelegramSend({ project, message, cfg, client, stateRoot: defaultStateRoot() });
15104
- console.log(chalk29.green(`sent to group ${chatId} topic ${topicId}`));
15455
+ console.log(chalk30.green(`sent to group ${chatId} topic ${topicId}`));
15105
15456
  } catch (e) {
15106
- console.error(chalk29.red(e.message));
15457
+ console.error(chalk30.red(e.message));
15107
15458
  process.exit(1);
15108
15459
  }
15109
15460
  });
@@ -15111,7 +15462,7 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
15111
15462
  // packages/cli/src/commands/hooks.ts
15112
15463
  init_dist2();
15113
15464
  init_dist4();
15114
- import { Command as Command30 } from "commander";
15465
+ import { Command as Command31 } from "commander";
15115
15466
  import { join as join28 } from "path";
15116
15467
  import { homedir as homedir20 } from "os";
15117
15468
  var SOCK4 = join28(homedir20(), ".config", "squadrant", "squadrant.sock");
@@ -15138,7 +15489,7 @@ function mapHookSub(sub, payload, taskId) {
15138
15489
  }
15139
15490
  }
15140
15491
  function hooksCommand() {
15141
- const hooks = new Command30("hooks").description("(internal) receive lifecycle hook events from agent processes");
15492
+ const hooks = new Command31("hooks").description("(internal) receive lifecycle hook events from agent processes");
15142
15493
  hooks.command("claude <sub>", { hidden: true }).description("internal: bridge a NativeHookSource claude hook to squadrantd").action(async (sub) => {
15143
15494
  const taskId = process.env.SQUADRANT_CREW_TASK_ID;
15144
15495
  const project = process.env.SQUADRANT_CREW_PROJECT;
@@ -15209,7 +15560,7 @@ if (process.argv[2] !== "config") {
15209
15560
  if (!process.env.SQUADRANT_DAEMON_SKIP) {
15210
15561
  ensureDaemon();
15211
15562
  }
15212
- var program = new Command31();
15563
+ var program = new Command32();
15213
15564
  program.name("squadrant").description("Multi-project orchestration for your coding agents (Claude, Codex, opencode, Gemini)").version(pkg.version);
15214
15565
  program.addCommand(doctorCommand);
15215
15566
  program.addCommand(initCommand);
@@ -15218,6 +15569,7 @@ program.addCommand(statusCommand);
15218
15569
  addControlPlaneCrewCommands(crewCommand);
15219
15570
  program.addCommand(crewCommand);
15220
15571
  program.addCommand(sideCommand);
15572
+ program.addCommand(diffCommand);
15221
15573
  program.addCommand(commandCommand);
15222
15574
  program.addCommand(dashboardCommand);
15223
15575
  program.addCommand(launchCommand);