squadrant 0.16.4 → 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",
@@ -4309,8 +4322,12 @@ var init_commands = __esm({
4309
4322
  build: (a) => a[0] ? ok("crews", ["crew", "list", a[0]]) : usage("crews", "usage: /crews <project>")
4310
4323
  },
4311
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.
4312
4329
  usage: "/launch <project>",
4313
- 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>")
4314
4331
  },
4315
4332
  effort: {
4316
4333
  usage: "/effort [max|balance|low]",
@@ -4484,6 +4501,9 @@ ${ev.message}` : "");
4484
4501
  case "task.blocked":
4485
4502
  return `\u{1F6A7} [${project}] CREW BLOCKED \xB7 ${ev.id}
4486
4503
  ${ev.question}`;
4504
+ case "task.review":
4505
+ return `\u{1F440} [${project}] CREW REVIEW \xB7 ${ev.id}` + (ev.message ? `
4506
+ ${ev.message}` : "");
4487
4507
  case "task.idle":
4488
4508
  return `\u{1F4A4} [${project}] CREW IDLE \xB7 ${ev.id}`;
4489
4509
  case "task.failed":
@@ -4716,6 +4736,7 @@ var init_tiers = __esm({
4716
4736
  ALERTS = /* @__PURE__ */ new Set([
4717
4737
  ...DONE_ONLY,
4718
4738
  "task.blocked",
4739
+ "task.review",
4719
4740
  "task.approval.requested",
4720
4741
  "task.input.requested",
4721
4742
  "task.timeout"
@@ -5932,7 +5953,7 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
5932
5953
  if (task) {
5933
5954
  if (TERMINAL_STATES.has(task.state)) {
5934
5955
  await deps.emitEvent(project, { type: "task.reopened", id: task.id });
5935
- } else if (task.state === "blocked" || task.state === "awaiting-input") {
5956
+ } else if (task.state === "blocked" || task.state === "awaiting-input" || task.state === "review") {
5936
5957
  await deps.emitEvent(project, { type: "task.started", id: task.id });
5937
5958
  }
5938
5959
  }
@@ -6277,6 +6298,18 @@ function cmux(args) {
6277
6298
  );
6278
6299
  });
6279
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
+ }
6280
6313
  function parseList(output) {
6281
6314
  let parsed;
6282
6315
  try {
@@ -6676,6 +6709,37 @@ function createCmuxDriver() {
6676
6709
  }
6677
6710
  throw new DeferDelivery(draft);
6678
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
+ },
6679
6743
  async listSurfaces(workspaceId) {
6680
6744
  let output;
6681
6745
  try {
@@ -9944,7 +10008,7 @@ var init_require_daemon = __esm({
9944
10008
  // packages/cli/src/index.ts
9945
10009
  init_dist();
9946
10010
  init_dist2();
9947
- import { Command as Command31 } from "commander";
10011
+ import { Command as Command32 } from "commander";
9948
10012
  import { readFileSync as readFileSync15, existsSync as existsSync12, writeFileSync as writeFileSync11 } from "fs";
9949
10013
  import { fileURLToPath as fileURLToPath6 } from "url";
9950
10014
  import { dirname as dirname9, join as join29 } from "path";
@@ -10975,6 +11039,7 @@ init_dist4();
10975
11039
  import { Command as Command8 } from "commander";
10976
11040
  import { createConnection as createConnection3 } from "net";
10977
11041
  import { randomUUID as randomUUID4 } from "crypto";
11042
+ import { execFileSync as execFileSync6 } from "child_process";
10978
11043
  import { homedir as homedir16 } from "os";
10979
11044
  import { join as join21 } from "path";
10980
11045
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "fs";
@@ -11374,6 +11439,8 @@ function buildSignalRequest(signal, o) {
11374
11439
  };
11375
11440
  } else if (signal === "blocked") {
11376
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 } : {} };
11377
11444
  } else {
11378
11445
  event = { type: "task.failed", id: taskId, error: o.error ?? "crew signaled failed" };
11379
11446
  }
@@ -11402,6 +11469,49 @@ async function runCrewSignal(signal, o, deps) {
11402
11469
  const req = buildSignalRequest(signal, { ...o, writeResult: o.writeResult ?? defaultWriteResult });
11403
11470
  await deps.call(req);
11404
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
+ }
11405
11515
  function addControlPlaneCrewCommands(crew) {
11406
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) => {
11407
11517
  const req = buildDispatchRequest({ project, task, provider: opts.provider, mode: opts.mode, cwd: opts.cwd });
@@ -11476,9 +11586,9 @@ function addControlPlaneCrewCommands(crew) {
11476
11586
  }
11477
11587
  process.exit(0);
11478
11588
  });
11479
- 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) => {
11480
- if (state !== "done" && state !== "blocked" && state !== "failed") {
11481
- 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)
11482
11592
  `);
11483
11593
  process.exit(2);
11484
11594
  }
@@ -11494,6 +11604,17 @@ function addControlPlaneCrewCommands(crew) {
11494
11604
  process.exit(0);
11495
11605
  } catch (e) {
11496
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}
11497
11618
  `);
11498
11619
  process.exit(1);
11499
11620
  }
@@ -11651,6 +11772,199 @@ crewCommand.command("close").description("Shutdown a crew session (closes its ta
11651
11772
  }
11652
11773
  });
11653
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
+
11654
11968
  // packages/cli/src/commands/side.ts
11655
11969
  init_dist();
11656
11970
  init_dist3();
@@ -11658,11 +11972,11 @@ init_dist4();
11658
11972
  init_dist3();
11659
11973
  init_dist();
11660
11974
  init_dist2();
11661
- import { Command as Command10 } from "commander";
11975
+ import { Command as Command11 } from "commander";
11662
11976
  import fs20 from "fs";
11663
11977
  import path22 from "path";
11664
11978
  import os12 from "os";
11665
- import chalk10 from "chalk";
11979
+ import chalk11 from "chalk";
11666
11980
  var TEMPLATES_DIR3 = path22.join(os12.homedir(), ".config", "squadrant", "templates");
11667
11981
  async function runSideSpawn2(input) {
11668
11982
  const config = loadConfig();
@@ -11724,7 +12038,7 @@ async function runSideClose2(project, name) {
11724
12038
  config.defaults.worktreeDir ?? ".worktrees"
11725
12039
  );
11726
12040
  }
11727
- var sideCommand = new Command10("side").description(
12041
+ var sideCommand = new Command11("side").description(
11728
12042
  "Spawn and manage side-sessions (research/debug) \u2014 fresh-context tabs off the daemon lifecycle"
11729
12043
  );
11730
12044
  sideCommand.command("spawn").description(
@@ -11749,9 +12063,9 @@ sideCommand.command("spawn").description(
11749
12063
  direction: opts.direction,
11750
12064
  agent: opts.agent
11751
12065
  });
11752
- 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})`));
11753
12067
  } catch (err) {
11754
- console.error(chalk10.red(err.message));
12068
+ console.error(chalk11.red(err.message));
11755
12069
  process.exit(1);
11756
12070
  }
11757
12071
  }
@@ -11760,14 +12074,14 @@ sideCommand.command("list").description("List live side-sessions for a project")
11760
12074
  try {
11761
12075
  const sessions = await runSideList2(project);
11762
12076
  if (sessions.length === 0) {
11763
- console.log(chalk10.yellow(`No live side-sessions for ${project}.`));
12077
+ console.log(chalk11.yellow(`No live side-sessions for ${project}.`));
11764
12078
  return;
11765
12079
  }
11766
12080
  for (const s of sessions) {
11767
12081
  console.log(` ${s.name} (${s.surfaceId})`);
11768
12082
  }
11769
12083
  } catch (err) {
11770
- console.error(chalk10.red(err.message));
12084
+ console.error(chalk11.red(err.message));
11771
12085
  process.exit(1);
11772
12086
  }
11773
12087
  });
@@ -11780,9 +12094,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
11780
12094
  label: "message"
11781
12095
  });
11782
12096
  await runSideSend2(project, name, resolvedMessage);
11783
- console.log(chalk10.green(`\u2714 Sent to ${project}:${name}`));
12097
+ console.log(chalk11.green(`\u2714 Sent to ${project}:${name}`));
11784
12098
  } catch (err) {
11785
- console.error(chalk10.red(err.message));
12099
+ console.error(chalk11.red(err.message));
11786
12100
  process.exit(1);
11787
12101
  }
11788
12102
  }
@@ -11790,9 +12104,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
11790
12104
  sideCommand.command("close").description("Close a side-session (closes its tab)").argument("<project>", "Project name").argument("<name>", "Session name").action(async (project, name) => {
11791
12105
  try {
11792
12106
  await runSideClose2(project, name);
11793
- console.log(chalk10.green(`\u2714 Closed ${project}:${name}`));
12107
+ console.log(chalk11.green(`\u2714 Closed ${project}:${name}`));
11794
12108
  } catch (err) {
11795
- console.error(chalk10.red(err.message));
12109
+ console.error(chalk11.red(err.message));
11796
12110
  process.exit(1);
11797
12111
  }
11798
12112
  });
