squadrant 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -744,6 +744,7 @@ var init_update_check = __esm({
744
744
 
745
745
  // packages/shared/dist/lib/git-worktree.js
746
746
  import { execFileSync as execFileSync2 } from "child_process";
747
+ import fs4 from "fs";
747
748
  import path4 from "path";
748
749
  function worktreePath(repoRoot, worktreeDir, project, name) {
749
750
  return path4.resolve(repoRoot, worktreeDir, `${project}-${name}`);
@@ -751,6 +752,18 @@ function worktreePath(repoRoot, worktreeDir, project, name) {
751
752
  function crewBranch(name) {
752
753
  return `crew/${name}`;
753
754
  }
755
+ function ensureSpotlightExcluded(repoRoot, worktreeDir) {
756
+ if (process.platform !== "darwin")
757
+ return;
758
+ try {
759
+ const dir = path4.resolve(repoRoot, worktreeDir);
760
+ fs4.mkdirSync(dir, { recursive: true });
761
+ const marker = path4.join(dir, ".metadata_never_index");
762
+ if (!fs4.existsSync(marker))
763
+ fs4.writeFileSync(marker, "");
764
+ } catch {
765
+ }
766
+ }
754
767
  function resolveWorktreeBase(repoRoot, fallback = "develop") {
755
768
  try {
756
769
  const ref = execFileSync2("git", ["-C", repoRoot, "symbolic-ref", "refs/remotes/origin/HEAD"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
@@ -763,6 +776,7 @@ function resolveWorktreeBase(repoRoot, fallback = "develop") {
763
776
  return fallback;
764
777
  }
765
778
  function addWorktree(spec) {
779
+ ensureSpotlightExcluded(spec.repoRoot, spec.worktreeDir);
766
780
  const originalBranch = crewBranch(spec.name);
767
781
  let targetName = spec.name;
768
782
  let targetBranch = originalBranch;
@@ -798,8 +812,25 @@ function addWorktree(spec) {
798
812
  }
799
813
  const wt = worktreePath(spec.repoRoot, spec.worktreeDir, spec.project, targetName);
800
814
  execFileSync2("git", ["-C", spec.repoRoot, "worktree", "add", wt, "-b", targetBranch, spec.base], { stdio: "pipe" });
815
+ installWorktreeDependencies(wt);
801
816
  return wt;
802
817
  }
818
+ function installWorktreeDependencies(wt) {
819
+ if (!fs4.existsSync(path4.join(wt, "package.json")))
820
+ return;
821
+ if (fs4.existsSync(path4.join(wt, "pnpm-lock.yaml"))) {
822
+ execFileSync2("pnpm", ["-C", wt, "install", "--frozen-lockfile"], { stdio: "pipe" });
823
+ } else if (fs4.existsSync(path4.join(wt, "yarn.lock"))) {
824
+ execFileSync2("yarn", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
825
+ } else if (fs4.existsSync(path4.join(wt, "package-lock.json"))) {
826
+ execFileSync2("npm", ["ci"], { cwd: wt, stdio: "pipe" });
827
+ } else if (fs4.existsSync(path4.join(wt, "bun.lockb"))) {
828
+ execFileSync2("bun", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
829
+ } else {
830
+ process.stderr.write(`worktree ${wt}: package.json present but no lockfile \u2014 dependencies not installed; local typechecks/tests may resolve against the main checkout instead of this worktree.
831
+ `);
832
+ }
833
+ }
803
834
  function removeWorktree(repoRoot, wtPath) {
804
835
  try {
805
836
  execFileSync2("git", ["-C", repoRoot, "worktree", "remove", wtPath], { stdio: "pipe" });
@@ -813,7 +844,7 @@ var init_git_worktree = __esm({
813
844
  });
814
845
 
815
846
  // packages/shared/dist/lib/resolve-text-input.js
816
- import fs4 from "fs";
847
+ import fs5 from "fs";
817
848
  async function readAllStdin() {
818
849
  const chunks = [];
819
850
  for await (const chunk of process.stdin) {
@@ -825,7 +856,7 @@ function flagName(label) {
825
856
  return label === "task" ? "--task-file" : "--message-file";
826
857
  }
827
858
  async function resolveTextInput(opts, deps) {
828
- const readFile6 = deps?.readFile ?? ((p) => fs4.readFileSync(p, "utf8"));
859
+ const readFile6 = deps?.readFile ?? ((p) => fs5.readFileSync(p, "utf8"));
829
860
  const readStdin3 = deps?.readStdin ?? readAllStdin;
830
861
  if (opts.filePath) {
831
862
  if (opts.filePath === "-") {
@@ -853,19 +884,19 @@ var init_resolve_text_input = __esm({
853
884
  });
854
885
 
855
886
  // packages/shared/dist/lib/runtime-sync.js
856
- import fs5 from "fs";
887
+ import fs6 from "fs";
857
888
  import path5 from "path";
858
889
  function copyIfDifferent(src, dest) {
859
- if (fs5.existsSync(dest)) {
860
- if (fs5.readFileSync(src).equals(fs5.readFileSync(dest)))
890
+ if (fs6.existsSync(dest)) {
891
+ if (fs6.readFileSync(src).equals(fs6.readFileSync(dest)))
861
892
  return false;
862
893
  }
863
- fs5.copyFileSync(src, dest);
894
+ fs6.copyFileSync(src, dest);
864
895
  return true;
865
896
  }
866
897
  function mirrorDir(src, dest) {
867
- fs5.mkdirSync(dest, { recursive: true });
868
- const srcEntries = fs5.readdirSync(src, { withFileTypes: true });
898
+ fs6.mkdirSync(dest, { recursive: true });
899
+ const srcEntries = fs6.readdirSync(src, { withFileTypes: true });
869
900
  const srcNames = new Set(srcEntries.map((e) => e.name));
870
901
  for (const entry of srcEntries) {
871
902
  const srcPath = path5.join(src, entry.name);
@@ -876,25 +907,25 @@ function mirrorDir(src, dest) {
876
907
  copyIfDifferent(srcPath, destPath);
877
908
  }
878
909
  }
879
- for (const entry of fs5.readdirSync(dest, { withFileTypes: true })) {
910
+ for (const entry of fs6.readdirSync(dest, { withFileTypes: true })) {
880
911
  if (!srcNames.has(entry.name)) {
881
- fs5.rmSync(path5.join(dest, entry.name), { recursive: true, force: true });
912
+ fs6.rmSync(path5.join(dest, entry.name), { recursive: true, force: true });
882
913
  }
883
914
  }
884
915
  }
885
916
  function mirrorFlat(src, dest, match, chmod) {
886
- fs5.mkdirSync(dest, { recursive: true });
887
- const matched = fs5.readdirSync(src, { withFileTypes: true }).filter((e) => e.isFile() && match.test(e.name)).map((e) => e.name);
917
+ fs6.mkdirSync(dest, { recursive: true });
918
+ const matched = fs6.readdirSync(src, { withFileTypes: true }).filter((e) => e.isFile() && match.test(e.name)).map((e) => e.name);
888
919
  const matchedSet = new Set(matched);
889
920
  for (const name of matched) {
890
921
  const destPath = path5.join(dest, name);
891
922
  const copied = copyIfDifferent(path5.join(src, name), destPath);
892
923
  if (copied && chmod !== void 0)
893
- fs5.chmodSync(destPath, chmod);
924
+ fs6.chmodSync(destPath, chmod);
894
925
  }
895
- for (const entry of fs5.readdirSync(dest, { withFileTypes: true })) {
926
+ for (const entry of fs6.readdirSync(dest, { withFileTypes: true })) {
896
927
  if (!matchedSet.has(entry.name)) {
897
- fs5.rmSync(path5.join(dest, entry.name), { recursive: true, force: true });
928
+ fs6.rmSync(path5.join(dest, entry.name), { recursive: true, force: true });
898
929
  }
899
930
  }
900
931
  }
@@ -903,7 +934,7 @@ function ensureRuntimeSynced(opts) {
903
934
  for (const t of targets) {
904
935
  const srcDir = path5.join(opts.sourceRoot, t.srcRel);
905
936
  try {
906
- if (!fs5.existsSync(srcDir))
937
+ if (!fs6.existsSync(srcDir))
907
938
  continue;
908
939
  const destDir = path5.join(opts.runtimeRoot, t.name);
909
940
  if (t.mode === "tree") {
@@ -969,7 +1000,7 @@ var init_tool_compat = __esm({
969
1000
  });
970
1001
 
971
1002
  // packages/shared/dist/lib/canonical-source.js
972
- import fs6 from "fs";
1003
+ import fs7 from "fs";
973
1004
  import path6 from "path";
974
1005
  function parseSkill(raw) {
975
1006
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
@@ -1011,7 +1042,7 @@ async function readSkills(driver, skillsDir) {
1011
1042
  function readRoleTemplates(opts) {
1012
1043
  if (!opts.pkgRoot)
1013
1044
  return "";
1014
- const reader = opts.readFile ?? ((p) => fs6.readFileSync(p, "utf-8"));
1045
+ const reader = opts.readFile ?? ((p) => fs7.readFileSync(p, "utf-8"));
1015
1046
  const sections = [];
1016
1047
  for (const { file, heading } of ROLE_TEMPLATES) {
1017
1048
  const full = path6.join(opts.pkgRoot, "templates", file);
@@ -1051,7 +1082,7 @@ var init_canonical_source = __esm({
1051
1082
 
1052
1083
  // packages/shared/dist/lib/daily-logs.js
1053
1084
  import { execSync } from "child_process";
1054
- import fs7 from "fs";
1085
+ import fs8 from "fs";
1055
1086
  import path7 from "path";
1056
1087
  import matter from "gray-matter";
1057
1088
  function iso(d) {
@@ -1104,7 +1135,7 @@ function getGitCommits(projectPath, dateStr) {
1104
1135
  }
1105
1136
  function getGitCommitsInRange(projectPath, since, until) {
1106
1137
  const resolved = resolveHome(projectPath);
1107
- if (!fs7.existsSync(path7.join(resolved, ".git")))
1138
+ if (!fs8.existsSync(path7.join(resolved, ".git")))
1108
1139
  return [];
1109
1140
  const untilArg = until ? ` --until="${until}"` : "";
1110
1141
  try {
@@ -1118,7 +1149,7 @@ function getGitCommitsInRange(projectPath, since, until) {
1118
1149
  }
1119
1150
  function getMergedPRsInRange(projectPath, since, until) {
1120
1151
  const resolved = resolveHome(projectPath);
1121
- if (!fs7.existsSync(path7.join(resolved, ".git")))
1152
+ if (!fs8.existsSync(path7.join(resolved, ".git")))
1122
1153
  return [];
1123
1154
  const untilArg = until ? ` --until="${until}"` : "";
1124
1155
  try {
@@ -1830,7 +1861,7 @@ var init_reduce = __esm({
1830
1861
  });
1831
1862
 
1832
1863
  // packages/core/dist/mailbox.js
1833
- import { promises as fs8 } from "fs";
1864
+ import { promises as fs9 } from "fs";
1834
1865
  import { join as join5 } from "path";
1835
1866
  import { randomUUID } from "crypto";
1836
1867
  function inboxDir(stateRoot) {
@@ -1847,7 +1878,7 @@ async function listRotatedOldestFirst(stateRoot, project) {
1847
1878
  const dir = inboxDir(stateRoot);
1848
1879
  let entries;
1849
1880
  try {
1850
- entries = await fs8.readdir(dir);
1881
+ entries = await fs9.readdir(dir);
1851
1882
  } catch {
1852
1883
  return [];
1853
1884
  }
@@ -1856,7 +1887,7 @@ async function listRotatedOldestFirst(stateRoot, project) {
1856
1887
  }
1857
1888
  async function readMaxSeqFromFile(file) {
1858
1889
  try {
1859
- const buf = await fs8.readFile(file, "utf-8");
1890
+ const buf = await fs9.readFile(file, "utf-8");
1860
1891
  if (!buf.trim())
1861
1892
  return 0;
1862
1893
  const lines = buf.trim().split("\n");
@@ -1897,12 +1928,12 @@ function withProjectLock(project, fn) {
1897
1928
  function appendEntry(stateRoot, project, build) {
1898
1929
  return withProjectLock(project, async () => {
1899
1930
  const dir = inboxDir(stateRoot);
1900
- await fs8.mkdir(dir, { recursive: true });
1931
+ await fs9.mkdir(dir, { recursive: true });
1901
1932
  const file = logPath(stateRoot, project);
1902
1933
  const lastSeq = await readMaxSeq(stateRoot, project);
1903
1934
  const seq = lastSeq + 1;
1904
1935
  const entry = build(seq);
1905
- await fs8.appendFile(file, JSON.stringify(entry) + "\n", { encoding: "utf-8" });
1936
+ await fs9.appendFile(file, JSON.stringify(entry) + "\n", { encoding: "utf-8" });
1906
1937
  return seq;
1907
1938
  });
1908
1939
  }
@@ -1919,7 +1950,7 @@ async function appendToMailbox(opts) {
1919
1950
  }));
1920
1951
  }
1921
1952
  async function appendCaptainMessage(opts) {
1922
- await appendEntry(opts.stateRoot, opts.project, (seq) => ({
1953
+ return appendEntry(opts.stateRoot, opts.project, (seq) => ({
1923
1954
  seq,
1924
1955
  ts: (/* @__PURE__ */ new Date()).toISOString(),
1925
1956
  kind: "captain.message",
@@ -1933,7 +1964,7 @@ function cursorPath(stateRoot, project, subscriber) {
1933
1964
  async function readCursor(opts) {
1934
1965
  let buf;
1935
1966
  try {
1936
- buf = await fs8.readFile(cursorPath(opts.stateRoot, opts.project, opts.subscriber), "utf-8");
1967
+ buf = await fs9.readFile(cursorPath(opts.stateRoot, opts.project, opts.subscriber), "utf-8");
1937
1968
  } catch (e) {
1938
1969
  if (e.code === "ENOENT")
1939
1970
  return null;
@@ -1947,8 +1978,20 @@ async function readCursor(opts) {
1947
1978
  return null;
1948
1979
  }
1949
1980
  }
1981
+ async function waitForCaptainDelivery(opts) {
1982
+ const subscriber = opts.subscriber ?? "captain";
1983
+ const deadline = Date.now() + opts.timeoutMs;
1984
+ for (; ; ) {
1985
+ const cursor = await readCursor({ stateRoot: opts.stateRoot, project: opts.project, subscriber });
1986
+ if (cursor && cursor.lastAckedSeq >= opts.seq)
1987
+ return true;
1988
+ if (Date.now() >= deadline)
1989
+ return false;
1990
+ await new Promise((r) => setTimeout(r, opts.pollMs));
1991
+ }
1992
+ }
1950
1993
  async function writeCursor(opts) {
1951
- await fs8.mkdir(inboxDir(opts.stateRoot), { recursive: true });
1994
+ await fs9.mkdir(inboxDir(opts.stateRoot), { recursive: true });
1952
1995
  const dest = cursorPath(opts.stateRoot, opts.project, opts.subscriber);
1953
1996
  const tmp = `${dest}.${process.pid}.${randomUUID()}.tmp`;
1954
1997
  const data = {
@@ -1956,7 +1999,7 @@ async function writeCursor(opts) {
1956
1999
  subscriber: opts.subscriber,
1957
2000
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1958
2001
  };
1959
- const handle = await fs8.open(tmp, "w");
2002
+ const handle = await fs9.open(tmp, "w");
1960
2003
  try {
1961
2004
  await handle.writeFile(JSON.stringify(data), { encoding: "utf-8" });
1962
2005
  await handle.sync();
@@ -1964,9 +2007,9 @@ async function writeCursor(opts) {
1964
2007
  await handle.close();
1965
2008
  }
1966
2009
  try {
1967
- await fs8.rename(tmp, dest);
2010
+ await fs9.rename(tmp, dest);
1968
2011
  } catch (e) {
1969
- await fs8.unlink(tmp).catch(() => {
2012
+ await fs9.unlink(tmp).catch(() => {
1970
2013
  });
1971
2014
  throw e;
1972
2015
  }
@@ -1977,7 +2020,7 @@ async function* readFromCursor(opts) {
1977
2020
  for (const file of files) {
1978
2021
  let buf;
1979
2022
  try {
1980
- buf = await fs8.readFile(file, "utf-8");
2023
+ buf = await fs9.readFile(file, "utf-8");
1981
2024
  } catch (e) {
1982
2025
  if (e.code === "ENOENT")
1983
2026
  continue;
@@ -2003,7 +2046,7 @@ async function mailboxStats(stateRoot, project) {
2003
2046
  let sizeBytes = 0;
2004
2047
  for (const f of [file, ...rotated]) {
2005
2048
  try {
2006
- sizeBytes += (await fs8.stat(f)).size;
2049
+ sizeBytes += (await fs9.stat(f)).size;
2007
2050
  } catch (e) {
2008
2051
  if (e.code !== "ENOENT")
2009
2052
  throw e;
@@ -2019,7 +2062,7 @@ async function mailboxStats(stateRoot, project) {
2019
2062
  }
2020
2063
  async function oldestEntryAgeMs(file) {
2021
2064
  try {
2022
- const buf = await fs8.readFile(file, "utf-8");
2065
+ const buf = await fs9.readFile(file, "utf-8");
2023
2066
  const firstLine2 = buf.split("\n").find((l) => l.trim());
2024
2067
  if (!firstLine2)
2025
2068
  return 0;
@@ -2034,7 +2077,7 @@ async function rotateIfNeeded(opts) {
2034
2077
  const file = logPath(opts.stateRoot, opts.project);
2035
2078
  let size = 0;
2036
2079
  try {
2037
- size = (await fs8.stat(file)).size;
2080
+ size = (await fs9.stat(file)).size;
2038
2081
  } catch (e) {
2039
2082
  if (e.code === "ENOENT")
2040
2083
  return { rotated: false };
@@ -2050,22 +2093,22 @@ async function rotateIfNeeded(opts) {
2050
2093
  const dst = `${file}.${n + 1}`;
2051
2094
  if (n + 1 > opts.keepCount) {
2052
2095
  try {
2053
- await fs8.unlink(src);
2096
+ await fs9.unlink(src);
2054
2097
  } catch (e) {
2055
2098
  if (e.code !== "ENOENT")
2056
2099
  throw e;
2057
2100
  }
2058
2101
  } else {
2059
2102
  try {
2060
- await fs8.rename(src, dst);
2103
+ await fs9.rename(src, dst);
2061
2104
  } catch (e) {
2062
2105
  if (e.code !== "ENOENT")
2063
2106
  throw e;
2064
2107
  }
2065
2108
  }
2066
2109
  }
2067
- await fs8.rename(file, `${file}.1`);
2068
- await fs8.writeFile(file, "", { encoding: "utf-8" });
2110
+ await fs9.rename(file, `${file}.1`);
2111
+ await fs9.writeFile(file, "", { encoding: "utf-8" });
2069
2112
  return { rotated: true, from: file, to: `${file}.1` };
2070
2113
  });
2071
2114
  }
@@ -2358,6 +2401,9 @@ function reconcileLiveness(prev, next) {
2358
2401
  }
2359
2402
  if (next.startedAt >= prev.startedAt || next.lastState === "end")
2360
2403
  return next;
2404
+ const prevAlive = prev.lastState === "start" && prev.pidAlive;
2405
+ if (!prevAlive && next.lastState === "start" && next.pidAlive)
2406
+ return next;
2361
2407
  return prev;
2362
2408
  }
2363
2409
  var CREW_STALE_MS, CREW_GONE_MS, TERMINAL;
@@ -3363,9 +3409,15 @@ async function runLivenessTick(deps) {
3363
3409
  return;
3364
3410
  }
3365
3411
  const seen = /* @__PURE__ */ new Set();
3412
+ const knownCaptainSessions = /* @__PURE__ */ new Map();
3413
+ for (const e of deps.registry.all()) {
3414
+ if (e.role === "captain")
3415
+ knownCaptainSessions.set(e.sessionId, e.project);
3416
+ }
3366
3417
  const byProject = /* @__PURE__ */ new Map();
3367
3418
  for (const r of records) {
3368
- if (r.role !== "captain")
3419
+ const role = r.role === "captain" || knownCaptainSessions.get(r.sessionId) === r.project ? "captain" : r.role;
3420
+ if (role !== "captain")
3369
3421
  continue;
3370
3422
  let arr = byProject.get(r.project);
3371
3423
  if (!arr) {
@@ -3397,10 +3449,14 @@ async function runLivenessTick(deps) {
3397
3449
  logEntry(deps.log, project, deps.registry.get(project));
3398
3450
  }
3399
3451
  for (const e of deps.registry.all()) {
3400
- if (e.role === "captain" && e.lastState === "start" && !seen.has(e.project)) {
3401
- deps.registry.markEnded(e.project, now);
3402
- logEntry(deps.log, e.project, deps.registry.get(e.project));
3452
+ if (e.role !== "captain" || e.lastState !== "start" || seen.has(e.project))
3453
+ continue;
3454
+ if (e.pid == null || deps.isPidAlive(e.pid)) {
3455
+ deps.log?.(`[${e.role}/runtime] ${e.project} pid=${e.pid} missing from snapshot but not confirmed dead \u2014 leaving alive`);
3456
+ continue;
3403
3457
  }
3458
+ deps.registry.markEnded(e.project, now);
3459
+ logEntry(deps.log, e.project, deps.registry.get(e.project));
3404
3460
  }
3405
3461
  if (deps.reap) {
3406
3462
  for (const e of deps.registry.all()) {
@@ -3994,35 +4050,35 @@ var init_start = __esm({
3994
4050
 
3995
4051
  // packages/core/dist/session-freshness.js
3996
4052
  import crypto from "crypto";
3997
- import fs9 from "fs";
4053
+ import fs10 from "fs";
3998
4054
  import path8 from "path";
3999
4055
  function loadSessions(sessionsPath) {
4000
4056
  try {
4001
- return JSON.parse(fs9.readFileSync(sessionsPath, "utf-8"));
4057
+ return JSON.parse(fs10.readFileSync(sessionsPath, "utf-8"));
4002
4058
  } catch {
4003
4059
  return { workspaces: {} };
4004
4060
  }
4005
4061
  }
4006
4062
  function saveSessions(sessionsPath, sessions) {
4007
4063
  const dir = path8.dirname(sessionsPath);
4008
- fs9.mkdirSync(dir, { recursive: true });
4009
- fs9.writeFileSync(sessionsPath, JSON.stringify(sessions, null, 2) + "\n");
4064
+ fs10.mkdirSync(dir, { recursive: true });
4065
+ fs10.writeFileSync(sessionsPath, JSON.stringify(sessions, null, 2) + "\n");
4010
4066
  }
4011
4067
  function computeTemplateHash(role, templatesDir) {
4012
4068
  const hash = crypto.createHash("sha256");
4013
4069
  const roleFile = path8.join(templatesDir, `${role}.claude.md`);
4014
4070
  const legacyRoleFile = path8.join(templatesDir, `${role}.CLAUDE.md`);
4015
- if (fs9.existsSync(roleFile)) {
4016
- hash.update(fs9.readFileSync(roleFile, "utf-8"));
4017
- } else if (fs9.existsSync(legacyRoleFile)) {
4018
- hash.update(fs9.readFileSync(legacyRoleFile, "utf-8"));
4071
+ if (fs10.existsSync(roleFile)) {
4072
+ hash.update(fs10.readFileSync(roleFile, "utf-8"));
4073
+ } else if (fs10.existsSync(legacyRoleFile)) {
4074
+ hash.update(fs10.readFileSync(legacyRoleFile, "utf-8"));
4019
4075
  }
4020
4076
  const pluginSkillsDir = path8.join(templatesDir, "..", "plugin", "skills");
4021
- if (fs9.existsSync(pluginSkillsDir)) {
4022
- for (const skill of fs9.readdirSync(pluginSkillsDir).sort()) {
4077
+ if (fs10.existsSync(pluginSkillsDir)) {
4078
+ for (const skill of fs10.readdirSync(pluginSkillsDir).sort()) {
4023
4079
  const skillFile = path8.join(pluginSkillsDir, skill, "SKILL.md");
4024
- if (fs9.existsSync(skillFile)) {
4025
- hash.update(fs9.readFileSync(skillFile, "utf-8"));
4080
+ if (fs10.existsSync(skillFile)) {
4081
+ hash.update(fs10.readFileSync(skillFile, "utf-8"));
4026
4082
  }
4027
4083
  }
4028
4084
  }
@@ -4086,6 +4142,9 @@ function shellQuote(p) {
4086
4142
  function titleFor(project, name) {
4087
4143
  return `\u{1F527} ${project}:${name}`;
4088
4144
  }
4145
+ function niceCrewCommand(cmd) {
4146
+ return `nice -n ${CREW_NICE_LEVEL} ${cmd}`;
4147
+ }
4089
4148
  function isCrewTitle(project, title) {
4090
4149
  return title.startsWith(`\u{1F527} ${project}:`);
4091
4150
  }
@@ -4104,8 +4163,10 @@ function nextAutoName(existingTitles, project) {
4104
4163
  i++;
4105
4164
  return `crew-${i}`;
4106
4165
  }
4166
+ var CREW_NICE_LEVEL;
4107
4167
  var init_crew_protocol = __esm({
4108
4168
  "packages/core/dist/crew-protocol.js"() {
4169
+ CREW_NICE_LEVEL = 10;
4109
4170
  }
4110
4171
  });
4111
4172
 
@@ -4431,7 +4492,7 @@ var init_format = __esm({
4431
4492
  });
4432
4493
 
4433
4494
  // packages/core/dist/telegram/state.js
4434
- import fs10 from "fs";
4495
+ import fs11 from "fs";
4435
4496
  import path9 from "path";
4436
4497
  function statePath(stateRoot) {
4437
4498
  return path9.join(stateRoot, "telegram-state.json");
@@ -4441,7 +4502,7 @@ function topicKey(project, scope = "project") {
4441
4502
  }
4442
4503
  function loadState(stateRoot) {
4443
4504
  try {
4444
- const raw = fs10.readFileSync(statePath(stateRoot), "utf-8");
4505
+ const raw = fs11.readFileSync(statePath(stateRoot), "utf-8");
4445
4506
  const data = JSON.parse(raw);
4446
4507
  const result = {
4447
4508
  offset: typeof data.offset === "number" ? data.offset : 0,
@@ -4456,8 +4517,8 @@ function loadState(stateRoot) {
4456
4517
  }
4457
4518
  }
4458
4519
  function saveState(stateRoot, s) {
4459
- fs10.mkdirSync(stateRoot, { recursive: true });
4460
- fs10.writeFileSync(statePath(stateRoot), JSON.stringify(s, null, 2) + "\n");
4520
+ fs11.mkdirSync(stateRoot, { recursive: true });
4521
+ fs11.writeFileSync(statePath(stateRoot), JSON.stringify(s, null, 2) + "\n");
4461
4522
  }
4462
4523
  function setTopic(stateRoot, project, topicId, scope = "project") {
4463
4524
  const s = loadState(stateRoot);
@@ -5063,7 +5124,7 @@ var init_restart_daemon = __esm({
5063
5124
  });
5064
5125
 
5065
5126
  // packages/core/dist/telegram/setup.js
5066
- import fs11 from "fs";
5127
+ import fs12 from "fs";
5067
5128
  function resolveSetupGroup(existingSupergroupId, opts) {
5068
5129
  if (existingSupergroupId !== void 0 && !opts.redetect)
5069
5130
  return "reuse";
@@ -5114,7 +5175,7 @@ function writeTelegramConfig(configPath, opts) {
5114
5175
  let config;
5115
5176
  let raw = null;
5116
5177
  try {
5117
- raw = fs11.readFileSync(configPath, "utf-8");
5178
+ raw = fs12.readFileSync(configPath, "utf-8");
5118
5179
  } catch (err) {
5119
5180
  if (err.code !== "ENOENT") {
5120
5181
  throw new Error(`refusing to overwrite unreadable config at ${configPath}: ${String(err)}`);
@@ -5142,7 +5203,7 @@ function writeTelegramConfig(configPath, opts) {
5142
5203
  if (remoteControl !== void 0)
5143
5204
  next.remoteControl = remoteControl;
5144
5205
  config.telegram = next;
5145
- fs11.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
5206
+ fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
5146
5207
  }
5147
5208
  var init_setup = __esm({
5148
5209
  "packages/core/dist/telegram/setup.js"() {
@@ -5470,7 +5531,7 @@ var init_launch_workspace = __esm({
5470
5531
  });
5471
5532
 
5472
5533
  // packages/core/dist/side-session.js
5473
- import fs12 from "fs";
5534
+ import fs13 from "fs";
5474
5535
  function sideTitleFor(project, name) {
5475
5536
  return `\u{1F5D2} ${project}:${name}`;
5476
5537
  }
@@ -5572,7 +5633,7 @@ async function runSideClose(runtime, workspaceId, project, name, projPath, workt
5572
5633
  await runtime.closePane(pane);
5573
5634
  if (projPath) {
5574
5635
  const wtPath = worktreePath(projPath, worktreeDir, project, name);
5575
- if (fs12.existsSync(wtPath)) {
5636
+ if (fs13.existsSync(wtPath)) {
5576
5637
  try {
5577
5638
  removeWorktree(projPath, wtPath);
5578
5639
  } catch (e) {
@@ -5592,7 +5653,7 @@ var init_side_session = __esm({
5592
5653
  });
5593
5654
 
5594
5655
  // packages/core/dist/crew-spawn.js
5595
- import fs13 from "fs";
5656
+ import fs14 from "fs";
5596
5657
  import os5 from "os";
5597
5658
  import path11 from "path";
5598
5659
  async function listCrewPanes(runtime, workspaceId, project) {
@@ -5660,7 +5721,7 @@ async function runCrewSpawn(input, config, deps) {
5660
5721
  if (input.taskFile && input.taskFile !== "-" && !input.shared) {
5661
5722
  const absTaskFile = path11.resolve(input.taskFile);
5662
5723
  const basename = path11.basename(absTaskFile);
5663
- fs13.copyFileSync(absTaskFile, path11.join(spawnCwd, basename));
5724
+ fs14.copyFileSync(absTaskFile, path11.join(spawnCwd, basename));
5664
5725
  firstTurnTask = `Read ./${basename} to get your task brief, then execute it.`;
5665
5726
  }
5666
5727
  const route = !input.agentExplicit && !input.model ? resolveCrewRoute(input.task, config) : null;
@@ -5674,7 +5735,7 @@ async function runCrewSpawn(input, config, deps) {
5674
5735
  }
5675
5736
  if (agentName === "codex") {
5676
5737
  const codexRoleFile = path11.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
5677
- const roleInstructions = fs13.existsSync(codexRoleFile) ? fs13.readFileSync(codexRoleFile, "utf8") : void 0;
5738
+ const roleInstructions = fs14.existsSync(codexRoleFile) ? fs14.readFileSync(codexRoleFile, "utf8") : void 0;
5678
5739
  return runCodexInteractiveSpawn({
5679
5740
  project: input.project,
5680
5741
  task: input.task,
@@ -5725,7 +5786,7 @@ async function runCrewSpawn(input, config, deps) {
5725
5786
  const title2 = titleFor(input.project, name);
5726
5787
  const pane2 = await deps.runtime.newPane({ workspaceId: captain.id, direction: direction2, title: title2 });
5727
5788
  const envPrefix = `SQUADRANT_CREW_TASK_ID=${rec.id} SQUADRANT_CREW_PROJECT=${input.project}`;
5728
- await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} ${cliCommand2}`);
5789
+ await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} ${niceCrewCommand(cliCommand2)}`);
5729
5790
  const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
5730
5791
  const claudeResult = await deps.sendFirstTurn(pane2, `${firstTurnTask}
5731
5792
 
@@ -5773,7 +5834,7 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
5773
5834
  const title2 = titleFor(input.project, name);
5774
5835
  const pane2 = await deps.runtime.newPane({ workspaceId: captain.id, direction: direction2, title: title2 });
5775
5836
  const envPrefix = `SQUADRANT_CREW_TASK_ID=${rec.id} SQUADRANT_CREW_PROJECT=${input.project}`;
5776
- await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} OPENCODE_CONFIG=${opencodeConfigPath} ${cliCommand2}`);
5837
+ await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} OPENCODE_CONFIG=${opencodeConfigPath} ${niceCrewCommand(cliCommand2)}`);
5777
5838
  const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
5778
5839
  const opencodeResult = await deps.sendFirstTurn(pane2, `${firstTurnTask}
5779
5840
 
@@ -5808,7 +5869,7 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
5808
5869
  const direction = input.direction ?? "tab";
5809
5870
  const title = titleFor(input.project, name);
5810
5871
  const pane = await deps.runtime.newPane({ workspaceId: captain.id, direction, title });
5811
- await deps.runtime.sendToPane(pane, cliCommand);
5872
+ await deps.runtime.sendToPane(pane, niceCrewCommand(cliCommand));
5812
5873
  if (interactive) {
5813
5874
  const preLaunchScreen = await deps.runtime.readPaneScreen(pane) ?? "";
5814
5875
  const genericResult = await deps.sendFirstTurn(pane, firstTurnTask, preLaunchScreen);
@@ -5846,8 +5907,7 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
5846
5907
  throw new Error(blockedByModalMessage());
5847
5908
  }
5848
5909
  if (!delivered) {
5849
- process.stderr.write(`\u26A0\uFE0F Message not delivered to crew '${name}' \u2014 use 'squadrant crew send ${project} ${name}' to re-send.
5850
- `);
5910
+ throw new Error(`Message not delivered to crew '${name}' \u2014 the paste/submit could not be confirmed. Re-send with 'squadrant crew send ${project} ${name}'.`);
5851
5911
  }
5852
5912
  }
5853
5913
  async function runCrewRead(project, name, runtime, workspaceId) {
@@ -6040,6 +6100,7 @@ __export(dist_exports2, {
6040
6100
  maskToken: () => maskToken,
6041
6101
  nameFromTitle: () => nameFromTitle,
6042
6102
  nextAutoName: () => nextAutoName,
6103
+ niceCrewCommand: () => niceCrewCommand,
6043
6104
  notifyToggle: () => notifyToggle,
6044
6105
  parseCommand: () => parseCommand,
6045
6106
  parseNotifyPref: () => parseNotifyPref,
@@ -6107,6 +6168,7 @@ __export(dist_exports2, {
6107
6168
  topicKey: () => topicKey,
6108
6169
  topicName: () => topicName,
6109
6170
  tryAcquireDaemonLock: () => tryAcquireDaemonLock,
6171
+ waitForCaptainDelivery: () => waitForCaptainDelivery,
6110
6172
  waitForWarmup: () => waitForWarmup,
6111
6173
  writeCursor: () => writeCursor,
6112
6174
  writeTelegramConfig: () => writeTelegramConfig
@@ -6745,7 +6807,7 @@ var init_notifiers = __esm({
6745
6807
  });
6746
6808
 
6747
6809
  // packages/workspaces/dist/workspaces/obsidian.js
6748
- import fs14 from "fs/promises";
6810
+ import fs15 from "fs/promises";
6749
6811
  import { existsSync as existsSync9 } from "fs";
6750
6812
  import path12 from "path";
6751
6813
  function resolveInRoot(root, relative) {
@@ -6770,16 +6832,16 @@ function createObsidianDriver(scope) {
6770
6832
  };
6771
6833
  },
6772
6834
  async read(rel) {
6773
- return fs14.readFile(resolveInRoot(root, rel), "utf-8");
6835
+ return fs15.readFile(resolveInRoot(root, rel), "utf-8");
6774
6836
  },
6775
6837
  async write(rel, content) {
6776
6838
  const abs = resolveInRoot(root, rel);
6777
- await fs14.mkdir(path12.dirname(abs), { recursive: true });
6778
- await fs14.writeFile(abs, content);
6839
+ await fs15.mkdir(path12.dirname(abs), { recursive: true });
6840
+ await fs15.writeFile(abs, content);
6779
6841
  },
6780
6842
  async exists(rel) {
6781
6843
  try {
6782
- await fs14.access(resolveInRoot(root, rel));
6844
+ await fs15.access(resolveInRoot(root, rel));
6783
6845
  return true;
6784
6846
  } catch {
6785
6847
  return false;
@@ -6787,13 +6849,13 @@ function createObsidianDriver(scope) {
6787
6849
  },
6788
6850
  async list(rel) {
6789
6851
  try {
6790
- return await fs14.readdir(resolveInRoot(root, rel));
6852
+ return await fs15.readdir(resolveInRoot(root, rel));
6791
6853
  } catch {
6792
6854
  return [];
6793
6855
  }
6794
6856
  },
6795
6857
  async mkdir(rel) {
6796
- await fs14.mkdir(resolveInRoot(root, rel), { recursive: true });
6858
+ await fs15.mkdir(resolveInRoot(root, rel), { recursive: true });
6797
6859
  }
6798
6860
  };
6799
6861
  }
@@ -8018,7 +8080,7 @@ var init_registry4 = __esm({
8018
8080
  });
8019
8081
 
8020
8082
  // packages/agents/dist/drivers/launch-cmd.js
8021
- import fs15 from "fs";
8083
+ import fs16 from "fs";
8022
8084
  import path13 from "path";
8023
8085
  function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model, templatesDir) {
8024
8086
  const driver = registry.getDriver(agentName);
@@ -8037,12 +8099,12 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
8037
8099
  if (templatesDir) {
8038
8100
  const roleFile2 = path13.join(templatesDir, `${role}.claude.md`);
8039
8101
  const legacyRoleFile = path13.join(templatesDir, `${role}.CLAUDE.md`);
8040
- const actualRoleFile = fs15.existsSync(roleFile2) ? roleFile2 : fs15.existsSync(legacyRoleFile) ? legacyRoleFile : null;
8102
+ const actualRoleFile = fs16.existsSync(roleFile2) ? roleFile2 : fs16.existsSync(legacyRoleFile) ? legacyRoleFile : null;
8041
8103
  if (actualRoleFile) {
8042
8104
  cmd += ` --append-system-prompt-file ${actualRoleFile}`;
8043
8105
  }
8044
8106
  const pluginDir = path13.join(templatesDir, "..", "plugin");
8045
- if (fs15.existsSync(pluginDir)) {
8107
+ if (fs16.existsSync(pluginDir)) {
8046
8108
  cmd += ` --plugin-dir ${pluginDir}`;
8047
8109
  }
8048
8110
  }
@@ -8055,7 +8117,7 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
8055
8117
  role,
8056
8118
  model,
8057
8119
  autoApprove: true,
8058
- promptFile: roleFile && fs15.existsSync(roleFile) ? roleFile : void 0
8120
+ promptFile: roleFile && fs16.existsSync(roleFile) ? roleFile : void 0
8059
8121
  });
8060
8122
  }
8061
8123
  var init_launch_cmd = __esm({
@@ -9819,7 +9881,7 @@ init_dist();
9819
9881
  init_dist3();
9820
9882
  import { Command } from "commander";
9821
9883
  import { execSync as execSync8 } from "child_process";
9822
- import fs16 from "fs";
9884
+ import fs17 from "fs";
9823
9885
  import { stat } from "fs/promises";
9824
9886
  import path18 from "path";
9825
9887
  import chalk3 from "chalk";
@@ -9924,7 +9986,7 @@ function settingsHaveAgentTeams() {
9924
9986
  try {
9925
9987
  const home = process.env.HOME || "";
9926
9988
  const settings = JSON.parse(
9927
- fs16.readFileSync(`${home}/.claude/settings.json`, "utf-8")
9989
+ fs17.readFileSync(`${home}/.claude/settings.json`, "utf-8")
9928
9990
  );
9929
9991
  return settings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1";
9930
9992
  } catch {
@@ -9935,7 +9997,7 @@ function pluginInstalled(pluginKey) {
9935
9997
  try {
9936
9998
  const home = process.env.HOME || "";
9937
9999
  const plugins = JSON.parse(
9938
- fs16.readFileSync(
10000
+ fs17.readFileSync(
9939
10001
  `${home}/.claude/plugins/installed_plugins.json`,
9940
10002
  "utf-8"
9941
10003
  )
@@ -9980,7 +10042,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
9980
10042
  ));
9981
10043
  results.push(check(
9982
10044
  "Obsidian installed",
9983
- commandExists("obsidian") || fs16.existsSync("/Applications/Obsidian.app"),
10045
+ commandExists("obsidian") || fs17.existsSync("/Applications/Obsidian.app"),
9984
10046
  "Install from: https://obsidian.md"
9985
10047
  ));
9986
10048
  results.push(check(
@@ -10087,7 +10149,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
10087
10149
  results.push(
10088
10150
  check(
10089
10151
  "Squadrant config exists",
10090
- fs16.existsSync(
10152
+ fs17.existsSync(
10091
10153
  process.env.SQUADRANT_CONFIG || `${process.env.HOME}/.config/squadrant/config.json`
10092
10154
  ),
10093
10155
  "Run: squadrant init"
@@ -10161,7 +10223,7 @@ init_dist();
10161
10223
  init_dist3();
10162
10224
  init_dist();
10163
10225
  import { Command as Command2 } from "commander";
10164
- import fs17 from "fs";
10226
+ import fs18 from "fs";
10165
10227
  import path19 from "path";
10166
10228
  import os10 from "os";
10167
10229
  import readline from "readline";
@@ -10318,20 +10380,20 @@ init_dist4();
10318
10380
  function findPackageRoot() {
10319
10381
  let dir = path19.dirname(new URL(import.meta.url).pathname);
10320
10382
  while (dir !== "/") {
10321
- if (fs17.existsSync(path19.join(dir, "package.json"))) return dir;
10383
+ if (fs18.existsSync(path19.join(dir, "package.json"))) return dir;
10322
10384
  dir = path19.dirname(dir);
10323
10385
  }
10324
10386
  return process.cwd();
10325
10387
  }
10326
10388
  function copyDirRecursive(src, dest) {
10327
- fs17.mkdirSync(dest, { recursive: true });
10328
- for (const entry of fs17.readdirSync(src, { withFileTypes: true })) {
10389
+ fs18.mkdirSync(dest, { recursive: true });
10390
+ for (const entry of fs18.readdirSync(src, { withFileTypes: true })) {
10329
10391
  const srcPath = path19.join(src, entry.name);
10330
10392
  const destPath = path19.join(dest, entry.name);
10331
10393
  if (entry.isDirectory()) {
10332
10394
  copyDirRecursive(srcPath, destPath);
10333
10395
  } else {
10334
- fs17.copyFileSync(srcPath, destPath);
10396
+ fs18.copyFileSync(srcPath, destPath);
10335
10397
  }
10336
10398
  }
10337
10399
  }
@@ -10377,8 +10439,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
10377
10439
  }
10378
10440
  const wsRegistry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
10379
10441
  try {
10380
- if (fs17.existsSync(DEFAULT_CONFIG_PATH)) {
10381
- const existing = JSON.parse(fs17.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
10442
+ if (fs18.existsSync(DEFAULT_CONFIG_PATH)) {
10443
+ const existing = JSON.parse(fs18.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
10382
10444
  wsRegistry.get(existing.workspace ?? "obsidian");
10383
10445
  }
10384
10446
  } catch (err) {
@@ -10386,7 +10448,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
10386
10448
  return;
10387
10449
  }
10388
10450
  stepHeader(1, 5, "Hub vault");
10389
- if (fs17.existsSync(DEFAULT_CONFIG_PATH)) {
10451
+ if (fs18.existsSync(DEFAULT_CONFIG_PATH)) {
10390
10452
  console.log(chalk4.yellow(" \u26A0 Config already exists, skipping creation"));
10391
10453
  } else {
10392
10454
  const config = getDefaultConfig();
@@ -10395,36 +10457,36 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
10395
10457
  console.log(chalk4.green(` \u2714 Config created at ${DEFAULT_CONFIG_PATH}`));
10396
10458
  }
10397
10459
  const hubTemplate = path19.join(pkgRoot, "obsidian", "hub");
10398
- if (fs17.existsSync(hubPath)) {
10460
+ if (fs18.existsSync(hubPath)) {
10399
10461
  console.log(chalk4.yellow(` \u26A0 Hub vault already exists at ${hubPath}`));
10400
- } else if (fs17.existsSync(hubTemplate)) {
10462
+ } else if (fs18.existsSync(hubTemplate)) {
10401
10463
  copyDirRecursive(hubTemplate, hubPath);
10402
10464
  console.log(chalk4.green(` \u2714 Hub vault scaffolded at ${hubPath}`));
10403
10465
  } else {
10404
- fs17.mkdirSync(hubPath, { recursive: true });
10466
+ fs18.mkdirSync(hubPath, { recursive: true });
10405
10467
  console.log(chalk4.yellow(` \u26A0 Hub template not found; created empty directory at ${hubPath}`));
10406
10468
  }
10407
10469
  const hubDashboardSrc = path19.join(pkgRoot, "obsidian", "hub", "dashboard.md");
10408
10470
  const hubDashboardDest = path19.join(hubPath, "dashboard.md");
10409
- if (fs17.existsSync(hubDashboardSrc)) {
10410
- fs17.copyFileSync(hubDashboardSrc, hubDashboardDest);
10471
+ if (fs18.existsSync(hubDashboardSrc)) {
10472
+ fs18.copyFileSync(hubDashboardSrc, hubDashboardDest);
10411
10473
  console.log(chalk4.green(` \u2714 Dashboard refreshed`));
10412
10474
  }
10413
- fs17.mkdirSync(path19.join(hubPath, "projects"), { recursive: true });
10475
+ fs18.mkdirSync(path19.join(hubPath, "projects"), { recursive: true });
10414
10476
  ensureRuntimeSynced({ sourceRoot: pkgRoot, runtimeRoot: configDir });
10415
10477
  console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
10416
10478
  stepHeader(2, 5, "Agent + projection setup");
10417
10479
  const settingsPath = path19.join(os10.homedir(), ".claude", "settings.json");
10418
10480
  try {
10419
10481
  let settings = {};
10420
- if (fs17.existsSync(settingsPath)) {
10421
- settings = JSON.parse(fs17.readFileSync(settingsPath, "utf-8"));
10482
+ if (fs18.existsSync(settingsPath)) {
10483
+ settings = JSON.parse(fs18.readFileSync(settingsPath, "utf-8"));
10422
10484
  }
10423
10485
  const env = settings.env || {};
10424
10486
  if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS !== "1") {
10425
10487
  settings.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
10426
- fs17.mkdirSync(path19.dirname(settingsPath), { recursive: true });
10427
- fs17.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
10488
+ fs18.mkdirSync(path19.dirname(settingsPath), { recursive: true });
10489
+ fs18.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
10428
10490
  console.log(chalk4.green(" \u2714 Agent Teams enabled in ~/.claude/settings.json"));
10429
10491
  } else {
10430
10492
  console.log(chalk4.green(" \u2714 Agent Teams already enabled"));
@@ -10504,7 +10566,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
10504
10566
  init_dist();
10505
10567
  init_dist2();
10506
10568
  import { Command as Command3 } from "commander";
10507
- import fs18 from "fs";
10569
+ import fs19 from "fs";
10508
10570
  import path20 from "path";
10509
10571
  import chalk5 from "chalk";
10510
10572
  function restartAfterProjectsAdd(opts) {
@@ -10519,20 +10581,20 @@ function restartAfterProjectsAdd(opts) {
10519
10581
  function findPackageRoot2() {
10520
10582
  let dir = path20.dirname(new URL(import.meta.url).pathname);
10521
10583
  while (dir !== "/") {
10522
- if (fs18.existsSync(path20.join(dir, "package.json"))) return dir;
10584
+ if (fs19.existsSync(path20.join(dir, "package.json"))) return dir;
10523
10585
  dir = path20.dirname(dir);
10524
10586
  }
10525
10587
  return process.cwd();
10526
10588
  }
10527
10589
  function copyDirRecursive2(src, dest) {
10528
- fs18.mkdirSync(dest, { recursive: true });
10529
- for (const entry of fs18.readdirSync(src, { withFileTypes: true })) {
10590
+ fs19.mkdirSync(dest, { recursive: true });
10591
+ for (const entry of fs19.readdirSync(src, { withFileTypes: true })) {
10530
10592
  const srcPath = path20.join(src, entry.name);
10531
10593
  const destPath = path20.join(dest, entry.name);
10532
10594
  if (entry.isDirectory()) {
10533
10595
  copyDirRecursive2(srcPath, destPath);
10534
10596
  } else {
10535
- fs18.copyFileSync(srcPath, destPath);
10597
+ fs19.copyFileSync(srcPath, destPath);
10536
10598
  }
10537
10599
  }
10538
10600
  }
@@ -10568,7 +10630,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
10568
10630
  process.exit(1);
10569
10631
  }
10570
10632
  const resolvedPath = resolveHome(projectPath);
10571
- if (!fs18.existsSync(path20.join(resolvedPath, ".git"))) {
10633
+ if (!fs19.existsSync(path20.join(resolvedPath, ".git"))) {
10572
10634
  console.log(chalk5.yellow(`
10573
10635
  \u26A0 No .git found at ${resolvedPath}. Make sure this is the project root, not a parent directory.
10574
10636
  `));
@@ -10637,19 +10699,19 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
10637
10699
  restartAfterProjectsAdd({ noRestart: opts.restart === false });
10638
10700
  const pkgRoot = findPackageRoot2();
10639
10701
  const spokeTemplate = path20.join(pkgRoot, "obsidian", "spoke");
10640
- if (fs18.existsSync(spokeVault)) {
10702
+ if (fs19.existsSync(spokeVault)) {
10641
10703
  console.log(chalk5.yellow(` \u26A0 Spoke vault already exists at ${spokeVault}, skipping scaffold`));
10642
- } else if (fs18.existsSync(spokeTemplate)) {
10704
+ } else if (fs19.existsSync(spokeTemplate)) {
10643
10705
  copyDirRecursive2(spokeTemplate, spokeVault);
10644
10706
  const statusPath = path20.join(spokeVault, "status.md");
10645
- if (fs18.existsSync(statusPath)) {
10646
- const content = fs18.readFileSync(statusPath, "utf-8");
10707
+ if (fs19.existsSync(statusPath)) {
10708
+ const content = fs19.readFileSync(statusPath, "utf-8");
10647
10709
  const updated = content.replace(/^project: unnamed/m, `project: ${name}`);
10648
- fs18.writeFileSync(statusPath, updated);
10710
+ fs19.writeFileSync(statusPath, updated);
10649
10711
  }
10650
10712
  console.log(chalk5.green(` \u2714 Spoke vault scaffolded at ${spokeVault}`));
10651
10713
  } else {
10652
- fs18.mkdirSync(spokeVault, { recursive: true });
10714
+ fs19.mkdirSync(spokeVault, { recursive: true });
10653
10715
  console.log(chalk5.yellow(` \u26A0 Spoke template not found; created empty dir at ${spokeVault}`));
10654
10716
  }
10655
10717
  console.log("");
@@ -11506,7 +11568,7 @@ init_dist3();
11506
11568
  init_dist();
11507
11569
  init_dist2();
11508
11570
  import { Command as Command10 } from "commander";
11509
- import fs19 from "fs";
11571
+ import fs20 from "fs";
11510
11572
  import path22 from "path";
11511
11573
  import os12 from "os";
11512
11574
  import chalk10 from "chalk";
@@ -11542,7 +11604,7 @@ async function runSideSpawn2(input) {
11542
11604
  prompt: input.topic,
11543
11605
  workdir: spawnCwd,
11544
11606
  role: "side",
11545
- promptFile: fs19.existsSync(promptFile) ? promptFile : void 0,
11607
+ promptFile: fs20.existsSync(promptFile) ? promptFile : void 0,
11546
11608
  interactive: true,
11547
11609
  permissionMode: config.defaults.permissions?.crew ?? "auto",
11548
11610
  ...sideModel ? { model: sideModel } : {}
@@ -11806,7 +11868,7 @@ function renderDashboard(rows, opts) {
11806
11868
 
11807
11869
  // packages/web/dist/sync-hub.js
11808
11870
  init_dist();
11809
- import fs20 from "fs";
11871
+ import fs21 from "fs";
11810
11872
  import path23 from "path";
11811
11873
  function buildMirrorMarkdown(s) {
11812
11874
  const fenced = "```";
@@ -11833,8 +11895,8 @@ function buildMirrorMarkdown(s) {
11833
11895
  function syncHub(deps) {
11834
11896
  if (!deps.config.hubVault)
11835
11897
  return [];
11836
- const writeFile5 = deps.writeFile ?? ((p, c) => fs20.writeFileSync(p, c));
11837
- const mkdir5 = deps.mkdir ?? ((p) => fs20.mkdirSync(p, { recursive: true }));
11898
+ const writeFile5 = deps.writeFile ?? ((p, c) => fs21.writeFileSync(p, c));
11899
+ const mkdir5 = deps.mkdir ?? ((p) => fs21.mkdirSync(p, { recursive: true }));
11838
11900
  const projectsDir = path23.join(resolveHome(deps.config.hubVault), "projects");
11839
11901
  mkdir5(projectsDir);
11840
11902
  const out = [];
@@ -12966,7 +13028,7 @@ init_dist3();
12966
13028
  init_dist2();
12967
13029
  import { Command as Command12 } from "commander";
12968
13030
  import { execSync as execSync11 } from "child_process";
12969
- import fs21 from "fs";
13031
+ import fs22 from "fs";
12970
13032
  import path24 from "path";
12971
13033
  import os13 from "os";
12972
13034
  import chalk13 from "chalk";
@@ -13119,12 +13181,12 @@ var launchCommand = new Command12("launch").description(
13119
13181
  }
13120
13182
  if (opts.all) {
13121
13183
  const hubPath = resolveHome(config.hubVault);
13122
- fs21.mkdirSync(hubPath, { recursive: true });
13184
+ fs22.mkdirSync(hubPath, { recursive: true });
13123
13185
  console.log(chalk13.bold("\nLaunching all captain workspaces\n"));
13124
13186
  for (const [name, proj] of Object.entries(config.projects)) {
13125
13187
  const projPath = resolveHome(proj.path);
13126
13188
  const spokePath = resolveHome(proj.spokeVault);
13127
- if (!fs21.existsSync(spokePath)) {
13189
+ if (!fs22.existsSync(spokePath)) {
13128
13190
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
13129
13191
  await ensureSpokeLayout(spokeDriver);
13130
13192
  console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
@@ -13162,7 +13224,7 @@ Launching ${selected.length} captain workspace(s) in parallel
13162
13224
  const proj = config.projects[name];
13163
13225
  const projPath = resolveHome(proj.path);
13164
13226
  const spokePath = resolveHome(proj.spokeVault);
13165
- if (!fs21.existsSync(spokePath)) {
13227
+ if (!fs22.existsSync(spokePath)) {
13166
13228
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
13167
13229
  await ensureSpokeLayout(spokeDriver);
13168
13230
  console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
@@ -13186,7 +13248,7 @@ Launching ${selected.length} captain workspace(s) in parallel
13186
13248
  const proj = config.projects[project];
13187
13249
  const projPath = resolveHome(proj.path);
13188
13250
  const spokePath = resolveHome(proj.spokeVault);
13189
- if (!fs21.existsSync(spokePath)) {
13251
+ if (!fs22.existsSync(spokePath)) {
13190
13252
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
13191
13253
  await ensureSpokeLayout(spokeDriver);
13192
13254
  console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
@@ -13322,7 +13384,7 @@ Shutting down captain workspace for '${project}'...
13322
13384
  // packages/cli/src/commands/feedback.ts
13323
13385
  init_dist();
13324
13386
  import { Command as Command14 } from "commander";
13325
- import fs22 from "fs";
13387
+ import fs23 from "fs";
13326
13388
  import os14 from "os";
13327
13389
  import path25 from "path";
13328
13390
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -13332,14 +13394,14 @@ var REPO_URL = "https://github.com/tu11aa/squadrant";
13332
13394
  function readPkgVersion() {
13333
13395
  try {
13334
13396
  const pkgPath = path25.join(path25.dirname(fileURLToPath3(import.meta.url)), "..", "package.json");
13335
- return JSON.parse(fs22.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
13397
+ return JSON.parse(fs23.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
13336
13398
  } catch {
13337
13399
  return "unknown";
13338
13400
  }
13339
13401
  }
13340
13402
  function readMetrics(metricsPath) {
13341
13403
  try {
13342
- return JSON.parse(fs22.readFileSync(metricsPath, "utf-8"));
13404
+ return JSON.parse(fs23.readFileSync(metricsPath, "utf-8"));
13343
13405
  } catch {
13344
13406
  return {};
13345
13407
  }
@@ -13399,7 +13461,7 @@ init_dist();
13399
13461
  init_dist();
13400
13462
  init_dist3();
13401
13463
  import { Command as Command15 } from "commander";
13402
- import fs23 from "fs";
13464
+ import fs24 from "fs";
13403
13465
  import path26 from "path";
13404
13466
  import chalk16 from "chalk";
13405
13467
  import matter3 from "gray-matter";
@@ -13411,9 +13473,9 @@ async function getProjectStandup(name, project, dateStr, registry, config) {
13411
13473
  const spokeVault = resolveHome(project.spokeVault);
13412
13474
  const statusFile = path26.join(spokeVault, "status.md");
13413
13475
  let status = {};
13414
- if (fs23.existsSync(statusFile)) {
13476
+ if (fs24.existsSync(statusFile)) {
13415
13477
  try {
13416
- status = matter3(fs23.readFileSync(statusFile, "utf-8")).data;
13478
+ status = matter3(fs24.readFileSync(statusFile, "utf-8")).data;
13417
13479
  } catch {
13418
13480
  }
13419
13481
  }
@@ -13531,15 +13593,15 @@ init_dist();
13531
13593
  init_dist();
13532
13594
  init_dist3();
13533
13595
  import { Command as Command16 } from "commander";
13534
- import fs24 from "fs";
13596
+ import fs25 from "fs";
13535
13597
  import path27 from "path";
13536
13598
  import chalk17 from "chalk";
13537
13599
  import matter4 from "gray-matter";
13538
13600
  function readStatus(spokeVault) {
13539
13601
  const statusFile = path27.join(spokeVault, "status.md");
13540
- if (!fs24.existsSync(statusFile)) return {};
13602
+ if (!fs25.existsSync(statusFile)) return {};
13541
13603
  try {
13542
- return matter4(fs24.readFileSync(statusFile, "utf-8")).data;
13604
+ return matter4(fs25.readFileSync(statusFile, "utf-8")).data;
13543
13605
  } catch {
13544
13606
  return {};
13545
13607
  }
@@ -13730,7 +13792,9 @@ runtimeCommand.command("status").description("Print 'running' or 'stopped' for a
13730
13792
  process.exit(2);
13731
13793
  }
13732
13794
  });
13733
- async function runRuntimeSend(arg1, arg2, opts) {
13795
+ var SEND_CONFIRM_TIMEOUT_MS = 15e3;
13796
+ var SEND_CONFIRM_POLL_MS = 500;
13797
+ async function runRuntimeSend(arg1, arg2, opts, confirmOpts) {
13734
13798
  const config = loadConfig();
13735
13799
  const registry = buildRegistry();
13736
13800
  if (opts.command && arg2 !== void 0) {
@@ -13740,7 +13804,7 @@ async function runRuntimeSend(arg1, arg2, opts) {
13740
13804
  const message = opts.command ? arg1 : arg2;
13741
13805
  if (!message) throw new Error("Message is required");
13742
13806
  const { requireDaemon: requireDaemon2 } = await Promise.resolve().then(() => (init_require_daemon(), require_daemon_exports));
13743
- const { appendCaptainMessage: appendCaptainMessage2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
13807
+ const { appendCaptainMessage: appendCaptainMessage2, waitForCaptainDelivery: waitForCaptainDelivery2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
13744
13808
  await requireDaemon2();
13745
13809
  const resolved = resolveTarget(registry, config, target, !!opts.command);
13746
13810
  await needRef(resolved);
@@ -13748,16 +13812,30 @@ async function runRuntimeSend(arg1, arg2, opts) {
13748
13812
  const { join: join30, dirname: dirname10 } = await import("path");
13749
13813
  const { DEFAULT_CONFIG_PATH: DEFAULT_CONFIG_PATH2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
13750
13814
  const stateRoot = join30(dirname10(DEFAULT_CONFIG_PATH2), "state");
13751
- await appendCaptainMessage2({
13815
+ const seq = await appendCaptainMessage2({
13752
13816
  stateRoot,
13753
13817
  project: finalProject,
13754
13818
  text: message,
13755
13819
  source: "cli"
13756
13820
  });
13821
+ const timeoutMs = confirmOpts?.timeoutMs ?? SEND_CONFIRM_TIMEOUT_MS;
13822
+ const delivered = await waitForCaptainDelivery2({
13823
+ stateRoot,
13824
+ project: finalProject,
13825
+ seq,
13826
+ timeoutMs,
13827
+ pollMs: confirmOpts?.pollMs ?? SEND_CONFIRM_POLL_MS
13828
+ });
13829
+ if (!delivered) {
13830
+ throw new Error(
13831
+ `Message queued for '${finalProject}' (seq=${seq}) but delivery was not confirmed within ${Math.round(timeoutMs / 1e3)}s. It may still be pending \u2014 check with 'squadrant runtime read-screen ${finalProject}${opts.command ? " --command" : ""}'.`
13832
+ );
13833
+ }
13757
13834
  }
13758
13835
  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) => {
13759
13836
  try {
13760
13837
  await runRuntimeSend(arg1, arg2, opts);
13838
+ console.log(chalk18.green("\u2714 Delivered (confirmed)"));
13761
13839
  } catch (err) {
13762
13840
  console.error(chalk18.red(err.message));
13763
13841
  process.exit(1);
@@ -13956,7 +14034,7 @@ init_dist3();
13956
14034
  init_dist();
13957
14035
  import { Command as Command20 } from "commander";
13958
14036
  import chalk21 from "chalk";
13959
- import fs25 from "fs";
14037
+ import fs26 from "fs";
13960
14038
  import path28 from "path";
13961
14039
  import { fileURLToPath as fileURLToPath4 } from "url";
13962
14040
  function parseScope(v) {
@@ -13968,7 +14046,7 @@ function parseScope(v) {
13968
14046
  function findPackageRoot3() {
13969
14047
  let dir = path28.dirname(fileURLToPath4(import.meta.url));
13970
14048
  while (dir !== "/" && dir !== "") {
13971
- if (fs25.existsSync(path28.join(dir, "package.json"))) return dir;
14049
+ if (fs26.existsSync(path28.join(dir, "package.json"))) return dir;
13972
14050
  dir = path28.dirname(dir);
13973
14051
  }
13974
14052
  return process.cwd();
@@ -14164,12 +14242,12 @@ init_dist();
14164
14242
  init_dist();
14165
14243
  init_dist2();
14166
14244
  import { Command as Command22 } from "commander";
14167
- import fs26 from "fs";
14245
+ import fs27 from "fs";
14168
14246
  import { fileURLToPath as fileURLToPath5 } from "url";
14169
14247
  import { dirname as dirname6, join as join25 } from "path";
14170
14248
  import chalk22 from "chalk";
14171
14249
  function runConfigCheck(opts) {
14172
- const raw = JSON.parse(fs26.readFileSync(opts.configPath, "utf-8"));
14250
+ const raw = JSON.parse(fs27.readFileSync(opts.configPath, "utf-8"));
14173
14251
  const def = getDefaultConfig();
14174
14252
  const items = detectDrift(raw, def);
14175
14253
  let working = raw;
@@ -14186,7 +14264,7 @@ function runConfigCheck(opts) {
14186
14264
  stamped = true;
14187
14265
  }
14188
14266
  if (opts.fix || opts.accept || stamped) {
14189
- fs26.writeFileSync(opts.configPath, JSON.stringify(working, null, 2) + "\n");
14267
+ fs27.writeFileSync(opts.configPath, JSON.stringify(working, null, 2) + "\n");
14190
14268
  }
14191
14269
  return { items, applied, remaining, stamped };
14192
14270
  }
@@ -14257,7 +14335,7 @@ function printItems(items) {
14257
14335
  var configCommand = new Command22("config").description("Inspect and reconcile squadrant config");
14258
14336
  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) => {
14259
14337
  const pkgVersion = readPkgVersion2();
14260
- if (!fs26.existsSync(DEFAULT_CONFIG_PATH)) {
14338
+ if (!fs27.existsSync(DEFAULT_CONFIG_PATH)) {
14261
14339
  console.log(chalk22.yellow("No config found \u2014 run `squadrant init` first."));
14262
14340
  return;
14263
14341
  }
@@ -14303,7 +14381,7 @@ configCommand.command("set").description("Write a config value by dotted key (e.
14303
14381
  });
14304
14382
  function readPkgVersion2() {
14305
14383
  const pkgPath = join25(dirname6(fileURLToPath5(import.meta.url)), "..", "package.json");
14306
- return JSON.parse(fs26.readFileSync(pkgPath, "utf-8")).version;
14384
+ return JSON.parse(fs27.readFileSync(pkgPath, "utf-8")).version;
14307
14385
  }
14308
14386
 
14309
14387
  // packages/cli/src/commands/heal.ts
@@ -14543,7 +14621,7 @@ var cmuxCommand = new Command27("cmux").description("cmux integration helpers").
14543
14621
  // packages/cli/src/commands/effort.ts
14544
14622
  init_dist();
14545
14623
  init_dist2();
14546
- import fs27 from "fs";
14624
+ import fs28 from "fs";
14547
14625
  import path29 from "path";
14548
14626
  import { Command as Command28 } from "commander";
14549
14627
  import chalk28 from "chalk";
@@ -14571,7 +14649,7 @@ function runEffortSet(value, configPath = DEFAULT_CONFIG_PATH) {
14571
14649
  }
14572
14650
  function canonical(p) {
14573
14651
  try {
14574
- return fs27.realpathSync(p);
14652
+ return fs28.realpathSync(p);
14575
14653
  } catch {
14576
14654
  return path29.resolve(p);
14577
14655
  }