squadrant 0.11.1 → 0.11.3

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/README.md CHANGED
@@ -129,7 +129,7 @@ Six internal packages in a one-way dependency DAG. All are private (not publishe
129
129
  - `dist/index.js` — CLI bin (`squadrant` command), entry: `packages/cli/src/index.ts`
130
130
  - `dist/squadrantd.js` — daemon process, entry: `packages/cli/src/daemon-host.ts`
131
131
 
132
- See the [architecture diagram](docs/diagrams/2026-06-18-cockpit-monorepo-architecture.html) for a visual overview.
132
+ See the [architecture diagram](docs/diagrams/2026-06-18-squadrant-monorepo-architecture.html) for a visual overview.
133
133
 
134
134
  ## Architecture
135
135
 
package/dist/index.js CHANGED
@@ -428,13 +428,13 @@ var init_compat_manifest = __esm({
428
428
  "packages/shared/dist/lib/compat-manifest.js"() {
429
429
  compatManifest = {
430
430
  tools: {
431
- cmux: { min: "0.64.0", lastVerified: "0.64.16" },
431
+ cmux: { min: "0.64.0", lastVerified: "0.64.17" },
432
432
  claude: { min: "2.1.32" },
433
433
  node: { min: "18.0.0", lastVerified: "24.6.0" },
434
434
  // presence-checked; no floor enforced yet
435
435
  codex: { lastVerified: "0.139.0" },
436
436
  gemini: { lastVerified: "0.38.2" },
437
- opencode: { lastVerified: "1.17.4" }
437
+ opencode: { lastVerified: "1.17.9" }
438
438
  }
439
439
  };
440
440
  }
@@ -4902,7 +4902,8 @@ function healthRow(c, now) {
4902
4902
  function printServiceHealth(rows, now = Date.now()) {
4903
4903
  console.log(chalk2.bold("\nService Health\n"));
4904
4904
  if (rows == null) {
4905
- console.log(` ${chalk2.red("\u2718")} daemon unreachable \u2014 squadrant liveness is unknown (start the daemon)`);
4905
+ console.log(` ${chalk2.red("\u2718")} daemon unreachable \u2014 squadrant liveness is unknown`);
4906
+ console.log(` ${chalk2.cyan("\u2192")} ${chalk2.dim("Run: squadrant heal daemon")}`);
4906
4907
  return;
4907
4908
  }
4908
4909
  if (rows.length === 0) {
@@ -4983,29 +4984,61 @@ function tryGetVersion(cmd) {
4983
4984
  return "";
4984
4985
  }
4985
4986
  }
4986
- function check(label, pass) {
4987
+ function check(label, pass, hint) {
4987
4988
  const icon = pass ? chalk3.green("\u2714 PASS") : chalk3.red("\u2718 FAIL");
4988
4989
  console.log(` ${icon} ${label}`);
4990
+ if (!pass && hint) {
4991
+ console.log(` ${chalk3.cyan("\u2192")} ${chalk3.dim(hint)}`);
4992
+ }
4989
4993
  return pass;
4990
4994
  }
4991
4995
  var doctorCommand = new Command("doctor").description("Check system health and prerequisites").action(async () => {
4992
4996
  console.log(chalk3.bold("\nSquadrant Doctor\n"));
4993
4997
  const results = [];
4994
- results.push(check("Claude Code installed", commandExists("claude")));
4995
- results.push(check(`Claude Code version >= ${compatManifest.tools.claude.min}`, claudeVersionOk()));
4996
- results.push(check("Obsidian installed", commandExists("obsidian") || fs13.existsSync("/Applications/Obsidian.app")));
4997
- results.push(check("Node.js >= 18", nodeVersionOk()));
4998
+ results.push(check(
4999
+ "Claude Code installed",
5000
+ commandExists("claude"),
5001
+ "Install from: https://claude.ai/code"
5002
+ ));
5003
+ results.push(check(
5004
+ `Claude Code version >= ${compatManifest.tools.claude.min}`,
5005
+ claudeVersionOk(),
5006
+ `Update Claude Code to >= ${compatManifest.tools.claude.min}`
5007
+ ));
5008
+ results.push(check(
5009
+ "Obsidian installed",
5010
+ commandExists("obsidian") || fs13.existsSync("/Applications/Obsidian.app"),
5011
+ "Install from: https://obsidian.md"
5012
+ ));
5013
+ results.push(check(
5014
+ "Node.js >= 18",
5015
+ nodeVersionOk(),
5016
+ "Install from: https://nodejs.org"
5017
+ ));
4998
5018
  results.push(
4999
5019
  check(
5000
5020
  "Agent Teams enabled (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1)",
5001
- settingsHaveAgentTeams()
5021
+ settingsHaveAgentTeams(),
5022
+ "Run: squadrant init (enables automatically), or add to shell profile: export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"
5002
5023
  )
5003
5024
  );
5004
- results.push(check("Plugin: superpowers", pluginInstalled("superpowers@claude-plugins-official")));
5025
+ results.push(check(
5026
+ "Plugin: superpowers",
5027
+ pluginInstalled("superpowers@claude-plugins-official"),
5028
+ "In Claude Code, run: /plugin marketplace add superpowers"
5029
+ ));
5005
5030
  results.push(
5006
- check("Plugin: claude-mem", pluginInstalled("claude-mem@thedotmack"))
5031
+ check(
5032
+ "Plugin: claude-mem",
5033
+ pluginInstalled("claude-mem@thedotmack"),
5034
+ "In Claude Code, run: /plugin marketplace add thedotmack/claude-mem"
5035
+ )
5007
5036
  );
5008
- results.push(check("Plugin: context7", pluginInstalled("context7@claude-plugins-official")));
5037
+ results.push(check(
5038
+ "Plugin: context7",
5039
+ pluginInstalled("context7@claude-plugins-official"),
5040
+ "In Claude Code, run: /plugin marketplace add context7"
5041
+ ));
5009
5042
  const config = loadConfig();
5010
5043
  const runtimes = new RuntimeRegistry({ cmux: createCmuxDriver() });
5011
5044
  const probeResults = await runtimes.probeAll();
@@ -5013,7 +5046,8 @@ var doctorCommand = new Command("doctor").description("Check system health and p
5013
5046
  const globalProbe = probeResults[globalRuntimeName];
5014
5047
  results.push(check(
5015
5048
  `Runtime '${globalRuntimeName}' installed`,
5016
- !!globalProbe?.installed
5049
+ !!globalProbe?.installed,
5050
+ globalRuntimeName === "cmux" ? "Install: npm install -g cmux or download from https://cmux.dev" : void 0
5017
5051
  ));
5018
5052
  const overrides = /* @__PURE__ */ new Set();
5019
5053
  for (const proj of Object.values(config.projects)) {
@@ -5031,7 +5065,8 @@ var doctorCommand = new Command("doctor").description("Check system health and p
5031
5065
  const hubProbe = await hubDriver.probe();
5032
5066
  results.push(check(
5033
5067
  `Workspace '${config.workspace ?? "obsidian"}' \u2014 hub reachable`,
5034
- hubProbe.installed && hubProbe.rootExists
5068
+ hubProbe.installed && hubProbe.rootExists,
5069
+ "Run: squadrant init to scaffold the hub vault"
5035
5070
  ));
5036
5071
  for (const [name] of Object.entries(config.projects)) {
5037
5072
  const spokeDriver = workspaces.forProject(name, config);
@@ -5081,7 +5116,8 @@ var doctorCommand = new Command("doctor").description("Check system health and p
5081
5116
  "Squadrant config exists",
5082
5117
  fs13.existsSync(
5083
5118
  process.env.SQUADRANT_CONFIG || `${process.env.HOME}/.config/squadrant/config.json`
5084
- )
5119
+ ),
5120
+ "Run: squadrant init"
5085
5121
  )
5086
5122
  );
5087
5123
  const passed = results.filter(Boolean).length;
@@ -5151,10 +5187,12 @@ ${passed === total ? chalk3.green("All checks passed") : chalk3.yellow(`${passed
5151
5187
  init_dist();
5152
5188
  init_dist3();
5153
5189
  init_dist();
5190
+ init_dist4();
5154
5191
  import { Command as Command2 } from "commander";
5155
5192
  import fs14 from "fs";
5156
5193
  import path17 from "path";
5157
5194
  import os8 from "os";
5195
+ import readline from "readline";
5158
5196
  import chalk4 from "chalk";
5159
5197
  function findPackageRoot() {
5160
5198
  let dir = path17.dirname(new URL(import.meta.url).pathname);
@@ -5176,50 +5214,82 @@ function copyDirRecursive(src, dest) {
5176
5214
  }
5177
5215
  }
5178
5216
  }
5179
- var initCommand = new Command2("init").description("First-time setup: scaffold hub vault, scripts, and config").option("--hub <path>", "Hub vault path", "~/squadrant-hub").action((opts) => {
5217
+ function stepHeader(n, total, label) {
5218
+ console.log(chalk4.bold(`
5219
+ ${n}/${total} ${label}`));
5220
+ }
5221
+ function promptLine(question) {
5222
+ return new Promise((resolve3) => {
5223
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
5224
+ rl.question(question, (answer) => {
5225
+ rl.close();
5226
+ resolve3(answer.trim());
5227
+ });
5228
+ });
5229
+ }
5230
+ 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) => {
5180
5231
  const hubPath = resolveHome(opts.hub);
5181
5232
  const pkgRoot = findPackageRoot();
5182
5233
  const configDir = path17.join(os8.homedir(), ".config", "squadrant");
5234
+ const isTTY = process.stdin.isTTY === true;
5183
5235
  console.log(chalk4.bold("\nSquadrant Init\n"));
5184
- const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
5236
+ if (!isTTY) {
5237
+ console.log(" Run these steps to get started:\n");
5238
+ console.log(chalk4.bold(" 1/5 Hub vault"));
5239
+ console.log(chalk4.cyan(` squadrant init --hub ${opts.hub}`));
5240
+ console.log(chalk4.bold("\n 2/5 Agent + projection setup"));
5241
+ console.log(" (handled automatically by: " + chalk4.cyan("squadrant init") + ")");
5242
+ console.log(chalk4.bold("\n 3/5 Plugins \u2014 open Claude Code and run:"));
5243
+ console.log(chalk4.cyan(" /plugin marketplace add superpowers"));
5244
+ console.log(chalk4.cyan(" /plugin marketplace add thedotmack/claude-mem"));
5245
+ console.log(chalk4.cyan(" /plugin marketplace add context7"));
5246
+ console.log(chalk4.bold("\n 4/5 Register first project"));
5247
+ console.log(chalk4.cyan(" squadrant projects add <name> <path>"));
5248
+ console.log(chalk4.bold("\n 5/5 Telegram (optional)"));
5249
+ console.log(chalk4.cyan(" squadrant telegram setup"));
5250
+ console.log(chalk4.bold("\n Then:"));
5251
+ console.log(chalk4.cyan(" squadrant launch <projectname>\n"));
5252
+ return;
5253
+ }
5254
+ const wsRegistry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
5185
5255
  try {
5186
5256
  if (fs14.existsSync(DEFAULT_CONFIG_PATH)) {
5187
5257
  const existing = JSON.parse(fs14.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
5188
- const wsName = existing.workspace ?? "obsidian";
5189
- registry.get(wsName);
5258
+ wsRegistry.get(existing.workspace ?? "obsidian");
5190
5259
  }
5191
5260
  } catch (err) {
5192
5261
  console.log(chalk4.red(` \u2718 ${err.message}`));
5193
5262
  return;
5194
5263
  }
5264
+ stepHeader(1, 5, "Hub vault");
5195
5265
  if (fs14.existsSync(DEFAULT_CONFIG_PATH)) {
5196
- console.log(chalk4.yellow(" \u26A0 Config already exists, skipping creation"));
5266
+ console.log(chalk4.yellow(" \u26A0 Config already exists, skipping creation"));
5197
5267
  } else {
5198
5268
  const config = getDefaultConfig();
5199
5269
  config.hubVault = hubPath;
5200
5270
  saveConfig(config);
5201
- console.log(chalk4.green(` \u2714 Config created at ${DEFAULT_CONFIG_PATH}`));
5271
+ console.log(chalk4.green(` \u2714 Config created at ${DEFAULT_CONFIG_PATH}`));
5202
5272
  }
5203
5273
  const hubTemplate = path17.join(pkgRoot, "obsidian", "hub");
5204
5274
  if (fs14.existsSync(hubPath)) {
5205
- console.log(chalk4.yellow(` \u26A0 Hub vault already exists at ${hubPath}, skipping`));
5275
+ console.log(chalk4.yellow(` \u26A0 Hub vault already exists at ${hubPath}`));
5206
5276
  } else if (fs14.existsSync(hubTemplate)) {
5207
5277
  copyDirRecursive(hubTemplate, hubPath);
5208
- console.log(chalk4.green(` \u2714 Hub vault scaffolded at ${hubPath}`));
5278
+ console.log(chalk4.green(` \u2714 Hub vault scaffolded at ${hubPath}`));
5209
5279
  } else {
5210
5280
  fs14.mkdirSync(hubPath, { recursive: true });
5211
- console.log(chalk4.yellow(` \u26A0 Hub template not found; created empty dir at ${hubPath}`));
5281
+ console.log(chalk4.yellow(` \u26A0 Hub template not found; created empty directory at ${hubPath}`));
5212
5282
  }
5213
5283
  const hubDashboardSrc = path17.join(pkgRoot, "obsidian", "hub", "dashboard.md");
5214
5284
  const hubDashboardDest = path17.join(hubPath, "dashboard.md");
5215
5285
  if (fs14.existsSync(hubDashboardSrc)) {
5216
5286
  fs14.copyFileSync(hubDashboardSrc, hubDashboardDest);
5217
- console.log(chalk4.green(` \u2714 Dashboard page refreshed at ${hubDashboardDest}`));
5287
+ console.log(chalk4.green(` \u2714 Dashboard refreshed`));
5218
5288
  }
5219
- const projectsDir = path17.join(hubPath, "projects");
5220
- fs14.mkdirSync(projectsDir, { recursive: true });
5289
+ fs14.mkdirSync(path17.join(hubPath, "projects"), { recursive: true });
5221
5290
  ensureRuntimeSynced({ sourceRoot: pkgRoot, runtimeRoot: configDir });
5222
- console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
5291
+ console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
5292
+ stepHeader(2, 5, "Agent + projection setup");
5223
5293
  const settingsPath = path17.join(os8.homedir(), ".claude", "settings.json");
5224
5294
  try {
5225
5295
  let settings = {};
@@ -5231,29 +5301,70 @@ var initCommand = new Command2("init").description("First-time setup: scaffold h
5231
5301
  settings.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
5232
5302
  fs14.mkdirSync(path17.dirname(settingsPath), { recursive: true });
5233
5303
  fs14.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5234
- console.log(chalk4.green(" \u2714 Agent Teams enabled in ~/.claude/settings.json"));
5304
+ console.log(chalk4.green(" \u2714 Agent Teams enabled in ~/.claude/settings.json"));
5235
5305
  } else {
5236
- console.log(chalk4.green(" \u2714 Agent Teams already enabled"));
5306
+ console.log(chalk4.green(" \u2714 Agent Teams already enabled"));
5237
5307
  }
5238
5308
  } catch {
5239
- console.log(chalk4.yellow(" \u26A0 Could not update ~/.claude/settings.json"));
5309
+ console.log(chalk4.yellow(" \u26A0 Could not update ~/.claude/settings.json"));
5310
+ console.log(chalk4.dim(" Add manually to shell profile: export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1"));
5240
5311
  }
5241
- console.log(chalk4.bold("\nManual steps required:\n"));
5242
- console.log(" 1. Install plugins in Claude Code:");
5243
- console.log(chalk4.cyan(" /plugin marketplace add thedotmack/claude-mem"));
5244
- console.log(chalk4.cyan(" /plugin install claude-mem"));
5245
- console.log(" Install context7 and superpowers from /plugin marketplace");
5246
- console.log("");
5247
- console.log(" 2. Install cmux if not already installed:");
5248
- console.log(chalk4.cyan(" npm install -g cmux"));
5249
- console.log("");
5250
- console.log(" 3. Open Obsidian and add the hub vault:");
5251
- console.log(chalk4.cyan(` ${hubPath}`));
5252
- console.log("");
5253
- console.log(" 4. Run " + chalk4.cyan("squadrant doctor") + " to verify setup\n");
5254
- if (!fs14.existsSync("/Applications/cmux.app")) {
5255
- console.log(chalk4.yellow(" \u26A0 cmux not found \u2014 download from https://cmux.dev\n"));
5312
+ try {
5313
+ const projRegistry = new ProjectionRegistry({
5314
+ cursor: createCursorEmitter,
5315
+ codex: createCodexEmitter,
5316
+ gemini: createGeminiEmitter,
5317
+ opencode: createOpencodeEmitter
5318
+ });
5319
+ const workspace = createObsidianDriver({ root: process.cwd() });
5320
+ const source = await readUserLevelSource(workspace, { pkgRoot });
5321
+ for (const name of projRegistry.list()) {
5322
+ const emitter = projRegistry.get(name);
5323
+ for (const dest of emitter.destinations("user")) {
5324
+ const result = await emitter.emit(source, dest);
5325
+ if (result.written) {
5326
+ console.log(chalk4.green(` \u2714 ${name} \u2192 ${dest.path}`));
5327
+ } else {
5328
+ console.log(chalk4.dim(` - ${name} \u2192 ${dest.path} (unchanged)`));
5329
+ }
5330
+ }
5331
+ }
5332
+ } catch (err) {
5333
+ console.log(chalk4.yellow(` \u26A0 Projection emit skipped: ${err.message}`));
5334
+ }
5335
+ stepHeader(3, 5, "Plugins");
5336
+ console.log(" Install these plugins inside Claude Code:\n");
5337
+ console.log(chalk4.cyan(" /plugin marketplace add superpowers"));
5338
+ console.log(chalk4.cyan(" /plugin marketplace add thedotmack/claude-mem"));
5339
+ console.log(chalk4.cyan(" /plugin marketplace add context7\n"));
5340
+ console.log(chalk4.dim(" Squadrant never auto-installs plugins \u2014 open Claude Code and run the commands above."));
5341
+ stepHeader(4, 5, "Register first project");
5342
+ const projectPath = await promptLine(
5343
+ chalk4.cyan(" Absolute path to your first project (Enter to skip): ")
5344
+ );
5345
+ if (projectPath) {
5346
+ const projectName = path17.basename(projectPath);
5347
+ console.log(chalk4.bold(`
5348
+ Run this to register it:`));
5349
+ console.log(chalk4.cyan(` squadrant projects add ${projectName} ${projectPath}
5350
+ `));
5351
+ } else {
5352
+ console.log(chalk4.dim(" Skipped. Register later with:"));
5353
+ console.log(chalk4.cyan(" squadrant projects add <name> <path>"));
5256
5354
  }
5355
+ stepHeader(5, 5, "Telegram (optional)");
5356
+ console.log(" Get notified on your phone when crews complete tasks:");
5357
+ console.log(chalk4.cyan(" squadrant telegram setup\n"));
5358
+ let launchTarget = "<projectname>";
5359
+ try {
5360
+ const cfg = loadConfig();
5361
+ const first = Object.keys(cfg.projects)[0];
5362
+ if (first) launchTarget = first;
5363
+ } catch {
5364
+ }
5365
+ console.log(chalk4.bold.green("\n \u2714 You are ready!\n"));
5366
+ console.log(` Run: ${chalk4.cyan(`squadrant launch ${launchTarget}`)}
5367
+ `);
5257
5368
  });
5258
5369
 
5259
5370
  // packages/cli/src/commands/projects.ts
@@ -6226,6 +6337,9 @@ var CREW_PERMISSION_ALLOWLIST = [
6226
6337
  "Bash(sleep:*)",
6227
6338
  "Bash(printf:*)"
6228
6339
  ];
6340
+ function healStaleCockpitRefs(raw) {
6341
+ return raw.replaceAll("cockpit crew _hook", "squadrant crew _hook").replaceAll(".config/cockpit", ".config/squadrant").replaceAll("cockpit-hub", "squadrant-hub");
6342
+ }
6229
6343
  function mergeCrewPermissions(settings) {
6230
6344
  const next = structuredClone(settings ?? {});
6231
6345
  const permissions = next.permissions ??= {};
@@ -6239,7 +6353,7 @@ function writePerCrewSettingsLocal(o) {
6239
6353
  const file = join17(dir, "settings.local.json");
6240
6354
  let existing = {};
6241
6355
  try {
6242
- const raw = readFileSync9(file, "utf-8");
6356
+ const raw = healStaleCockpitRefs(readFileSync9(file, "utf-8"));
6243
6357
  existing = JSON.parse(raw);
6244
6358
  } catch {
6245
6359
  }