@@ -11800,15 +12114,15 @@ sideCommand.command("close").description("Close a side-session (closes its tab)"
11800
12114
  // packages/cli/src/commands/dashboard.ts
11801
12115
  init_dist();
11802
12116
  init_dist3();
11803
- import { Command as Command11 } from "commander";
12117
+ import { Command as Command12 } from "commander";
11804
12118
  import { execSync as execSync10 } from "child_process";
11805
12119
  import { homedir as homedir18 } from "os";
11806
12120
  import { join as join23 } from "path";
11807
- import chalk12 from "chalk";
12121
+ import chalk13 from "chalk";
11808
12122
 
11809
12123
  // packages/web/dist/read-status.js
11810
12124
  function deriveState(tasks) {
11811
- 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"))
11812
12126
  return "blocked";
11813
12127
  if (tasks.some((t) => t.state === "failed" || t.state === "stalled"))
11814
12128
  return "errored";
@@ -11823,14 +12137,14 @@ function deriveRowState(tasks, captainState) {
11823
12137
  }
11824
12138
  function buildExcerpt(tasks) {
11825
12139
  const working = tasks.filter((t) => t.state === "working").length;
11826
- 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;
11827
12141
  const parts = [];
11828
12142
  if (working > 0)
11829
12143
  parts.push(`${working} working`);
11830
12144
  if (blocked > 0)
11831
12145
  parts.push(`${blocked} blocked`);
11832
12146
  const summary = parts.length > 0 ? parts.join(", ") : "idle";
11833
- 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));
11834
12148
  const titles = active.slice(0, 3).map((t) => {
11835
12149
  const firstLine2 = t.task ? t.task.split("\n")[0] : "";
11836
12150
  return t.name ?? (firstLine2 || t.id.slice(0, 8));
@@ -11881,14 +12195,14 @@ async function readAllStatuses(deps) {
11881
12195
  }
11882
12196
 
11883
12197
  // packages/web/dist/render.js
11884
- import chalk11 from "chalk";
12198
+ import chalk12 from "chalk";
11885
12199
  var ICON = {
11886
- idle: chalk11.green,
11887
- busy: chalk11.cyan,
11888
- blocked: chalk11.yellow,
11889
- errored: chalk11.red,
11890
- offline: chalk11.dim,
11891
- 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
11892
12206
  };
11893
12207
  var ICON_CHAR = {
11894
12208
  idle: "\u25CF",
@@ -11931,10 +12245,10 @@ function renderDashboard(rows, opts) {
11931
12245
  const width = opts.width ?? 100;
11932
12246
  const lines = [];
11933
12247
  lines.push("");
11934
- 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));
11935
12249
  lines.push("");
11936
12250
  if (rows.length === 0) {
11937
- 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>"));
11938
12252
  lines.push("");
11939
12253
  return lines.join("\n");
11940
12254
  }
@@ -11945,14 +12259,14 @@ function renderDashboard(rows, opts) {
11945
12259
  const excerptW = Math.max(20, width - FIXED);
11946
12260
  for (const r of rows) {
11947
12261
  const icon = ICON[r.state](ICON_CHAR[r.state]);
11948
- const name = chalk11.cyan(pad(r.project, NAME_W));
12262
+ const name = chalk12.cyan(pad(r.project, NAME_W));
11949
12263
  const state = ICON[r.state](pad(r.state, STATE_W));
11950
12264
  const age = pad(formatAge(r.lastChecked, opts.now), AGE_W);
11951
- const excerpt = chalk11.dim(truncate(firstLine(r.excerpt), excerptW));
12265
+ const excerpt = chalk12.dim(truncate(firstLine(r.excerpt), excerptW));
11952
12266
  lines.push(` ${icon} ${name} ${state} ${age} \u2502 ${excerpt}`);
11953
12267
  }
11954
12268
  lines.push("");
11955
- 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"));
11956
12270
  lines.push("");
11957
12271
  return lines.join("\n");
11958
12272
  }
@@ -13075,10 +13389,10 @@ async function runDashboardWeb(input) {
13075
13389
  sockPath: SOCK3,
13076
13390
  runners: defaultProbeRunners()
13077
13391
  });
13078
- console.log(chalk12.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
13079
- 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`));
13080
13394
  }
13081
- 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) => {
13082
13396
  try {
13083
13397
  if (opts.web) {
13084
13398
  await runDashboardWeb({ port: opts.port, interval: opts.interval ?? 5 });
@@ -13086,12 +13400,12 @@ var dashboardCommand = new Command11("dashboard").description("Live status grid
13086
13400
  }
13087
13401
  if (opts.pane) {
13088
13402
  const pane = await runDashboardPane({ direction: opts.direction, interval: opts.interval ?? 10 });
13089
- 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}`));
13090
13404
  return;
13091
13405
  }
13092
13406
  await runDashboardOnce();
13093
13407
  } catch (err) {
13094
- console.error(chalk12.red(err.message));
13408
+ console.error(chalk13.red(err.message));
13095
13409
  process.exit(1);
13096
13410
  }
13097
13411
  });
@@ -13102,12 +13416,12 @@ dashboardCommand.command("sync-hub").description("Mirror each spoke status.md in
13102
13416
  return;
13103
13417
  }
13104
13418
  if (results.length === 0) {
13105
- 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"));
13106
13420
  return;
13107
13421
  }
13108
- console.log(chalk12.bold("\n \u{1F4CA} Hub mirror sync\n"));
13422
+ console.log(chalk13.bold("\n \u{1F4CA} Hub mirror sync\n"));
13109
13423
  for (const r of results) {
13110
- 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)}`);
13111
13425
  }
13112
13426
  console.log("");
13113
13427
  });
@@ -13117,12 +13431,12 @@ init_dist();
13117
13431
  init_dist4();
13118
13432
  init_dist3();
13119
13433
  init_dist2();
13120
- import { Command as Command12 } from "commander";
13434
+ import { Command as Command13 } from "commander";
13121
13435
  import { execSync as execSync11 } from "child_process";
13122
13436
  import fs22 from "fs";
13123
13437
  import path24 from "path";
13124
13438
  import os13 from "os";
13125
- import chalk13 from "chalk";
13439
+ import chalk14 from "chalk";
13126
13440
 
13127
13441
  // packages/cli/src/commands/launch-interactive.ts
13128
13442
  import checkbox, { Separator } from "@inquirer/checkbox";
@@ -13203,16 +13517,16 @@ var TEMPLATES_DIR4 = path24.join(os13.homedir(), ".config", "squadrant", "templa
13203
13517
  var SESSIONS_PATH2 = path24.join(os13.homedir(), ".config", "squadrant", "sessions.json");
13204
13518
  function ensureCmuxReady(headless) {
13205
13519
  if (headless || isInsideCmux()) return;
13206
- 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"));
13207
13521
  execSync11(`open "${CMUX_APP}"`, { stdio: "inherit" });
13208
- 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"));
13209
13523
  process.exit(0);
13210
13524
  }
13211
- var launchCommand = new Command12("launch").description(
13525
+ var launchCommand = new Command13("launch").description(
13212
13526
  "Launch a project captain (with project arg) or all captains (--all). Use `squadrant command` for one-shot Command tasks."
13213
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) => {
13214
13528
  if (opts.fresh && opts.keep) {
13215
- 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"));
13216
13530
  process.exit(1);
13217
13531
  }
13218
13532
  const config = loadConfig();
@@ -13260,29 +13574,29 @@ var launchCommand = new Command12("launch").description(
13260
13574
  return null;
13261
13575
  }
13262
13576
  },
13263
- onFreshReason: (reason) => console.log(chalk13.cyan(` \u21BB ${reason}`)),
13264
- onStoppingStale: (name) => console.log(chalk13.yellow(` Closing stale workspace '${name}' for fresh start`)),
13265
- onAlreadyExists: (name) => console.log(chalk13.yellow(` Workspace '${name}' already exists \u2014 switching to it`)),
13266
- 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`))
13267
13581
  });
13268
13582
  } catch (err) {
13269
- console.error(chalk13.red(` \u2718 Failed: ${err.message}`));
13583
+ console.error(chalk14.red(` \u2718 Failed: ${err.message}`));
13270
13584
  hadFailure = true;
13271
13585
  }
13272
13586
  }
