squadrant 0.18.0 → 0.18.1

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
@@ -9,15 +9,87 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
- // packages/shared/dist/config.js
12
+ // packages/shared/dist/lib/config-io.js
13
13
  import fs from "fs";
14
14
  import path from "path";
15
+ function tightenModeSync(p, expectedMode) {
16
+ try {
17
+ if (!fs.existsSync(p))
18
+ return;
19
+ const stat2 = fs.statSync(p);
20
+ const currentMode = stat2.mode & 4095;
21
+ if ((currentMode & ~expectedMode) !== 0) {
22
+ fs.chmodSync(p, expectedMode);
23
+ if (!didLogMigration) {
24
+ console.warn(`\x1B[33m\u26A0\x1B[0m squadrant: tightened config file permissions (security fix #668)`);
25
+ didLogMigration = true;
26
+ }
27
+ }
28
+ } catch {
29
+ }
30
+ }
31
+ function writeConfigFileSync(filePath, content) {
32
+ ensureDirSync(path.dirname(filePath));
33
+ fs.writeFileSync(filePath, content, { mode: 384 });
34
+ tightenModeSync(filePath, 384);
35
+ }
36
+ function ensureDirSync(dirPath) {
37
+ if (dirPath !== CONFIG_DIR && !dirPath.startsWith(CONFIG_DIR + path.sep)) {
38
+ fs.mkdirSync(dirPath, { recursive: true });
39
+ return;
40
+ }
41
+ fs.mkdirSync(dirPath, { recursive: true, mode: 448 });
42
+ let current = dirPath;
43
+ while (current === CONFIG_DIR || current.startsWith(CONFIG_DIR + path.sep)) {
44
+ tightenModeSync(current, 448);
45
+ if (current === CONFIG_DIR)
46
+ break;
47
+ const parent = path.dirname(current);
48
+ if (parent === current)
49
+ break;
50
+ current = parent;
51
+ }
52
+ }
53
+ function readConfigFileSync(filePath) {
54
+ return fs.readFileSync(filePath, "utf-8");
55
+ }
56
+ function migrateConfigPermsSync() {
57
+ if (migrationRun)
58
+ return;
59
+ migrationRun = true;
60
+ tightenModeSync(CONFIG_DIR, 448);
61
+ tightenModeSync(path.join(CONFIG_DIR, "config.json"), 384);
62
+ const projectsDir = path.join(CONFIG_DIR, "projects");
63
+ tightenModeSync(projectsDir, 448);
64
+ try {
65
+ if (fs.existsSync(projectsDir)) {
66
+ const files = fs.readdirSync(projectsDir);
67
+ for (const file of files) {
68
+ if (file.endsWith(".json")) {
69
+ tightenModeSync(path.join(projectsDir, file), 384);
70
+ }
71
+ }
72
+ }
73
+ } catch {
74
+ }
75
+ }
76
+ var didLogMigration, migrationRun;
77
+ var init_config_io = __esm({
78
+ "packages/shared/dist/lib/config-io.js"() {
79
+ init_config();
80
+ didLogMigration = false;
81
+ migrationRun = false;
82
+ }
83
+ });
84
+
85
+ // packages/shared/dist/config.js
86
+ import path2 from "path";
15
87
  import os from "os";
16
88
  import chalk from "chalk";
