squadrant 0.26.0 → 0.26.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
@@ -10216,8 +10216,12 @@ var init_paths = __esm({
10216
10216
 
10217
10217
  // packages/core/dist/knowledge/store.js
10218
10218
  import fs15 from "fs";
10219
+ import os7 from "os";
10219
10220
  import path15 from "path";
10220
10221
  import matter2 from "gray-matter";
10222
+ function duplicateIdProblem(id, ignored, used) {
10223
+ return `duplicate id ${id}: ${ignored} is ignored, ${used} is used (live files win over _proposed, then path order). Fix: rename the id in one file, or delete or move one of them.`;
10224
+ }
10221
10225
  function splitStatement(body) {
10222
10226
  const first = body.trim().split(/\n\s*\n/)[0] ?? "";
10223
10227
  return first.replace(/\s+/g, " ").trim();
@@ -10262,7 +10266,7 @@ function loadRulesDir(dir, layer, opts = {}) {
10262
10266
  const fm = parsed.data;
10263
10267
  const prior = seen.get(fm.id);
10264
10268
  if (prior) {
10265
- errors.push({ file, problems: [`duplicate id ${fm.id} (also in ${prior})`] });
10269
+ errors.push({ file, problems: [duplicateIdProblem(fm.id, file, prior)] });
10266
10270
  continue;
10267
10271
  }
10268
10272
  seen.set(fm.id, file);
@@ -10270,7 +10274,65 @@ function loadRulesDir(dir, layer, opts = {}) {
10270
10274
  }
10271
10275
  return { rules, errors };
10272
10276
  }
10273
- var NO_JS_FRONTMATTER;
10277
+ function resolveSourceRef(ref, kbRoot, sources) {
10278
+ const bases = /* @__PURE__ */ new Set([kbRoot]);
10279
+ for (const s of sources) {
10280
+ let d = path15.resolve(kbRoot, expandHome(s.path.split(/[*?[{]/)[0]));
10281
+ for (let i = 0; i < 6 && d !== path15.dirname(d); i++) {
10282
+ d = path15.dirname(d);
10283
+ bases.add(d);
10284
+ }
10285
+ }
10286
+ const cands = [path15.resolve(kbRoot, expandHome(ref)), ...[...bases].map((b) => path15.join(b, ref))];
10287
+ return cands.find((c) => fs15.existsSync(c) && fs15.statSync(c).isFile());
10288
+ }
10289
+ function validateKb(opts) {
10290
+ const { rules, errors } = loadRulesDir(opts.rulesDir, "kb", { includeProposed: true });
10291
+ const checks = [];
10292
+ const dups = errors.filter((e) => e.problems.some((p) => p.startsWith("duplicate id")));
10293
+ const invalid = errors.filter((e) => !dups.includes(e));
10294
+ const proposed = rules.filter((r) => path15.relative(opts.rulesDir, r.file).split(path15.sep).includes(PROPOSED_DIR)).length;
10295
+ checks.push({ level: "ok", message: `${rules.length + errors.length} rule files read (${rules.length - proposed} live, ${proposed} proposed parsed OK)` });
10296
+ if (invalid.length)
10297
+ for (const e of invalid)
10298
+ checks.push({ level: "fail", message: `invalid ${e.file}: ${e.problems.join("; ")}` });
10299
+ else
10300
+ checks.push({ level: "ok", message: "no invalid rule files" });
10301
+ if (dups.length)
10302
+ for (const e of dups)
10303
+ checks.push({ level: "fail", message: e.problems.join("; ") });
10304
+ else
10305
+ checks.push({ level: "ok", message: "no duplicate ids" });
10306
+ const cache = /* @__PURE__ */ new Map();
10307
+ let verified = 0, unresolved = 0;
10308
+ for (const r of rules) {
10309
+ r.sources.forEach((src, i) => {
10310
+ const file = resolveSourceRef(src.ref, opts.kbRoot, opts.sources);
10311
+ if (!file) {
10312
+ unresolved++;
10313
+ return;
10314
+ }
10315
+ if (!cache.has(file))
10316
+ cache.set(file, fs15.readFileSync(file, "utf8"));
10317
+ if (cache.get(file).includes(src.quote))
10318
+ verified++;
10319
+ else
10320
+ checks.push({ level: "fail", message: `quote not found in ${file}: ${r.id} sources[${i}] (${r.file})` });
10321
+ });
10322
+ }
10323
+ if (!checks.some((c) => c.message.startsWith("quote not found")))
10324
+ checks.push({ level: "ok", message: `${verified} source quotes found in their source files` });
10325
+ if (unresolved)
10326
+ checks.push({ level: "skip", message: `${unresolved} source refs not resolvable on this machine, quote not checked` });
10327
+ if (opts.domains) {
10328
+ for (const r of rules) {
10329
+ if (!opts.domains.includes(r.domain))
10330
+ checks.push({ level: "warn", message: `${r.id}: domain '${r.domain}' not in allowed domains [${opts.domains.join(", ")}] (${r.file})` });
10331
+ }
10332
+ }
10333
+ return { checks, ok: !checks.some((c) => c.level === "fail") };
10334
+ }
10335
+ var NO_JS_FRONTMATTER, expandHome;
10274
10336
  var init_store2 = __esm({
10275
10337
  "packages/core/dist/knowledge/store.js"() {
10276
10338
  init_dist();
@@ -10280,6 +10342,7 @@ var init_store2 = __esm({
10280
10342
  throw new Error("only YAML front matter is allowed");
10281
10343
  } } }
10282
10344
  };
10345
+ expandHome = (p) => p === "~" || p.startsWith("~/") ? path15.join(os7.homedir(), p.slice(1)) : p;
10283
10346
  }
10284
10347
  });
10285
10348
 
@@ -10368,7 +10431,7 @@ var init_index_file = __esm({
10368
10431
 
10369
10432
  // packages/core/dist/knowledge/search.js
10370
10433
  function searchRules(rules, query, opts = {}) {
10371
- const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
10434
+ const terms = [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
10372
10435
  if (terms.length === 0)
10373
10436
  return [];
10374
10437
  const statuses = opts.statuses ?? DEFAULT_SEARCH_STATUSES;
@@ -10380,30 +10443,45 @@ function searchRules(rules, query, opts = {}) {
10380
10443
  const expanded = lower(rule2.triggers?.expanded);
10381
10444
  const when = (rule2.triggers?.when ?? "").toLowerCase();
10382
10445
  const statement = rule2.statement.toLowerCase();
10446
+ const rationale = rationaleOf(rule2);
10383
10447
  const id = rule2.id.toLowerCase();
10384
10448
  let score = 0;
10449
+ let matched = 0;
10450
+ let curated = false;
10385
10451
  for (const t of terms) {
10452
+ let s = 0;
10386
10453
  if (id.includes(t))
10387
- score += 3;
10454
+ s += 3;
10455
+ if (keywords.includes(t) || expanded.includes(t))
10456
+ curated = true;
10388
10457
  if (keywords.includes(t))
10389
- score += 3;
10458
+ s += 3;
10390
10459
  if (expanded.includes(t))
10391
- score += 2;
10460
+ s += 3;
10392
10461
  if (when.includes(t))
10393
- score += 2;
10462
+ s += 2;
10394
10463
  if (statement.includes(t))
10395
- score += 1;
10464
+ s += 2;
10465
+ if (rationale.includes(t))
10466
+ s += 1;
10467
+ if (s > 0) {
10468
+ matched++;
10469
+ score += s;
10470
+ }
10396
10471
  }
10397
10472
  if (score > 0)
10398
- hits.push({ rule: rule2, score });
10473
+ hits.push({ rule: rule2, score, matched, curated });
10399
10474
  }
10400
- return hits.sort((a, b) => b.score - a.score || a.rule.id.localeCompare(b.rule.id));
10475
+ const strong = terms.length >= 2 ? hits.filter((h) => h.matched >= 2 || h.curated) : hits;
10476
+ const out = strong.length ? strong : hits;
10477
+ return out.sort((a, b) => b.score - a.score || a.rule.id.localeCompare(b.rule.id));
10401
10478
  }
10402
- var DEFAULT_SEARCH_STATUSES, lower;
10479
+ var DEFAULT_SEARCH_STATUSES, lower, rationaleOf;
10403
10480
  var init_search = __esm({
10404
10481
  "packages/core/dist/knowledge/search.js"() {
10405
10482
  DEFAULT_SEARCH_STATUSES = ["active", "stale"];
10406
10483
  lower = (xs) => (xs ?? []).map((x) => x.toLowerCase());
10484
+ rationaleOf = (rule2) => rule2.body.slice(rule2.statement.length).toLowerCase();
10407
10485
  }
10408
10486
  });
10409
10487
 
@@ -13626,6 +13704,7 @@ __export(dist_exports2, {
13626
13704
  discoverCaptainSurface: () => discoverCaptainSurface,
13627
13705
  discoverLiveOpencodeServer: () => discoverLiveOpencodeServer,
13628
13706
  dispatchToSibling: () => dispatchToSibling,
13707
+ duplicateIdProblem: () => duplicateIdProblem,
13629
13708
  encodeFrame: () => encodeFrame,
13630
13709
  encodeMsg: () => encodeMsg,
13631
13710
  ensureDaemon: () => ensureDaemon,
@@ -13813,6 +13892,7 @@ __export(dist_exports2, {
13813
13892
  topicName: () => topicName,
13814
13893
  tryAcquireDaemonLock: () => tryAcquireDaemonLock,
13815
13894
  usageFromJson: () => usageFromJson,
13895
+ validateKb: () => validateKb,
13816
13896
  waitForCaptainDelivery: () => waitForCaptainDelivery,
13817
13897
  waitForWarmup: () => waitForWarmup,
13818
13898
  writeCaptainAddress: () => writeCaptainAddress,
@@ -14464,7 +14544,7 @@ var init_drivers = __esm({
14464
14544
  // packages/agents/dist/projection/cursor.js
14465
14545
  import { mkdir, readFile, writeFile } from "fs/promises";
14466
14546
  import path19 from "path";
14467
- import os7 from "os";
14547
+ import os8 from "os";
14468
14548
  function renderMdc(source) {
14469
14549
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
14470
14550
 
@@ -14512,7 +14592,7 @@ function createCursorEmitter() {
14512
14592
  if (scope === "user") {
14513
14593
  return [
14514
14594
  {
14515
- path: path19.join(os7.homedir(), ".cursor/rules/squadrant-global.mdc"),
14595
+ path: path19.join(os8.homedir(), ".cursor/rules/squadrant-global.mdc"),
14516
14596
  shared: false,
14517
14597
  format: "mdc"
14518
14598
  }
@@ -14591,7 +14671,7 @@ var init_marker = __esm({
14591
14671
  // packages/agents/dist/projection/codex.js
14592
14672
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
14593
14673
  import path20 from "path";
14594
- import os8 from "os";
14674
+ import os9 from "os";
14595
14675
  function renderMarkdown(source) {
14596
14676
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
14597
14677
 
@@ -14615,7 +14695,7 @@ function createCodexEmitter() {
14615
14695
  destinations(scope, projectRoot) {
14616
14696
  if (scope === "user") {
14617
14697
  return [{
14618
- path: path20.join(os8.homedir(), ".codex/AGENTS.md"),
14698
+ path: path20.join(os9.homedir(), ".codex/AGENTS.md"),
14619
14699
  shared: true,
14620
14700
  format: "markdown"
14621
14701
  }];
@@ -14663,7 +14743,7 @@ var init_codex2 = __esm({
14663
14743
  // packages/agents/dist/projection/gemini.js
14664
14744
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
14665
14745
  import path21 from "path";
14666
- import os9 from "os";
14746
+ import os10 from "os";
14667
14747
  function renderMarkdown2(source) {
14668
14748
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
14669
14749
 
@@ -14687,7 +14767,7 @@ function createGeminiEmitter() {
14687
14767
  destinations(scope, projectRoot) {
14688
14768
  if (scope === "user") {
14689
14769
  return [{
14690
- path: path21.join(os9.homedir(), ".gemini/GEMINI.md"),
14770
+ path: path21.join(os10.homedir(), ".gemini/GEMINI.md"),
14691
14771
  shared: true,
14692
14772
  format: "markdown"
14693
14773
  }];
@@ -14735,7 +14815,7 @@ var init_gemini2 = __esm({
14735
14815
  // packages/agents/dist/projection/opencode.js
14736
14816
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
14737
14817
  import path22 from "path";
14738
- import os10 from "os";
14818
+ import os11 from "os";
14739
14819
  function renderMarkdown3(source) {
14740
14820
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
14741
14821
 
@@ -14759,7 +14839,7 @@ function createOpencodeEmitter() {
14759
14839
  destinations(scope, projectRoot) {
14760
14840
  if (scope === "user") {
14761
14841
  return [{
14762
- path: path22.join(os10.homedir(), ".config", "opencode", "AGENTS.md"),
14842
+ path: path22.join(os11.homedir(), ".config", "opencode", "AGENTS.md"),
14763
14843
  shared: true,
14764
14844
  format: "markdown"
14765
14845
  }];
@@ -14806,10 +14886,10 @@ var init_opencode2 = __esm({
14806
14886
 
14807
14887
  // packages/agents/dist/projection/opencode-skills.js
14808
14888
  import fs21 from "fs";
14809
- import os11 from "os";
14889
+ import os12 from "os";
14810
14890
  import path23 from "path";
14811
14891
  function defaultOpencodeSkillsRoot(env = process.env) {
14812
- const configHome = env.XDG_CONFIG_HOME || path23.join(os11.homedir(), ".config");
14892
+ const configHome = env.XDG_CONFIG_HOME || path23.join(os12.homedir(), ".config");
14813
14893
  return path23.join(configHome, "opencode", "skills");
14814
14894
  }
14815
14895
  function parseSkillName(raw) {
@@ -14904,7 +14984,7 @@ var init_opencode_skills = __esm({
14904
14984
  // packages/agents/dist/projection/claude.js
14905
14985
  import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
14906
14986
  import path24 from "path";
14907
- import os12 from "os";
14987
+ import os13 from "os";
14908
14988
  function renderMarkdown4(source) {
14909
14989
  const skillSections = source.skills.map((s) => `## Skill: ${s.name}
14910
14990
 
@@ -14928,7 +15008,7 @@ function createClaudeEmitter() {
14928
15008
  destinations(scope, projectRoot) {
14929
15009
  if (scope === "user") {
14930
15010
  return [{
14931
- path: path24.join(os12.homedir(), ".claude/CLAUDE.md"),
15011
+ path: path24.join(os13.homedir(), ".claude/CLAUDE.md"),
14932
15012
  shared: true,
14933
15013
  format: "markdown"
14934
15014
  }];
@@ -16883,13 +16963,13 @@ var init_receipt_listener = __esm({
16883
16963
 
16884
16964
  // packages/agents/dist/claude/api-key-approval.js
16885
16965
  import fs22 from "fs";
16886
- import os13 from "os";
16966
+ import os14 from "os";
16887
16967
  import path25 from "path";
16888
16968
  function ensureClaudeApiKeyApproved(apiKey, opts = {}) {
16889
16969
  const suffix = apiKey.slice(-20);
16890
16970
  if (!suffix)
16891
16971
  return { changed: false, reason: "empty api key" };
16892
- const file = opts.claudeJsonPath ?? path25.join(os13.homedir(), ".claude.json");
16972
+ const file = opts.claudeJsonPath ?? path25.join(os14.homedir(), ".claude.json");
16893
16973
  const log = opts.log ?? (() => {
16894
16974
  });
16895
16975
  if (!fs22.existsSync(file)) {
@@ -19483,6 +19563,20 @@ var init_runtime2 = __esm({
19483
19563
  }
19484
19564
  });
19485
19565
 
19566
+ // packages/cli/src/lib/suppress-sqlite-warning.ts
19567
+ function isSqliteExperimentalWarning(warning, type2) {
19568
+ const name = typeof warning === "object" && warning !== null ? warning.name : typeof type2 === "string" ? type2 : void 0;
19569
+ const message = typeof warning === "string" ? warning : warning?.message;
19570
+ return name === "ExperimentalWarning" && typeof message === "string" && /SQLite is an experimental feature/i.test(message);
19571
+ }
19572
+ var original = process.emitWarning.bind(process);
19573
+ process.emitWarning = ((warning, ...rest) => {
19574
+ const first = rest[0];
19575
+ const type2 = typeof first === "string" ? first : first?.type;
19576
+ if (isSqliteExperimentalWarning(warning, type2)) return;
19577
+ original(warning, ...rest);
19578
+ });
19579
+
19486
19580
  // packages/cli/src/index.ts
19487
19581
  init_dist();
19488
19582
  init_dist2();
@@ -19931,7 +20025,7 @@ init_dist();
19931
20025
  import { Command as Command2 } from "commander";
19932
20026
  import fs25 from "fs";
19933
20027
  import path28 from "path";
19934
- import os14 from "os";
20028
+ import os15 from "os";
19935
20029
  import readline from "readline";
19936
20030
  import chalk4 from "chalk";
19937
20031
 
@@ -20238,7 +20332,7 @@ function warnIfNoAnthropic(preset, auth) {
20238
20332
  var initCommand = new Command2("init").description("Guided first-time setup: provider, hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").option("--preset <id>", "provider preset: a|b|c (non-interactive; d accepted as a deprecated alias for c)").action(async (opts) => {
20239
20333
  const hubPath = resolveHome(opts.hub);
20240
20334
  const pkgRoot = findPackageRoot();
20241
- const configDir = path28.join(os14.homedir(), ".config", "squadrant");
20335
+ const configDir = path28.join(os15.homedir(), ".config", "squadrant");
20242
20336
  const isTTY = process.stdin.isTTY === true;
20243
20337
  console.log(chalk4.bold("\nSquadrant Init\n"));
20244
20338
  let explicitPreset;
@@ -20343,7 +20437,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
20343
20437
  ensureRuntimeSynced({ sourceRoot: pkgRoot, runtimeRoot: configDir });
20344
20438
  console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
20345
20439
  stepHeader(2, 5, "Agent + projection setup");
20346
- const settingsPath = path28.join(os14.homedir(), ".claude", "settings.json");
20440
+ const settingsPath = path28.join(os15.homedir(), ".claude", "settings.json");
20347
20441
  try {
20348
20442
  let settings = {};
20349
20443
  if (fs25.existsSync(settingsPath)) {
@@ -20649,9 +20743,9 @@ init_dist();
20649
20743
  import { Command as Command5 } from "commander";
20650
20744
  import { execSync as execSync9 } from "child_process";
20651
20745
  import path30 from "path";
20652
- import os15 from "os";
20746
+ import os16 from "os";
20653
20747
  import chalk7 from "chalk";
20654
- var TEMPLATES_DIR2 = path30.join(os15.homedir(), ".config", "squadrant", "templates");
20748
+ var TEMPLATES_DIR2 = path30.join(os16.homedir(), ".config", "squadrant", "templates");
20655
20749
  var TASK_PROMPTS = {
20656
20750
  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.",
20657
20751
  "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.",
@@ -21982,9 +22076,9 @@ init_dist2();
21982
22076
  import { Command as Command12 } from "commander";
21983
22077
  import fs27 from "fs";
21984
22078
  import path31 from "path";
21985
- import os16 from "os";
22079
+ import os17 from "os";
21986
22080
  import chalk12 from "chalk";
21987
- var TEMPLATES_DIR3 = path31.join(os16.homedir(), ".config", "squadrant", "templates");
22081
+ var TEMPLATES_DIR3 = path31.join(os17.homedir(), ".config", "squadrant", "templates");
21988
22082
  async function runSideSpawn2(input) {
21989
22083
  const config = loadConfig();
21990
22084
  const proj = config.projects[input.project];
@@ -23631,7 +23725,7 @@ import { Command as Command14 } from "commander";
23631
23725
  import { execSync as execSync11, execFileSync as execFileSync11 } from "child_process";
23632
23726
  import fs29 from "fs";
23633
23727
  import path33 from "path";
23634
- import os17 from "os";
23728
+ import os18 from "os";
23635
23729
  import chalk15 from "chalk";
23636
23730
 
23637
23731
  // packages/cli/src/commands/launch-interactive.ts
@@ -23723,8 +23817,8 @@ function resolveLaunchBackend(o) {
23723
23817
  // packages/cli/src/commands/launch.ts
23724
23818
  init_dist2();
23725
23819
  var CMUX_APP = "/Applications/cmux.app";
23726
- var TEMPLATES_DIR4 = path33.join(os17.homedir(), ".config", "squadrant", "templates");
23727
- var SESSIONS_PATH2 = path33.join(os17.homedir(), ".config", "squadrant", "sessions.json");
23820
+ var TEMPLATES_DIR4 = path33.join(os18.homedir(), ".config", "squadrant", "templates");
23821
+ var SESSIONS_PATH2 = path33.join(os18.homedir(), ".config", "squadrant", "sessions.json");
23728
23822
  function shouldWireCaptainChannel(agentName, config) {
23729
23823
  return agentName === "claude" && resolveCaptainChannelMode(config.defaults) !== "off";
23730
23824
  }
@@ -23821,7 +23915,7 @@ var launchCommand = new Command14("launch").description(
23821
23915
  initialPrompt = "Run your startup checklist: use the squadrant:command-ops skill, complete your daily briefing, then report ready.";
23822
23916
  }
23823
23917
  const runtime = projectName ? runtimes.forProject(projectName, config) : runtimes.global(config);
23824
- const stateRoot = path33.join(os17.homedir(), ".config", "squadrant", "state");
23918
+ const stateRoot = path33.join(os18.homedir(), ".config", "squadrant", "state");
23825
23919
  const isOpencodeCaptain = role === "captain" && agentName === "opencode" && !!projectName;
23826
23920
  if (isOpencodeCaptain && !isOpencodeCaptainDir(cwd)) {
23827
23921
  console.error(chalk15.red(`
@@ -24178,7 +24272,7 @@ Shutting down captain workspace for '${project}'...
24178
24272
  init_dist();
24179
24273
  import { Command as Command16 } from "commander";
24180
24274
  import fs30 from "fs";
24181
- import os18 from "os";
24275
+ import os19 from "os";
24182
24276
  import path34 from "path";
24183
24277
  import { fileURLToPath as fileURLToPath3 } from "url";
24184
24278
  import { execSync as execSync12 } from "child_process";
@@ -24232,7 +24326,7 @@ function buildIssueUrl(metrics, squadrantVersion) {
24232
24326
  }
24233
24327
  var feedbackCommand = new Command16("feedback").description("Open a pre-filled GitHub issue for feedback or bug reports").action(() => {
24234
24328
  const config = loadConfig();
24235
- const metricsPath = config.metrics?.path || path34.join(os18.homedir(), ".config", "squadrant", "metrics.json");
24329
+ const metricsPath = config.metrics?.path || path34.join(os19.homedir(), ".config", "squadrant", "metrics.json");
24236
24330
  const metrics = readMetrics(metricsPath);
24237
24331
  const version = readStamp(config) ?? readPkgVersion();
24238
24332
  const issueUrl = buildIssueUrl(metrics, version);
@@ -25638,11 +25732,11 @@ var effortCommand = new Command29("effort").description("Get or set the crew tok
25638
25732
  init_dist();
25639
25733
  import fs35 from "fs";
25640
25734
  import path37 from "path";
25641
- import os19 from "os";
25735
+ import os20 from "os";
25642
25736
  import readline3 from "readline";
25643
25737
  import { Command as Command30 } from "commander";
25644
25738
  import chalk31 from "chalk";
25645
- var CLAUDE_PROJECTS_DIR = path37.join(os19.homedir(), ".claude", "projects");
25739
+ var CLAUDE_PROJECTS_DIR = path37.join(os20.homedir(), ".claude", "projects");
25646
25740
  function parseTranscriptLine(rawLine) {
25647
25741
  const line = rawLine.trim();
25648
25742
  if (!line) return { timestamp: null, usage: null };
@@ -26846,7 +26940,7 @@ var workCommand = new Command34("work").description("Track your own in-flight wo
26846
26940
  init_dist();
26847
26941
  import { Command as Command35 } from "commander";
26848
26942
  import path42 from "path";
26849
- import os20 from "os";
26943
+ import os21 from "os";
26850
26944
 
26851
26945
  // packages/cli/src/lib/handoff-live-repo.ts
26852
26946
  init_dist();
@@ -27258,7 +27352,7 @@ function readNewestArchivedHandoff(spokeVault, now) {
27258
27352
  }
27259
27353
 
27260
27354
  // packages/cli/src/commands/handoff.ts
27261
- var CLAUDE_MEM_DB_PATH = path42.join(os20.homedir(), ".claude-mem", "claude-mem.db");
27355
+ var CLAUDE_MEM_DB_PATH = path42.join(os21.homedir(), ".claude-mem", "claude-mem.db");
27262
27356
  async function defaultFetchTasks(project) {
27263
27357
  return await squadrantdCall({ kind: "list", project });
27264
27358
  }
@@ -27418,7 +27512,7 @@ var sessionsCommand = new Command36("sessions").description("List live agent ses
27418
27512
  init_dist();
27419
27513
  init_dist2();
27420
27514
  init_dist3();
27421
- import os21 from "os";
27515
+ import os22 from "os";
27422
27516
  import path43 from "path";
27423
27517
  import { Command as Command37 } from "commander";
27424
27518
  import chalk36 from "chalk";
@@ -27537,7 +27631,7 @@ function buildWhoamiDeps(opts = {}) {
27537
27631
  const projects = Object.fromEntries(
27538
27632
  Object.entries(config.projects).map(([n, p]) => [n, resolveHome(p.path)])
27539
27633
  );
27540
- const root = stateRoot ?? path43.join(os21.homedir(), ".config", "squadrant", "state");
27634
+ const root = stateRoot ?? path43.join(os22.homedir(), ".config", "squadrant", "state");
27541
27635
  const store = createStore(root);
27542
27636
  const merged = {
27543
27637
  env: process.env,
@@ -27668,13 +27762,33 @@ function runKnowledgeReindex(kb, configPath = DEFAULT_CONFIG_PATH) {
27668
27762
  const cfg = loadConfig(configPath);
27669
27763
  const hub = resolveHome(cfg.hubVault);
27670
27764
  if (!fs41.existsSync(kbDir(hub, kb))) throw new Error(`Knowledge base '${kb}' does not exist. Run: squadrant knowledge init ${kb}`);
27671
- const { rules, errors } = loadKbRules(cfg, kb);
27672
- const file = writeIndex(hub, kb, compileIndex(kb, rules));
27673
- return { file, count: rules.length, errors: errors.length };
27765
+ const { rules: all, errors } = loadKbRules(cfg, kb, { includeProposed: true });
27766
+ const proposedRoot = path44.join(kbRulesDir(hub, kb), PROPOSED_DIR) + path44.sep;
27767
+ const live = all.filter((r) => !r.file.startsWith(proposedRoot));
27768
+ const file = writeIndex(hub, kb, compileIndex(kb, live));
27769
+ return { file, count: live.length, proposed: all.length - live.length, errors: errors.length, invalid: errors };
27770
+ }
27771
+ function runKnowledgeValidate(kb, configPath = DEFAULT_CONFIG_PATH) {
27772
+ assertKbName(kb);
27773
+ const cfg = loadConfig(configPath);
27774
+ const hub = resolveHome(cfg.hubVault);
27775
+ if (!fs41.existsSync(kbDir(hub, kb))) throw new Error(`Knowledge base '${kb}' does not exist. Run: squadrant knowledge init ${kb}`);
27776
+ const { sources, errors } = loadSources(hub, kb);
27777
+ const res = validateKb({ rulesDir: kbRulesDir(hub, kb), kbRoot: kbDir(hub, kb), sources, domains: cfg.knowledge?.[kb]?.domains });
27778
+ const srcChecks = errors.map((e) => ({ level: "fail", message: `sources.yaml: ${e}` }));
27779
+ return { checks: [...srcChecks, ...res.checks], ok: res.ok && !errors.length };
27674
27780
  }
27675
27781
  knowledgeCommand.command("reindex <kb>").description("Recompile index.json from the KB's rule files").action((kb) => {
27676
27782
  const r = runKnowledgeReindex(kb);
27677
- console.log(`${r.file}: ${r.count} rules${r.errors ? chalk37.red(`, ${r.errors} invalid files skipped`) : ""}`);
27783
+ console.log(`${r.file}: ${r.count} active${r.proposed ? ` (+${r.proposed} proposed)` : ""}${r.errors ? chalk37.red(`, ${r.errors} invalid files skipped`) : ""}`);
27784
+ for (const e of r.invalid) console.log(chalk37.red(` \u2718 ${e.file}: ${e.problems.join("; ")}`));
27785
+ });
27786
+ knowledgeCommand.command("validate <kb>").description("CI-style check: invalid files, duplicate ids, quotes present in sources; exits non-zero on failure").action((kb) => {
27787
+ const { checks, ok: ok2 } = runKnowledgeValidate(kb);
27788
+ const mark2 = { ok: chalk37.green("\u2714"), fail: chalk37.red("\u2718"), warn: chalk37.yellow("!"), skip: chalk37.dim("-") };
27789
+ for (const c of checks) console.log(`${mark2[c.level]} ${c.message}`);
27790
+ console.log(ok2 ? chalk37.green("valid") : chalk37.red("invalid"));
27791
+ if (!ok2) process.exitCode = 1;
27678
27792
  });
27679
27793
 
27680
27794
  // packages/cli/src/commands/rules.ts
@@ -27682,6 +27796,7 @@ init_dist();
27682
27796
  init_dist2();
27683
27797
  import { Command as Command39 } from "commander";
27684
27798
  import chalk38 from "chalk";
27799
+ var DEFAULT_SEARCH_LIMIT = 5;
27685
27800
  function resolveRulesProject(cfg, opts) {
27686
27801
  const env = opts.env ?? process.env;
27687
27802
  const name = opts.project ?? env.SQUADRANT_CREW_PROJECT ?? detectCurrentProject(cfg, opts.cwd ?? process.cwd());
@@ -27697,6 +27812,32 @@ function formatRule(rule2) {
27697
27812
  ` source: ${src}`
27698
27813
  ].join("\n");
27699
27814
  }
27815
+ function formatBrief(rule2) {
27816
+ return `${rule2.modality.toUpperCase()} ${rule2.id}: ${rule2.statement}`;
27817
+ }
27818
+ function parseLimit(raw) {
27819
+ if (raw === void 0) return DEFAULT_SEARCH_LIMIT;
27820
+ const n = Number(raw);
27821
+ if (!Number.isInteger(n) || n < 1) throw new Error(`--limit must be a positive integer, got '${raw}'`);
27822
+ return n;
27823
+ }
27824
+ function renderSearch(hits, opts) {
27825
+ if (!hits.length) return ["(no matching rules)"];
27826
+ const limit = parseLimit(opts.limit);
27827
+ const shown = hits.slice(0, limit);
27828
+ const lines = shown.map((h) => opts.idsOnly ? h.rule.id : opts.brief ? `[${h.score}] ${formatBrief(h.rule)}` : `${formatRule(h.rule)}
27829
+ score: ${h.score}
27830
+ `);
27831
+ const more = hits.length - shown.length;
27832
+ if (more > 0) lines.push(`(+${more} more; use --limit)`);
27833
+ return lines;
27834
+ }
27835
+ function renderList2(rules, opts) {
27836
+ if (!rules.length) return ["(no rules)"];
27837
+ const sorted = [...rules].sort((a, b) => a.id.localeCompare(b.id));
27838
+ return sorted.map((r) => opts.brief ? formatBrief(r) : `${formatBrief(r)}
27839
+ ${r.status} \xB7 ${r.layer}`);
27840
+ }
27700
27841
  function loadFor(opts, configPath) {
27701
27842
  const cfg = loadConfig(configPath);
27702
27843
  const project = resolveRulesProject(cfg, opts);
@@ -27709,6 +27850,10 @@ function runRulesSearch(query, opts, configPath = DEFAULT_CONFIG_PATH) {
27709
27850
  const rules = loadFor(opts, configPath);
27710
27851
  return searchRules(rules, query, { statuses: opts.all ? [...RULE_STATUSES] : DEFAULT_SEARCH_STATUSES });
27711
27852
  }
27853
+ function runRulesList(opts, configPath = DEFAULT_CONFIG_PATH) {
27854
+ const statuses = opts.all ? RULE_STATUSES : DEFAULT_SEARCH_STATUSES;
27855
+ return loadFor(opts, configPath).filter((r) => statuses.includes(r.status));
27856
+ }
27712
27857
  function runRulesShow(id, opts, configPath = DEFAULT_CONFIG_PATH) {
27713
27858
  const rules = loadFor(opts, configPath);
27714
27859
  const statuses = opts.all ? RULE_STATUSES : DEFAULT_SEARCH_STATUSES;
@@ -27717,13 +27862,12 @@ function runRulesShow(id, opts, configPath = DEFAULT_CONFIG_PATH) {
27717
27862
  return rule2;
27718
27863
  }
27719
27864
  var rulesCommand = new Command39("rules").description("Look up rules from subscribed knowledge bases (#893)");
27720
- rulesCommand.command("search <query...>").description("Search rules by keyword").option("--project <name>", "project (default: from cwd or SQUADRANT_CREW_PROJECT)").option("--all", "include proposed and retired rules").action((query, opts) => {
27865
+ rulesCommand.command("search <query...>").description("Search rules by keyword (ranked; best match first)").option("--project <name>", "project (default: from cwd or SQUADRANT_CREW_PROJECT)").option("--all", "include proposed and retired rules").option("--limit <n>", `max results (default ${DEFAULT_SEARCH_LIMIT})`).option("--brief", "one line per rule: [score] MODALITY id: statement").option("--ids-only", "print only rule ids").action((query, opts) => {
27721
27866
  const hits = runRulesSearch(query.join(" "), opts);
27722
- if (!hits.length) {
27723
- console.log("(no matching rules)");
27724
- return;
27725
- }
27726
- for (const h of hits) console.log(formatRule(h.rule) + "\n");
27867
+ for (const line of renderSearch(hits, opts)) console.log(line);
27868
+ });
27869
+ rulesCommand.command("list").description("List every resolved rule for the project").option("--project <name>", "project (default: from cwd or SQUADRANT_CREW_PROJECT)").option("--all", "include proposed and retired rules").option("--brief", "one line per rule: MODALITY id: statement").action((opts) => {
27870
+ for (const line of renderList2(runRulesList(opts), opts)) console.log(line);
27727
27871
  });
27728
27872
  rulesCommand.command("show <ids...>").description("Show one or more rules in full").option("--project <name>", "project (default: from cwd or SQUADRANT_CREW_PROJECT)").option("--all", "include proposed and retired rules").action((ids, opts) => {
27729
27873
  for (const id of ids) {