13273
13587
  if (opts.all) {
13274
13588
  const hubPath = resolveHome(config.hubVault);
13275
13589
  fs22.mkdirSync(hubPath, { recursive: true });
13276
- console.log(chalk13.bold("\nLaunching all captain workspaces\n"));
13590
+ console.log(chalk14.bold("\nLaunching all captain workspaces\n"));
13277
13591
  for (const [name, proj] of Object.entries(config.projects)) {
13278
13592
  const projPath = resolveHome(proj.path);
13279
13593
  const spokePath = resolveHome(proj.spokeVault);
13280
13594
  if (!fs22.existsSync(spokePath)) {
13281
13595
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
13282
13596
  await ensureSpokeLayout(spokeDriver);
13283
- console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13597
+ console.log(chalk14.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13284
13598
  }
13285
- console.log(chalk13.bold(`
13599
+ console.log(chalk14.bold(`
13286
13600
  Captain: ${proj.captainName} (${name})`));
13287
13601
  await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
13288
13602
  }
@@ -13290,7 +13604,7 @@ var launchCommand = new Command12("launch").description(
13290
13604
  } else if (!project) {
13291
13605
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
13292
13606
  console.error(
13293
- chalk13.red(
13607
+ chalk14.red(
13294
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"
13295
13609
  )
13296
13610
  );
@@ -13305,10 +13619,10 @@ var launchCommand = new Command12("launch").description(
13305
13619
  }));
13306
13620
  const selected = await selectCaptainsInteractive(entries);
13307
13621
  if (selected.length === 0) {
13308
- console.log(chalk13.yellow("\n No captains selected.\n"));
13622
+ console.log(chalk14.yellow("\n No captains selected.\n"));
13309
13623
  return;
13310
13624
  }
13311
- console.log(chalk13.bold(`
13625
+ console.log(chalk14.bold(`
13312
13626
  Launching ${selected.length} captain workspace(s) in parallel
13313
13627
  `));
13314
13628
  await Promise.all(selected.map(async (name) => {
@@ -13318,9 +13632,9 @@ Launching ${selected.length} captain workspace(s) in parallel
13318
13632
  if (!fs22.existsSync(spokePath)) {
13319
13633
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
13320
13634
  await ensureSpokeLayout(spokeDriver);
13321
- console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13635
+ console.log(chalk14.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13322
13636
  }
13323
- console.log(chalk13.bold(`
13637
+ console.log(chalk14.bold(`
13324
13638
  Captain: ${proj.captainName} (${name})`));
13325
13639
  await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
13326
13640
  }));
@@ -13328,7 +13642,7 @@ Launching ${selected.length} captain workspace(s) in parallel
13328
13642
  } else {
13329
13643
  if (!config.projects[project]) {
13330
13644
  console.error(
13331
- chalk13.red(
13645
+ chalk14.red(
13332
13646
  `
13333
13647
  \u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
13334
13648
  `
@@ -13342,10 +13656,10 @@ Launching ${selected.length} captain workspace(s) in parallel
13342
13656
  if (!fs22.existsSync(spokePath)) {
13343
13657
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
13344
13658
  await ensureSpokeLayout(spokeDriver);
13345
- console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13659
+ console.log(chalk14.cyan(` \u2714 Created spoke vault at ${spokePath}`));
13346
13660
  }
13347
13661
  console.log(
13348
- chalk13.bold(
13662
+ chalk14.bold(
13349
13663
  `
13350
13664
  Launching captain workspace for '${project}' (${proj.captainName})
13351
13665
  `
@@ -13359,8 +13673,8 @@ Launching captain workspace for '${project}' (${proj.captainName})
13359
13673
  // packages/cli/src/commands/shutdown.ts
13360
13674
  init_dist();
13361
13675
  init_dist3();
13362
- import { Command as Command13 } from "commander";
13363
- import chalk14 from "chalk";
13676
+ import { Command as Command14 } from "commander";
13677
+ import chalk15 from "chalk";
13364
13678
  init_dist();
13365
13679
  function nameVariants(name) {
13366
13680
  const stripped = name.replace(/^⚓\s+/, "").trim();
@@ -13373,23 +13687,23 @@ async function closeMatching(runtime, variants, label) {
13373
13687
  const failed = [];
13374
13688
  if (matches.length === 0) {
13375
13689
  console.log(
13376
- chalk14.yellow(` \u26A0 Workspace '${label}' not found \u2014 already closed?`)
13690
+ chalk15.yellow(` \u26A0 Workspace '${label}' not found \u2014 already closed?`)
13377
13691
  );
13378
13692
  return { closed, failed };
13379
13693
  }
13380
13694
  for (const ws of matches) {
13381
13695
  try {
13382
13696
  await runtime.stop(ws.id);
13383
- console.log(chalk14.green(` \u2714 Closed: ${ws.name}`));
13697
+ console.log(chalk15.green(` \u2714 Closed: ${ws.name}`));
13384
13698
  closed.push(ws.name);
13385
13699
  } catch {
13386
- console.log(chalk14.red(` \u2718 Failed to close: ${ws.name}`));
13700
+ console.log(chalk15.red(` \u2718 Failed to close: ${ws.name}`));
13387
13701
  failed.push(ws.name);
13388
13702
  }
13389
13703
  }
13390
13704
  return { closed, failed };
13391
13705
  }
13392
- var shutdownCommand = new Command13("shutdown").description(
13706
+ var shutdownCommand = new Command14("shutdown").description(
13393
13707
  "Shutdown command + all captain workspaces (no args) or one captain workspace"
13394
13708
  ).argument("[project]", "Project name to shut down captain for").action(async (project) => {
13395
13709
  const config = loadConfig();
@@ -13405,11 +13719,11 @@ var shutdownCommand = new Command13("shutdown").description(
13405
13719
  const allVariants = /* @__PURE__ */ new Set([...captainVariants, ...commandVariants]);
13406
13720
  const squadrantWorkspaces = workspaces.filter((w) => allVariants.has(w.name));
13407
13721
  if (squadrantWorkspaces.length === 0) {
13408
- console.log(chalk14.yellow("\nNo squadrant workspaces found to close.\n"));
13722
+ console.log(chalk15.yellow("\nNo squadrant workspaces found to close.\n"));
13409
13723
  return;
13410
13724
  }
13411
13725
  console.log(
13412
- chalk14.bold(
13726
+ chalk15.bold(
13413
13727
  `
13414
13728
  Shutting down ${squadrantWorkspaces.length} workspace(s)...
13415
13729
  `
@@ -13429,9 +13743,9 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
13429
13743
  for (const ws of squadrantWorkspaces) {
13430
13744
  try {
13431
13745
  await globalRuntime.stop(ws.id);
13432
- console.log(chalk14.green(` \u2714 Closed: ${ws.name}`));
13746
+ console.log(chalk15.green(` \u2714 Closed: ${ws.name}`));
13433
13747
  } catch {
13434
- console.log(chalk14.red(` \u2718 Failed to close: ${ws.name}`));
13748
+ console.log(chalk15.red(` \u2718 Failed to close: ${ws.name}`));
13435
13749
  }
13436
13750
  }
13437
13751
  console.log("");
@@ -13439,7 +13753,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
13439
13753
  }
13440
13754
  if (!config.projects[project]) {
13441
13755
  console.error(
13442
- chalk14.red(
13756
+ chalk15.red(
13443
13757
  `
13444
13758
  \u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
13445
13759
  `
@@ -13450,7 +13764,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
13450
13764
  const captainName = config.projects[project].captainName;
13451
13765
  const runtime = runtimes.forProject(project, config);
13452
13766
  console.log(
13453
- chalk14.bold(`
13767
+ chalk15.bold(`
13454
13768
  Shutting down captain workspace for '${project}'...
13455
13769
  `)
13456
13770
  );
@@ -13474,13 +13788,13 @@ Shutting down captain workspace for '${project}'...
13474
13788
 
13475
13789
  // packages/cli/src/commands/feedback.ts
13476
13790
  init_dist();
13477
- import { Command as Command14 } from "commander";
13791
+ import { Command as Command15 } from "commander";
13478
13792
  import fs23 from "fs";
13479
13793
  import os14 from "os";
13480
13794
  import path25 from "path";
13481
13795
  import { fileURLToPath as fileURLToPath3 } from "url";
13482
13796
  import { execSync as execSync12 } from "child_process";
13483
- import chalk15 from "chalk";
13797
+ import chalk16 from "chalk";
13484
13798
  var REPO_URL = "https://github.com/tu11aa/squadrant";
13485
13799
  function readPkgVersion() {
13486
13800
  try {
@@ -13528,21 +13842,21 @@ function buildIssueUrl(metrics, squadrantVersion) {
13528
13842
  });
13529
13843
  return `${REPO_URL}/issues/new?${params.toString()}`;
13530
13844
  }
13531
- 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(() => {
13532
13846
  const config = loadConfig();
13533
13847
  const metricsPath = config.metrics?.path || path25.join(os14.homedir(), ".config", "squadrant", "metrics.json");
13534
13848
  const metrics = readMetrics(metricsPath);
13535
13849
  const version = readStamp(config) ?? readPkgVersion();
13536
13850
  const issueUrl = buildIssueUrl(metrics, version);
13537
- console.log(chalk15.bold("\nOpening feedback issue in browser...\n"));
13538
- 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)}...
13539
13853
  `));
13540
13854
  try {
13541
13855
  execSync12(`open "${issueUrl}"`, { stdio: "ignore" });
13542
- console.log(chalk15.green(" \u2714 Browser opened\n"));
13856
+ console.log(chalk16.green(" \u2714 Browser opened\n"));
13543
13857
  } catch {
13544
- console.log(chalk15.yellow(" \u26A0 Could not open browser automatically."));
13545
- 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)}
13546
13860
  `);
13547
13861
  }
13548
13862
  });
@@ -13551,10 +13865,10 @@ var feedbackCommand = new Command14("feedback").description("Open a pre-filled G
13551
13865
  init_dist();
13552
13866
  init_dist();
13553
13867
  init_dist3();
13554
- import { Command as Command15 } from "commander";
13868
+ import { Command as Command16 } from "commander";
13555
13869
  import fs24 from "fs";
13556
13870
  import path26 from "path";
13557
- import chalk16 from "chalk";
13871
+ import chalk17 from "chalk";
13558
13872
  import matter3 from "gray-matter";
13559
13873
  function getDateStr(yesterday) {
13560
13874
  return iso(daysAgo(yesterday ? 1 : 0));
@@ -13584,7 +13898,7 @@ function formatStandup(standups, dateStr, raw) {
13584
13898
  const lines = [];
13585
13899
  const header = `Standup \u2014 ${dateStr}`;
13586
13900
  if (!raw) {
13587
- lines.push(chalk16.bold(`
13901
+ lines.push(chalk17.bold(`
13588
13902
  ${header}
13589
13903
  `));
13590
13904
  } else {
@@ -13597,12 +13911,12 @@ ${header}
13597
13911
  const tasksTotal = s.status.tasks_total ?? 0;
13598
13912
  const tasksInProgress = s.status.tasks_in_progress ?? 0;
13599
13913
  if (!raw) {
13600
- lines.push(chalk16.cyan.bold(`## ${s.name}`));
13914
+ lines.push(chalk17.cyan.bold(`## ${s.name}`));
13601
13915
  } else {
13602
13916
  lines.push(`## ${s.name}`);
13603
13917
  }
13604
13918
  if (s.gitCommits.length > 0 || tasksDone > 0) {
13605
- lines.push(!raw ? chalk16.green("Done:") : "**Done:**");
13919
+ lines.push(!raw ? chalk17.green("Done:") : "**Done:**");
13606
13920
  for (const commit of s.gitCommits) {
13607
13921
  lines.push(` - ${commit}`);
13608
13922
  }
@@ -13611,7 +13925,7 @@ ${header}
13611
13925
  }
13612
13926
  }
13613
13927
  if (tasksInProgress > 0) {
13614
- lines.push(!raw ? chalk16.yellow("In Progress:") : "**In Progress:**");
13928
+ lines.push(!raw ? chalk17.yellow("In Progress:") : "**In Progress:**");
13615
13929
  lines.push(` - ${tasksInProgress} task(s) active`);
13616
13930
  }
13617
13931
  if (s.dailyLog) {
@@ -13621,7 +13935,7 @@ ${header}
13621
13935
  if (match) {
13622
13936
  const items = match[1].trim().split("\n").filter((l) => l.trim().startsWith("-"));
13623
13937
  if (items.length > 0 && section === "Tomorrow") {
13624
- lines.push(!raw ? chalk16.blue("Next:") : "**Next:**");
13938
+ lines.push(!raw ? chalk17.blue("Next:") : "**Next:**");
13625
13939
  for (const item of items) lines.push(` ${item.trim()}`);
13626
13940
  }
13627
13941
  }
@@ -13629,20 +13943,20 @@ ${header}
13629
13943
  }
13630
13944
  if (s.blockers.length > 0) {
13631
13945
  hasBlockers = true;
13632
- lines.push(!raw ? chalk16.red("Blocked:") : "**Blocked:**");
13946
+ lines.push(!raw ? chalk17.red("Blocked:") : "**Blocked:**");
13633
13947
  for (const b of s.blockers) {
13634
13948
  lines.push(` - ${b}`);
13635
13949
  }
13636
13950
  }
13637
13951
  if (s.gitCommits.length === 0 && tasksDone === 0 && !s.dailyLog) {
13638
- lines.push(!raw ? chalk16.dim(" (no activity)") : " (no activity)");
13952
+ lines.push(!raw ? chalk17.dim(" (no activity)") : " (no activity)");
13639
13953
  }
13640
13954
  lines.push("");
13641
13955
  }
13642
13956
  const totalCommits = standups.reduce((sum, s) => sum + s.gitCommits.length, 0);
13643
13957
  const totalDone = standups.reduce((sum, s) => sum + (s.status.tasks_completed ?? 0), 0);
13644
13958
  if (!raw) {
13645
- 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" : ""} ---
13646
13960
  `));
13647
13961
  } else {
13648
13962
  lines.push(`---
@@ -13651,12 +13965,12 @@ ${header}
13651
13965
  }
13652
13966
  return lines.join("\n");
13653
13967
  }
13654
- 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) => {
13655
13969
  const config = loadConfig();
13656
13970
  const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
13657
13971
  const projects = Object.entries(config.projects);
13658
13972
  if (projects.length === 0) {
13659
- 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"));
13660
13974
  return;
13661
13975
  }
13662
13976
  const dateStr = getDateStr(!!opts.yesterday);
@@ -13665,7 +13979,7 @@ var standupCommand = new Command15("standup").description("Generate daily standu
13665
13979
  if (opts.project) {
13666
13980
  const match = projects.find(([name]) => name === opts.project);
13667
13981
  if (!match) {
13668
- console.error(chalk16.red(`Project "${opts.project}" not found.`));
13982
+ console.error(chalk17.red(`Project "${opts.project}" not found.`));
13669
13983
  process.exit(1);
13670
13984
  }
13671
13985
  targets = [match];
@@ -13683,10 +13997,10 @@ var standupCommand = new Command15("standup").description("Generate daily standu
13683
13997
  init_dist();
13684
13998
  init_dist();
13685
13999
  init_dist3();
13686
- import { Command as Command16 } from "commander";
14000
+ import { Command as Command17 } from "commander";
13687
14001
  import fs25 from "fs";
13688
14002
  import path27 from "path";
13689
- import chalk17 from "chalk";
14003
+ import chalk18 from "chalk";
13690
14004
  import matter4 from "gray-matter";
13691
14005
  function readStatus(spokeVault) {
13692
14006
  const statusFile = path27.join(spokeVault, "status.md");
@@ -13756,7 +14070,7 @@ function formatRetro(retros, fromStr, toStr, raw) {
13756
14070
  const lines = [];
13757
14071
  const header = `Retro \u2014 ${fromStr} \u2192 ${toStr}`;
13758
14072
  lines.push(raw ? `# ${header}
13759
- ` : chalk17.bold(`
14073
+ ` : chalk18.bold(`
13760
14074
  ${header}
13761
14075
  `));
13762
14076
  let totalCommits = 0;
@@ -13766,39 +14080,39 @@ ${header}
13766
14080
  totalCommits += r.commits.length;
13767
14081
  totalPRs += r.mergedPRs.length;
13768
14082
  totalShipped += r.shipped.length;
13769
- lines.push(raw ? `## ${r.name}` : chalk17.cyan.bold(`## ${r.name}`));
13770
- 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);
13771
14085
  if (r.mergedPRs.length > 0) {
13772
- lines.push(raw ? `**PRs merged:**` : chalk17.green("PRs merged:"));
14086
+ lines.push(raw ? `**PRs merged:**` : chalk18.green("PRs merged:"));
13773
14087
  for (const pr of r.mergedPRs) lines.push(` - ${pr}`);
13774
14088
  }
13775
- renderList(lines, r.inProgress, raw, "In Progress", chalk17.yellow);
13776
- renderList(lines, r.blocked, raw, "Blocked", chalk17.red);
13777
- 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);
13778
14092
  const metricBits = [
13779
14093
  `${r.commits.length} commits`,
13780
14094
  `${r.mergedPRs.length} PRs merged`,
13781
14095
  `${r.shipped.length} shipped`
13782
14096
  ];
13783
- lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` : chalk17.dim(` ${metricBits.join(" \xB7 ")}`));
14097
+ lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` : chalk18.dim(` ${metricBits.join(" \xB7 ")}`));
13784
14098
  if (r.shipped.length === 0 && r.commits.length === 0 && r.mergedPRs.length === 0 && r.inProgress.length === 0 && r.blocked.length === 0) {
13785
- 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)"));
13786
14100
  }
13787
14101
  lines.push("");
13788
14102
  }
13789
14103
  const summary = `${totalShipped} items shipped \xB7 ${totalCommits} commits \xB7 ${totalPRs} PRs merged`;
13790
14104
  lines.push(raw ? `---
13791
14105
  *${summary}*
13792
- ` : chalk17.dim(`--- ${summary} ---
14106
+ ` : chalk18.dim(`--- ${summary} ---
13793
14107
  `));
13794
14108
  return lines.join("\n");
13795
14109
  }
13796
- 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) => {
13797
14111
  const config = loadConfig();
13798
14112
  const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
13799
14113
  const projects = Object.entries(config.projects);
13800
14114
  if (projects.length === 0) {
13801
- 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"));
13802
14116
  return;
13803
14117
  }
13804
14118
  let windowDays = 7;
@@ -13815,7 +14129,7 @@ var retroCommand = new Command16("retro").description("Generate a retro (weekly/
13815
14129
  if (opts.project) {
13816
14130
  const match = projects.find(([name]) => name === opts.project);
13817
14131
  if (!match) {
13818
- console.error(chalk17.red(`Project "${opts.project}" not found.`));
14132
+ console.error(chalk18.red(`Project "${opts.project}" not found.`));
13819
14133
  process.exit(1);
13820
14134
  }
13821
14135
  targets = [match];
@@ -13831,8 +14145,8 @@ var retroCommand = new Command16("retro").description("Generate a retro (weekly/
13831
14145
  // packages/cli/src/commands/runtime.ts
13832
14146
  init_dist();
13833
14147
  init_dist3();
13834
- import { Command as Command17 } from "commander";
13835
- import chalk18 from "chalk";
14148
+ import { Command as Command18 } from "commander";
14149
+ import chalk19 from "chalk";
13836
14150
  function buildRegistry() {
13837
14151
  return new RuntimeRegistry({
13838
14152
  cmux: createCmuxDriver()
@@ -13864,7 +14178,7 @@ async function needRef(resolved) {
13864
14178
  }
13865
14179
  return ref.id;
13866
14180
  }
13867
- 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.");
13868
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) => {
13869
14183
  const config = loadConfig();
13870
14184
  const registry = buildRegistry();
@@ -13879,7 +14193,7 @@ runtimeCommand.command("status").description("Print 'running' or 'stopped' for a
13879
14193
  process.exit(1);
13880
14194
  }
13881
14195
  } catch (err) {
13882
- console.error(chalk18.red(err.message));
14196
+ console.error(chalk19.red(err.message));
13883
14197
  process.exit(2);
13884
14198
  }
13885
14199
  });
@@ -13926,9 +14240,9 @@ async function runRuntimeSend(arg1, arg2, opts, confirmOpts) {
13926
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) => {
13927
14241
  try {
13928
14242
  await runRuntimeSend(arg1, arg2, opts);
13929
- console.log(chalk18.green("\u2714 Delivered (confirmed)"));
14243
+ console.log(chalk19.green("\u2714 Delivered (confirmed)"));
13930
14244
  } catch (err) {
13931
- console.error(chalk18.red(err.message));
14245
+ console.error(chalk19.red(err.message));
13932
14246
  process.exit(1);
13933
14247
  }
13934
14248
  });
@@ -13954,7 +14268,7 @@ runtimeCommand.command("read-screen").description("Print a terminal snapshot of
13954
14268
  const screen = await resolved.driver.readScreen(ref);
13955
14269
  process.stdout.write(screen);
13956
14270
  } catch (err) {
13957
- console.error(chalk18.red(err.message));
14271
+ console.error(chalk19.red(err.message));
13958
14272
  process.exit(1);
13959
14273
  }
13960
14274
  });
@@ -13965,13 +14279,13 @@ runtimeCommand.command("stop").description("Stop a target workspace").argument("
13965
14279
  const resolved = resolveTarget(registry, config, target, !!opts.command);
13966
14280
  const ref = await resolved.driver.status(resolved.workspaceName);
13967
14281
  if (!ref) {
13968
- console.log(chalk18.yellow(`Workspace '${resolved.workspaceName}' already stopped`));
14282
+ console.log(chalk19.yellow(`Workspace '${resolved.workspaceName}' already stopped`));
13969
14283
  return;
13970
14284
  }
13971
14285
  await resolved.driver.stop(ref.id);
13972
- console.log(chalk18.green(`\u2714 Stopped ${resolved.workspaceName}`));
14286
+ console.log(chalk19.green(`\u2714 Stopped ${resolved.workspaceName}`));
13973
14287
  } catch (err) {
13974
- console.error(chalk18.red(err.message));
14288
+ console.error(chalk19.red(err.message));
13975
14289
  process.exit(1);
13976
14290
  }
13977
14291
  });
@@ -13979,8 +14293,8 @@ runtimeCommand.command("stop").description("Stop a target workspace").argument("
13979
14293
  // packages/cli/src/commands/workspace.ts
13980
14294
  init_dist();
13981
14295
  init_dist3();
13982
- import { Command as Command18 } from "commander";
13983
- import chalk19 from "chalk";
14296
+ import { Command as Command19 } from "commander";
14297
+ import chalk20 from "chalk";
13984
14298
  function buildRegistry2() {
13985
14299
  return new WorkspaceRegistry({
13986
14300
  obsidian: createObsidianDriver
@@ -13998,7 +14312,7 @@ async function readStdin() {
13998
14312
  }
13999
14313
  return Buffer.concat(chunks).toString("utf-8");
14000
14314
  }
14001
- 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.");
14002
14316
  function resolveTargetAndPath(arg1, arg2, useHub) {
14003
14317
  if (useHub) {
14004
14318
  if (arg2 !== void 0) {
@@ -14020,7 +14334,7 @@ workspaceCommand.command("read").description("Print the contents of a scope-rela
14020
14334
  const content = await driver.read(path30);
14021
14335
  process.stdout.write(content);
14022
14336
  } catch (err) {
14023
- console.error(chalk19.red(err.message));
14337
+ console.error(chalk20.red(err.message));
14024
14338
  process.exit(1);
14025
14339
  }
14026
14340
  });
@@ -14050,7 +14364,7 @@ workspaceCommand.command("write").description("Write content to a scope-relative
14050
14364
  const payload = rawContent === "-" ? await readStdin() : rawContent;
14051
14365
  await driver.write(path30, payload);
14052
14366
  } catch (err) {
14053
- console.error(chalk19.red(err.message));
14367
+ console.error(chalk20.red(err.message));
14054
14368
  process.exit(1);
14055
14369
  }
14056
14370
  });
@@ -14063,7 +14377,7 @@ workspaceCommand.command("list").description("List entries in a scope-relative d
14063
14377
  const entries = await driver.list(path30);
14064
14378
  for (const entry of entries) console.log(entry);
14065
14379
  } catch (err) {
14066
- console.error(chalk19.red(err.message));
14380
+ console.error(chalk20.red(err.message));
14067
14381
  process.exit(1);
14068
14382
  }
14069
14383
  });
@@ -14076,7 +14390,7 @@ workspaceCommand.command("exists").description("Exit 0 if path exists, 1 if not"
14076
14390
  const ok2 = await driver.exists(path30);
14077
14391
  process.exit(ok2 ? 0 : 1);
14078
14392
  } catch (err) {
14079
- console.error(chalk19.red(err.message));
14393
+ console.error(chalk20.red(err.message));
14080
14394
  process.exit(2);
14081
14395
  }
14082
14396
  });
@@ -14088,7 +14402,7 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
14088
14402
  const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
14089
14403
  await driver.mkdir(path30);
14090
14404
  } catch (err) {
14091
- console.error(chalk19.red(err.message));
14405
+ console.error(chalk20.red(err.message));
14092
14406
  process.exit(1);
14093
14407
  }
14094
14408
  });
@@ -14096,8 +14410,8 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
14096
14410
  // packages/cli/src/commands/notify.ts
14097
14411
  init_dist();
14098
14412
  init_dist3();
14099
- import { Command as Command19 } from "commander";
14100
- import chalk20 from "chalk";
14413
+ import { Command as Command20 } from "commander";
14414
+ import chalk21 from "chalk";
14101
14415
  async function readStdin2() {
14102
14416
  const chunks = [];
14103
14417
  for await (const chunk of process.stdin) {
@@ -14105,7 +14419,7 @@ async function readStdin2() {
14105
14419
  }
14106
14420
  return Buffer.concat(chunks).toString("utf-8");
14107
14421
  }
14108
- 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) => {
14109
14423
  const config = loadConfig();
14110
14424
  const registry = new NotifierRegistry({ cmux: createCmuxNotifier });
14111
14425
  try {
@@ -14113,7 +14427,7 @@ var notifyCommand = new Command19("notify").description("Send a message to the u
14113
14427
  if (!payload) throw new Error("Empty message");
14114
14428
  await registry.get(config).notify(payload);
14115
14429
  } catch (err) {
14116
- console.error(chalk20.red(err.message));
14430
+ console.error(chalk21.red(err.message));
14117
14431
  process.exit(1);
14118
14432
  }
14119
14433
  });
@@ -14123,8 +14437,8 @@ init_dist();
14123
14437
  init_dist4();
14124
14438
  init_dist3();
14125
14439
  init_dist();
14126
- import { Command as Command20 } from "commander";
14127
- import chalk21 from "chalk";
14440
+ import { Command as Command21 } from "commander";
14441
+ import chalk22 from "chalk";
14128
14442
  import fs26 from "fs";
14129
14443
  import path28 from "path";
14130
14444
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -14171,17 +14485,17 @@ async function runEmit(opts) {
14171
14485
  for (const dest of emitter.destinations(scope, projectRoot)) {
14172
14486
  const result = await emitter.emit(source, dest, { dryRun: opts.dryRun });
14173
14487
  if (opts.dryRun) {
14174
- console.log(chalk21.cyan(`[${emitter.name}] ${dest.path}`));
14488
+ console.log(chalk22.cyan(`[${emitter.name}] ${dest.path}`));
14175
14489
  console.log(result.diff ?? "(no diff)");
14176
14490
  } else if (result.written) {
14177
14491
  console.log(
14178
- chalk21.green(
14492
+ chalk22.green(
14179
14493
  `\u2714 ${emitter.name} \u2192 ${dest.path} (${result.bytesWritten} bytes)`
14180
14494
  )
14181
14495
  );
14182
14496
  emittedCount.written++;
14183
14497
  } else {
14184
- console.log(chalk21.gray(`- ${emitter.name} \u2192 ${dest.path} (skipped)`));
14498
+ console.log(chalk22.gray(`- ${emitter.name} \u2192 ${dest.path} (skipped)`));
14185
14499
  emittedCount.skipped++;
14186
14500
  }
14187
14501
  }
@@ -14204,14 +14518,14 @@ async function runEmit(opts) {
14204
14518
  `Unknown project '${projectName}'. Available: ${Object.keys(cfg.projects).join(", ") || "(none)"}`
14205
14519
  );
14206
14520
  }
14207
- console.error(chalk21.yellow(`\u26A0 unknown project: ${projectName}`));
14521
+ console.error(chalk22.yellow(`\u26A0 unknown project: ${projectName}`));
14208
14522
  continue;
14209
14523
  }
14210
14524
  const source = await readProjectLevelSource(
14211
14525
  createObsidianDriver({ root: proj.path })
14212
14526
  );
14213
14527
  if (!source) {
14214
- console.log(chalk21.gray(`- ${projectName}: no AGENTS.md, skipping`));
14528
+ console.log(chalk22.gray(`- ${projectName}: no AGENTS.md, skipping`));
14215
14529
  continue;
14216
14530
  }
14217
14531
  for (const name of targets) {
@@ -14221,21 +14535,21 @@ async function runEmit(opts) {
14221
14535
  }
14222
14536
  if (!opts.dryRun) {
14223
14537
  console.log(
14224
- chalk21.bold(
14538
+ chalk22.bold(
14225
14539
  `
14226
14540
  Projection complete \u2014 ${emittedCount.written} written, ${emittedCount.skipped} skipped.`
14227
14541
  )
14228
14542
  );
14229
14543
  }
14230
14544
  }
14231
- var projectionCommand = new Command20("projection").description(
14545
+ var projectionCommand = new Command21("projection").description(
14232
14546
  "Project squadrant instructions and skills to supported agent formats"
14233
14547
  );
14234
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) => {
14235
14549
  try {
14236
14550
  await runEmit({ ...opts, dryRun: false });
14237
14551
  } catch (err) {
14238
- console.error(chalk21.red(err.message));
14552
+ console.error(chalk22.red(err.message));
14239
14553
  process.exit(1);
14240
14554
  }
14241
14555
  });
@@ -14243,7 +14557,7 @@ projectionCommand.command("diff").description("Preview changes without writing")
14243
14557
  try {
14244
14558
  await runEmit({ ...opts, dryRun: true });
14245
14559
  } catch (err) {
14246
- console.error(chalk21.red(err.message));
14560
+ console.error(chalk22.red(err.message));
14247
14561
  process.exit(1);
14248
14562
  }
14249
14563
  });