17
89
  function getDefaultConfig() {
18
90
  return {
19
91
  commandName: "\u{1F3DB}\uFE0F command",
20
- hubVault: path.join(os.homedir(), "squadrant-hub"),
92
+ hubVault: path2.join(os.homedir(), "squadrant-hub"),
21
93
  projects: {},
22
94
  agents: {
23
95
  claude: { cli: "claude", driver: "claude" }
@@ -61,13 +133,14 @@ function getDefaultConfig() {
61
133
  },
62
134
  metrics: {
63
135
  enabled: true,
64
- path: path.join(CONFIG_DIR, "metrics.json")
136
+ path: path2.join(CONFIG_DIR, "metrics.json")
65
137
  }
66
138
  };
67
139
  }
68
140
  function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
141
+ migrateConfigPermsSync();
69
142
  try {
70
- const raw = fs.readFileSync(configPath, "utf-8");
143
+ const raw = readConfigFileSync(configPath);
71
144
  const config = JSON.parse(raw);
72
145
  if (config.defaults.models && !config.defaults.roles) {
73
146
  const m = config.defaults.models;
@@ -92,34 +165,32 @@ function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
92
165
  }
93
166
  }
94
167
  function saveConfig(config, configPath = DEFAULT_CONFIG_PATH) {
95
- const dir = path.dirname(configPath);
96
- fs.mkdirSync(dir, { recursive: true });
97
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
168
+ writeConfigFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
98
169
  }
99
170
  function resolveHome(p) {
100
171
  return p.startsWith("~") ? p.replace("~", os.homedir()) : p;
101
172
  }
102
- var CONFIG_DIR, DEFAULT_CONFIG_PATH;
173
+ var DEFAULT_CONFIG_PATH, CONFIG_DIR;
103
174
  var init_config = __esm({
104
175
  "packages/shared/dist/config.js"() {
105
- CONFIG_DIR = path.join(os.homedir(), ".config", "squadrant");
106
- DEFAULT_CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
176
+ init_config_io();
177
+ DEFAULT_CONFIG_PATH = process.env.SQUADRANT_CONFIG || path2.join(os.homedir(), ".config", "squadrant", "config.json");
178
+ CONFIG_DIR = path2.dirname(DEFAULT_CONFIG_PATH);
107
179
  }
108
180
  });
109
181
 
110
182
  // packages/shared/dist/project-config.js
111
- import fs2 from "fs";
112
183
  import os2 from "os";
113
- import path2 from "path";
184
+ import path3 from "path";
114
185
  function defaultRoot() {
115
- return path2.join(os2.homedir(), ".config", "squadrant");
186
+ return path3.join(os2.homedir(), ".config", "squadrant");
116
187
  }
117
188
  function projectConfigPath(name, root = defaultRoot()) {
118
- return path2.join(root, "projects", `${name}.json`);
189
+ return path3.join(root, "projects", `${name}.json`);
119
190
  }
120
191
  function loadProjectOverride(name, root = defaultRoot()) {
121
192
  try {
122
- return JSON.parse(fs2.readFileSync(projectConfigPath(name, root), "utf-8"));
193
+ return JSON.parse(readConfigFileSync(projectConfigPath(name, root)));
123
194
  } catch {
124
195
  return {};
125
196
  }
@@ -136,8 +207,7 @@ function deepMerge(base, patch) {
136
207
  function saveProjectOverride(name, patch, root = defaultRoot()) {
137
208
  const merged = deepMerge(loadProjectOverride(name, root), patch);
138
209
  const file = projectConfigPath(name, root);
139
- fs2.mkdirSync(path2.dirname(file), { recursive: true });
140
- fs2.writeFileSync(file, JSON.stringify(merged, null, 2) + "\n");
210
+ writeConfigFileSync(file, JSON.stringify(merged, null, 2) + "\n");
141
211
  }
142
212
  function crewRank(tier) {
143
213
  return CREW_RANK[tier];
@@ -162,6 +232,7 @@ function resolveNotify(globalNotify, override) {
162
232
  var DEFAULT_NOTIFY, CREW_RANK;
163
233
  var init_project_config = __esm({
164
234
  "packages/shared/dist/project-config.js"() {
235
+ init_config_io();
165
236
  DEFAULT_NOTIFY = { active: false, cap: true, crew: "alert_only" };
166
237
  CREW_RANK = { none: 0, done_only: 1, alert_only: 2, all: 3 };
167
238
  }
@@ -234,22 +305,22 @@ function defaultCmuxConfigPath() {
234
305
  return join(homedir(), ".config", "cmux", "cmux.json");
235
306
  }
236
307
  function ensureSocketAutomation(opts = {}) {
237
- const path34 = opts.path ?? defaultCmuxConfigPath();
238
- if (!existsSync(path34)) {
239
- mkdirSync(dirname(path34), { recursive: true });
240
- writeFileSync(path34, MINIMAL_TEMPLATE);
241
- return { path: path34, changed: true, alreadySet: false };
308
+ const path35 = opts.path ?? defaultCmuxConfigPath();
309
+ if (!existsSync(path35)) {
310
+ mkdirSync(dirname(path35), { recursive: true });
311
+ writeFileSync(path35, MINIMAL_TEMPLATE);
312
+ return { path: path35, changed: true, alreadySet: false };
242
313
  }
243
- const text = readFileSync(path34, "utf-8");
314
+ const text = readFileSync(path35, "utf-8");
244
315
  const current = parse(text)?.automation?.socketControlMode;
245
316
  if (current === AUTOMATION_MODE) {
246
- return { path: path34, changed: false, alreadySet: true };
317
+ return { path: path35, changed: false, alreadySet: true };
247
318
  }
248
319
  const edits = modify(text, [...SOCKET_CONTROL_MODE_PATH], AUTOMATION_MODE, {
249
320
  formattingOptions: { insertSpaces: true, tabSize: 2 }
250
321
  });
251
- writeFileSync(path34, applyEdits(text, edits));
252
- return { path: path34, changed: true, alreadySet: false };
322
+ writeFileSync(path35, applyEdits(text, edits));
323
+ return { path: path35, changed: true, alreadySet: false };
253
324
  }
254
325
  var SOCKET_CONTROL_MODE_PATH, AUTOMATION_MODE, MINIMAL_TEMPLATE;
255
326
  var init_cmux_config = __esm({
@@ -396,15 +467,15 @@ writeFileSync(resultFile, JSON.stringify(result));
396
467
  });
397
468
 
398
469
  // packages/shared/dist/lib/cmux-autoconfig.js
399
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, rmSync as rmSync2 } from "fs";
470
+ import { existsSync as existsSync4, rmSync as rmSync2 } from "fs";
400
471
  import { homedir as homedir3 } from "os";
401
- import { dirname as dirname2, join as join4 } from "path";
472
+ import { join as join4 } from "path";
402
473
  function defaultStatePath() {
403
474
  return join4(homedir3(), ".config", "squadrant", "state", "cmux-autoconfig.json");
404
475
  }
405
- function readState(path34) {
476
+ function readState(path35) {
406
477
  try {
407
- return JSON.parse(readFileSync4(path34, "utf-8"));
478
+ return JSON.parse(readConfigFileSync(path35));
408
479
  } catch {
409
480
  return {};
410
481
  }
@@ -420,8 +491,7 @@ async function ensureCmuxAutoConfig(opts = {}) {
420
491
  if (needsRestart) {
421
492
  const already = readState(statePath2).promptedRestart === true;
422
493
  if (!already) {
423
- mkdirSync2(dirname2(statePath2), { recursive: true });
424
- writeFileSync3(statePath2, JSON.stringify({ promptedRestart: true }));
494
+ writeConfigFileSync(statePath2, JSON.stringify({ promptedRestart: true }));
425
495
  promptedThisRun = true;
426
496
  }
427
497
  } else if (verdict === "reachable") {
@@ -441,6 +511,7 @@ var init_cmux_autoconfig = __esm({
441
511
  "packages/shared/dist/lib/cmux-autoconfig.js"() {
442
512
  init_cmux_config();
443
513
  init_cmux_probe();
514
+ init_config_io();
444
515
  }
445
516
  });
446
517
 
@@ -624,8 +695,8 @@ var init_config_version = __esm({
624
695
  });
625
696
 
626
697
  // packages/shared/dist/lib/update-check.js
627
- import fs3 from "fs";
628
- import path3 from "path";
698
+ import fs2 from "fs";
699
+ import path4 from "path";
629
700
  import os3 from "os";
630
701
  import https from "https";
631
702
  function isUpdateCheckDisabled(config, env = process.env) {
@@ -648,8 +719,25 @@ function isNewerVersion(latest, current) {
648
719
  return lb > cb;
649
720
  return lc > cc;
650
721
  }
651
- function formatUpdateNotice(latest, current) {
652
- return `\u2B06 squadrant ${latest} available (you have ${current}) \u2014 npm i -g squadrant@latest`;
722
+ function detectInstallManager(modulePath = import.meta.url) {
723
+ if (modulePath.includes("Library/pnpm/") || modulePath.includes("/pnpm/"))
724
+ return "pnpm";
725
+ if (modulePath.includes("/yarn/global/node_modules/"))
726
+ return "yarn";
727
+ return "npm";
728
+ }
729
+ function upgradeCommandFor(manager) {
730
+ switch (manager) {
731
+ case "pnpm":
732
+ return "pnpm add -g squadrant@latest";
733
+ case "yarn":
734
+ return "yarn global add squadrant@latest";
735
+ case "npm":
736
+ return "npm i -g squadrant@latest";
737
+ }
738
+ }
739
+ function formatUpdateNotice(latest, current, modulePath = import.meta.url) {
740
+ return `\u2B06 squadrant ${latest} available (you have ${current}) \u2014 ${upgradeCommandFor(detectInstallManager(modulePath))}`;
653
741
  }
654
742
  async function fetchLatestVersion(requestFn = requestJson, timeoutMs = FETCH_TIMEOUT_MS) {
655
743
  const timeout = new Promise((resolve4) => {
@@ -681,15 +769,15 @@ async function checkForUpdate(opts) {
681
769
  }
682
770
  function readUpdateCheckState(statePath2 = UPDATE_CHECK_STATE_PATH) {
683
771
  try {
684
- return JSON.parse(fs3.readFileSync(statePath2, "utf-8"));
772
+ return JSON.parse(fs2.readFileSync(statePath2, "utf-8"));
685
773
  } catch {
686
774
  return void 0;
687
775
  }
688
776
  }
689
777
  function writeUpdateCheckState(state, statePath2 = UPDATE_CHECK_STATE_PATH) {
690
778
  try {
691
- fs3.mkdirSync(path3.dirname(statePath2), { recursive: true });
692
- fs3.writeFileSync(statePath2, JSON.stringify(state, null, 2) + "\n");
779
+ fs2.mkdirSync(path4.dirname(statePath2), { recursive: true });
780
+ fs2.writeFileSync(statePath2, JSON.stringify(state, null, 2) + "\n");
693
781
  } catch {
694
782
  }
695
783
  }
@@ -720,7 +808,7 @@ ${line}
720
808
  var UPDATE_CHECK_STATE_PATH, REGISTRY_URL, CHECK_INTERVAL_MS, FAILURE_RETRY_MS, FETCH_TIMEOUT_MS, requestJson;
721
809
  var init_update_check = __esm({
722
810
  "packages/shared/dist/lib/update-check.js"() {
723
- UPDATE_CHECK_STATE_PATH = path3.join(os3.homedir(), ".config", "squadrant", "update-check.json");
811
+ UPDATE_CHECK_STATE_PATH = path4.join(os3.homedir(), ".config", "squadrant", "update-check.json");
724
812
  REGISTRY_URL = "https://registry.npmjs.org/squadrant/latest";
725
813
  CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
726
814
  FAILURE_RETRY_MS = 60 * 60 * 1e3;
@@ -752,10 +840,10 @@ var init_update_check = __esm({
752
840
 
753
841
  // packages/shared/dist/lib/git-worktree.js
754
842
  import { execFileSync as execFileSync2 } from "child_process";
755
- import fs4 from "fs";
756
- import path4 from "path";
843
+ import fs3 from "fs";
844
+ import path5 from "path";
757
845
  function worktreePath(repoRoot, worktreeDir, project, name) {
758
- return path4.resolve(repoRoot, worktreeDir, `${project}-${name}`);
846
+ return path5.resolve(repoRoot, worktreeDir, `${project}-${name}`);
759
847
  }
760
848
  function crewBranch(name) {
761
849
  return `crew/${name}`;
@@ -764,11 +852,11 @@ function ensureSpotlightExcluded(repoRoot, worktreeDir) {
764
852
  if (process.platform !== "darwin")
765
853
  return;
766
854
  try {
767
- const dir = path4.resolve(repoRoot, worktreeDir);
768
- fs4.mkdirSync(dir, { recursive: true });
769
- const marker = path4.join(dir, ".metadata_never_index");
770
- if (!fs4.existsSync(marker))
771
- fs4.writeFileSync(marker, "");
855
+ const dir = path5.resolve(repoRoot, worktreeDir);
856
+ fs3.mkdirSync(dir, { recursive: true });
857
+ const marker = path5.join(dir, ".metadata_never_index");
858
+ if (!fs3.existsSync(marker))
859
+ fs3.writeFileSync(marker, "");
772
860
  } catch {
773
861
  }
774
862
  }
@@ -830,15 +918,15 @@ function addWorktree(spec) {
830
918
  return wt;
831
919
  }
832
920
  function installWorktreeDependencies(wt) {
833
- if (!fs4.existsSync(path4.join(wt, "package.json")))
921
+ if (!fs3.existsSync(path5.join(wt, "package.json")))
834
922
  return;
835
- if (fs4.existsSync(path4.join(wt, "pnpm-lock.yaml"))) {
923
+ if (fs3.existsSync(path5.join(wt, "pnpm-lock.yaml"))) {
836
924
  execFileSync2("pnpm", ["-C", wt, "install", "--frozen-lockfile"], { stdio: "pipe" });
837
- } else if (fs4.existsSync(path4.join(wt, "yarn.lock"))) {
925
+ } else if (fs3.existsSync(path5.join(wt, "yarn.lock"))) {
838
926
  execFileSync2("yarn", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
839
- } else if (fs4.existsSync(path4.join(wt, "package-lock.json"))) {
927
+ } else if (fs3.existsSync(path5.join(wt, "package-lock.json"))) {
840
928
  execFileSync2("npm", ["ci"], { cwd: wt, stdio: "pipe" });
841
- } else if (fs4.existsSync(path4.join(wt, "bun.lockb"))) {
929
+ } else if (fs3.existsSync(path5.join(wt, "bun.lockb"))) {
842
930
  execFileSync2("bun", ["install", "--frozen-lockfile"], { cwd: wt, stdio: "pipe" });
843
931
  } else {
844
932
  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.
@@ -862,7 +950,7 @@ var init_git_worktree = __esm({
862
950
  });
863
951
 
864
952
  // packages/shared/dist/lib/resolve-text-input.js
865
- import fs5 from "fs";
953
+ import fs4 from "fs";
866
954
  async function readAllStdin() {
867
955
  const chunks = [];
868
956
  for await (const chunk of process.stdin) {
@@ -874,7 +962,7 @@ function flagName(label) {
874
962
  return label === "task" ? "--task-file" : "--message-file";
875
963
  }
876
964
  async function resolveTextInput(opts, deps) {
877
- const readFile6 = deps?.readFile ?? ((p) => fs5.readFileSync(p, "utf8"));
965
+ const readFile6 = deps?.readFile ?? ((p) => fs4.readFileSync(p, "utf8"));
878
966
  const readStdin3 = deps?.readStdin ?? readAllStdin;
879
967
  if (opts.filePath) {
880
968
  if (opts.filePath === "-") {
@@ -902,75 +990,75 @@ var init_resolve_text_input = __esm({
902
990
  });
903
991
 
904
992
  // packages/shared/dist/lib/runtime-sync.js
905
- import fs6 from "fs";
906
- import path5 from "path";
993
+ import fs5 from "fs";
994
+ import path6 from "path";
907
995
  function copyIfDifferent(src, dest) {
908
- if (fs6.existsSync(dest)) {
909
- if (fs6.readFileSync(src).equals(fs6.readFileSync(dest)))
996
+ if (fs5.existsSync(dest)) {
997
+ if (fs5.readFileSync(src).equals(fs5.readFileSync(dest)))
910
998
  return false;
911
999
  }
912
- fs6.copyFileSync(src, dest);
1000
+ fs5.copyFileSync(src, dest);
913
1001
  return true;
914
1002
  }
915
1003
  function mirrorDir(src, dest) {
916
- fs6.mkdirSync(dest, { recursive: true });
917
- const srcEntries = fs6.readdirSync(src, { withFileTypes: true });
1004
+ fs5.mkdirSync(dest, { recursive: true });
1005
+ const srcEntries = fs5.readdirSync(src, { withFileTypes: true });
918
1006
  const srcNames = new Set(srcEntries.map((e) => e.name));
919
1007
  for (const entry of srcEntries) {
920
- const srcPath = path5.join(src, entry.name);
921
- const destPath = path5.join(dest, entry.name);
1008
+ const srcPath = path6.join(src, entry.name);
1009
+ const destPath = path6.join(dest, entry.name);
922
1010
  if (entry.isDirectory()) {
923
1011
  mirrorDir(srcPath, destPath);
924
1012
  } else {
925
1013
  copyIfDifferent(srcPath, destPath);
926
1014
  }
927
1015
  }
928
- for (const entry of fs6.readdirSync(dest, { withFileTypes: true })) {
1016
+ for (const entry of fs5.readdirSync(dest, { withFileTypes: true })) {
929
1017
  if (!srcNames.has(entry.name)) {
930
- fs6.rmSync(path5.join(dest, entry.name), { recursive: true, force: true });
1018
+ fs5.rmSync(path6.join(dest, entry.name), { recursive: true, force: true });
931
1019
  }
932
1020
  }
933
1021
  }
934
1022
  function mirrorFlat(src, dest, match, chmod) {
935
- fs6.mkdirSync(dest, { recursive: true });
936
- const matched = fs6.readdirSync(src, { withFileTypes: true }).filter((e) => e.isFile() && match.test(e.name)).map((e) => e.name);
1023
+ fs5.mkdirSync(dest, { recursive: true });
1024
+ const matched = fs5.readdirSync(src, { withFileTypes: true }).filter((e) => e.isFile() && match.test(e.name)).map((e) => e.name);
937
1025
  const matchedSet = new Set(matched);
938
1026
  for (const name of matched) {
939
- const destPath = path5.join(dest, name);
940
- const copied = copyIfDifferent(path5.join(src, name), destPath);
1027
+ const destPath = path6.join(dest, name);
1028
+ const copied = copyIfDifferent(path6.join(src, name), destPath);
941
1029
  if (copied && chmod !== void 0)
942
- fs6.chmodSync(destPath, chmod);
1030
+ fs5.chmodSync(destPath, chmod);
943
1031
  }
944
- for (const entry of fs6.readdirSync(dest, { withFileTypes: true })) {
1032
+ for (const entry of fs5.readdirSync(dest, { withFileTypes: true })) {
945
1033
  if (!matchedSet.has(entry.name)) {
946
- fs6.rmSync(path5.join(dest, entry.name), { recursive: true, force: true });
1034
+ fs5.rmSync(path6.join(dest, entry.name), { recursive: true, force: true });
947
1035
  }
948
1036
  }
949
1037
  }
950
1038
  function mirrorPluginSubset(src, dest, skills) {
951
- fs6.mkdirSync(dest, { recursive: true });
952
- mirrorDir(path5.join(src, ".claude-plugin"), path5.join(dest, ".claude-plugin"));
953
- const skillsDest = path5.join(dest, "skills");
954
- fs6.mkdirSync(skillsDest, { recursive: true });
1039
+ fs5.mkdirSync(dest, { recursive: true });
1040
+ mirrorDir(path6.join(src, ".claude-plugin"), path6.join(dest, ".claude-plugin"));
1041
+ const skillsDest = path6.join(dest, "skills");
1042
+ fs5.mkdirSync(skillsDest, { recursive: true });
955
1043
  for (const name of skills) {
956
- const skillSrc = path5.join(src, "skills", name);
957
- if (fs6.existsSync(skillSrc))
958
- mirrorDir(skillSrc, path5.join(skillsDest, name));
1044
+ const skillSrc = path6.join(src, "skills", name);
1045
+ if (fs5.existsSync(skillSrc))
1046
+ mirrorDir(skillSrc, path6.join(skillsDest, name));
959
1047
  }
960
- for (const entry of fs6.readdirSync(skillsDest, { withFileTypes: true })) {
1048
+ for (const entry of fs5.readdirSync(skillsDest, { withFileTypes: true })) {
961
1049
  if (!skills.includes(entry.name)) {
962
- fs6.rmSync(path5.join(skillsDest, entry.name), { recursive: true, force: true });
1050
+ fs5.rmSync(path6.join(skillsDest, entry.name), { recursive: true, force: true });
963
1051
  }
964
1052
  }
965
1053
  }
966
1054
  function ensureRuntimeSynced(opts) {
967
1055
  const targets = opts.targets ?? MANAGED_TARGETS;
968
1056
  for (const t of targets) {
969
- const srcDir = path5.join(opts.sourceRoot, t.srcRel);
1057
+ const srcDir = path6.join(opts.sourceRoot, t.srcRel);
970
1058
  try {
971
- if (!fs6.existsSync(srcDir))
1059
+ if (!fs5.existsSync(srcDir))
972
1060
  continue;
973
- const destDir = path5.join(opts.runtimeRoot, t.name);
1061
+ const destDir = path6.join(opts.runtimeRoot, t.name);
974
1062
  if (t.mode === "tree") {
975
1063
  mirrorDir(srcDir, destDir);
976
1064
  } else if (t.mode === "flat") {
@@ -1038,8 +1126,8 @@ var init_tool_compat = __esm({
1038
1126
  });
1039
1127
 
1040
1128
  // packages/shared/dist/lib/canonical-source.js
1041
- import fs7 from "fs";
1042
- import path6 from "path";
1129
+ import fs6 from "fs";
1130
+ import path7 from "path";
1043
1131
  function parseSkill(raw) {
1044
1132
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
1045
1133
  if (!match)
@@ -1080,10 +1168,10 @@ async function readSkills(driver, skillsDir) {
1080
1168
  function readRoleTemplates(opts) {
1081
1169
  if (!opts.pkgRoot)
1082
1170
  return "";
1083
- const reader = opts.readFile ?? ((p) => fs7.readFileSync(p, "utf-8"));
1171
+ const reader = opts.readFile ?? ((p) => fs6.readFileSync(p, "utf-8"));
1084
1172
  const sections = [];
1085
1173
  for (const { file, heading } of ROLE_TEMPLATES) {
1086
- const full = path6.join(opts.pkgRoot, "templates", file);
1174
+ const full = path7.join(opts.pkgRoot, "templates", file);
1087
1175
  let body = "";
1088
1176
  try {
1089
1177
  body = reader(full);
@@ -1120,8 +1208,8 @@ var init_canonical_source = __esm({
1120
1208
 
1121
1209
  // packages/shared/dist/lib/daily-logs.js
1122
1210
  import { execSync } from "child_process";
1123
- import fs8 from "fs";
1124
- import path7 from "path";
1211
+ import fs7 from "fs";
1212
+ import path8 from "path";
1125
1213
  import matter from "gray-matter";
1126
1214
  function iso(d) {
1127
1215
  return d.toISOString().slice(0, 10);
@@ -1173,7 +1261,7 @@ function getGitCommits(projectPath, dateStr) {
1173
1261
  }
1174
1262
  function getGitCommitsInRange(projectPath, since, until) {
1175
1263
  const resolved = resolveHome(projectPath);
1176
- if (!fs8.existsSync(path7.join(resolved, ".git")))
1264
+ if (!fs7.existsSync(path8.join(resolved, ".git")))
1177
1265
  return [];
1178
1266
  const untilArg = until ? ` --until="${until}"` : "";
1179
1267
  try {
@@ -1187,7 +1275,7 @@ function getGitCommitsInRange(projectPath, since, until) {
1187
1275
  }
1188
1276
  function getMergedPRsInRange(projectPath, since, until) {
1189
1277
  const resolved = resolveHome(projectPath);
1190
- if (!fs8.existsSync(path7.join(resolved, ".git")))
1278
+ if (!fs7.existsSync(path8.join(resolved, ".git")))
1191
1279
  return [];
1192
1280
  const untilArg = until ? ` --until="${until}"` : "";
1193
1281
  try {
@@ -1247,6 +1335,7 @@ var init_daemon_keys = __esm({
1247
1335
  var dist_exports = {};
1248
1336
  __export(dist_exports, {
1249
1337
  AUTOMATION_MODE: () => AUTOMATION_MODE,
1338
+ CONFIG_DIR: () => CONFIG_DIR,
1250
1339
  CREW_SKILLS: () => CREW_SKILLS,
1251
1340
  DEFAULT_CONFIG_PATH: () => DEFAULT_CONFIG_PATH,
1252
1341
  DEFAULT_NOTIFY: () => DEFAULT_NOTIFY,
@@ -1270,7 +1359,9 @@ __export(dist_exports, {
1270
1359
  defaultCmuxConfigPath: () => defaultCmuxConfigPath,
1271
1360
  defaultStatePath: () => defaultStatePath,
1272
1361
  detectDrift: () => detectDrift,
1362
+ detectInstallManager: () => detectInstallManager,
1273
1363
  ensureCmuxAutoConfig: () => ensureCmuxAutoConfig,
1364
+ ensureDirSync: () => ensureDirSync,
1274
1365
  ensureRuntimeSynced: () => ensureRuntimeSynced,
1275
1366
  ensureSocketAutomation: () => ensureSocketAutomation,
1276
1367
  ensureSpokeLayout: () => ensureSpokeLayout,
@@ -1289,6 +1380,7 @@ __export(dist_exports, {
1289
1380
  iso: () => iso,
1290
1381
  loadConfig: () => loadConfig,
1291
1382
  loadProjectOverride: () => loadProjectOverride,
1383
+ migrateConfigPermsSync: () => migrateConfigPermsSync,
1292
1384
  mirrorDir: () => mirrorDir,
1293
1385
  mirrorFlat: () => mirrorFlat,
1294
1386
  needsCheck: () => needsCheck,
@@ -1296,6 +1388,7 @@ __export(dist_exports, {
1296
1388
  parseSection: () => parseSection,
1297
1389
  probeCmuxDaemonDirect: () => probeCmuxDaemonDirect,
1298
1390
  projectConfigPath: () => projectConfigPath,
1391
+ readConfigFileSync: () => readConfigFileSync,
1299
1392
  readDailyLog: () => readDailyLog,
1300
1393
  readProjectLevelSource: () => readProjectLevelSource,
1301
1394
  readStamp: () => readStamp,
@@ -1314,6 +1407,7 @@ __export(dist_exports, {
1314
1407
  withStamp: () => withStamp,
1315
1408
  worktreeDirtyFiles: () => worktreeDirtyFiles,
1316
1409
  worktreePath: () => worktreePath,
1410
+ writeConfigFileSync: () => writeConfigFileSync,
1317
1411
  writeUpdateCheckState: () => writeUpdateCheckState
1318
1412
  });
1319
1413
  var init_dist = __esm({
@@ -1335,6 +1429,7 @@ var init_dist = __esm({
1335
1429
  init_config_drift();
1336
1430
  init_config_version();
1337
1431
  init_update_check();
1432
+ init_config_io();
1338
1433
  init_git_worktree();
1339
1434
  init_resolve_text_input();
1340
1435
  init_runtime_sync();
@@ -1974,7 +2069,7 @@ var init_reduce = __esm({
1974
2069
  });
1975
2070
 
1976
2071
  // packages/core/dist/mailbox.js
1977
- import { promises as fs9 } from "fs";
2072
+ import { promises as fs8 } from "fs";
1978
2073
  import { join as join5 } from "path";
1979
2074
  import { randomUUID } from "crypto";
1980
2075
  function inboxDir(stateRoot) {
@@ -1991,7 +2086,7 @@ async function listRotatedOldestFirst(stateRoot, project) {
1991
2086
  const dir = inboxDir(stateRoot);
1992
2087
  let entries;
1993
2088
  try {
1994
- entries = await fs9.readdir(dir);
2089
+ entries = await fs8.readdir(dir);
1995
2090
  } catch {
1996
2091
  return [];
1997
2092
  }
@@ -2000,7 +2095,7 @@ async function listRotatedOldestFirst(stateRoot, project) {
2000
2095
  }
2001
2096
  async function readMaxSeqFromFile(file) {
2002
2097
  try {
2003
- const buf = await fs9.readFile(file, "utf-8");
2098
+ const buf = await fs8.readFile(file, "utf-8");
2004
2099
  if (!buf.trim())
2005
2100
  return 0;
2006
2101
  const lines = buf.trim().split("\n");
@@ -2041,12 +2136,12 @@ function withProjectLock(project, fn) {
2041
2136
  function appendEntry(stateRoot, project, build) {
2042
2137
  return withProjectLock(project, async () => {
2043
2138
  const dir = inboxDir(stateRoot);
2044
- await fs9.mkdir(dir, { recursive: true });
2139
+ await fs8.mkdir(dir, { recursive: true });
2045
2140
  const file = logPath(stateRoot, project);
2046
2141
  const lastSeq = await readMaxSeq(stateRoot, project);
2047
2142
  const seq = lastSeq + 1;
2048
2143
  const entry = build(seq);
2049
- await fs9.appendFile(file, JSON.stringify(entry) + "\n", { encoding: "utf-8" });
2144
+ await fs8.appendFile(file, JSON.stringify(entry) + "\n", { encoding: "utf-8" });
2050
2145
  return seq;
2051
2146
  });
2052
2147
  }
@@ -2077,7 +2172,7 @@ function cursorPath(stateRoot, project, subscriber) {
2077
2172
  async function readCursor(opts) {
2078
2173
  let buf;
2079
2174
  try {
2080
- buf = await fs9.readFile(cursorPath(opts.stateRoot, opts.project, opts.subscriber), "utf-8");
2175
+ buf = await fs8.readFile(cursorPath(opts.stateRoot, opts.project, opts.subscriber), "utf-8");
2081
2176
  } catch (e) {
2082
2177
  if (e.code === "ENOENT")
2083
2178
  return null;
@@ -2104,7 +2199,7 @@ async function waitForCaptainDelivery(opts) {
2104
2199
  }
2105
2200
  }
2106
2201
  async function writeCursor(opts) {
2107
- await fs9.mkdir(inboxDir(opts.stateRoot), { recursive: true });
2202
+ await fs8.mkdir(inboxDir(opts.stateRoot), { recursive: true });
2108
2203
  const dest = cursorPath(opts.stateRoot, opts.project, opts.subscriber);
2109
2204
  const tmp = `${dest}.${process.pid}.${randomUUID()}.tmp`;
2110
2205
  const data = {
@@ -2112,7 +2207,7 @@ async function writeCursor(opts) {
2112
2207
  subscriber: opts.subscriber,
2113
2208
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2114
2209
  };
2115
- const handle = await fs9.open(tmp, "w");
2210
+ const handle = await fs8.open(tmp, "w");
2116
2211
  try {
2117
2212
  await handle.writeFile(JSON.stringify(data), { encoding: "utf-8" });
2118
2213
  await handle.sync();
@@ -2120,9 +2215,9 @@ async function writeCursor(opts) {
2120
2215
  await handle.close();
2121
2216
  }
2122
2217
  try {
2123
- await fs9.rename(tmp, dest);
2218
+ await fs8.rename(tmp, dest);
2124
2219
  } catch (e) {
2125
- await fs9.unlink(tmp).catch(() => {
2220
+ await fs8.unlink(tmp).catch(() => {
2126
2221
  });
2127
2222
  throw e;
2128
2223
  }
@@ -2133,7 +2228,7 @@ async function* readFromCursor(opts) {
2133
2228
  for (const file of files) {
2134
2229
  let buf;
2135
2230
  try {
2136
- buf = await fs9.readFile(file, "utf-8");
2231
+ buf = await fs8.readFile(file, "utf-8");
2137
2232
  } catch (e) {
2138
2233
  if (e.code === "ENOENT")
2139
2234
  continue;
@@ -2159,7 +2254,7 @@ async function mailboxStats(stateRoot, project) {
2159
2254
  let sizeBytes = 0;
2160
2255
  for (const f of [file, ...rotated]) {
2161
2256
  try {
2162
- sizeBytes += (await fs9.stat(f)).size;
2257
+ sizeBytes += (await fs8.stat(f)).size;
2163
2258
  } catch (e) {
2164
2259
  if (e.code !== "ENOENT")
2165
2260
  throw e;
@@ -2175,7 +2270,7 @@ async function mailboxStats(stateRoot, project) {
2175
2270
  }
2176
2271
  async function oldestEntryAgeMs(file) {
2177
2272
  try {
2178
- const buf = await fs9.readFile(file, "utf-8");
2273
+ const buf = await fs8.readFile(file, "utf-8");
2179
2274
  const firstLine2 = buf.split("\n").find((l) => l.trim());
2180
2275
  if (!firstLine2)
2181
2276
  return 0;
@@ -2190,7 +2285,7 @@ async function rotateIfNeeded(opts) {
2190
2285
  const file = logPath(opts.stateRoot, opts.project);
2191
2286
  let size = 0;
2192
2287
  try {
2193
- size = (await fs9.stat(file)).size;
2288
+ size = (await fs8.stat(file)).size;
2194
2289
  } catch (e) {
2195
2290
  if (e.code === "ENOENT")
2196
2291
  return { rotated: false };
@@ -2206,22 +2301,22 @@ async function rotateIfNeeded(opts) {
2206
2301
  const dst = `${file}.${n + 1}`;
2207
2302
  if (n + 1 > opts.keepCount) {
2208
2303
  try {
2209
- await fs9.unlink(src);
2304
+ await fs8.unlink(src);
2210
2305
  } catch (e) {
2211
2306
  if (e.code !== "ENOENT")
2212
2307
  throw e;
2213
2308
  }
2214
2309
  } else {
2215
2310
  try {
2216
- await fs9.rename(src, dst);
2311
+ await fs8.rename(src, dst);
2217
2312
  } catch (e) {
2218
2313
  if (e.code !== "ENOENT")
2219
2314
  throw e;
2220
2315
  }
2221
2316
  }
2222
2317
  }
2223
- await fs9.rename(file, `${file}.1`);
2224
- await fs9.writeFile(file, "", { encoding: "utf-8" });
2318
+ await fs8.rename(file, `${file}.1`);
2319
+ await fs8.writeFile(file, "", { encoding: "utf-8" });
2225
2320
  return { rotated: true, from: file, to: `${file}.1` };
2226
2321
  });
2227
2322
  }
@@ -2234,7 +2329,7 @@ var init_mailbox = __esm({
2234
2329
 
2235
2330
  // packages/core/dist/protocol.js
2236
2331
  import { createServer, createConnection } from "net";
2237
- import { existsSync as existsSync5, unlinkSync } from "fs";
2332
+ import { existsSync as existsSync5, unlinkSync, chmodSync } from "fs";
2238
2333
  function encodeMsg(obj) {
2239
2334
  return JSON.stringify(obj) + "\n";
2240
2335
  }
@@ -2347,6 +2442,12 @@ function startServer(sockPath, handlerOrCallbacks, onListenError = defaultListen
2347
2442
  });
2348
2443
  });
2349
2444
  server.on("error", onListenError);
2445
+ server.on("listening", () => {
2446
+ try {
2447
+ chmodSync(sockPath, 384);
2448
+ } catch {
2449
+ }
2450
+ });
2350
2451
  server.listen(sockPath);
2351
2452
  return server;
2352
2453
  }
@@ -2530,7 +2631,7 @@ var init_liveness2 = __esm({
2530
2631
  });
2531
2632
 
2532
2633
  // packages/core/dist/store.js
2533
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync, renameSync, writeFileSync as writeFileSync4, existsSync as existsSync6, rmSync as rmSync3, statSync } from "fs";
2634
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, readdirSync, renameSync, writeFileSync as writeFileSync3, existsSync as existsSync6, rmSync as rmSync3, statSync } from "fs";
2534
2635
  import { join as join6, resolve, sep } from "path";
2535
2636
  function safeSegment(kind, s) {
2536
2637
  if (typeof s !== "string" || s.length === 0) {
@@ -2556,10 +2657,10 @@ function createStore(root) {
2556
2657
  const taskFile = (p, id) => assertUnderRoot(join6(projDir(p), `${safeSegment("id", id)}.json`));
2557
2658
  return {
2558
2659
  put(rec) {
2559
- mkdirSync3(projDir(rec.project), { recursive: true });
2660
+ mkdirSync2(projDir(rec.project), { recursive: true });
2560
2661
  const dest = taskFile(rec.project, rec.id);
2561
2662
  const tmp = `${dest}.tmp`;
2562
- writeFileSync4(tmp, JSON.stringify(rec, null, 2));
2663
+ writeFileSync3(tmp, JSON.stringify(rec, null, 2));
2563
2664
  renameSync(tmp, dest);
2564
2665
  },
2565
2666
  get(project, id) {
@@ -2567,7 +2668,7 @@ function createStore(root) {
2567
2668
  if (!existsSync6(f))
2568
2669
  return void 0;
2569
2670
  try {
2570
- return JSON.parse(readFileSync5(f, "utf-8"));
2671
+ return JSON.parse(readFileSync4(f, "utf-8"));
2571
2672
  } catch {
2572
2673
  return void 0;
2573
2674
  }
@@ -2578,7 +2679,7 @@ function createStore(root) {
2578
2679
  return [];
2579
2680
  return readdirSync(d).filter((n) => n.endsWith(".json")).map((n) => {
2580
2681
  try {
2581
- return JSON.parse(readFileSync5(join6(d, n), "utf-8"));
2682
+ return JSON.parse(readFileSync4(join6(d, n), "utf-8"));
2582
2683
  } catch {
2583
2684
  return void 0;
2584
2685
  }
@@ -2616,7 +2717,7 @@ var init_store = __esm({
2616
2717
  import { homedir as homedir4 } from "os";
2617
2718
  import { join as join7, resolve as resolve2, sep as sep2 } from "path";
2618
2719
  import { randomBytes } from "crypto";
2619
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, renameSync as renameSync2, writeFileSync as writeFileSync5, existsSync as existsSync7, rmSync as rmSync4, statSync as statSync2 } from "fs";
2720
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, renameSync as renameSync2, writeFileSync as writeFileSync4, existsSync as existsSync7, rmSync as rmSync4, statSync as statSync2 } from "fs";
2620
2721
  function defaultWorkRoot() {
2621
2722
  return join7(homedir4(), ".config", "squadrant", "work");
2622
2723
  }
@@ -2644,10 +2745,10 @@ function createWorkStore(root = defaultWorkRoot()) {
2644
2745
  const itemFile = (p, id) => assertUnderRoot(join7(projDir(p), `${safeSegment2("id", id)}.json`));
2645
2746
  return {
2646
2747
  put(item) {
2647
- mkdirSync4(projDir(item.project), { recursive: true });
2748
+ mkdirSync3(projDir(item.project), { recursive: true });
2648
2749
  const dest = itemFile(item.project, item.id);
2649
2750
  const tmp = `${dest}.tmp`;
2650
- writeFileSync5(tmp, JSON.stringify(item, null, 2));
2751
+ writeFileSync4(tmp, JSON.stringify(item, null, 2));
2651
2752
  renameSync2(tmp, dest);
2652
2753
  },
2653
2754
  get(project, id) {
@@ -2655,7 +2756,7 @@ function createWorkStore(root = defaultWorkRoot()) {
2655
2756
  if (!existsSync7(f))
2656
2757
  return void 0;
2657
2758
  try {
2658
- return JSON.parse(readFileSync6(f, "utf-8"));
2759
+ return JSON.parse(readFileSync5(f, "utf-8"));
2659
2760
  } catch {
2660
2761
  return void 0;
2661
2762
  }
@@ -2666,7 +2767,7 @@ function createWorkStore(root = defaultWorkRoot()) {
2666
2767
  return [];
2667
2768
  return readdirSync2(d).filter((n) => n.endsWith(".json") && !n.endsWith(".json.tmp")).map((n) => {
2668
2769
  try {
2669
- return JSON.parse(readFileSync6(join7(d, n), "utf-8"));
2770
+ return JSON.parse(readFileSync5(join7(d, n), "utf-8"));
2670
2771
  } catch {
2671
2772
  return void 0;
2672
2773
  }
@@ -2810,15 +2911,15 @@ var init_snapshot = __esm({
2810
2911
 
2811
2912
  // packages/core/dist/launchd.js
2812
2913
  import { execFileSync as execFileSync3 } from "child_process";
2813
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6, readFileSync as readFileSync7, existsSync as existsSync8, openSync, writeSync, closeSync, unlinkSync as unlinkSync2, constants } from "fs";
2914
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5, readFileSync as readFileSync6, existsSync as existsSync8, openSync, writeSync, closeSync, unlinkSync as unlinkSync2, constants } from "fs";
2814
2915
  import { homedir as homedir5 } from "os";
2815
- import { dirname as dirname3, join as join8 } from "path";
2916
+ import { dirname as dirname2, join as join8 } from "path";
2816
2917
  import { fileURLToPath } from "url";
2817
2918
  function plistPath() {
2818
2919
  return join8(homedir5(), "Library", "LaunchAgents", `${LABEL}.plist`);
2819
2920
  }
2820
2921
  function daemonEntryPath() {
2821
- const p = join8(dirname3(fileURLToPath(import.meta.url)), "squadrantd.js");
2922
+ const p = join8(dirname2(fileURLToPath(import.meta.url)), "squadrantd.js");
2822
2923
  if (!existsSync8(p)) {
2823
2924
  throw new Error(`daemonEntryPath: compiled entry not found at '${p}'; run 'npm run build' \u2014 a src-tree or missing path in the launchd plist causes a MODULE_NOT_FOUND crash-loop (#259)`);
2824
2925
  }
@@ -2827,10 +2928,28 @@ function daemonEntryPath() {
2827
2928
  function xmlEscape(s) {
2828
2929
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2829
2930
  }
2830
- function sanitizePathForPlist(path34) {
2931
+ function xmlUnescape(s) {
2932
+ return s.replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
2933
+ }
2934
+ function parseProgramArgs(plistXml) {
2935
+ const m = plistXml.match(/<key>ProgramArguments<\/key>\s*<array><string>([^<]*)<\/string><string>([^<]*)<\/string><\/array>/);
2936
+ if (!m)
2937
+ return null;
2938
+ return { nodeBin: xmlUnescape(m[1]), daemonEntry: xmlUnescape(m[2]) };
2939
+ }
2940
+ function detectForeignInstall(parsed, thisEntry, registeredEntryExists) {
2941
+ if (!parsed)
2942
+ return null;
2943
+ if (parsed.daemonEntry === thisEntry)
2944
+ return null;
2945
+ if (!registeredEntryExists)
2946
+ return null;
2947
+ return { registeredEntry: parsed.daemonEntry, thisEntry };
2948
+ }
2949
+ function sanitizePathForPlist(path35) {
2831
2950
  const seen = /* @__PURE__ */ new Set();
2832
2951
  const stable = [];
2833
- for (const p of path34.split(":")) {
2952
+ for (const p of path35.split(":")) {
2834
2953
  if (!p)
2835
2954
  continue;
2836
2955
  if (p.includes("/.claude/plugins/"))
@@ -2849,7 +2968,7 @@ function resolveAgentBinDirs() {
2849
2968
  const out = execFileSync3("which", [bin], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
2850
2969
  const resolved = out.trim();
2851
2970
  if (resolved)
2852
- dirs.push(dirname3(resolved));
2971
+ dirs.push(dirname2(resolved));
2853
2972
  } catch {
2854
2973
  }
2855
2974
  }
@@ -2911,7 +3030,7 @@ function tryAcquireDaemonLock() {
2911
3030
  const lp = daemonLockPath();
2912
3031
  if (existsSync8(lp)) {
2913
3032
  try {
2914
- const pid = parseInt(readFileSync7(lp, "utf-8").trim(), 10);
3033
+ const pid = parseInt(readFileSync6(lp, "utf-8").trim(), 10);
2915
3034
  if (!Number.isFinite(pid) || pid <= 0) {
2916
3035
  unlinkSync2(lp);
2917
3036
  } else {
@@ -2948,17 +3067,19 @@ function computeDaemonDrift(nodeBin) {
2948
3067
  const p = plistPath();
2949
3068
  const entry = daemonEntryPath();
2950
3069
  const desired = renderPlist(nodeBin, entry, buildDaemonPath(process.env.PATH ?? ""));
2951
- const current = existsSync8(p) ? readFileSync7(p, "utf-8") : null;
3070
+ const current = existsSync8(p) ? readFileSync6(p, "utf-8") : null;
2952
3071
  const uid = process.getuid?.() ?? 0;
2953
3072
  const target = `gui/${uid}/${LABEL}`;
2954
3073
  const changed = current !== desired;
2955
3074
  const programChanged = current !== null && changed && !current.includes(programArgsBlock(nodeBin, entry));
2956
- return { plistPath: p, target, desired, current, changed, programChanged };
3075
+ const parsedCurrent = current !== null ? parseProgramArgs(current) : null;
3076
+ const foreignInstall = detectForeignInstall(parsedCurrent, entry, parsedCurrent !== null && existsSync8(parsedCurrent.daemonEntry));
3077
+ return { plistPath: p, target, desired, current, changed, programChanged, foreignInstall };
2957
3078
  }
2958
3079
  function applyDaemonDrift(drift) {
2959
3080
  if (drift.changed) {
2960
- mkdirSync5(dirname3(drift.plistPath), { recursive: true });
2961
- writeFileSync6(drift.plistPath, drift.desired);
3081
+ mkdirSync4(dirname2(drift.plistPath), { recursive: true });
3082
+ writeFileSync5(drift.plistPath, drift.desired);
2962
3083
  }
2963
3084
  if (drift.programChanged) {
2964
3085
  try {
@@ -2994,7 +3115,12 @@ function ensureDaemon(nodeBin = process.execPath, opts = {}) {
2994
3115
  return;
2995
3116
  }
2996
3117
  try {
2997
- applyDaemonDrift(computeDaemonDrift(nodeBin));
3118
+ const drift = computeDaemonDrift(nodeBin);
3119
+ if (drift.foreignInstall) {
3120
+ process.stderr.write(printForeignInstallError(drift.foreignInstall));
3121
+ return;
3122
+ }
3123
+ applyDaemonDrift(drift);
2998
3124
  } catch (e) {
2999
3125
  process.stderr.write(`[squadrant] warn: ensureDaemon failed (${e instanceof Error ? e.message : e})
3000
3126
  `);
@@ -3002,6 +3128,13 @@ function ensureDaemon(nodeBin = process.execPath, opts = {}) {
3002
3128
  releaseDaemonLock();
3003
3129
  }
3004
3130
  }
3131
+ function printForeignInstallError(foreign) {
3132
+ return `[squadrant] refusing to restart the daemon: the registered launchd config belongs to a DIFFERENT squadrant install than this one.
3133
+ registered install: ${foreign.registeredEntry}
3134
+ this install: ${foreign.thisEntry}
3135
+ Two squadrant installs on this machine will keep fighting over the daemon (#670). Uninstall the one you don't use, then run \`squadrant heal daemon\` to reconcile.
3136
+ `;
3137
+ }
3005
3138
  function reregisterDaemon(nodeBin = process.execPath) {
3006
3139
  if (!tryAcquireDaemonLock())
3007
3140
  return;
@@ -3165,7 +3298,7 @@ var init_gate = __esm({
3165
3298
  });
3166
3299
 
3167
3300
  // packages/core/dist/daemon/liveness-registry.js
3168
- import { writeFileSync as writeFileSync7, readFileSync as readFileSync8, renameSync as renameSync3 } from "fs";
3301
+ import { writeFileSync as writeFileSync6, readFileSync as readFileSync7, renameSync as renameSync3 } from "fs";
3169
3302
  var LivenessRegistry;
3170
3303
  var init_liveness_registry = __esm({
3171
3304
  "packages/core/dist/daemon/liveness-registry.js"() {
@@ -3179,13 +3312,13 @@ var init_liveness_registry = __esm({
3179
3312
  this.path = opts.path;
3180
3313
  this.readFile = opts.readFile ?? ((p) => {
3181
3314
  try {
3182
- return readFileSync8(p, "utf-8");
3315
+ return readFileSync7(p, "utf-8");
3183
3316
  } catch {
3184
3317
  return void 0;
3185
3318
  }
3186
3319
  });
3187
3320
  this.writeFile = opts.writeFile ?? ((p, c) => {
3188
- writeFileSync7(`${p}.tmp`, c);
3321
+ writeFileSync6(`${p}.tmp`, c);
3189
3322
  renameSync3(`${p}.tmp`, p);
3190
3323
  });
3191
3324
  }
@@ -3238,7 +3371,7 @@ var init_liveness_registry = __esm({
3238
3371
  import { homedir as homedir6 } from "os";
3239
3372
  import { join as join9 } from "path";
3240
3373
  import { spawn as realSpawn } from "child_process";
3241
- import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
3374
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
3242
3375
  function defaultIsPidAlive(pid) {
3243
3376
  try {
3244
3377
  process.kill(pid, 0);
@@ -3258,10 +3391,10 @@ function buildContext(opts) {
3258
3391
  const isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
3259
3392
  const spawn2 = opts.spawn ?? realSpawn;
3260
3393
  const resultsDir = join9(stateRoot, "_results");
3261
- mkdirSync6(resultsDir, { recursive: true });
3394
+ mkdirSync5(resultsDir, { recursive: true });
3262
3395
  const writeResult = (id, payload) => {
3263
3396
  const p = join9(resultsDir, `${id}.txt`);
3264
- writeFileSync8(p, payload);
3397
+ writeFileSync7(p, payload);
3265
3398
  return p;
3266
3399
  };
3267
3400
  const log = (m) => process.stderr.write(`[squadrantd] ${(/* @__PURE__ */ new Date()).toISOString()} ${m}
@@ -4007,7 +4140,7 @@ var init_server = __esm({
4007
4140
  // packages/core/dist/daemon/snapshot-gather.js
4008
4141
  import { fileURLToPath as fileURLToPath2 } from "url";
4009
4142
  import { join as join10 } from "path";
4010
- import { statSync as statSync3, openSync as openSync2, readSync, closeSync as closeSync2, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
4143
+ import { statSync as statSync3, openSync as openSync2, readSync, closeSync as closeSync2, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "fs";
4011
4144
  function distBuiltAt() {
4012
4145
  try {
4013
4146
  return statSync3(SELF_PATH).mtimeMs;
@@ -4015,10 +4148,10 @@ function distBuiltAt() {
4015
4148
  return 0;
4016
4149
  }
4017
4150
  }
4018
- function gatherLogStats(path34, now, windowMs) {
4151
+ function gatherLogStats(path35, now, windowMs) {
4019
4152
  let sizeBytes = 0;
4020
4153
  try {
4021
- sizeBytes = statSync3(path34).size;
4154
+ sizeBytes = statSync3(path35).size;
4022
4155
  } catch {
4023
4156
  return { errorCount: 0, sizeBytes: 0, windowMs };
4024
4157
  }
@@ -4029,7 +4162,7 @@ function gatherLogStats(path34, now, windowMs) {
4029
4162
  const len = sizeBytes - start;
4030
4163
  let text = "";
4031
4164
  try {
4032
- const fd = openSync2(path34, "r");
4165
+ const fd = openSync2(path35, "r");
4033
4166
  try {
4034
4167
  const buf = Buffer.alloc(len);
4035
4168
  readSync(fd, buf, 0, len, start);
@@ -4070,7 +4203,7 @@ function gatherStoreStats(store, stateRoot, project) {
4070
4203
  if (!n.endsWith(".json"))
4071
4204
  continue;
4072
4205
  try {
4073
- JSON.parse(readFileSync9(join10(dir, n), "utf-8"));
4206
+ JSON.parse(readFileSync8(join10(dir, n), "utf-8"));
4074
4207
  } catch {
4075
4208
  corruptCount++;
4076
4209
  }
@@ -4105,7 +4238,7 @@ var init_snapshot_gather = __esm({
4105
4238
  });
4106
4239
 
4107
4240
  // packages/core/dist/daemon/start.js
4108
- import { join as join11, dirname as dirname4 } from "path";
4241
+ import { join as join11, dirname as dirname3 } from "path";
4109
4242
  import { readdir } from "fs/promises";
4110
4243
  function startDaemon(ctx, opts, pkgVersion) {
4111
4244
  const { stateRoot, store, log, isPidAlive, resultsDir, taskTimeoutMs, inFlightHeadlessIds, activeHeadlessKills, broadcast, cancelPromotionsFor } = ctx;
@@ -4181,7 +4314,7 @@ function startDaemon(ctx, opts, pkgVersion) {
4181
4314
  return out;
4182
4315
  }
4183
4316
  async function gatherSnapshotInputs(now) {
4184
- const logPath2 = join11(dirname4(stateRoot), "squadrantd.log");
4317
+ const logPath2 = join11(dirname3(stateRoot), "squadrantd.log");
4185
4318
  const tier2Projects = opts.registeredProjects ?? Object.keys(loadConfig().projects);
4186
4319
  const projects = await Promise.all(tier2Projects.map(async (project) => {
4187
4320
  const cursor = await readCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER2 });
@@ -4386,35 +4519,33 @@ var init_start = __esm({
4386
4519
 
4387
4520
  // packages/core/dist/session-freshness.js
4388
4521
  import crypto from "crypto";
4389
- import fs10 from "fs";
4390
- import path8 from "path";
4522
+ import fs9 from "fs";
4523
+ import path9 from "path";
4391
4524
  function loadSessions(sessionsPath) {
4392
4525
  try {
4393
- return JSON.parse(fs10.readFileSync(sessionsPath, "utf-8"));
4526
+ return JSON.parse(readConfigFileSync(sessionsPath));
4394
4527
  } catch {
4395
4528
  return { workspaces: {} };
4396
4529
  }
4397
4530
  }
4398
4531
  function saveSessions(sessionsPath, sessions) {
4399
- const dir = path8.dirname(sessionsPath);
4400
- fs10.mkdirSync(dir, { recursive: true });
4401
- fs10.writeFileSync(sessionsPath, JSON.stringify(sessions, null, 2) + "\n");
4532
+ writeConfigFileSync(sessionsPath, JSON.stringify(sessions, null, 2) + "\n");
4402
4533
  }
4403
4534
  function computeTemplateHash(role, templatesDir) {
4404
4535
  const hash = crypto.createHash("sha256");
4405
- const roleFile = path8.join(templatesDir, `${role}.claude.md`);
4406
- const legacyRoleFile = path8.join(templatesDir, `${role}.CLAUDE.md`);
4407
- if (fs10.existsSync(roleFile)) {
4408
- hash.update(fs10.readFileSync(roleFile, "utf-8"));
4409
- } else if (fs10.existsSync(legacyRoleFile)) {
4410
- hash.update(fs10.readFileSync(legacyRoleFile, "utf-8"));
4536
+ const roleFile = path9.join(templatesDir, `${role}.claude.md`);
4537
+ const legacyRoleFile = path9.join(templatesDir, `${role}.CLAUDE.md`);
4538
+ if (fs9.existsSync(roleFile)) {
4539
+ hash.update(fs9.readFileSync(roleFile, "utf-8"));
4540
+ } else if (fs9.existsSync(legacyRoleFile)) {
4541
+ hash.update(fs9.readFileSync(legacyRoleFile, "utf-8"));
4411
4542
  }
4412
- const pluginSkillsDir = path8.join(templatesDir, "..", "plugin", "skills");
4413
- if (fs10.existsSync(pluginSkillsDir)) {
4414
- for (const skill of fs10.readdirSync(pluginSkillsDir).sort()) {
4415
- const skillFile = path8.join(pluginSkillsDir, skill, "SKILL.md");
4416
- if (fs10.existsSync(skillFile)) {
4417
- hash.update(fs10.readFileSync(skillFile, "utf-8"));
4543
+ const pluginSkillsDir = path9.join(templatesDir, "..", "plugin", "skills");
4544
+ if (fs9.existsSync(pluginSkillsDir)) {
4545
+ for (const skill of fs9.readdirSync(pluginSkillsDir).sort()) {
4546
+ const skillFile = path9.join(pluginSkillsDir, skill, "SKILL.md");
4547
+ if (fs9.existsSync(skillFile)) {
4548
+ hash.update(fs9.readFileSync(skillFile, "utf-8"));
4418
4549
  }
4419
4550
  }
4420
4551
  }
@@ -4446,6 +4577,7 @@ function recordSession(workspaceName, role, opts) {
4446
4577
  }
4447
4578
  var init_session_freshness = __esm({
4448
4579
  "packages/core/dist/session-freshness.js"() {
4580
+ init_dist();
4449
4581
  }
4450
4582
  });
4451
4583
 
@@ -4835,17 +4967,16 @@ var init_format = __esm({
4835
4967
  });
4836
4968
 
4837
4969
  // packages/core/dist/telegram/state.js
4838
- import fs11 from "fs";
4839
- import path9 from "path";
4970
+ import path10 from "path";
4840
4971
  function statePath(stateRoot) {
4841
- return path9.join(stateRoot, "telegram-state.json");
4972
+ return path10.join(stateRoot, "telegram-state.json");
4842
4973
  }
4843
4974
  function topicKey(project, scope = "project") {
4844
4975
  return `${project}::${scope}`;
4845
4976
  }
4846
4977
  function loadState(stateRoot) {
4847
4978
  try {
4848
- const raw = fs11.readFileSync(statePath(stateRoot), "utf-8");
4979
+ const raw = readConfigFileSync(statePath(stateRoot));
4849
4980
  const data = JSON.parse(raw);
4850
4981
  const result = {
4851
4982
  offset: typeof data.offset === "number" ? data.offset : 0,
@@ -4860,8 +4991,7 @@ function loadState(stateRoot) {
4860
4991
  }
4861
4992
  }
4862
4993
  function saveState(stateRoot, s) {
4863
- fs11.mkdirSync(stateRoot, { recursive: true });
4864
- fs11.writeFileSync(statePath(stateRoot), JSON.stringify(s, null, 2) + "\n");
4994
+ writeConfigFileSync(statePath(stateRoot), JSON.stringify(s, null, 2) + "\n");
4865
4995
  }
4866
4996
  function setTopic(stateRoot, project, topicId, scope = "project") {
4867
4997
  const s = loadState(stateRoot);
@@ -4895,6 +5025,7 @@ function findProjectByThread(stateRoot, threadId) {
4895
5025
  }
4896
5026
  var init_state = __esm({
4897
5027
  "packages/core/dist/telegram/state.js"() {
5028
+ init_dist();
4898
5029
  }
4899
5030
  });
4900
5031
 
@@ -5046,7 +5177,7 @@ var init_tiers = __esm({
5046
5177
 
5047
5178
  // packages/core/dist/telegram/bridge.js
5048
5179
  import os4 from "os";
5049
- import path10 from "path";
5180
+ import path11 from "path";
5050
5181
  function parseNotifyPref(text) {
5051
5182
  const parts = text.trim().split(/\s+/);
5052
5183
  if (stripBotMention(parts[0] ?? "").toLowerCase() !== "/notify")
@@ -5073,7 +5204,7 @@ function notifyToggle(text) {
5073
5204
  }
5074
5205
  function createTelegramBridge(opts) {
5075
5206
  const { cfg, stateRoot, client, appendCaptainMessage: appendCaptainMessage2, log, ensureCaptainAlive, runCommand, sendReply } = opts;
5076
- const configRoot = opts.configRoot ?? path10.join(os4.homedir(), ".config", "squadrant");
5207
+ const configRoot = opts.configRoot ?? path11.join(os4.homedir(), ".config", "squadrant");
5077
5208
  const pollMs = cfg.pollMs ?? 1e3;
5078
5209
  let running = false;
5079
5210
  let lastSuccessfulPollAt = null;
@@ -5204,14 +5335,14 @@ function createTelegramBridge(opts) {
5204
5335
  }
5205
5336
  function currentEffort() {
5206
5337
  try {
5207
- return loadConfig(path10.join(configRoot, "config.json")).defaults.effort ?? "balance";
5338
+ return loadConfig(path11.join(configRoot, "config.json")).defaults.effort ?? "balance";
5208
5339
  } catch {
5209
5340
  return "balance";
5210
5341
  }
5211
5342
  }
5212
5343
  function projectNames() {
5213
5344
  try {
5214
- return Object.keys(loadConfig(path10.join(configRoot, "config.json")).projects);
5345
+ return Object.keys(loadConfig(path11.join(configRoot, "config.json")).projects);
5215
5346
  } catch {
5216
5347
  return [];
5217
5348
  }
@@ -5479,7 +5610,7 @@ var init_restart_daemon = __esm({
5479
5610
  });
5480
5611
 
5481
5612
  // packages/core/dist/telegram/setup.js
5482
- import fs12 from "fs";
5613
+ import fs10 from "fs";
5483
5614
  function resolveSetupGroup(existingSupergroupId, opts) {
5484
5615
  if (existingSupergroupId !== void 0 && !opts.redetect)
5485
5616
  return "reuse";
@@ -5530,7 +5661,7 @@ function writeTelegramConfig(configPath, opts) {
5530
5661
  let config;
5531
5662
  let raw = null;
5532
5663
  try {
5533
- raw = fs12.readFileSync(configPath, "utf-8");
5664
+ raw = fs10.readFileSync(configPath, "utf-8");
5534
5665
  } catch (err) {
5535
5666
  if (err.code !== "ENOENT") {
5536
5667
  throw new Error(`refusing to overwrite unreadable config at ${configPath}: ${String(err)}`);
@@ -5558,10 +5689,11 @@ function writeTelegramConfig(configPath, opts) {
5558
5689
  if (remoteControl !== void 0)
5559
5690
  next.remoteControl = remoteControl;
5560
5691
  config.telegram = next;
5561
- fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
5692
+ writeConfigFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
5562
5693
  }
5563
5694
  var init_setup = __esm({
5564
5695
  "packages/core/dist/telegram/setup.js"() {
5696
+ init_dist();
5565
5697
  init_state();
5566
5698
  init_bot_commands();
5567
5699
  init_restart_daemon();
@@ -5887,7 +6019,7 @@ var init_launch_workspace = __esm({
5887
6019
  });
5888
6020
 
5889
6021
  // packages/core/dist/side-session.js
5890
- import fs13 from "fs";
6022
+ import fs11 from "fs";
5891
6023
  function sideTitleFor(project, name) {
5892
6024
  return `\u{1F5D2} ${project}:${name}`;
5893
6025
  }
@@ -5989,7 +6121,7 @@ async function runSideClose(runtime, workspaceId, project, name, projPath, workt
5989
6121
  await runtime.closePane(pane);
5990
6122
  if (projPath) {
5991
6123
  const wtPath = worktreePath(projPath, worktreeDir, project, name);
5992
- if (fs13.existsSync(wtPath)) {
6124
+ if (fs11.existsSync(wtPath)) {
5993
6125
  try {
5994
6126
  removeWorktree(projPath, wtPath);
5995
6127
  } catch (e) {
@@ -6009,9 +6141,9 @@ var init_side_session = __esm({
6009
6141
  });
6010
6142
 
6011
6143
  // packages/core/dist/crew-spawn.js
6012
- import fs14 from "fs";
6144
+ import fs12 from "fs";
6013
6145
  import os5 from "os";
6014
- import path11 from "path";
6146
+ import path12 from "path";
6015
6147
  async function listCrewPanes(runtime, workspaceId, project) {
6016
6148
  const surfaces = await runtime.listSurfaces(workspaceId);
6017
6149
  return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
@@ -6080,9 +6212,9 @@ async function runCrewSpawn(input, config, deps) {
6080
6212
  }) : proj.path;
6081
6213
  let firstTurnTask = input.task;
6082
6214
  if (input.taskFile && input.taskFile !== "-" && !input.shared) {
6083
- const absTaskFile = path11.resolve(input.taskFile);
6084
- const basename = path11.basename(absTaskFile);
6085
- fs14.copyFileSync(absTaskFile, path11.join(spawnCwd, basename));
6215
+ const absTaskFile = path12.resolve(input.taskFile);
6216
+ const basename = path12.basename(absTaskFile);
6217
+ fs12.copyFileSync(absTaskFile, path12.join(spawnCwd, basename));
6086
6218
  firstTurnTask = `Read ./${basename} to get your task brief, then execute it.`;
6087
6219
  }
6088
6220
  const route = !input.agentExplicit && !input.model ? resolveCrewRoute(input.task, config) : null;
@@ -6095,8 +6227,8 @@ async function runCrewSpawn(input, config, deps) {
6095
6227
  throw new Error(`Unknown agent '${agentName}'. Known: claude, codex, gemini, opencode.`);
6096
6228
  }
6097
6229
  if (agentName === "codex") {
6098
- const codexRoleFile = path11.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
6099
- const roleInstructions = fs14.existsSync(codexRoleFile) ? fs14.readFileSync(codexRoleFile, "utf8") : void 0;
6230
+ const codexRoleFile = path12.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
6231
+ const roleInstructions = fs12.existsSync(codexRoleFile) ? fs12.readFileSync(codexRoleFile, "utf8") : void 0;
6100
6232
  return runCodexInteractiveSpawn({
6101
6233
  project: input.project,
6102
6234
  task: input.task,
@@ -6112,7 +6244,7 @@ async function runCrewSpawn(input, config, deps) {
6112
6244
  sendCodexFirstTurn: deps.sendCodexFirstTurn
6113
6245
  });
6114
6246
  }
6115
- const promptFile = path11.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
6247
+ const promptFile = path12.join(TEMPLATES_DIR, `crew.${agent.templateSuffix}.md`);
6116
6248
  const interactive = agent.name === "claude" || agent.name === "opencode";
6117
6249
  const crewRole = config.defaults.roles?.crew;
6118
6250
  const configModel = crewRole && crewRole.agent === agent.name ? crewRole.model : void 0;
@@ -6294,7 +6426,7 @@ function buildRecoveryHint(sessId, provider, worktreeCwd) {
6294
6426
  return "";
6295
6427
  if (provider === "claude") {
6296
6428
  const escaped = worktreeCwd.replace(/[^a-zA-Z0-9]/g, "-");
6297
- const transcriptPath = path11.join(os5.homedir(), ".claude", "projects", escaped, `${sessId}.jsonl`);
6429
+ const transcriptPath = path12.join(os5.homedir(), ".claude", "projects", escaped, `${sessId}.jsonl`);
6298
6430
  return `
6299
6431
  transcript: ${transcriptPath}
6300
6432
  resume: claude --resume ${sessId} (run from the worktree path above)
@@ -6390,8 +6522,8 @@ var init_crew_spawn = __esm({
6390
6522
  init_crew_routing();
6391
6523
  init_crew_protocol();
6392
6524
  init_crew_lifecycle();
6393
- TEMPLATES_DIR = path11.join(os5.homedir(), ".config", "squadrant", "templates");
6394
- STATE_ROOT = path11.join(os5.homedir(), ".config", "squadrant", "state");
6525
+ TEMPLATES_DIR = path12.join(os5.homedir(), ".config", "squadrant", "templates");
6526
+ STATE_ROOT = path12.join(os5.homedir(), ".config", "squadrant", "state");
6395
6527
  CLOSE_LOOKUP_RETRIES = 3;
6396
6528
  CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
6397
6529
  }
@@ -6489,6 +6621,7 @@ __export(dist_exports2, {
6489
6621
  deliverStartupPrompt: () => deliverStartupPrompt,
6490
6622
  deliverable: () => deliverable,
6491
6623
  deriveCaptainState: () => deriveCaptainState,
6624
+ detectForeignInstall: () => detectForeignInstall,
6492
6625
  detectGroupAndUser: () => detectGroupAndUser,
6493
6626
  detectGroupId: () => detectGroupId,
6494
6627
  discoverCaptainSurface: () => discoverCaptainSurface,
@@ -6528,7 +6661,9 @@ __export(dist_exports2, {
6528
6661
  notifyToggle: () => notifyToggle,
6529
6662
  parseCommand: () => parseCommand,
6530
6663
  parseNotifyPref: () => parseNotifyPref,
6664
+ parseProgramArgs: () => parseProgramArgs,
6531
6665
  plistPath: () => plistPath,
6666
+ printForeignInstallError: () => printForeignInstallError,
6532
6667
  programArgsBlock: () => programArgsBlock,
6533
6668
  projectHealth: () => projectHealth,
6534
6669
  purgeExpiredWorkItems: () => purgeExpiredWorkItems,
@@ -7280,13 +7415,13 @@ var init_notifiers = __esm({
7280
7415
  });
7281
7416
 
7282
7417
  // packages/workspaces/dist/workspaces/obsidian.js
7283
- import fs15 from "fs/promises";
7418
+ import fs13 from "fs/promises";
7284
7419
  import { existsSync as existsSync10 } from "fs";
7285
- import path12 from "path";
7420
+ import path13 from "path";
7286
7421
  function resolveInRoot(root, relative) {
7287
- const joined = path12.resolve(root, relative);
7288
- const normalized = path12.resolve(root) + path12.sep;
7289
- if (joined !== path12.resolve(root) && !joined.startsWith(normalized)) {
7422
+ const joined = path13.resolve(root, relative);
7423
+ const normalized = path13.resolve(root) + path13.sep;
7424
+ if (joined !== path13.resolve(root) && !joined.startsWith(normalized)) {
7290
7425
  throw new Error(`Path '${relative}' escapes workspace root`);
7291
7426
  }
7292
7427
  return joined;
@@ -7305,16 +7440,16 @@ function createObsidianDriver(scope) {
7305
7440
  };
7306
7441
  },
7307
7442
  async read(rel) {
7308
- return fs15.readFile(resolveInRoot(root, rel), "utf-8");
7443
+ return fs13.readFile(resolveInRoot(root, rel), "utf-8");
7309
7444
  },
7310
7445
  async write(rel, content) {
7311
7446
  const abs = resolveInRoot(root, rel);
7312
- await fs15.mkdir(path12.dirname(abs), { recursive: true });
7313
- await fs15.writeFile(abs, content);
7447
+ await fs13.mkdir(path13.dirname(abs), { recursive: true });
7448
+ await fs13.writeFile(abs, content);
7314
7449
  },
7315
7450
  async exists(rel) {
7316
7451
  try {
7317
- await fs15.access(resolveInRoot(root, rel));
7452
+ await fs13.access(resolveInRoot(root, rel));
7318
7453
  return true;
7319
7454
  } catch {
7320
7455
  return false;
@@ -7322,13 +7457,13 @@ function createObsidianDriver(scope) {
7322
7457
  },
7323
7458
  async list(rel) {
7324
7459
  try {
7325
- return await fs15.readdir(resolveInRoot(root, rel));
7460
+ return await fs13.readdir(resolveInRoot(root, rel));
7326
7461
  } catch {
7327
7462
  return [];
7328
7463
  }
7329
7464
  },
7330
7465
  async mkdir(rel) {
7331
- await fs15.mkdir(resolveInRoot(root, rel), { recursive: true });
7466
+ await fs13.mkdir(resolveInRoot(root, rel), { recursive: true });
7332
7467
  }
7333
7468
  };
7334
7469
  }
@@ -7592,7 +7727,7 @@ var init_store_fingerprint = __esm({
7592
7727
  });
7593
7728
 
7594
7729
  // packages/workspaces/dist/cmux-daemon/daemon-cmux.js
7595
- import { readdirSync as readdirSync4, readFileSync as readFileSync10 } from "fs";
7730
+ import { readdirSync as readdirSync4, readFileSync as readFileSync9 } from "fs";
7596
7731
  import { join as join14 } from "path";
7597
7732
  import { homedir as homedir9 } from "os";
7598
7733
  var DaemonCmux;
@@ -7665,7 +7800,7 @@ var init_daemon_cmux = __esm({
7665
7800
  } catch (e) {
7666
7801
  throw new Error(`liveness: could not read cmux state dir ${dir}: ${e.message}`);
7667
7802
  }
7668
- return readLivenessSnapshot(files, (f) => readFileSync10(join14(dir, f), "utf-8"), projects);
7803
+ return readLivenessSnapshot(files, (f) => readFileSync9(join14(dir, f), "utf-8"), projects);
7669
7804
  }
7670
7805
  };
7671
7806
  }
@@ -7674,7 +7809,7 @@ var init_daemon_cmux = __esm({
7674
7809
  // packages/workspaces/dist/cmux-daemon/cmux-store-source.js
7675
7810
  import { join as join15 } from "path";
7676
7811
  import { homedir as homedir10 } from "os";
7677
- import { watch, readdirSync as readdirSync5, readFileSync as readFileSync11, existsSync as existsSync11 } from "fs";
7812
+ import { watch, readdirSync as readdirSync5, readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
7678
7813
  function parseLifecycleState(s) {
7679
7814
  if (s === "running" || s === "idle" || s === "needsInput" || s === "unknown") {
7680
7815
  return s;
@@ -7696,9 +7831,9 @@ function defaultListFiles(dir) {
7696
7831
  return [];
7697
7832
  }
7698
7833
  }
7699
- function defaultReadFile(path34) {
7834
+ function defaultReadFile(path35) {
7700
7835
  try {
7701
- return readFileSync11(path34, "utf-8");
7836
+ return readFileSync10(path35, "utf-8");
7702
7837
  } catch {
7703
7838
  return void 0;
7704
7839
  }
@@ -7852,7 +7987,7 @@ var init_cmux_store_source = __esm({
7852
7987
  // packages/workspaces/dist/native-hooks/native-hook-source.js
7853
7988
  import { join as join16 } from "path";
7854
7989
  import { homedir as homedir11 } from "os";
7855
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
7990
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
7856
7991
  function installClaudeHooks(opts = {}) {
7857
7992
  const settingsPath = opts.settingsPath ?? join16(homedir11(), ".claude", "settings.json");
7858
7993
  const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
@@ -7948,16 +8083,16 @@ function extractDetail(sub, payload) {
7948
8083
  }
7949
8084
  return void 0;
7950
8085
  }
7951
- function defaultReadFile2(path34) {
8086
+ function defaultReadFile2(path35) {
7952
8087
  try {
7953
- return readFileSync12(path34, "utf-8");
8088
+ return readFileSync11(path35, "utf-8");
7954
8089
  } catch {
7955
8090
  return void 0;
7956
8091
  }
7957
8092
  }
7958
- function defaultWriteFile(path34, content) {
7959
- mkdirSync7(path34.replace(/\/[^/]+$/, ""), { recursive: true });
7960
- writeFileSync9(path34, content, "utf-8");
8093
+ function defaultWriteFile(path35, content) {
8094
+ mkdirSync6(path35.replace(/\/[^/]+$/, ""), { recursive: true });
8095
+ writeFileSync8(path35, content, "utf-8");
7961
8096
  }
7962
8097
  var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, NativeHookSource;
7963
8098
  var init_native_hook_source = __esm({
@@ -8576,8 +8711,8 @@ var init_registry4 = __esm({
8576
8711
  });
8577
8712
 
8578
8713
  // packages/agents/dist/drivers/launch-cmd.js
8579
- import fs16 from "fs";
8580
- import path13 from "path";
8714
+ import fs14 from "fs";
8715
+ import path14 from "path";
8581
8716
  function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model, templatesDir) {
8582
8717
  const driver = registry.getDriver(agentName);
8583
8718
  if (driver.name === "claude") {
@@ -8593,27 +8728,27 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
8593
8728
  cmd += ` --model ${model}`;
8594
8729
  }
8595
8730
  if (templatesDir) {
8596
- const roleFile2 = path13.join(templatesDir, `${role}.claude.md`);
8597
- const legacyRoleFile = path13.join(templatesDir, `${role}.CLAUDE.md`);
8598
- const actualRoleFile = fs16.existsSync(roleFile2) ? roleFile2 : fs16.existsSync(legacyRoleFile) ? legacyRoleFile : null;
8731
+ const roleFile2 = path14.join(templatesDir, `${role}.claude.md`);
8732
+ const legacyRoleFile = path14.join(templatesDir, `${role}.CLAUDE.md`);
8733
+ const actualRoleFile = fs14.existsSync(roleFile2) ? roleFile2 : fs14.existsSync(legacyRoleFile) ? legacyRoleFile : null;
8599
8734
  if (actualRoleFile) {
8600
8735
  cmd += ` --append-system-prompt-file ${actualRoleFile}`;
8601
8736
  }
8602
- const pluginDir = path13.join(templatesDir, "..", "plugin");
8603
- if (fs16.existsSync(pluginDir)) {
8737
+ const pluginDir = path14.join(templatesDir, "..", "plugin");
8738
+ if (fs14.existsSync(pluginDir)) {
8604
8739
  cmd += ` --plugin-dir ${pluginDir}`;
8605
8740
  }
8606
8741
  }
8607
8742
  return cmd;
8608
8743
  }
8609
- const roleFile = templatesDir ? path13.join(templatesDir, `${role}.${driver.templateSuffix}.md`) : void 0;
8744
+ const roleFile = templatesDir ? path14.join(templatesDir, `${role}.${driver.templateSuffix}.md`) : void 0;
8610
8745
  return driver.buildCommand({
8611
8746
  prompt: `You are a squadrant ${role}. Read your instructions from ${roleFile ?? role} and begin.`,
8612
8747
  workdir: process.cwd(),
8613
8748
  role,
8614
8749
  model,
8615
8750
  autoApprove: true,
8616
- promptFile: roleFile && fs16.existsSync(roleFile) ? roleFile : void 0
8751
+ promptFile: roleFile && fs14.existsSync(roleFile) ? roleFile : void 0
8617
8752
  });
8618
8753
  }
8619
8754
  var init_launch_cmd = __esm({
@@ -8636,7 +8771,7 @@ var init_drivers = __esm({
8636
8771
 
8637
8772
  // packages/agents/dist/projection/cursor.js
8638
8773
  import { mkdir, readFile, writeFile } from "fs/promises";
8639
- import path14 from "path";
8774
+ import path15 from "path";
8640
8775
  import os6 from "os";
8641
8776
  function renderMdc(source) {
8642
8777
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
@@ -8685,7 +8820,7 @@ function createCursorEmitter() {
8685
8820
  if (scope === "user") {
8686
8821
  return [
8687
8822
  {
8688
- path: path14.join(os6.homedir(), ".cursor/rules/squadrant-global.mdc"),
8823
+ path: path15.join(os6.homedir(), ".cursor/rules/squadrant-global.mdc"),
8689
8824
  shared: false,
8690
8825
  format: "mdc"
8691
8826
  }
@@ -8695,7 +8830,7 @@ function createCursorEmitter() {
8695
8830
  return [];
8696
8831
  return [
8697
8832
  {
8698
- path: path14.join(projectRoot, ".cursor/rules/squadrant.mdc"),
8833
+ path: path15.join(projectRoot, ".cursor/rules/squadrant.mdc"),
8699
8834
  shared: false,
8700
8835
  format: "mdc"
8701
8836
  }
@@ -8712,7 +8847,7 @@ function createCursorEmitter() {
8712
8847
  diff: buildDiff(existing, generated)
8713
8848
  };
8714
8849
  }
8715
- await mkdir(path14.dirname(dest.path), { recursive: true });
8850
+ await mkdir(path15.dirname(dest.path), { recursive: true });
8716
8851
  await writeFile(dest.path, generated, "utf-8");
8717
8852
  return {
8718
8853
  written: true,
@@ -8763,7 +8898,7 @@ var init_marker = __esm({
8763
8898
 
8764
8899
  // packages/agents/dist/projection/codex.js
8765
8900
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
8766
- import path15 from "path";
8901
+ import path16 from "path";
8767
8902
  import os7 from "os";
8768
8903
  function renderMarkdown(source) {
8769
8904
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
@@ -8788,7 +8923,7 @@ function createCodexEmitter() {
8788
8923
  destinations(scope, projectRoot) {
8789
8924
  if (scope === "user") {
8790
8925
  return [{
8791
- path: path15.join(os7.homedir(), ".codex/AGENTS.md"),
8926
+ path: path16.join(os7.homedir(), ".codex/AGENTS.md"),
8792
8927
  shared: true,
8793
8928
  format: "markdown"
8794
8929
  }];
@@ -8796,7 +8931,7 @@ function createCodexEmitter() {
8796
8931
  if (!projectRoot)
8797
8932
  return [];
8798
8933
  return [{
8799
- path: path15.join(projectRoot, "AGENTS.md"),
8934
+ path: path16.join(projectRoot, "AGENTS.md"),
8800
8935
  shared: true,
8801
8936
  format: "markdown"
8802
8937
  }];
@@ -8817,7 +8952,7 @@ ${existing ?? ""}
8817
8952
  ${generated}`
8818
8953
  };
8819
8954
  }
8820
- await mkdir2(path15.dirname(dest.path), { recursive: true });
8955
+ await mkdir2(path16.dirname(dest.path), { recursive: true });
8821
8956
  await writeFile2(dest.path, generated, "utf-8");
8822
8957
  return {
8823
8958
  written: true,
@@ -8835,7 +8970,7 @@ var init_codex2 = __esm({
8835
8970
 
8836
8971
  // packages/agents/dist/projection/gemini.js
8837
8972
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
8838
- import path16 from "path";
8973
+ import path17 from "path";
8839
8974
  import os8 from "os";
8840
8975
  function renderMarkdown2(source) {
8841
8976
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
@@ -8860,7 +8995,7 @@ function createGeminiEmitter() {
8860
8995
  destinations(scope, projectRoot) {
8861
8996
  if (scope === "user") {
8862
8997
  return [{
8863
- path: path16.join(os8.homedir(), ".gemini/GEMINI.md"),
8998
+ path: path17.join(os8.homedir(), ".gemini/GEMINI.md"),
8864
8999
  shared: true,
8865
9000
  format: "markdown"
8866
9001
  }];
@@ -8868,7 +9003,7 @@ function createGeminiEmitter() {
8868
9003
  if (!projectRoot)
8869
9004
  return [];
8870
9005
  return [{
8871
- path: path16.join(projectRoot, "GEMINI.md"),
9006
+ path: path17.join(projectRoot, "GEMINI.md"),
8872
9007
  shared: true,
8873
9008
  format: "markdown"
8874
9009
  }];
@@ -8889,7 +9024,7 @@ ${existing ?? ""}
8889
9024
  ${generated}`
8890
9025
  };
8891
9026
  }
8892
- await mkdir3(path16.dirname(dest.path), { recursive: true });
9027
+ await mkdir3(path17.dirname(dest.path), { recursive: true });
8893
9028
  await writeFile3(dest.path, generated, "utf-8");
8894
9029
  return {
8895
9030
  written: true,
@@ -8907,7 +9042,7 @@ var init_gemini2 = __esm({
8907
9042
 
8908
9043
  // packages/agents/dist/projection/opencode.js
8909
9044
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
8910
- import path17 from "path";
9045
+ import path18 from "path";
8911
9046
  import os9 from "os";
8912
9047
  function renderMarkdown3(source) {
8913
9048
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
@@ -8932,7 +9067,7 @@ function createOpencodeEmitter() {
8932
9067
  destinations(scope, projectRoot) {
8933
9068
  if (scope === "user") {
8934
9069
  return [{
8935
- path: path17.join(os9.homedir(), ".config", "opencode", "AGENTS.md"),
9070
+ path: path18.join(os9.homedir(), ".config", "opencode", "AGENTS.md"),
8936
9071
  shared: true,
8937
9072
  format: "markdown"
8938
9073
  }];
@@ -8940,7 +9075,7 @@ function createOpencodeEmitter() {
8940
9075
  if (!projectRoot)
8941
9076
  return [];
8942
9077
  return [{
8943
- path: path17.join(projectRoot, "AGENTS.md"),
9078
+ path: path18.join(projectRoot, "AGENTS.md"),
8944
9079
  shared: true,
8945
9080
  format: "markdown"
8946
9081
  }];
@@ -8961,7 +9096,7 @@ ${existing ?? ""}
8961
9096
  ${generated}`
8962
9097
  };
8963
9098
  }
8964
- await mkdir4(path17.dirname(dest.path), { recursive: true });
9099
+ await mkdir4(path18.dirname(dest.path), { recursive: true });
8965
9100
  await writeFile4(dest.path, generated, "utf-8");
8966
9101
  return {
8967
9102
  written: true,
@@ -9820,7 +9955,7 @@ var init_sse_bridge = __esm({
9820
9955
 
9821
9956
  // packages/agents/dist/interactive/claude.js
9822
9957
  import { execSync as execSync7 } from "child_process";
9823
- import { readFileSync as readFileSync13 } from "fs";
9958
+ import { readFileSync as readFileSync12 } from "fs";
9824
9959
  import { homedir as homedir14 } from "os";
9825
9960
  import { join as join19 } from "path";
9826
9961
  function probeClaudeSettingsFlag() {
@@ -9884,7 +10019,7 @@ function deriveTranscriptPath(sessionId, cwd) {
9884
10019
  }
9885
10020
  function readLastAssistantText(transcriptPath) {
9886
10021
  try {
9887
- const raw = readFileSync13(transcriptPath, "utf-8");
10022
+ const raw = readFileSync12(transcriptPath, "utf-8");
9888
10023
  const lines = raw.split(/\r?\n/);
9889
10024
  for (let i = lines.length - 1; i >= 0; i--) {
9890
10025
  const line = lines[i].trim();
@@ -9926,8 +10061,8 @@ function resolveLastAssistantText(payload) {
9926
10061
  const derived = deriveTranscriptPath(p?.session_id, cwd);
9927
10062
  if (derived)
9928
10063
  candidates.push(derived);
9929
- for (const path34 of candidates) {
9930
- const text = readLastAssistantText(path34);
10064
+ for (const path35 of candidates) {
10065
+ const text = readLastAssistantText(path35);
9931
10066
  if (text != null)
9932
10067
  return text;
9933
10068
  }
@@ -10454,9 +10589,9 @@ async function runRuntimeSend(arg1, arg2, opts, confirmOpts) {
10454
10589
  const resolved = resolveTarget(registry, config, target, !!opts.command);
10455
10590
  await needRef(resolved);
10456
10591
  const finalProject = opts.command ? config.commandName : target;
10457
- const { join: join31, dirname: dirname10 } = await import("path");
10592
+ const { join: join31, dirname: dirname9 } = await import("path");
10458
10593
  const { DEFAULT_CONFIG_PATH: DEFAULT_CONFIG_PATH2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
10459
- const stateRoot = join31(dirname10(DEFAULT_CONFIG_PATH2), "state");
10594
+ const stateRoot = join31(dirname9(DEFAULT_CONFIG_PATH2), "state");
10460
10595
  const seq = await appendCaptainMessage2({
10461
10596
  stateRoot,
10462
10597
  project: finalProject,
@@ -10562,9 +10697,9 @@ var init_runtime2 = __esm({
10562
10697
  init_dist();
10563
10698
  init_dist2();
10564
10699
  import { Command as Command35 } from "commander";
10565
- import { readFileSync as readFileSync16, existsSync as existsSync13, writeFileSync as writeFileSync12 } from "fs";
10700
+ import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
10566
10701
  import { fileURLToPath as fileURLToPath6 } from "url";
10567
- import { dirname as dirname9, join as join30 } from "path";
10702
+ import { dirname as dirname8, join as join30 } from "path";
10568
10703
  import { homedir as homedir22 } from "os";
10569
10704
 
10570
10705
  // packages/cli/src/commands/doctor.ts
@@ -10574,9 +10709,9 @@ init_dist();
10574
10709
  init_dist3();
10575
10710
  import { Command } from "commander";
10576
10711
  import { execSync as execSync8 } from "child_process";
10577
- import fs17 from "fs";
10712
+ import fs15 from "fs";
10578
10713
  import { stat } from "fs/promises";
10579
- import path18 from "path";
10714
+ import path19 from "path";
10580
10715
  import chalk3 from "chalk";
10581
10716
 
10582
10717
  // packages/cli/src/commands/health-view.ts
@@ -10679,7 +10814,7 @@ function settingsHaveAgentTeams() {
10679
10814
  try {
10680
10815
  const home = process.env.HOME || "";
10681
10816
  const settings = JSON.parse(
10682
- fs17.readFileSync(`${home}/.claude/settings.json`, "utf-8")
10817
+ fs15.readFileSync(`${home}/.claude/settings.json`, "utf-8")
10683
10818
  );
10684
10819
  return settings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1";
10685
10820
  } catch {
@@ -10690,7 +10825,7 @@ function pluginInstalled(pluginKey) {
10690
10825
  try {
10691
10826
  const home = process.env.HOME || "";
10692
10827
  const plugins = JSON.parse(
10693
- fs17.readFileSync(
10828
+ fs15.readFileSync(
10694
10829
  `${home}/.claude/plugins/installed_plugins.json`,
10695
10830
  "utf-8"
10696
10831
  )
@@ -10712,6 +10847,43 @@ function tryGetVersion(cmd) {
10712
10847
  return "";
10713
10848
  }
10714
10849
  }
10850
+ function candidateGlobalInstalls(roots) {
10851
+ const out = [];
10852
+ if (roots.npm) out.push({ manager: "npm", packageJsonPath: path19.join(roots.npm, "squadrant", "package.json") });
10853
+ if (roots.pnpm) out.push({ manager: "pnpm", packageJsonPath: path19.join(roots.pnpm, "squadrant", "package.json") });
10854
+ if (roots.yarn) out.push({ manager: "yarn", packageJsonPath: path19.join(roots.yarn, "node_modules", "squadrant", "package.json") });
10855
+ return out;
10856
+ }
10857
+ function findInstalledSquadrants(candidates, readVersion) {
10858
+ const out = [];
10859
+ for (const c of candidates) {
10860
+ const version = readVersion(c.packageJsonPath);
10861
+ if (version) out.push({ manager: c.manager, packageJsonPath: c.packageJsonPath, version });
10862
+ }
10863
+ return out;
10864
+ }
10865
+ function formatDuplicateInstallWarning(installs) {
10866
+ if (installs.length <= 1) return null;
10867
+ const lines = installs.map((i) => ` ${i.manager}: ${i.packageJsonPath} (v${i.version})`);
10868
+ return `Multiple squadrant installs detected:
10869
+ ${lines.join("\n")}
10870
+ Two installs fight over the daemon plist (#670) \u2014 uninstall the one you don't use, then run \`squadrant heal daemon\`.`;
10871
+ }
10872
+ function readPackageVersion(packageJsonPath) {
10873
+ try {
10874
+ const pkg2 = JSON.parse(fs15.readFileSync(packageJsonPath, "utf-8"));
10875
+ return typeof pkg2.version === "string" ? pkg2.version : null;
10876
+ } catch {
10877
+ return null;
10878
+ }
10879
+ }
10880
+ function tryGetGlobalRoot(cmd, args) {
10881
+ try {
10882
+ return execSync8(`${cmd} ${args.join(" ")}`, { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim() || void 0;
10883
+ } catch {
10884
+ return void 0;
10885
+ }
10886
+ }
10715
10887
  function check(label, pass, hint) {
10716
10888
  const icon = pass ? chalk3.green("\u2714 PASS") : chalk3.red("\u2718 FAIL");
10717
10889
  console.log(` ${icon} ${label}`);
@@ -10735,7 +10907,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
10735
10907
  ));
10736
10908
  results.push(check(
10737
10909
  "Obsidian installed",
10738
- commandExists("obsidian") || fs17.existsSync("/Applications/Obsidian.app"),
10910
+ commandExists("obsidian") || fs15.existsSync("/Applications/Obsidian.app"),
10739
10911
  "Install from: https://obsidian.md"
10740
10912
  ));
10741
10913
  results.push(check(
@@ -10829,7 +11001,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
10829
11001
  const emitter = projectionRegistry.get(name);
10830
11002
  const [userDest] = emitter.destinations("user");
10831
11003
  if (!userDest) continue;
10832
- const dir = path18.dirname(userDest.path);
11004
+ const dir = path19.dirname(userDest.path);
10833
11005
  let status;
10834
11006
  try {
10835
11007
  await stat(dir);
@@ -10842,7 +11014,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
10842
11014
  results.push(
10843
11015
  check(
10844
11016
  "Squadrant config exists",
10845
- fs17.existsSync(
11017
+ fs15.existsSync(
10846
11018
  process.env.SQUADRANT_CONFIG || `${process.env.HOME}/.config/squadrant/config.json`
10847
11019
  ),
10848
11020
  "Run: squadrant init"
@@ -10855,6 +11027,16 @@ var doctorCommand = new Command("doctor").description("Check system health and p
10855
11027
  ${passed === total ? chalk3.green("All checks passed") : chalk3.yellow(`${passed}/${total} checks passed`)}
10856
11028
  `
10857
11029
  );
11030
+ const installCandidates = candidateGlobalInstalls({
11031
+ npm: tryGetGlobalRoot("npm", ["root", "-g"]),
11032
+ pnpm: tryGetGlobalRoot("pnpm", ["root", "-g"]),
11033
+ yarn: tryGetGlobalRoot("yarn", ["global", "dir"])
11034
+ });
11035
+ const duplicateWarning = formatDuplicateInstallWarning(findInstalledSquadrants(installCandidates, readPackageVersion));
11036
+ if (duplicateWarning) {
11037
+ console.log(`
11038
+ ${chalk3.yellow("\u26A0 WARN")} ${duplicateWarning}`);
11039
+ }
10858
11040
  printServiceHealth(await queryHealth());
10859
11041
  console.log(chalk3.bold("\nTool Version Compat\n"));
10860
11042
  const toolVersionMap = {
@@ -10916,16 +11098,16 @@ init_dist();
10916
11098
  init_dist3();
10917
11099
  init_dist();
10918
11100
  import { Command as Command2 } from "commander";
10919
- import fs18 from "fs";
10920
- import path19 from "path";
11101
+ import fs16 from "fs";
11102
+ import path20 from "path";
10921
11103
  import os10 from "os";
10922
11104
  import readline from "readline";
10923
11105
  import chalk4 from "chalk";
10924
11106
 
10925
11107
  // packages/cli/src/lib/per-crew-settings.ts
10926
11108
  init_dist4();
10927
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
10928
- import { dirname as dirname5, join as join20 } from "path";
11109
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
11110
+ import { dirname as dirname4, join as join20 } from "path";
10929
11111
  import { homedir as homedir15 } from "os";
10930
11112
  var CREW_PERMISSION_ALLOWLIST = [
10931
11113
  // git — read + safe mutations (reset/clean/config intentionally excluded)
@@ -11018,28 +11200,28 @@ function mergeCrewPermissions(settings) {
11018
11200
  }
11019
11201
  function writePerCrewSettingsLocal(o) {
11020
11202
  const dir = join20(o.projectCwd, ".claude");
11021
- mkdirSync8(dir, { recursive: true });
11203
+ mkdirSync7(dir, { recursive: true });
11022
11204
  const file = join20(dir, "settings.local.json");
11023
11205
  let existing = {};
11024
11206
  try {
11025
- const raw = healStaleCockpitRefs(readFileSync14(file, "utf-8"));
11207
+ const raw = healStaleCockpitRefs(readFileSync13(file, "utf-8"));
11026
11208
  existing = JSON.parse(raw);
11027
11209
  } catch {
11028
11210
  }
11029
11211
  const withHooks = mergeClaudeHooks(existing, o.hookCmd ?? "squadrant crew _hook");
11030
11212
  const merged = mergeCrewPermissions(withHooks);
11031
- writeFileSync10(file, JSON.stringify(merged, null, 2));
11213
+ writeFileSync9(file, JSON.stringify(merged, null, 2));
11032
11214
  return file;
11033
11215
  }
11034
11216
  var DEFAULT_GLOBAL_OPENCODE_CONFIG_PATH = join20(homedir15(), ".config", "opencode", "opencode.json");
11035
11217
  function ensureGlobalOpencodeConfig(configPath = DEFAULT_GLOBAL_OPENCODE_CONFIG_PATH) {
11036
- mkdirSync8(dirname5(configPath), { recursive: true });
11218
+ mkdirSync7(dirname4(configPath), { recursive: true });
11037
11219
  const defaultConfig = {
11038
11220
  $schema: "https://opencode.ai/config.json",
11039
11221
  model: "anthropic/claude-sonnet-4-5"
11040
11222
  };
11041
11223
  try {
11042
- writeFileSync10(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", { flag: "wx" });
11224
+ writeFileSync9(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", { flag: "wx" });
11043
11225
  return configPath;
11044
11226
  } catch (err) {
11045
11227
  if (err.code === "EEXIST") return null;
@@ -11048,7 +11230,7 @@ function ensureGlobalOpencodeConfig(configPath = DEFAULT_GLOBAL_OPENCODE_CONFIG_
11048
11230
  }
11049
11231
  function writePerCrewOpencodeConfig(o) {
11050
11232
  const dir = join20(o.stateRoot, o.project, o.taskId);
11051
- mkdirSync8(dir, { recursive: true });
11233
+ mkdirSync7(dir, { recursive: true });
11052
11234
  const file = join20(dir, "opencode.json");
11053
11235
  const config = {
11054
11236
  permission: {
@@ -11064,29 +11246,29 @@ function writePerCrewOpencodeConfig(o) {
11064
11246
  external_directory: { "**": "allow" }
11065
11247
  }
11066
11248
  };
11067
- writeFileSync10(file, JSON.stringify(config, null, 2));
11249
+ writeFileSync9(file, JSON.stringify(config, null, 2));
11068
11250
  return file;
11069
11251
  }
11070
11252
 
11071
11253
  // packages/cli/src/commands/init.ts
11072
11254
  init_dist4();
11073
11255
  function findPackageRoot() {
11074
- let dir = path19.dirname(new URL(import.meta.url).pathname);
11256
+ let dir = path20.dirname(new URL(import.meta.url).pathname);
11075
11257
  while (dir !== "/") {
11076
- if (fs18.existsSync(path19.join(dir, "package.json"))) return dir;
11077
- dir = path19.dirname(dir);
11258
+ if (fs16.existsSync(path20.join(dir, "package.json"))) return dir;
11259
+ dir = path20.dirname(dir);
11078
11260
  }
11079
11261
  return process.cwd();
11080
11262
  }
11081
11263
  function copyDirRecursive(src, dest) {
11082
- fs18.mkdirSync(dest, { recursive: true });
11083
- for (const entry of fs18.readdirSync(src, { withFileTypes: true })) {
11084
- const srcPath = path19.join(src, entry.name);
11085
- const destPath = path19.join(dest, entry.name);
11264
+ fs16.mkdirSync(dest, { recursive: true });
11265
+ for (const entry of fs16.readdirSync(src, { withFileTypes: true })) {
11266
+ const srcPath = path20.join(src, entry.name);
11267
+ const destPath = path20.join(dest, entry.name);
11086
11268
  if (entry.isDirectory()) {
11087
11269
  copyDirRecursive(srcPath, destPath);
11088
11270
  } else {
11089
- fs18.copyFileSync(srcPath, destPath);
11271
+ fs16.copyFileSync(srcPath, destPath);
11090
11272
  }
11091
11273
  }
11092
11274
  }
@@ -11106,7 +11288,7 @@ function promptLine(question) {
11106
11288
  var initCommand = new Command2("init").description("Guided first-time setup: hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").action(async (opts) => {
11107
11289
  const hubPath = resolveHome(opts.hub);
11108
11290
  const pkgRoot = findPackageRoot();
11109
- const configDir = path19.join(os10.homedir(), ".config", "squadrant");
11291
+ const configDir = path20.join(os10.homedir(), ".config", "squadrant");
11110
11292
  const isTTY = process.stdin.isTTY === true;
11111
11293
  console.log(chalk4.bold("\nSquadrant Init\n"));
11112
11294
  if (!isTTY) {
@@ -11132,8 +11314,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
11132
11314
  }
11133
11315
  const wsRegistry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
11134
11316
  try {
11135
- if (fs18.existsSync(DEFAULT_CONFIG_PATH)) {
11136
- const existing = JSON.parse(fs18.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
11317
+ if (fs16.existsSync(DEFAULT_CONFIG_PATH)) {
11318
+ const existing = JSON.parse(fs16.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
11137
11319
  wsRegistry.get(existing.workspace ?? "obsidian");
11138
11320
  }
11139
11321
  } catch (err) {
@@ -11141,7 +11323,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
11141
11323
  return;
11142
11324
  }
11143
11325
  stepHeader(1, 5, "Hub vault");
11144
- if (fs18.existsSync(DEFAULT_CONFIG_PATH)) {
11326
+ if (fs16.existsSync(DEFAULT_CONFIG_PATH)) {
11145
11327
  console.log(chalk4.yellow(" \u26A0 Config already exists, skipping creation"));
11146
11328
  } else {
11147
11329
  const config = getDefaultConfig();
@@ -11149,37 +11331,37 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
11149
11331
  saveConfig(config);
11150
11332
  console.log(chalk4.green(` \u2714 Config created at ${DEFAULT_CONFIG_PATH}`));
11151
11333
  }
11152
- const hubTemplate = path19.join(pkgRoot, "obsidian", "hub");
11153
- if (fs18.existsSync(hubPath)) {
11334
+ const hubTemplate = path20.join(pkgRoot, "obsidian", "hub");
11335
+ if (fs16.existsSync(hubPath)) {
11154
11336
  console.log(chalk4.yellow(` \u26A0 Hub vault already exists at ${hubPath}`));
11155
- } else if (fs18.existsSync(hubTemplate)) {
11337
+ } else if (fs16.existsSync(hubTemplate)) {
11156
11338
  copyDirRecursive(hubTemplate, hubPath);
11157
11339
  console.log(chalk4.green(` \u2714 Hub vault scaffolded at ${hubPath}`));
11158
11340
  } else {
11159
- fs18.mkdirSync(hubPath, { recursive: true });
11341
+ fs16.mkdirSync(hubPath, { recursive: true });
11160
11342
  console.log(chalk4.yellow(` \u26A0 Hub template not found; created empty directory at ${hubPath}`));
11161
11343
  }
11162
- const hubDashboardSrc = path19.join(pkgRoot, "obsidian", "hub", "dashboard.md");
11163
- const hubDashboardDest = path19.join(hubPath, "dashboard.md");
11164
- if (fs18.existsSync(hubDashboardSrc)) {
11165
- fs18.copyFileSync(hubDashboardSrc, hubDashboardDest);
11344
+ const hubDashboardSrc = path20.join(pkgRoot, "obsidian", "hub", "dashboard.md");
11345
+ const hubDashboardDest = path20.join(hubPath, "dashboard.md");
11346
+ if (fs16.existsSync(hubDashboardSrc)) {
11347
+ fs16.copyFileSync(hubDashboardSrc, hubDashboardDest);
11166
11348
  console.log(chalk4.green(` \u2714 Dashboard refreshed`));
11167
11349
  }
11168
- fs18.mkdirSync(path19.join(hubPath, "projects"), { recursive: true });
11350
+ fs16.mkdirSync(path20.join(hubPath, "projects"), { recursive: true });
11169
11351
  ensureRuntimeSynced({ sourceRoot: pkgRoot, runtimeRoot: configDir });
11170
11352
  console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
11171
11353
  stepHeader(2, 5, "Agent + projection setup");
11172
- const settingsPath = path19.join(os10.homedir(), ".claude", "settings.json");
11354
+ const settingsPath = path20.join(os10.homedir(), ".claude", "settings.json");
11173
11355
  try {
11174
11356
  let settings = {};
11175
- if (fs18.existsSync(settingsPath)) {
11176
- settings = JSON.parse(fs18.readFileSync(settingsPath, "utf-8"));
11357
+ if (fs16.existsSync(settingsPath)) {
11358
+ settings = JSON.parse(fs16.readFileSync(settingsPath, "utf-8"));
11177
11359
  }
11178
11360
  const env = settings.env || {};
11179
11361
  if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS !== "1") {
11180
11362
  settings.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
11181
- fs18.mkdirSync(path19.dirname(settingsPath), { recursive: true });
11182
- fs18.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
11363
+ fs16.mkdirSync(path20.dirname(settingsPath), { recursive: true });
11364
+ fs16.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
11183
11365
  console.log(chalk4.green(" \u2714 Agent Teams enabled in ~/.claude/settings.json"));
11184
11366
  } else {
11185
11367
  console.log(chalk4.green(" \u2714 Agent Teams already enabled"));
@@ -11231,7 +11413,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
11231
11413
  chalk4.cyan(" Absolute path to your first project (Enter to skip): ")
11232
11414
  );
11233
11415
  if (projectPath) {
11234
- const projectName = path19.basename(projectPath);
11416
+ const projectName = path20.basename(projectPath);
11235
11417
  console.log(chalk4.bold(`
11236
11418
  Run this to register it:`));
11237
11419
  console.log(chalk4.cyan(` squadrant projects add ${projectName} ${projectPath}
@@ -11259,8 +11441,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: hub
11259
11441
  init_dist();
11260
11442
  init_dist2();
11261
11443
  import { Command as Command3 } from "commander";
11262
- import fs19 from "fs";
11263
- import path20 from "path";
11444
+ import fs17 from "fs";
11445
+ import path21 from "path";
11264
11446
  import chalk5 from "chalk";
11265
11447
  function restartAfterProjectsAdd(opts) {
11266
11448
  const doRestart = opts.doRestart ?? restartDaemonIfRunning;
@@ -11272,22 +11454,22 @@ function restartAfterProjectsAdd(opts) {
11272
11454
  }
11273
11455
  }
11274
11456
  function findPackageRoot2() {
11275
- let dir = path20.dirname(new URL(import.meta.url).pathname);
11457
+ let dir = path21.dirname(new URL(import.meta.url).pathname);
11276
11458
  while (dir !== "/") {
11277
- if (fs19.existsSync(path20.join(dir, "package.json"))) return dir;
11278
- dir = path20.dirname(dir);
11459
+ if (fs17.existsSync(path21.join(dir, "package.json"))) return dir;
11460
+ dir = path21.dirname(dir);
11279
11461
  }
11280
11462
  return process.cwd();
11281
11463
  }
11282
11464
  function copyDirRecursive2(src, dest) {
11283
- fs19.mkdirSync(dest, { recursive: true });
11284
- for (const entry of fs19.readdirSync(src, { withFileTypes: true })) {
11285
- const srcPath = path20.join(src, entry.name);
11286
- const destPath = path20.join(dest, entry.name);
11465
+ fs17.mkdirSync(dest, { recursive: true });
11466
+ for (const entry of fs17.readdirSync(src, { withFileTypes: true })) {
11467
+ const srcPath = path21.join(src, entry.name);
11468
+ const destPath = path21.join(dest, entry.name);
11287
11469
  if (entry.isDirectory()) {
11288
11470
  copyDirRecursive2(srcPath, destPath);
11289
11471
  } else {
11290
- fs19.copyFileSync(srcPath, destPath);
11472
+ fs17.copyFileSync(srcPath, destPath);
11291
11473
  }
11292
11474
  }
11293
11475
  }
@@ -11323,7 +11505,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
11323
11505
  process.exit(1);
11324
11506
  }
11325
11507
  const resolvedPath = resolveHome(projectPath);
11326
- if (!fs19.existsSync(path20.join(resolvedPath, ".git"))) {
11508
+ if (!fs17.existsSync(path21.join(resolvedPath, ".git"))) {
11327
11509
  console.log(chalk5.yellow(`
11328
11510
  \u26A0 No .git found at ${resolvedPath}. Make sure this is the project root, not a parent directory.
11329
11511
  `));
@@ -11376,7 +11558,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
11376
11558
  \u26A0 Group '${group}' already has '${primary[0]}' as primary. Overriding.`));
11377
11559
  }
11378
11560
  }
11379
- const spokeVault = opts.spoke ? resolveHome(opts.spoke) : path20.join(config.hubVault, "spokes", name);
11561
+ const spokeVault = opts.spoke ? resolveHome(opts.spoke) : path21.join(config.hubVault, "spokes", name);
11380
11562
  const project = {
11381
11563
  path: resolvedPath,
11382
11564
  captainName,
@@ -11391,20 +11573,20 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
11391
11573
  \u2714 Project '${name}' registered`));
11392
11574
  restartAfterProjectsAdd({ noRestart: opts.restart === false });
11393
11575
  const pkgRoot = findPackageRoot2();
11394
- const spokeTemplate = path20.join(pkgRoot, "obsidian", "spoke");
11395
- if (fs19.existsSync(spokeVault)) {
11576
+ const spokeTemplate = path21.join(pkgRoot, "obsidian", "spoke");
11577
+ if (fs17.existsSync(spokeVault)) {
11396
11578
  console.log(chalk5.yellow(` \u26A0 Spoke vault already exists at ${spokeVault}, skipping scaffold`));
11397
- } else if (fs19.existsSync(spokeTemplate)) {
11579
+ } else if (fs17.existsSync(spokeTemplate)) {
11398
11580
  copyDirRecursive2(spokeTemplate, spokeVault);
11399
- const statusPath = path20.join(spokeVault, "status.md");
11400
- if (fs19.existsSync(statusPath)) {
11401
- const content = fs19.readFileSync(statusPath, "utf-8");
11581
+ const statusPath = path21.join(spokeVault, "status.md");
11582
+ if (fs17.existsSync(statusPath)) {
11583
+ const content = fs17.readFileSync(statusPath, "utf-8");
11402
11584
  const updated = content.replace(/^project: unnamed/m, `project: ${name}`);
11403
- fs19.writeFileSync(statusPath, updated);
11585
+ fs17.writeFileSync(statusPath, updated);
11404
11586
  }
11405
11587
  console.log(chalk5.green(` \u2714 Spoke vault scaffolded at ${spokeVault}`));
11406
11588
  } else {
11407
- fs19.mkdirSync(spokeVault, { recursive: true });
11589
+ fs17.mkdirSync(spokeVault, { recursive: true });
11408
11590
  console.log(chalk5.yellow(` \u26A0 Spoke template not found; created empty dir at ${spokeVault}`));
11409
11591
  }
11410
11592
  console.log("");
@@ -11473,10 +11655,10 @@ init_dist4();
11473
11655
  init_dist();
11474
11656
  import { Command as Command5 } from "commander";
11475
11657
  import { execSync as execSync9 } from "child_process";
11476
- import path21 from "path";
11658
+ import path22 from "path";
11477
11659
  import os11 from "os";
11478
11660
  import chalk7 from "chalk";
11479
- var TEMPLATES_DIR2 = path21.join(os11.homedir(), ".config", "squadrant", "templates");
11661
+ var TEMPLATES_DIR2 = path22.join(os11.homedir(), ".config", "squadrant", "templates");
11480
11662
  var TASK_PROMPTS = {
11481
11663
  briefing: "Run your daily briefing using the squadrant:command-ops skill. Read all spoke handoffs, yesterday's logs, current status; produce a concise cross-project briefing; save to {hubVault}/daily-logs/YYYY-MM-DD.md; then exit.",
11482
11664
  "learnings-review": "Run a learnings review using the squadrant:command-ops skill. Scan {spokeVault}/learnings across all projects, identify cross-project patterns, propose captured-skill or fix actions, and exit when done.",
@@ -11509,7 +11691,7 @@ async function runCommandSpawn(input) {
11509
11691
  if (!agent) {
11510
11692
  throw new Error(`Unknown agent '${agentName}'. Known: claude, codex, gemini, opencode.`);
11511
11693
  }
11512
- const promptFile = path21.join(TEMPLATES_DIR2, `command.${agent.templateSuffix}.md`);
11694
+ const promptFile = path22.join(TEMPLATES_DIR2, `command.${agent.templateSuffix}.md`);
11513
11695
  const cliCommand = agent.buildCommand({
11514
11696
  prompt,
11515
11697
  workdir: process.cwd(),
@@ -11551,7 +11733,7 @@ import { randomUUID as randomUUID4 } from "crypto";
11551
11733
  import { execFileSync as execFileSync6 } from "child_process";
11552
11734
  import { homedir as homedir18 } from "os";
11553
11735
  import { join as join23 } from "path";
11554
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
11736
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "fs";
11555
11737
 
11556
11738
  // packages/cli/src/commands/crew-output.ts
11557
11739
  function tailLines(text, maxLines = 40, maxBytes = 4096) {
@@ -12010,9 +12192,9 @@ function buildSignalRequest(signal, o) {
12010
12192
  }
12011
12193
  function defaultWriteResult(id, payload) {
12012
12194
  const dir = join23(homedir18(), ".config", "squadrant", "state", "_results");
12013
- mkdirSync9(dir, { recursive: true });
12195
+ mkdirSync8(dir, { recursive: true });
12014
12196
  const file = join23(dir, `${id}.txt`);
12015
- writeFileSync11(file, payload);
12197
+ writeFileSync10(file, payload);
12016
12198
  return file;
12017
12199
  }
12018
12200
  async function runCrewSignal(signal, o, deps) {
@@ -12628,11 +12810,11 @@ init_dist3();
12628
12810
  init_dist();
12629
12811
  init_dist2();
12630
12812
  import { Command as Command12 } from "commander";
12631
- import fs20 from "fs";
12632
- import path22 from "path";
12813
+ import fs18 from "fs";
12814
+ import path23 from "path";
12633
12815
  import os12 from "os";
12634
12816
  import chalk12 from "chalk";
12635
- var TEMPLATES_DIR3 = path22.join(os12.homedir(), ".config", "squadrant", "templates");
12817
+ var TEMPLATES_DIR3 = path23.join(os12.homedir(), ".config", "squadrant", "templates");
12636
12818
  async function runSideSpawn2(input) {
12637
12819
  const config = loadConfig();
12638
12820
  const proj = config.projects[input.project];
@@ -12656,7 +12838,7 @@ async function runSideSpawn2(input) {
12656
12838
  throw new Error(`Unknown agent '${agentName}'. Known: claude, codex, gemini, opencode.`);
12657
12839
  }
12658
12840
  const sideModel = sideRole?.model;
12659
- const promptFile = path22.join(
12841
+ const promptFile = path23.join(
12660
12842
  TEMPLATES_DIR3,
12661
12843
  `side.${input.role}.${agent.templateSuffix}.md`
12662
12844
  );
@@ -12664,7 +12846,7 @@ async function runSideSpawn2(input) {
12664
12846
  prompt: input.topic,
12665
12847
  workdir: spawnCwd,
12666
12848
  role: "side",
12667
- promptFile: fs20.existsSync(promptFile) ? promptFile : void 0,
12849
+ promptFile: fs18.existsSync(promptFile) ? promptFile : void 0,
12668
12850
  interactive: true,
12669
12851
  permissionMode: config.defaults.permissions?.crew ?? "auto",
12670
12852
  ...sideModel ? { model: sideModel } : {}
@@ -12928,8 +13110,8 @@ function renderDashboard(rows, opts) {
12928
13110
 
12929
13111
  // packages/web/dist/sync-hub.js
12930
13112
  init_dist();
12931
- import fs21 from "fs";
12932
- import path23 from "path";
13113
+ import fs19 from "fs";
13114
+ import path24 from "path";
12933
13115
  function buildMirrorMarkdown(s) {
12934
13116
  const fenced = "```";
12935
13117
  return [
@@ -12955,15 +13137,15 @@ function buildMirrorMarkdown(s) {
12955
13137
  function syncHub(deps) {
12956
13138
  if (!deps.config.hubVault)
12957
13139
  return [];
12958
- const writeFile5 = deps.writeFile ?? ((p, c) => fs21.writeFileSync(p, c));
12959
- const mkdir5 = deps.mkdir ?? ((p) => fs21.mkdirSync(p, { recursive: true }));
12960
- const projectsDir = path23.join(resolveHome(deps.config.hubVault), "projects");
13140
+ const writeFile5 = deps.writeFile ?? ((p, c) => fs19.writeFileSync(p, c));
13141
+ const mkdir5 = deps.mkdir ?? ((p) => fs19.mkdirSync(p, { recursive: true }));
13142
+ const projectsDir = path24.join(resolveHome(deps.config.hubVault), "projects");
12961
13143
  mkdir5(projectsDir);
12962
13144
  const out = [];
12963
13145
  for (const s of deps.statuses) {
12964
13146
  if (s.state === "unknown")
12965
13147
  continue;
12966
- const hubPath = path23.join(projectsDir, `${s.project}.md`);
13148
+ const hubPath = path24.join(projectsDir, `${s.project}.md`);
12967
13149
  try {
12968
13150
  writeFile5(hubPath, buildMirrorMarkdown(s));
12969
13151
  out.push({ project: s.project, hubPath });
@@ -12987,7 +13169,7 @@ init_dist();
12987
13169
  init_dist();
12988
13170
  import { join as join24 } from "path";
12989
13171
  import { homedir as homedir19 } from "os";
12990
- import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
13172
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
12991
13173
  import { execFile as execFile4 } from "child_process";
12992
13174
  var DEFAULT_TIMEOUT_MS = 2e3;
12993
13175
  var AGENT_CLIS = ["claude", "codex", "gemini", "opencode"];
@@ -13097,7 +13279,7 @@ function onPath(cli) {
13097
13279
  return dirs.some((d) => existsSync12(join24(d, cli)));
13098
13280
  }
13099
13281
  function readSessionsHashes() {
13100
- const raw = JSON.parse(readFileSync15(SESSIONS_PATH, "utf-8"));
13282
+ const raw = JSON.parse(readFileSync14(SESSIONS_PATH, "utf-8"));
13101
13283
  const hashes = Object.values(raw.workspaces ?? {}).map((w) => w.templateHash).filter((h) => typeof h === "string" && h.length > 0);
13102
13284
  return [...new Set(hashes)];
13103
13285
  }
@@ -14088,8 +14270,8 @@ init_dist3();
14088
14270
  init_dist2();
14089
14271
  import { Command as Command14 } from "commander";
14090
14272
  import { execSync as execSync11 } from "child_process";
14091
- import fs22 from "fs";
14092
- import path24 from "path";
14273
+ import fs20 from "fs";
14274
+ import path25 from "path";
14093
14275
  import os13 from "os";
14094
14276
  import chalk15 from "chalk";
14095
14277
 
@@ -14168,8 +14350,8 @@ async function selectCaptainsInteractive(entries, yesterday = getYesterday()) {
14168
14350
  // packages/cli/src/commands/launch.ts
14169
14351
  init_dist2();
14170
14352
  var CMUX_APP = "/Applications/cmux.app";
14171
- var TEMPLATES_DIR4 = path24.join(os13.homedir(), ".config", "squadrant", "templates");
14172
- var SESSIONS_PATH2 = path24.join(os13.homedir(), ".config", "squadrant", "sessions.json");
14353
+ var TEMPLATES_DIR4 = path25.join(os13.homedir(), ".config", "squadrant", "templates");
14354
+ var SESSIONS_PATH2 = path25.join(os13.homedir(), ".config", "squadrant", "sessions.json");
14173
14355
  function ensureCmuxReady(headless) {
14174
14356
  if (headless || isInsideCmux()) return;
14175
14357
  console.log(chalk15.yellow("\n Not running inside cmux. Opening cmux app...\n"));
@@ -14241,12 +14423,12 @@ var launchCommand = new Command14("launch").description(
14241
14423
  }
14242
14424
  if (opts.all) {
14243
14425
  const hubPath = resolveHome(config.hubVault);
14244
- fs22.mkdirSync(hubPath, { recursive: true });
14426
+ fs20.mkdirSync(hubPath, { recursive: true });
14245
14427
  console.log(chalk15.bold("\nLaunching all captain workspaces\n"));
14246
14428
  for (const [name, proj] of Object.entries(config.projects)) {
14247
14429
  const projPath = resolveHome(proj.path);
14248
14430
  const spokePath = resolveHome(proj.spokeVault);
14249
- if (!fs22.existsSync(spokePath)) {
14431
+ if (!fs20.existsSync(spokePath)) {
14250
14432
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
14251
14433
  await ensureSpokeLayout(spokeDriver);
14252
14434
  console.log(chalk15.cyan(` \u2714 Created spoke vault at ${spokePath}`));
@@ -14284,7 +14466,7 @@ Launching ${selected.length} captain workspace(s) in parallel
14284
14466
  const proj = config.projects[name];
14285
14467
  const projPath = resolveHome(proj.path);
14286
14468
  const spokePath = resolveHome(proj.spokeVault);
14287
- if (!fs22.existsSync(spokePath)) {
14469
+ if (!fs20.existsSync(spokePath)) {
14288
14470
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
14289
14471
  await ensureSpokeLayout(spokeDriver);
14290
14472
  console.log(chalk15.cyan(` \u2714 Created spoke vault at ${spokePath}`));
@@ -14308,7 +14490,7 @@ Launching ${selected.length} captain workspace(s) in parallel
14308
14490
  const proj = config.projects[project];
14309
14491
  const projPath = resolveHome(proj.path);
14310
14492
  const spokePath = resolveHome(proj.spokeVault);
14311
- if (!fs22.existsSync(spokePath)) {
14493
+ if (!fs20.existsSync(spokePath)) {
14312
14494
  const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
14313
14495
  await ensureSpokeLayout(spokeDriver);
14314
14496
  console.log(chalk15.cyan(` \u2714 Created spoke vault at ${spokePath}`));
@@ -14444,24 +14626,24 @@ Shutting down captain workspace for '${project}'...
14444
14626
  // packages/cli/src/commands/feedback.ts
14445
14627
  init_dist();
14446
14628
  import { Command as Command16 } from "commander";
14447
- import fs23 from "fs";
14629
+ import fs21 from "fs";
14448
14630
  import os14 from "os";
14449
- import path25 from "path";
14631
+ import path26 from "path";
14450
14632
  import { fileURLToPath as fileURLToPath3 } from "url";
14451
14633
  import { execSync as execSync12 } from "child_process";
14452
14634
  import chalk17 from "chalk";
14453
14635
  var REPO_URL = "https://github.com/tu11aa/squadrant";
14454
14636
  function readPkgVersion() {
14455
14637
  try {
14456
- const pkgPath = path25.join(path25.dirname(fileURLToPath3(import.meta.url)), "..", "package.json");
14457
- return JSON.parse(fs23.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
14638
+ const pkgPath = path26.join(path26.dirname(fileURLToPath3(import.meta.url)), "..", "package.json");
14639
+ return JSON.parse(fs21.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
14458
14640
  } catch {
14459
14641
  return "unknown";
14460
14642
  }
14461
14643
  }
14462
14644
  function readMetrics(metricsPath) {
14463
14645
  try {
14464
- return JSON.parse(fs23.readFileSync(metricsPath, "utf-8"));
14646
+ return JSON.parse(fs21.readFileSync(metricsPath, "utf-8"));
14465
14647
  } catch {
14466
14648
  return {};
14467
14649
  }
@@ -14499,7 +14681,7 @@ function buildIssueUrl(metrics, squadrantVersion) {
14499
14681
  }
14500
14682
  var feedbackCommand = new Command16("feedback").description("Open a pre-filled GitHub issue for feedback or bug reports").action(() => {
14501
14683
  const config = loadConfig();
14502
- const metricsPath = config.metrics?.path || path25.join(os14.homedir(), ".config", "squadrant", "metrics.json");
14684
+ const metricsPath = config.metrics?.path || path26.join(os14.homedir(), ".config", "squadrant", "metrics.json");
14503
14685
  const metrics = readMetrics(metricsPath);
14504
14686
  const version = readStamp(config) ?? readPkgVersion();
14505
14687
  const issueUrl = buildIssueUrl(metrics, version);
@@ -14799,9 +14981,9 @@ workspaceCommand.command("read").description("Print the contents of a scope-rela
14799
14981
  const config = loadConfig();
14800
14982
  const registry = buildRegistry2();
14801
14983
  try {
14802
- const { projectTarget, path: path34 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
14984
+ const { projectTarget, path: path35 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
14803
14985
  const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
14804
- const content = await driver.read(path34);
14986
+ const content = await driver.read(path35);
14805
14987
  process.stdout.write(content);
14806
14988
  } catch (err) {
14807
14989
  console.error(chalk20.red(err.message));
@@ -14813,26 +14995,26 @@ workspaceCommand.command("write").description("Write content to a scope-relative
14813
14995
  const registry = buildRegistry2();
14814
14996
  try {
14815
14997
  let projectTarget;
14816
- let path34;
14998
+ let path35;
14817
14999
  let rawContent;
14818
15000
  if (opts.hub) {
14819
15001
  if (arg3 !== void 0) {
14820
15002
  throw new Error("With --hub, pass only the path and content");
14821
15003
  }
14822
15004
  projectTarget = void 0;
14823
- path34 = arg1;
15005
+ path35 = arg1;
14824
15006
  rawContent = arg2;
14825
15007
  } else {
14826
15008
  if (arg3 === void 0) {
14827
15009
  throw new Error("Missing content \u2014 usage: <project> <path> <content>");
14828
15010
  }
14829
15011
  projectTarget = arg1;
14830
- path34 = arg2;
15012
+ path35 = arg2;
14831
15013
  rawContent = arg3;
14832
15014
  }
14833
15015
  const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
14834
15016
  const payload = rawContent === "-" ? await readStdin() : rawContent;
14835
- await driver.write(path34, payload);
15017
+ await driver.write(path35, payload);
14836
15018
  } catch (err) {
14837
15019
  console.error(chalk20.red(err.message));
14838
15020
  process.exit(1);
@@ -14842,9 +15024,9 @@ workspaceCommand.command("list").description("List entries in a scope-relative d
14842
15024
  const config = loadConfig();
14843
15025
  const registry = buildRegistry2();
14844
15026
  try {
14845
- const { projectTarget, path: path34 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
15027
+ const { projectTarget, path: path35 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
14846
15028
  const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
14847
- const entries = await driver.list(path34);
15029
+ const entries = await driver.list(path35);
14848
15030
  for (const entry of entries) console.log(entry);
14849
15031
  } catch (err) {
14850
15032
  console.error(chalk20.red(err.message));
@@ -14855,9 +15037,9 @@ workspaceCommand.command("exists").description("Exit 0 if path exists, 1 if not"
14855
15037
  const config = loadConfig();
14856
15038
  const registry = buildRegistry2();
14857
15039
  try {
14858
- const { projectTarget, path: path34 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
15040
+ const { projectTarget, path: path35 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
14859
15041
  const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
14860
- const ok2 = await driver.exists(path34);
15042
+ const ok2 = await driver.exists(path35);
14861
15043
  process.exit(ok2 ? 0 : 1);
14862
15044
  } catch (err) {
14863
15045
  console.error(chalk20.red(err.message));
@@ -14868,9 +15050,9 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
14868
15050
  const config = loadConfig();
14869
15051
  const registry = buildRegistry2();
14870
15052
  try {
14871
- const { projectTarget, path: path34 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
15053
+ const { projectTarget, path: path35 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
14872
15054
  const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
14873
- await driver.mkdir(path34);
15055
+ await driver.mkdir(path35);
14874
15056
  } catch (err) {
14875
15057
  console.error(chalk20.red(err.message));
14876
15058
  process.exit(1);
@@ -14909,8 +15091,8 @@ init_dist3();
14909
15091
  init_dist();
14910
15092
  import { Command as Command21 } from "commander";
14911
15093
  import chalk22 from "chalk";
14912
- import fs24 from "fs";
14913
- import path26 from "path";
15094
+ import fs22 from "fs";
15095
+ import path27 from "path";
14914
15096
  import { fileURLToPath as fileURLToPath4 } from "url";
14915
15097
  function parseScope(v) {
14916
15098
  if (v !== "user" && v !== "project") {
@@ -14919,10 +15101,10 @@ function parseScope(v) {
14919
15101
  return v;
14920
15102
  }
14921
15103
  function findPackageRoot3() {
14922
- let dir = path26.dirname(fileURLToPath4(import.meta.url));
15104
+ let dir = path27.dirname(fileURLToPath4(import.meta.url));
14923
15105
  while (dir !== "/" && dir !== "") {
14924
- if (fs24.existsSync(path26.join(dir, "package.json"))) return dir;
14925
- dir = path26.dirname(dir);
15106
+ if (fs22.existsSync(path27.join(dir, "package.json"))) return dir;
15107
+ dir = path27.dirname(dir);
14926
15108
  }
14927
15109
  return process.cwd();
14928
15110
  }
@@ -15117,12 +15299,12 @@ init_dist();
15117
15299
  init_dist();
15118
15300
  init_dist2();
15119
15301
  import { Command as Command23 } from "commander";
15120
- import fs25 from "fs";
15302
+ import fs23 from "fs";
15121
15303
  import { fileURLToPath as fileURLToPath5 } from "url";
15122
- import { dirname as dirname6, join as join26 } from "path";
15304
+ import { dirname as dirname5, join as join26 } from "path";
15123
15305
  import chalk23 from "chalk";
15124
15306
  function runConfigCheck(opts) {
15125
- const raw = JSON.parse(fs25.readFileSync(opts.configPath, "utf-8"));
15307
+ const raw = JSON.parse(fs23.readFileSync(opts.configPath, "utf-8"));
15126
15308
  const def = getDefaultConfig();
15127
15309
  const items = detectDrift(raw, def);
15128
15310
  let working = raw;
@@ -15139,7 +15321,7 @@ function runConfigCheck(opts) {
15139
15321
  stamped = true;
15140
15322
  }
15141
15323
  if (opts.fix || opts.accept || stamped) {
15142
- fs25.writeFileSync(opts.configPath, JSON.stringify(working, null, 2) + "\n");
15324
+ writeConfigFileSync(opts.configPath, JSON.stringify(working, null, 2) + "\n");
15143
15325
  }
15144
15326
  return { items, applied, remaining, stamped };
15145
15327
  }
@@ -15210,7 +15392,7 @@ function printItems(items) {
15210
15392
  var configCommand = new Command23("config").description("Inspect and reconcile squadrant config");
15211
15393
  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) => {
15212
15394
  const pkgVersion = readPkgVersion2();
15213
- if (!fs25.existsSync(DEFAULT_CONFIG_PATH)) {
15395
+ if (!fs23.existsSync(DEFAULT_CONFIG_PATH)) {
15214
15396
  console.log(chalk23.yellow("No config found \u2014 run `squadrant init` first."));
15215
15397
  return;
15216
15398
  }
@@ -15255,8 +15437,8 @@ configCommand.command("set").description("Write a config value by dotted key (e.
15255
15437
  }
15256
15438
  });
15257
15439
  function readPkgVersion2() {
15258
- const pkgPath = join26(dirname6(fileURLToPath5(import.meta.url)), "..", "package.json");
15259
- return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
15440
+ const pkgPath = join26(dirname5(fileURLToPath5(import.meta.url)), "..", "package.json");
15441
+ return JSON.parse(fs23.readFileSync(pkgPath, "utf-8")).version;
15260
15442
  }
15261
15443
 
15262
15444
  // packages/cli/src/commands/heal.ts
@@ -15280,6 +15462,15 @@ function buildHealStatus(components) {
15280
15462
  }
15281
15463
  async function runHealStatus(opts) {
15282
15464
  const { project, json, stdout, stderr } = opts;
15465
+ const isDaemonAlive = opts.isDaemonAlive ?? (() => isDaemonSocketLive(SOCK));
15466
+ if (!await isDaemonAlive()) {
15467
+ if (json) {
15468
+ stdout.write(JSON.stringify({ healthy: false, daemonUnreachable: true, components: [] }) + "\n");
15469
+ } else {
15470
+ stderr.write("daemon unreachable \u2014 start the daemon first (squadrant heal daemon)\n");
15471
+ }
15472
+ return 1;
15473
+ }
15283
15474
  let rows;
15284
15475
  try {
15285
15476
  rows = await opts.queryHealth(project);
@@ -15411,7 +15602,7 @@ init_dist();
15411
15602
  init_dist2();
15412
15603
  init_runtime2();
15413
15604
  init_require_daemon();
15414
- import { join as join27, dirname as dirname7 } from "path";
15605
+ import { join as join27, dirname as dirname6 } from "path";
15415
15606
  import { Command as Command27 } from "commander";
15416
15607
  import chalk27 from "chalk";
15417
15608
  async function runPing(project, message) {
@@ -15420,7 +15611,7 @@ async function runPing(project, message) {
15420
15611
  const resolved = resolveTarget(registry, config, project, false);
15421
15612
  await requireDaemon();
15422
15613
  await needRef(resolved);
15423
- const stateRoot = join27(dirname7(DEFAULT_CONFIG_PATH), "state");
15614
+ const stateRoot = join27(dirname6(DEFAULT_CONFIG_PATH), "state");
15424
15615
  await appendCaptainMessage({
15425
15616
  stateRoot,
15426
15617
  project,
@@ -15497,8 +15688,8 @@ var cmuxCommand = new Command28("cmux").description("cmux integration helpers").
15497
15688
  // packages/cli/src/commands/effort.ts
15498
15689
  init_dist();
15499
15690
  init_dist2();
15500
- import fs26 from "fs";
15501
- import path27 from "path";
15691
+ import fs24 from "fs";
15692
+ import path28 from "path";
15502
15693
  import { Command as Command29 } from "commander";
15503
15694
  import chalk29 from "chalk";
15504
15695
  var VALID_EFFORTS = ["max", "balance", "low"];
@@ -15541,9 +15732,9 @@ function effortScopeLabel(projectName) {
15541
15732
  }
15542
15733
  function canonical(p) {
15543
15734
  try {
15544
- return fs26.realpathSync(p);
15735
+ return fs24.realpathSync(p);
15545
15736
  } catch {
15546
- return path27.resolve(p);
15737
+ return path28.resolve(p);
15547
15738
  }
15548
15739
  }
15549
15740
  async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(), append, scopeProject, projectConfigRoot) {
@@ -15593,7 +15784,7 @@ var effortCommand = new Command29("effort").description("Get or set the crew tok
15593
15784
  const config = loadConfig();
15594
15785
  const registry = new RuntimeRegistry2({ cmux: createCmuxDriver2() });
15595
15786
  const driver = registry.global(config);
15596
- const stateRoot = path27.join(path27.dirname(DEFAULT_CONFIG_PATH), "state");
15787
+ const stateRoot = path28.join(path28.dirname(DEFAULT_CONFIG_PATH), "state");
15597
15788
  const append = (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" });
15598
15789
  await notifyCaptainsOfEffort(effort, config, driver, process.cwd(), append, options.project);
15599
15790
  } catch {
@@ -15603,13 +15794,13 @@ var effortCommand = new Command29("effort").description("Get or set the crew tok
15603
15794
 
15604
15795
  // packages/cli/src/commands/tokens.ts
15605
15796
  init_dist();
15606
- import fs27 from "fs";
15607
- import path28 from "path";
15797
+ import fs25 from "fs";
15798
+ import path29 from "path";
15608
15799
  import os15 from "os";
15609
15800
  import readline3 from "readline";
15610
15801
  import { Command as Command30 } from "commander";
15611
15802
  import chalk30 from "chalk";
15612
- var CLAUDE_PROJECTS_DIR = path28.join(os15.homedir(), ".claude", "projects");
15803
+ var CLAUDE_PROJECTS_DIR = path29.join(os15.homedir(), ".claude", "projects");
15613
15804
  function parseTranscriptLine(rawLine) {
15614
15805
  const line = rawLine.trim();
15615
15806
  if (!line) return { timestamp: null, usage: null };
@@ -15659,7 +15850,7 @@ function foldTranscriptLine(agg, rawLine, state) {
15659
15850
  async function aggregateTranscriptFile(filePath) {
15660
15851
  const agg = emptySessionAggregate();
15661
15852
  const state = { lastCacheRead: null };
15662
- const rl = readline3.createInterface({ input: fs27.createReadStream(filePath), crlfDelay: Infinity });
15853
+ const rl = readline3.createInterface({ input: fs25.createReadStream(filePath), crlfDelay: Infinity });
15663
15854
  for await (const line of rl) {
15664
15855
  foldTranscriptLine(agg, line, state);
15665
15856
  }
@@ -15727,22 +15918,22 @@ function buildRoleReport(role, sessions) {
15727
15918
  }
15728
15919
  async function readdirSafe(dir) {
15729
15920
  try {
15730
- return await fs27.promises.readdir(dir);
15921
+ return await fs25.promises.readdir(dir);
15731
15922
  } catch {
15732
15923
  return [];
15733
15924
  }
15734
15925
  }
15735
15926
  async function listJsonlFiles(dir) {
15736
15927
  const entries = await readdirSafe(dir);
15737
- return entries.filter((e) => e.endsWith(".jsonl")).map((e) => path28.join(dir, e));
15928
+ return entries.filter((e) => e.endsWith(".jsonl")).map((e) => path29.join(dir, e));
15738
15929
  }
15739
15930
  async function findTranscriptDirs(claudeProjectsDir, captainSlug) {
15740
15931
  const entries = await readdirSafe(claudeProjectsDir);
15741
15932
  const captainDirs = [];
15742
15933
  const crewDirs = [];
15743
15934
  for (const entry of entries) {
15744
- if (entry === captainSlug) captainDirs.push(path28.join(claudeProjectsDir, entry));
15745
- else if (isCrewDirName(entry, captainSlug)) crewDirs.push(path28.join(claudeProjectsDir, entry));
15935
+ if (entry === captainSlug) captainDirs.push(path29.join(claudeProjectsDir, entry));
15936
+ else if (isCrewDirName(entry, captainSlug)) crewDirs.push(path29.join(claudeProjectsDir, entry));
15746
15937
  }
15747
15938
  return { captainDirs, crewDirs };
15748
15939
  }
@@ -15894,12 +16085,12 @@ var tokensCommand = new Command30("tokens").description(
15894
16085
  // packages/cli/src/commands/telegram.ts
15895
16086
  init_dist();
15896
16087
  init_dist2();
15897
- import { join as join28, dirname as dirname8 } from "path";
16088
+ import { join as join28, dirname as dirname7 } from "path";
15898
16089
  import { emitKeypressEvents } from "readline";
15899
16090
  import { Command as Command31 } from "commander";
15900
16091
  import chalk31 from "chalk";
15901
16092
  function defaultStateRoot() {
15902
- return join28(dirname8(DEFAULT_CONFIG_PATH), "state");
16093
+ return join28(dirname7(DEFAULT_CONFIG_PATH), "state");
15903
16094
  }
15904
16095
  async function questionMasked() {
15905
16096
  return new Promise((resolve4) => {
@@ -16229,8 +16420,8 @@ import { join as join29 } from "path";
16229
16420
  import { homedir as homedir21 } from "os";
16230
16421
 
16231
16422
  // packages/cli/src/lib/captain-session-registry.ts
16232
- import fs28 from "fs";
16233
- import path29 from "path";
16423
+ import fs26 from "fs";
16424
+ import path30 from "path";
16234
16425
 
16235
16426
  // packages/cli/src/lib/handoff-facts.ts
16236
16427
  var STALE_FETCH_WARNING_MS = 24 * 60 * 60 * 1e3;
@@ -16281,15 +16472,15 @@ function assembleHandoffFacts(live, claudeMem, gapSessions, checkpoint, now, ext
16281
16472
  // packages/cli/src/lib/captain-session-registry.ts
16282
16473
  var CAPTAIN_SESSION_REGISTRY_FILE = "captain-sessions.jsonl";
16283
16474
  function appendCaptainSession(spokeVault, record) {
16284
- fs28.mkdirSync(spokeVault, { recursive: true });
16285
- const file = path29.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
16286
- fs28.appendFileSync(file, JSON.stringify(record) + "\n");
16475
+ fs26.mkdirSync(spokeVault, { recursive: true });
16476
+ const file = path30.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
16477
+ fs26.appendFileSync(file, JSON.stringify(record) + "\n");
16287
16478
  }
16288
16479
  function readCaptainSessionRegistry(spokeVault) {
16289
- const file = path29.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
16290
- if (!fs28.existsSync(file)) return [];
16480
+ const file = path30.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
16481
+ if (!fs26.existsSync(file)) return [];
16291
16482
  const records = [];
16292
- for (const line of fs28.readFileSync(file, "utf-8").split("\n")) {
16483
+ for (const line of fs26.readFileSync(file, "utf-8").split("\n")) {
16293
16484
  if (!line.trim()) continue;
16294
16485
  try {
16295
16486
  records.push(JSON.parse(line));
@@ -16392,13 +16583,13 @@ function hooksCommand() {
16392
16583
  // packages/cli/src/commands/work.ts
16393
16584
  init_dist();
16394
16585
  init_dist2();
16395
- import path30 from "path";
16586
+ import path31 from "path";
16396
16587
  import { Command as Command33 } from "commander";
16397
16588
  import chalk32 from "chalk";
16398
16589
  function detectCurrentProject(config, cwd = process.cwd()) {
16399
16590
  for (const [name, proj] of Object.entries(config.projects)) {
16400
16591
  const projPath = resolveHome(proj.path);
16401
- if (cwd === projPath || cwd.startsWith(projPath + path30.sep)) return name;
16592
+ if (cwd === projPath || cwd.startsWith(projPath + path31.sep)) return name;
16402
16593
  }
16403
16594
  return void 0;
16404
16595
  }
@@ -16530,14 +16721,14 @@ var workCommand = new Command33("work").description("Track your own in-flight wo
16530
16721
  // packages/cli/src/commands/handoff.ts
16531
16722
  init_dist();
16532
16723
  import { Command as Command34 } from "commander";
16533
- import path33 from "path";
16724
+ import path34 from "path";
16534
16725
  import os16 from "os";
16535
16726
 
16536
16727
  // packages/cli/src/lib/handoff-live-repo.ts
16537
16728
  init_dist();
16538
16729
  import { execFileSync as execFileSync8 } from "child_process";
16539
- import fs29 from "fs";
16540
- import path31 from "path";
16730
+ import fs27 from "fs";
16731
+ import path32 from "path";
16541
16732
 
16542
16733
  // packages/cli/src/lib/handoff-branch-state.ts
16543
16734
  function tryRun(runner, cmd, args, cwd) {
@@ -16676,7 +16867,7 @@ function localAheadOfBase(runner, projectPath, base) {
16676
16867
  }
16677
16868
  function readFetchAgeMs(projectPath, now) {
16678
16869
  try {
16679
- const stat2 = fs29.statSync(path31.join(projectPath, ".git", "FETCH_HEAD"));
16870
+ const stat2 = fs27.statSync(path32.join(projectPath, ".git", "FETCH_HEAD"));
16680
16871
  return Math.max(0, now - stat2.mtime.getTime());
16681
16872
  } catch {
16682
16873
  return null;
@@ -16747,7 +16938,7 @@ function gatherLiveRepoState(projectPath, fallbackBaseBranch, tasks, runner = de
16747
16938
 
16748
16939
  // packages/cli/src/lib/handoff-claude-mem.ts
16749
16940
  import { createRequire } from "module";
16750
- import fs30 from "fs";
16941
+ import fs28 from "fs";
16751
16942
  var { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
16752
16943
  var CLAUDE_MEM_RECENCY_LIMIT = 20;
16753
16944
  function decisionText(row) {
@@ -16761,7 +16952,7 @@ function decisionText(row) {
16761
16952
  return row.narrative ?? "";
16762
16953
  }
16763
16954
  function queryClaudeMem(dbPath, project) {
16764
- if (!fs30.existsSync(dbPath)) return null;
16955
+ if (!fs28.existsSync(dbPath)) return null;
16765
16956
  let db;
16766
16957
  try {
16767
16958
  db = new DatabaseSync(dbPath, { readOnly: true });
@@ -16804,7 +16995,7 @@ function queryClaudeMem(dbPath, project) {
16804
16995
  }
16805
16996
 
16806
16997
  // packages/cli/src/lib/handoff-transcript.ts
16807
- import fs31 from "fs";
16998
+ import fs29 from "fs";
16808
16999
  var TRANSCRIPT_BYTE_CAP = 2e5;
16809
17000
  function tailOf(content, byteCap) {
16810
17001
  const buf = Buffer.from(content, "utf-8");
@@ -16833,27 +17024,27 @@ function extractMessages(tailText) {
16833
17024
  return { lastUserMessage, lastAssistantText };
16834
17025
  }
16835
17026
  function extractTranscriptTail(transcriptPath, byteCap = TRANSCRIPT_BYTE_CAP) {
16836
- if (!fs31.existsSync(transcriptPath)) return null;
16837
- const content = fs31.readFileSync(transcriptPath, "utf-8");
17027
+ if (!fs29.existsSync(transcriptPath)) return null;
17028
+ const content = fs29.readFileSync(transcriptPath, "utf-8");
16838
17029
  const { lastUserMessage, lastAssistantText } = extractMessages(tailOf(content, byteCap));
16839
- const mtimeIso = fs31.statSync(transcriptPath).mtime.toISOString();
17030
+ const mtimeIso = fs29.statSync(transcriptPath).mtime.toISOString();
16840
17031
  return { path: transcriptPath, mtimeIso, lastUserMessage, lastAssistantText };
16841
17032
  }
16842
17033
 
16843
17034
  // packages/cli/src/lib/handoff-archive.ts
16844
- import fs32 from "fs";
16845
- import path32 from "path";
17035
+ import fs30 from "fs";
17036
+ import path33 from "path";
16846
17037
  function readNewestArchivedHandoff(spokeVault, now) {
16847
- const dir = path32.join(spokeVault, "handoffs");
16848
- if (!fs32.existsSync(dir)) return null;
16849
- const candidates = fs32.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
16850
- const full = path32.join(dir, e.name);
16851
- return { name: e.name, full, mtime: fs32.statSync(full).mtime };
17038
+ const dir = path33.join(spokeVault, "handoffs");
17039
+ if (!fs30.existsSync(dir)) return null;
17040
+ const candidates = fs30.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
17041
+ const full = path33.join(dir, e.name);
17042
+ return { name: e.name, full, mtime: fs30.statSync(full).mtime };
16852
17043
  }).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
16853
17044
  for (const candidate of candidates) {
16854
17045
  let content;
16855
17046
  try {
16856
- content = JSON.parse(fs32.readFileSync(candidate.full, "utf-8"));
17047
+ content = JSON.parse(fs30.readFileSync(candidate.full, "utf-8"));
16857
17048
  } catch {
16858
17049
  continue;
16859
17050
  }
@@ -16863,7 +17054,7 @@ function readNewestArchivedHandoff(spokeVault, now) {
16863
17054
  }
16864
17055
 
16865
17056
  // packages/cli/src/commands/handoff.ts
16866
- var CLAUDE_MEM_DB_PATH = path33.join(os16.homedir(), ".claude-mem", "claude-mem.db");
17057
+ var CLAUDE_MEM_DB_PATH = path34.join(os16.homedir(), ".claude-mem", "claude-mem.db");
16867
17058
  async function defaultFetchTasks(project) {
16868
17059
  return await squadrantdCall({ kind: "list", project });
16869
17060
  }
@@ -16922,8 +17113,8 @@ init_dist();
16922
17113
  init_dist();
16923
17114
  init_dist();
16924
17115
  init_dist();
16925
- var __dirname = dirname9(fileURLToPath6(import.meta.url));
16926
- var pkg = JSON.parse(readFileSync16(join30(__dirname, "..", "package.json"), "utf-8"));
17116
+ var __dirname = dirname8(fileURLToPath6(import.meta.url));
17117
+ var pkg = JSON.parse(readFileSync15(join30(__dirname, "..", "package.json"), "utf-8"));
16927
17118
  ensureRuntimeSynced({
16928
17119
  sourceRoot: join30(__dirname, ".."),
16929
17120
  runtimeRoot: join30(homedir22(), ".config", "squadrant")
@@ -16932,11 +17123,11 @@ if (process.argv[2] !== "config") {
16932
17123
  try {
16933
17124
  const cfgPath = join30(homedir22(), ".config", "squadrant", "config.json");
16934
17125
  if (existsSync13(cfgPath)) {
16935
- const cfg = JSON.parse(readFileSync16(cfgPath, "utf-8"));
17126
+ const cfg = JSON.parse(readConfigFileSync(cfgPath));
16936
17127
  if (needsCheck(cfg, pkg.version)) {
16937
17128
  const items = detectDrift(cfg, getDefaultConfig());
16938
17129
  if (items.length === 0) {
16939
- writeFileSync12(cfgPath, JSON.stringify(withStamp(cfg, pkg.version), null, 2) + "\n");
17130
+ writeConfigFileSync(cfgPath, JSON.stringify(withStamp(cfg, pkg.version), null, 2) + "\n");
16940
17131
  } else {
16941
17132
  const from = cfg._squadrantVersion ?? "an earlier version";
16942
17133
  process.stderr.write(