@@ -14253,7 +14567,7 @@ projectionCommand.command("list").description("List registered projection target
14253
14567
  const emitter = registry.get(name);
14254
14568
  const userDests = emitter.destinations("user").map((d) => d.path);
14255
14569
  const projectDests = emitter.destinations("project", "<project>").map((d) => d.path);
14256
- console.log(chalk21.bold(name));
14570
+ console.log(chalk22.bold(name));
14257
14571
  console.log(` user: ${userDests.join(", ") || "(none)"}`);
14258
14572
  console.log(` project: ${projectDests.join(", ") || "(none)"}`);
14259
14573
  }
@@ -14261,9 +14575,9 @@ projectionCommand.command("list").description("List registered projection target
14261
14575
 
14262
14576
  // packages/cli/src/commands/codex-chat-smoke.ts
14263
14577
  init_dist4();
14264
- import { Command as Command21 } from "commander";
14578
+ import { Command as Command22 } from "commander";
14265
14579
  import { resolve as resolve2 } from "path";
14266
- 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(
14267
14581
  "--approval",
14268
14582
  "include the approval round-trip (Phase 1 PASS requires this)",
14269
14583
  false
@@ -14332,11 +14646,11 @@ init_dist();
14332
14646
  init_dist();
14333
14647
  init_dist();
14334
14648
  init_dist2();
14335
- import { Command as Command22 } from "commander";
14649
+ import { Command as Command23 } from "commander";
14336
14650
  import fs27 from "fs";
14337
14651
  import { fileURLToPath as fileURLToPath5 } from "url";
14338
14652
  import { dirname as dirname6, join as join25 } from "path";
14339
- import chalk22 from "chalk";
14653
+ import chalk23 from "chalk";
14340
14654
  function runConfigCheck(opts) {
14341
14655
  const raw = JSON.parse(fs27.readFileSync(opts.configPath, "utf-8"));
14342
14656
  const def = getDefaultConfig();
@@ -14391,14 +14705,14 @@ function runConfigSet(key, value, configPath = DEFAULT_CONFIG_PATH) {
14391
14705
  }
14392
14706
  function printRestartOutcome(outcome) {
14393
14707
  if (outcome === "skipped-not-running") {
14394
- 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)"));
14395
14709
  } else if (outcome === "skipped-opt-out") {
14396
- console.log(chalk22.dim("(run 'squadrant heal daemon' to apply)"));
14710
+ console.log(chalk23.dim("(run 'squadrant heal daemon' to apply)"));
14397
14711
  }
14398
14712
  }
14399
14713
  function runConfigSetAction(opts) {
14400
14714
  runConfigSet(opts.key, opts.value, opts.configPath);
14401
- console.log(chalk22.green(`\u2714 set ${opts.key} = ${opts.value}`));
14715
+ console.log(chalk23.green(`\u2714 set ${opts.key} = ${opts.value}`));
14402
14716
  if (isDaemonCachedKey(opts.key)) {
14403
14717
  const doRestart = opts.doRestart ?? restartDaemonIfRunning;
14404
14718
  const outcome = doRestart({ reason: `config ${opts.key}`, noRestart: opts.noRestart });
@@ -14406,9 +14720,9 @@ function runConfigSetAction(opts) {
14406
14720
  }
14407
14721
  }
14408
14722
  var SEV_COLOR = {
14409
- info: chalk22.green,
14410
- advisory: chalk22.yellow,
14411
- warn: chalk22.red
14723
+ info: chalk23.green,
14724
+ advisory: chalk23.yellow,
14725
+ warn: chalk23.red
14412
14726
  };
14413
14727
  var KIND_GLYPH = {
14414
14728
  missing: "+",
@@ -14420,14 +14734,14 @@ function printItems(items) {
14420
14734
  for (const i of items) {
14421
14735
  const color = SEV_COLOR[i.severity] ?? ((s) => s);
14422
14736
  const detail = i.note ? ` (${i.note})` : i.suggested !== void 0 ? ` \u2192 ${JSON.stringify(i.suggested)}` : "";
14423
- 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));
14424
14738
  }
14425
14739
  }
14426
- var configCommand = new Command22("config").description("Inspect and reconcile squadrant config");
14740
+ var configCommand = new Command23("config").description("Inspect and reconcile squadrant config");
14427
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) => {
14428
14742
  const pkgVersion = readPkgVersion2();
14429
14743
  if (!fs27.existsSync(DEFAULT_CONFIG_PATH)) {
14430
- 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."));
14431
14745
  return;
14432
14746
  }
14433
14747
  const res = runConfigCheck({ configPath: DEFAULT_CONFIG_PATH, pkgVersion, fix: opts.fix, accept: opts.accept });
@@ -14436,21 +14750,21 @@ configCommand.command("check").description("Detect config drift vs the current d
14436
14750
  return;
14437
14751
  }
14438
14752
  if (res.items.length === 0) {
14439
- 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."));
14440
14754
  return;
14441
14755
  }
14442
- console.log(chalk22.bold("\nConfig drift:\n"));
14756
+ console.log(chalk23.bold("\nConfig drift:\n"));
14443
14757
  printItems(res.items);
14444
14758
  if (opts.fix && res.applied.length) {
14445
- console.log(chalk22.green(`
14759
+ console.log(chalk23.green(`
14446
14760
  \u2714 Applied ${res.applied.length} safe item(s): ${res.applied.join(", ")}`));
14447
14761
  }
14448
14762
  const judgment = res.remaining.filter((i) => i.kind === "changed-default" || i.kind === "invalid");
14449
14763
  if (judgment.length) {
14450
- console.log(chalk22.yellow(`
14764
+ console.log(chalk23.yellow(`
14451
14765
  ${judgment.length} item(s) need review \u2014 run the config-doctor skill, or \`squadrant config check --accept\` to keep your values.`));
14452
14766
  } else if (res.stamped) {
14453
- console.log(chalk22.green("\n\u2714 Config reconciled and stamped."));
14767
+ console.log(chalk23.green("\n\u2714 Config reconciled and stamped."));
14454
14768
  }
14455
14769
  });
14456
14770
  configCommand.command("get").description("Read a config value by dotted key (e.g. defaults.effort)").argument("<key>", "dotted config key").action((key) => {
@@ -14458,7 +14772,7 @@ configCommand.command("get").description("Read a config value by dotted key (e.g
14458
14772
  const value = runConfigGet(key);
14459
14773
  console.log(typeof value === "string" ? value : JSON.stringify(value));
14460
14774
  } catch (e) {
14461
- console.error(chalk22.red(e.message));
14775
+ console.error(chalk23.red(e.message));
14462
14776
  process.exit(1);
14463
14777
  }
14464
14778
  });
@@ -14466,7 +14780,7 @@ configCommand.command("set").description("Write a config value by dotted key (e.
14466
14780
  try {
14467
14781
  runConfigSetAction({ key, value, noRestart: opts.restart === false });
14468
14782
  } catch (e) {
14469
- console.error(chalk22.red(e.message));
14783
+ console.error(chalk23.red(e.message));
14470
14784
  process.exit(1);
14471
14785
  }
14472
14786
  });
@@ -14476,8 +14790,8 @@ function readPkgVersion2() {
14476
14790
  }
14477
14791
 
14478
14792
  // packages/cli/src/commands/heal.ts
14479
- import { Command as Command23 } from "commander";
14480
- import chalk23 from "chalk";
14793
+ import { Command as Command24 } from "commander";
14794
+ import chalk24 from "chalk";
14481
14795
  init_dist2();
14482
14796
  init_dist2();
14483
14797
  function buildHealStatus(components) {
@@ -14518,15 +14832,15 @@ async function runHealStatus(opts) {
14518
14832
  return result.healthy ? 0 : 2;
14519
14833
  }
14520
14834
  if (result.healthy) {
14521
- stdout.write(chalk23.green("\u2714 all components healthy\n"));
14835
+ stdout.write(chalk24.green("\u2714 all components healthy\n"));
14522
14836
  return 0;
14523
14837
  }
14524
- stdout.write(chalk23.bold("Unhealthy components:\n\n"));
14838
+ stdout.write(chalk24.bold("Unhealthy components:\n\n"));
14525
14839
  for (const c of result.components) {
14526
14840
  if (c.healCmd) {
14527
- 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}
14528
14842
  `);
14529
- stdout.write(` heal: ${chalk23.cyan(c.healCmd)}
14843
+ stdout.write(` heal: ${chalk24.cyan(c.healCmd)}
14530
14844
  `);
14531
14845
  }
14532
14846
  }
@@ -14537,7 +14851,7 @@ async function runHealDaemon(opts) {
14537
14851
  stdout.write("restarting squadrantd via launchd kickstart...\n");
14538
14852
  try {
14539
14853
  opts.ensureDaemon();
14540
- stdout.write(chalk23.green("\u2714 daemon kickstart complete\n"));
14854
+ stdout.write(chalk24.green("\u2714 daemon kickstart complete\n"));
14541
14855
  return 0;
14542
14856
  } catch (e) {
14543
14857
  stderr.write(`heal daemon failed: ${e.message}
@@ -14545,8 +14859,8 @@ async function runHealDaemon(opts) {
14545
14859
  return 1;
14546
14860
  }
14547
14861
  }
14548
- 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(
14549
- 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) => {
14550
14864
  const code = await runHealStatus({
14551
14865
  project: opts.project,
14552
14866
  json: opts.json ?? false,
@@ -14557,7 +14871,7 @@ var healCommand = new Command23("heal").description("Targeted, idempotent remedi
14557
14871
  process.exit(code);
14558
14872
  })
14559
14873
  ).addCommand(
14560
- 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 () => {
14561
14875
  const code = await runHealDaemon({
14562
14876
  ensureDaemon: () => restartDaemonIfRunning({ reason: "heal", isRunning: () => true }),
14563
14877
  stdout: process.stdout,
@@ -14568,15 +14882,15 @@ var healCommand = new Command23("heal").description("Targeted, idempotent remedi
14568
14882
  );
14569
14883
 
14570
14884
  // packages/cli/src/commands/group.ts
14571
- import { Command as Command25 } from "commander";
14572
- import chalk25 from "chalk";
14885
+ import { Command as Command26 } from "commander";
14886
+ import chalk26 from "chalk";
14573
14887
 
14574
14888
  // packages/cli/src/commands/dispatch.ts
14575
14889
  init_dist();
14576
14890
  init_dist2();
14577
- import { Command as Command24 } from "commander";
14891
+ import { Command as Command25 } from "commander";
14578
14892
  import { execSync as execSync13 } from "child_process";
14579
- import chalk24 from "chalk";
14893
+ import chalk25 from "chalk";
14580
14894
  async function runDispatch(toProject, task, opts) {
14581
14895
  const fromProject = resolveCurrentProject(loadConfig());
14582
14896
  if (!fromProject) {
@@ -14601,21 +14915,21 @@ async function runDispatch(toProject, task, opts) {
14601
14915
  async function dispatchAction(toProject, task, opts) {
14602
14916
  try {
14603
14917
  const result = await runDispatch(toProject, task, opts);
14604
- console.log(chalk24.green(`\u2714 Dispatched to '${toProject}' (task ${result.id.slice(0, 8)})`));
14605
- console.log(chalk24.dim(` originProject: ${result.originProject ?? "none"}`));
14606
- 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)."));
14607
14921
  } catch (e) {
14608
- console.error(chalk24.red(`\u2718 ${e.message}`));
14922
+ console.error(chalk25.red(`\u2718 ${e.message}`));
14609
14923
  process.exit(1);
14610
14924
  }
14611
14925
  }
14612
- 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);
14613
14927
 
14614
14928
  // packages/cli/src/commands/group.ts
14615
14929
  init_dist2();
14616
- var groupCommand = new Command25("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
14617
- 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) => {
14618
- 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(
14619
14933
  `\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
14620
14934
  ));
14621
14935
  await dispatchAction(toProject, task, opts);
@@ -14626,8 +14940,8 @@ var groupCommand = new Command25("group").description("Cross-project intra-group
14626
14940
  init_dist();
14627
14941
  init_dist2();
14628
14942
  import { join as join26, dirname as dirname7 } from "path";
14629
- import { Command as Command26 } from "commander";
14630
- import chalk26 from "chalk";
14943
+ import { Command as Command27 } from "commander";
14944
+ import chalk27 from "chalk";
14631
14945
  init_require_daemon();
14632
14946
  async function runPing(project, message) {
14633
14947
  const config = loadConfig();
@@ -14643,20 +14957,20 @@ async function runPing(project, message) {
14643
14957
  source: "cli"
14644
14958
  });
14645
14959
  }
14646
- 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) => {
14647
14961
  try {
14648
14962
  await runPing(project, message);
14649
- console.log(chalk26.green(`\u2714 Pinged '${project}'`));
14963
+ console.log(chalk27.green(`\u2714 Pinged '${project}'`));
14650
14964
  } catch (err) {
14651
- console.error(chalk26.red(err.message));
14965
+ console.error(chalk27.red(err.message));
14652
14966
  process.exit(1);
14653
14967
  }
14654
14968
  });
14655
14969
 
14656
14970
  // packages/cli/src/commands/cmux.ts
14657
14971
  init_dist();
14658
- import { Command as Command27 } from "commander";
14659
- import chalk27 from "chalk";
14972
+ import { Command as Command28 } from "commander";
14973
+ import chalk28 from "chalk";
14660
14974
  async function runCmuxAutoconfig(opts) {
14661
14975
  const { json, stdout, stderr } = opts;
14662
14976
  let r;
@@ -14675,19 +14989,19 @@ async function runCmuxAutoconfig(opts) {
14675
14989
  stdout.write(`wrote cmux automation config \u2192 ${r.configPath}
14676
14990
  `);
14677
14991
  } else {
14678
- 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})
14679
14993
  `));
14680
14994
  }
14681
14995
  if (r.verdict === "reachable") {
14682
- 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"));
14683
14997
  return 0;
14684
14998
  }
14685
14999
  if (r.needsRestart) {
14686
15000
  stdout.write(
14687
- 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")
14688
15002
  );
14689
15003
  if (r.promptedThisRun) {
14690
- 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"));
14691
15005
  }
14692
15006
  return 2;
14693
15007
  }
@@ -14696,8 +15010,8 @@ async function runCmuxAutoconfig(opts) {
14696
15010
  );
14697
15011
  return 1;
14698
15012
  }
14699
- var cmuxCommand = new Command27("cmux").description("cmux integration helpers").addCommand(
14700
- new Command27("autoconfig").description(
15013
+ var cmuxCommand = new Command28("cmux").description("cmux integration helpers").addCommand(
15014
+ new Command28("autoconfig").description(
14701
15015
  "Write the cmux automation socket config and probe whether daemon-direct\ndelivery is reachable. Idempotent; prompts once if a cmux restart is needed."
14702
15016
  ).option("--json", "output machine-readable JSON (exit 0=reachable, 1=unknown, 2=restart-needed)").action(async (opts) => {
14703
15017
  const code = await runCmuxAutoconfig({
@@ -14714,8 +15028,8 @@ init_dist();
14714
15028
  init_dist2();
14715
15029
  import fs28 from "fs";
14716
15030
  import path29 from "path";
14717
- import { Command as Command28 } from "commander";
14718
- import chalk28 from "chalk";
15031
+ import { Command as Command29 } from "commander";
15032
+ import chalk29 from "chalk";
14719
15033
  var VALID_EFFORTS = ["max", "balance", "low"];
14720
15034
  var EFFORT_MEANING = {
14721
15035
  max: "tokens are plentiful \u2014 bias crew spawns toward claude/opus",
@@ -14780,29 +15094,29 @@ async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(
14780
15094
  }
14781
15095
  }
14782
15096
  }
14783
- 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) => {
14784
15098
  if (value === void 0) {
14785
15099
  let result;
14786
15100
  try {
14787
15101
  result = runEffortGet(void 0, options.project);
14788
15102
  } catch (err) {
14789
- console.error(chalk28.red(err.message));
15103
+ console.error(chalk29.red(err.message));
14790
15104
  process.exit(1);
14791
15105
  }
14792
15106
  const label = options.project ? `${options.project} project` : "global";
14793
- console.log(chalk28.bold(`Current effort (${label}):`), chalk28.cyan(result.effort));
14794
- 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]));
14795
15109
  return;
14796
15110
  }
14797
15111
  try {
14798
15112
  runEffortSet(value, void 0, options.project);
14799
15113
  } catch (err) {
14800
- console.error(chalk28.red(err.message));
15114
+ console.error(chalk29.red(err.message));
14801
15115
  process.exit(1);
14802
15116
  }
14803
15117
  const effort = value;
14804
- console.log(chalk28.green(`\u2714 effort \u2192 ${effort} (${effortScopeLabel(options.project)})`));
14805
- 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]));
14806
15120
  try {
14807
15121
  const { createCmuxDriver: createCmuxDriver2, RuntimeRegistry: RuntimeRegistry2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports3));
14808
15122
  const config = loadConfig();
@@ -14812,7 +15126,7 @@ var effortCommand = new Command28("effort").description("Get or set the crew tok
14812
15126
  const append = (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" });
14813
15127
  await notifyCaptainsOfEffort(effort, config, driver, process.cwd(), append, options.project);
14814
15128
  } catch {
14815
- 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)"));
14816
15130
  }
14817
15131
  });
14818
15132
 
@@ -14821,8 +15135,8 @@ init_dist();
14821
15135
  init_dist2();
14822
15136
  import { join as join27, dirname as dirname8 } from "path";
14823
15137
  import { emitKeypressEvents } from "readline";
14824
- import { Command as Command29 } from "commander";
14825
- import chalk29 from "chalk";
15138
+ import { Command as Command30 } from "commander";
15139
+ import chalk30 from "chalk";
14826
15140
  function defaultStateRoot() {
14827
15141
  return join27(dirname8(DEFAULT_CONFIG_PATH), "state");
14828
15142
  }
@@ -14869,11 +15183,11 @@ async function questionYesNo(prompt) {
14869
15183
  });
14870
15184
  });
14871
15185
  }
14872
- 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");
14873
15187
  telegramCommand.command("status").description("Show Telegram config and linked projects").action(() => {
14874
15188
  const { tokenSet, supergroupId, links } = runTelegramStatus({ config: loadConfig(), stateRoot: defaultStateRoot() });
14875
- console.log(`token: ${tokenSet ? chalk29.green("set") : chalk29.yellow("unset")}`);
14876
- 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")}`);
14877
15191
  if (links.length === 0) {
14878
15192
  console.log("no projects linked");
14879
15193
  return;
@@ -14883,32 +15197,32 @@ telegramCommand.command("status").description("Show Telegram config and linked p
14883
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) => {
14884
15198
  const cfg = loadConfig().telegram;
14885
15199
  if (!cfg) {
14886
- 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"));
14887
15201
  process.exit(1);
14888
15202
  }
14889
15203
  const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
14890
15204
  if (!token) {
14891
- 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"));
14892
15206
  process.exit(1);
14893
15207
  }
14894
15208
  const client = createTelegramClient({ token });
14895
15209
  const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
14896
- 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}`));
14897
15211
  });
14898
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) => {
14899
15213
  if (!process.stdin.isTTY) {
14900
- 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"));
14901
15215
  process.exit(1);
14902
15216
  }
14903
15217
  console.log();
14904
- 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");
14905
15219
  console.log();
14906
15220
  console.log("Before you start you need:");
14907
15221
  console.log(" 1. A bot token from @BotFather (send /newbot)");
14908
15222
  console.log(" 2. A forum supergroup with the bot added as an admin (Topics enabled)");
14909
15223
  console.log(" 3. Bot privacy mode set to OFF (@BotFather \u2192 /setprivacy \u2192 Disable)");
14910
15224
  console.log();
14911
- console.log(chalk29.bold("Step 1/3 \u2014 Bot token"));
15225
+ console.log(chalk30.bold("Step 1/3 \u2014 Bot token"));
14912
15226
  const existingCfg = loadConfig().telegram;
14913
15227
  const existingToken = existingCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
14914
15228
  const decision = resolveSetupToken(existingToken, { resetToken: opts.resetToken ?? false });
@@ -14920,67 +15234,67 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
14920
15234
  try {
14921
15235
  botUser = await client.getMe();
14922
15236
  token = existingToken;
14923
- console.log(chalk29.green(`Using existing bot token (@${botUser.username})`));
15237
+ console.log(chalk30.green(`Using existing bot token (@${botUser.username})`));
14924
15238
  console.log();
14925
15239
  } catch {
14926
- 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."));
14927
15241
  console.log("Paste your bot token then press Enter (input is hidden):");
14928
15242
  token = await questionMasked();
14929
15243
  if (!token) {
14930
- console.error(chalk29.red("token required"));
15244
+ console.error(chalk30.red("token required"));
14931
15245
  process.exit(1);
14932
15246
  }
14933
15247
  client = createTelegramClient({ token });
14934
15248
  try {
14935
15249
  botUser = await client.getMe();
14936
15250
  } catch (e) {
14937
- console.error(chalk29.red(`token rejected: ${e.message}`));
15251
+ console.error(chalk30.red(`token rejected: ${e.message}`));
14938
15252
  process.exit(1);
14939
15253
  }
14940
- console.log(chalk29.green(`Connected as @${botUser.username}`));
15254
+ console.log(chalk30.green(`Connected as @${botUser.username}`));
14941
15255
  console.log();
14942
15256
  }
14943
15257
  } else {
14944
15258
  console.log("Paste your bot token then press Enter (input is hidden):");
14945
15259
  token = await questionMasked();
14946
15260
  if (!token) {
14947
- console.error(chalk29.red("token required"));
15261
+ console.error(chalk30.red("token required"));
14948
15262
  process.exit(1);
14949
15263
  }
14950
15264
  client = createTelegramClient({ token });
14951
15265
  try {
14952
15266
  botUser = await client.getMe();
14953
15267
  } catch (e) {
14954
- console.error(chalk29.red(`token rejected: ${e.message}`));
15268
+ console.error(chalk30.red(`token rejected: ${e.message}`));
14955
15269
  process.exit(1);
14956
15270
  }
14957
- console.log(chalk29.green(`Connected as @${botUser.username}`));
15271
+ console.log(chalk30.green(`Connected as @${botUser.username}`));
14958
15272
  console.log();
14959
15273
  }
14960
- console.log(chalk29.bold("Step 2/3 \u2014 Supergroup"));
15274
+ console.log(chalk30.bold("Step 2/3 \u2014 Supergroup"));
14961
15275
  const groupDecision = resolveSetupGroup(existingCfg?.supergroupId, { redetect: opts.redetect ?? false });
14962
15276
  let supergroupId;
14963
15277
  let detectedUserId;
14964
15278
  if (groupDecision === "reuse") {
14965
15279
  supergroupId = existingCfg.supergroupId;
14966
- console.log(chalk29.green(`Using existing group: ${supergroupId}`));
15280
+ console.log(chalk30.green(`Using existing group: ${supergroupId}`));
14967
15281
  console.log();
14968
15282
  } else {
14969
15283
  console.log("Add the bot to your forum supergroup, then send any message in it.");
14970
- 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"));
14971
15285
  try {
14972
15286
  ({ supergroupId, userId: detectedUserId } = await detectGroupAndUser(client, { timeoutMs: 6e4 }));
14973
15287
  } catch {
14974
- console.error(chalk29.red("Timed out \u2014 no supergroup message received within 60s."));
14975
- 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"));
14976
15290
  process.exit(1);
14977
15291
  }
14978
- console.log(chalk29.green(`Found group: ${supergroupId}`));
15292
+ console.log(chalk30.green(`Found group: ${supergroupId}`));
14979
15293
  console.log();
14980
15294
  }
14981
- console.log(chalk29.bold("Step 3/3 \u2014 Remote control + Save"));
14982
- console.log(chalk29.dim("Remote control enables auto-launching captains and the General command channel"));
14983
- 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)."));
14984
15298
  const finalUserId = resolveSetupUserId(opts.userId, detectedUserId, defaultStateRoot());
14985
15299
  let users;
14986
15300
  let remoteControl;
@@ -14994,32 +15308,32 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
14994
15308
  remoteControl = true;
14995
15309
  }
14996
15310
  } else if (groupDecision === "detect") {
14997
- console.log(chalk29.yellow("Could not read your user-id from that message \u2014 skipping remote control."));
14998
- 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."));
14999
15313
  printedRemoteControlState = true;
15000
15314
  } else {
15001
15315
  const existingUsers = existingCfg?.users;
15002
15316
  if (existingUsers && existingUsers.length > 0) {
15003
- 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.`));
15004
15318
  } else {
15005
- 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."));
15006
15320
  }
15007
15321
  printedRemoteControlState = true;
15008
15322
  }
15009
15323
  writeTelegramConfig(DEFAULT_CONFIG_PATH, { token, supergroupId, users, remoteControl });
15010
- 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}`));
15011
15325
  if (!printedRemoteControlState) {
15012
15326
  if (remoteControl) {
15013
- 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]})`));
15014
15328
  } else {
15015
- 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."));
15016
15330
  }
15017
15331
  }
15018
15332
  try {
15019
15333
  await runRegisterCommands({ client });
15020
- console.log(chalk29.dim("Registered the /command menu."));
15334
+ console.log(chalk30.dim("Registered the /command menu."));
15021
15335
  } catch (e) {
15022
- console.log(chalk29.yellow(`command-menu registration skipped: ${e.message}`));
15336
+ console.log(chalk30.yellow(`command-menu registration skipped: ${e.message}`));
15023
15337
  }
15024
15338
  const topics = loadState(defaultStateRoot()).topics;
15025
15339
  const topicEntries = Object.entries(topics);
@@ -15028,28 +15342,28 @@ telegramCommand.command("setup").description("Interactive wizard \u2014 bot toke
15028
15342
  const project = key.slice(0, key.indexOf("::"));
15029
15343
  return `${project}\u2192${id}`;
15030
15344
  }).join(", ");
15031
- 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)`));
15032
15346
  } else {
15033
- 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>"));
15034
15348
  }
15035
15349
  runTelegramPostSetup({});
15036
15350
  console.log();
15037
- console.log(`Next: ${chalk29.cyan("squadrant telegram link <project>")}`);
15351
+ console.log(`Next: ${chalk30.cyan("squadrant telegram link <project>")}`);
15038
15352
  });
15039
15353
  telegramCommand.command("register-commands").description("Register (or re-register) the bot's / command menu with Telegram").action(async () => {
15040
15354
  const cfg = loadConfig().telegram;
15041
15355
  if (!cfg) {
15042
- 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"));
15043
15357
  process.exit(1);
15044
15358
  }
15045
15359
  const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
15046
15360
  if (!token) {
15047
- 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"));
15048
15362
  process.exit(1);
15049
15363
  }
15050
15364
  const client = createTelegramClient({ token });
15051
15365
  await runRegisterCommands({ client });
15052
- console.log(chalk29.green(`registered ${BOT_COMMANDS.length} bot commands`));
15366
+ console.log(chalk30.green(`registered ${BOT_COMMANDS.length} bot commands`));
15053
15367
  });
15054
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) => {
15055
15369
  const stateRoot = defaultStateRoot();
@@ -15060,7 +15374,7 @@ telegramCommand.command("notify").argument("[project]", "project to toggle").arg
15060
15374
  return;
15061
15375
  }
15062
15376
  for (const r of rows) {
15063
- 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)")}`);
15064
15378
  }
15065
15379
  return;
15066
15380
  }
@@ -15069,53 +15383,53 @@ telegramCommand.command("notify").argument("[project]", "project to toggle").arg
15069
15383
  const token = tgCfg?.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
15070
15384
  if (state === "crew" || state === "cap") {
15071
15385
  if (value === void 0) {
15072
- console.error(chalk29.red(`usage: squadrant telegram notify <project> ${state} <value>`));
15386
+ console.error(chalk30.red(`usage: squadrant telegram notify <project> ${state} <value>`));
15073
15387
  process.exit(1);
15074
15388
  }
15075
15389
  const resolved2 = resolveNotify(globalNotify, loadProjectOverride(project));
15076
15390
  const before2 = { ...resolved2, active: isNotifyActive(stateRoot, project) };
15077
15391
  const res = runTelegramNotifyPref({ project, dimension: state, value });
15078
15392
  if (!res.ok) {
15079
- console.error(chalk29.red(res.message));
15393
+ console.error(chalk30.red(res.message));
15080
15394
  process.exit(1);
15081
15395
  }
15082
- console.log(chalk29.green(`${project} ${state} = ${value}`));
15396
+ console.log(chalk30.green(`${project} ${state} = ${value}`));
15083
15397
  const after2 = state === "crew" ? { ...before2, crew: value } : { ...before2, cap: value === "on" };
15084
15398
  if (tgCfg && token) {
15085
15399
  const client = createTelegramClient({ token });
15086
15400
  const sent = await runNotifyConfirmation({ project, before: before2, after: after2, cfg: tgCfg, client, stateRoot });
15087
- if (sent) console.log(chalk29.dim(`\u2192 notified ${project} topic`));
15401
+ if (sent) console.log(chalk30.dim(`\u2192 notified ${project} topic`));
15088
15402
  }
15089
15403
  return;
15090
15404
  }
15091
15405
  if (state !== "on" && state !== "off") {
15092
- 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>>"));
15093
15407
  process.exit(1);
15094
15408
  }
15095
15409
  const resolved = resolveNotify(globalNotify, loadProjectOverride(project));
15096
15410
  const before = { ...resolved, active: isNotifyActive(stateRoot, project) };
15097
15411
  const after = { ...before, active: state === "on" };
15098
15412
  runTelegramNotifySet({ project, active: state === "on", stateRoot });
15099
- console.log(chalk29.green(`${project} notifications ${state === "on" ? "ON" : "OFF"}`));
15413
+ console.log(chalk30.green(`${project} notifications ${state === "on" ? "ON" : "OFF"}`));
15100
15414
  if (tgCfg && token) {
15101
15415
  const client = createTelegramClient({ token });
15102
15416
  const sent = await runNotifyConfirmation({ project, before, after, cfg: tgCfg, client, stateRoot });
15103
- if (sent) console.log(chalk29.dim(`\u2192 notified ${project} topic`));
15417
+ if (sent) console.log(chalk30.dim(`\u2192 notified ${project} topic`));
15104
15418
  }
15105
15419
  });
15106
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) => {
15107
15421
  const cfg = loadConfig().telegram;
15108
15422
  if (!cfg) {
15109
- 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"));
15110
15424
  process.exit(1);
15111
15425
  }
15112
15426
  const token = cfg.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
15113
15427
  if (!token) {
15114
- 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"));
15115
15429
  process.exit(1);
15116
15430
  }
15117
15431
  if (!capAllowed(project, cfg.notify)) {
15118
- 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`));
15119
15433
  return;
15120
15434
  }
15121
15435
  let message;
@@ -15128,19 +15442,19 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
15128
15442
  for await (const line of rl) lines.push(line);
15129
15443
  message = lines.join("\n").trimEnd();
15130
15444
  if (!message) {
15131
- console.error(chalk29.red("no message provided (stdin was empty)"));
15445
+ console.error(chalk30.red("no message provided (stdin was empty)"));
15132
15446
  process.exit(1);
15133
15447
  }
15134
15448
  } else {
15135
- 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"));
15136
15450
  process.exit(1);
15137
15451
  }
15138
15452
  const client = createTelegramClient({ token });
15139
15453
  try {
15140
15454
  const { chatId, topicId } = await runTelegramSend({ project, message, cfg, client, stateRoot: defaultStateRoot() });
15141
- console.log(chalk29.green(`sent to group ${chatId} topic ${topicId}`));
15455
+ console.log(chalk30.green(`sent to group ${chatId} topic ${topicId}`));
15142
15456
  } catch (e) {
15143
- console.error(chalk29.red(e.message));
15457
+ console.error(chalk30.red(e.message));
15144
15458
  process.exit(1);
15145
15459
  }
15146
15460
  });
@@ -15148,7 +15462,7 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
15148
15462
  // packages/cli/src/commands/hooks.ts
15149
15463
  init_dist2();
15150
15464
  init_dist4();
15151
- import { Command as Command30 } from "commander";
15465
+ import { Command as Command31 } from "commander";
15152
15466
  import { join as join28 } from "path";
15153
15467
  import { homedir as homedir20 } from "os";
15154
15468
  var SOCK4 = join28(homedir20(), ".config", "squadrant", "squadrant.sock");
@@ -15175,7 +15489,7 @@ function mapHookSub(sub, payload, taskId) {
15175
15489
  }
15176
15490
  }
15177
15491
  function hooksCommand() {
15178
- 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");
15179
15493
  hooks.command("claude <sub>", { hidden: true }).description("internal: bridge a NativeHookSource claude hook to squadrantd").action(async (sub) => {
15180
15494
  const taskId = process.env.SQUADRANT_CREW_TASK_ID;
15181
15495
  const project = process.env.SQUADRANT_CREW_PROJECT;
@@ -15246,7 +15560,7 @@ if (process.argv[2] !== "config") {
15246
15560
  if (!process.env.SQUADRANT_DAEMON_SKIP) {
15247
15561
  ensureDaemon();
15248
15562
  }
15249
- var program = new Command31();
15563
+ var program = new Command32();
15250
15564
  program.name("squadrant").description("Multi-project orchestration for your coding agents (Claude, Codex, opencode, Gemini)").version(pkg.version);
15251
15565
  program.addCommand(doctorCommand);
15252
15566
  program.addCommand(initCommand);
@@ -15255,6 +15569,7 @@ program.addCommand(statusCommand);
15255
15569
  addControlPlaneCrewCommands(crewCommand);
15256
15570
  program.addCommand(crewCommand);
15257
15571
  program.addCommand(sideCommand);
15572
+ program.addCommand(diffCommand);
15258
15573
  program.addCommand(commandCommand);
15259
15574
  program.addCommand(dashboardCommand);
15260
15575
  program.addCommand(launchCommand);