@swarmdo/cli 1.30.0 → 1.45.0

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.
Files changed (201) hide show
  1. package/README.md +11 -6
  2. package/dist/src/affected/affected.d.ts +6 -1
  3. package/dist/src/affected/affected.d.ts.map +1 -1
  4. package/dist/src/affected/affected.js +7 -2
  5. package/dist/src/affected/affected.js.map +1 -1
  6. package/dist/src/apply/apply.d.ts.map +1 -1
  7. package/dist/src/apply/apply.js +24 -8
  8. package/dist/src/apply/apply.js.map +1 -1
  9. package/dist/src/changelog/changelog.d.ts +21 -0
  10. package/dist/src/changelog/changelog.d.ts.map +1 -1
  11. package/dist/src/changelog/changelog.js +47 -1
  12. package/dist/src/changelog/changelog.js.map +1 -1
  13. package/dist/src/codegraph/codegraph.d.ts.map +1 -1
  14. package/dist/src/codegraph/codegraph.js +11 -1
  15. package/dist/src/codegraph/codegraph.js.map +1 -1
  16. package/dist/src/commands/changelog.d.ts.map +1 -1
  17. package/dist/src/commands/changelog.js +15 -4
  18. package/dist/src/commands/changelog.js.map +1 -1
  19. package/dist/src/commands/comms.d.ts +16 -0
  20. package/dist/src/commands/comms.d.ts.map +1 -0
  21. package/dist/src/commands/comms.js +280 -0
  22. package/dist/src/commands/comms.js.map +1 -0
  23. package/dist/src/commands/compact.d.ts.map +1 -1
  24. package/dist/src/commands/compact.js +6 -2
  25. package/dist/src/commands/compact.js.map +1 -1
  26. package/dist/src/commands/config.d.ts.map +1 -1
  27. package/dist/src/commands/config.js +7 -1
  28. package/dist/src/commands/config.js.map +1 -1
  29. package/dist/src/commands/coupling.d.ts +17 -0
  30. package/dist/src/commands/coupling.d.ts.map +1 -0
  31. package/dist/src/commands/coupling.js +85 -0
  32. package/dist/src/commands/coupling.js.map +1 -0
  33. package/dist/src/commands/hooks.d.ts.map +1 -1
  34. package/dist/src/commands/hooks.js +176 -1
  35. package/dist/src/commands/hooks.js.map +1 -1
  36. package/dist/src/commands/hotspots.d.ts.map +1 -1
  37. package/dist/src/commands/hotspots.js +10 -3
  38. package/dist/src/commands/hotspots.js.map +1 -1
  39. package/dist/src/commands/index.d.ts.map +1 -1
  40. package/dist/src/commands/index.js +12 -3
  41. package/dist/src/commands/index.js.map +1 -1
  42. package/dist/src/commands/permissions.d.ts +14 -0
  43. package/dist/src/commands/permissions.d.ts.map +1 -0
  44. package/dist/src/commands/permissions.js +92 -0
  45. package/dist/src/commands/permissions.js.map +1 -0
  46. package/dist/src/commands/redact.d.ts.map +1 -1
  47. package/dist/src/commands/redact.js +11 -0
  48. package/dist/src/commands/redact.js.map +1 -1
  49. package/dist/src/commands/release.d.ts.map +1 -1
  50. package/dist/src/commands/release.js +27 -5
  51. package/dist/src/commands/release.js.map +1 -1
  52. package/dist/src/commands/task.d.ts.map +1 -1
  53. package/dist/src/commands/task.js +57 -2
  54. package/dist/src/commands/task.js.map +1 -1
  55. package/dist/src/commands/usage.d.ts.map +1 -1
  56. package/dist/src/commands/usage.js +34 -1
  57. package/dist/src/commands/usage.js.map +1 -1
  58. package/dist/src/comms/mailbox.d.ts +88 -0
  59. package/dist/src/comms/mailbox.d.ts.map +1 -0
  60. package/dist/src/comms/mailbox.js +138 -0
  61. package/dist/src/comms/mailbox.js.map +1 -0
  62. package/dist/src/comms/store.d.ts +21 -0
  63. package/dist/src/comms/store.d.ts.map +1 -0
  64. package/dist/src/comms/store.js +44 -0
  65. package/dist/src/comms/store.js.map +1 -0
  66. package/dist/src/compact/compact.d.ts.map +1 -1
  67. package/dist/src/compact/compact.js +4 -1
  68. package/dist/src/compact/compact.js.map +1 -1
  69. package/dist/src/compact-snapshot/compact-snapshot.d.ts.map +1 -1
  70. package/dist/src/compact-snapshot/compact-snapshot.js +8 -3
  71. package/dist/src/compact-snapshot/compact-snapshot.js.map +1 -1
  72. package/dist/src/config-lint/agents-lint.d.ts +30 -0
  73. package/dist/src/config-lint/agents-lint.d.ts.map +1 -0
  74. package/dist/src/config-lint/agents-lint.js +87 -0
  75. package/dist/src/config-lint/agents-lint.js.map +1 -0
  76. package/dist/src/config-lint/lint.d.ts +6 -1
  77. package/dist/src/config-lint/lint.d.ts.map +1 -1
  78. package/dist/src/config-lint/lint.js +14 -4
  79. package/dist/src/config-lint/lint.js.map +1 -1
  80. package/dist/src/coupling/coupling.d.ts +56 -0
  81. package/dist/src/coupling/coupling.d.ts.map +1 -0
  82. package/dist/src/coupling/coupling.js +86 -0
  83. package/dist/src/coupling/coupling.js.map +1 -0
  84. package/dist/src/env/env.d.ts.map +1 -1
  85. package/dist/src/env/env.js +40 -14
  86. package/dist/src/env/env.js.map +1 -1
  87. package/dist/src/hooks-recipe/command-guard.d.ts +46 -0
  88. package/dist/src/hooks-recipe/command-guard.d.ts.map +1 -0
  89. package/dist/src/hooks-recipe/command-guard.js +120 -0
  90. package/dist/src/hooks-recipe/command-guard.js.map +1 -0
  91. package/dist/src/hooks-recipe/recipe.d.ts.map +1 -1
  92. package/dist/src/hooks-recipe/recipe.js +28 -1
  93. package/dist/src/hooks-recipe/recipe.js.map +1 -1
  94. package/dist/src/hotspots/hotspots.d.ts +3 -0
  95. package/dist/src/hotspots/hotspots.d.ts.map +1 -1
  96. package/dist/src/hotspots/hotspots.js +9 -0
  97. package/dist/src/hotspots/hotspots.js.map +1 -1
  98. package/dist/src/index.d.ts.map +1 -1
  99. package/dist/src/index.js +8 -0
  100. package/dist/src/index.js.map +1 -1
  101. package/dist/src/license/license.d.ts.map +1 -1
  102. package/dist/src/license/license.js +4 -1
  103. package/dist/src/license/license.js.map +1 -1
  104. package/dist/src/mcp-client.d.ts.map +1 -1
  105. package/dist/src/mcp-client.js +4 -0
  106. package/dist/src/mcp-client.js.map +1 -1
  107. package/dist/src/mcp-tools/comms-tools.d.ts +11 -0
  108. package/dist/src/mcp-tools/comms-tools.d.ts.map +1 -0
  109. package/dist/src/mcp-tools/comms-tools.js +93 -0
  110. package/dist/src/mcp-tools/comms-tools.js.map +1 -0
  111. package/dist/src/mcp-tools/coupling-tools.d.ts +14 -0
  112. package/dist/src/mcp-tools/coupling-tools.d.ts.map +1 -0
  113. package/dist/src/mcp-tools/coupling-tools.js +56 -0
  114. package/dist/src/mcp-tools/coupling-tools.js.map +1 -0
  115. package/dist/src/mcp-tools/index.d.ts +2 -0
  116. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  117. package/dist/src/mcp-tools/index.js +2 -0
  118. package/dist/src/mcp-tools/index.js.map +1 -1
  119. package/dist/src/mcp-tools/profiles.d.ts.map +1 -1
  120. package/dist/src/mcp-tools/profiles.js +1 -0
  121. package/dist/src/mcp-tools/profiles.js.map +1 -1
  122. package/dist/src/mcp-tools/task-deps.d.ts +35 -0
  123. package/dist/src/mcp-tools/task-deps.d.ts.map +1 -1
  124. package/dist/src/mcp-tools/task-deps.js +87 -0
  125. package/dist/src/mcp-tools/task-deps.js.map +1 -1
  126. package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
  127. package/dist/src/mcp-tools/task-tools.js +4 -0
  128. package/dist/src/mcp-tools/task-tools.js.map +1 -1
  129. package/dist/src/memory/memory-bridge.d.ts +2 -0
  130. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  131. package/dist/src/memory/memory-bridge.js +4 -2
  132. package/dist/src/memory/memory-bridge.js.map +1 -1
  133. package/dist/src/memory-inject/select.d.ts +82 -0
  134. package/dist/src/memory-inject/select.d.ts.map +1 -0
  135. package/dist/src/memory-inject/select.js +164 -0
  136. package/dist/src/memory-inject/select.js.map +1 -0
  137. package/dist/src/memory-vault/import.d.ts.map +1 -1
  138. package/dist/src/memory-vault/import.js +11 -1
  139. package/dist/src/memory-vault/import.js.map +1 -1
  140. package/dist/src/pack/pack.d.ts.map +1 -1
  141. package/dist/src/pack/pack.js +6 -2
  142. package/dist/src/pack/pack.js.map +1 -1
  143. package/dist/src/permissions/audit.d.ts +50 -0
  144. package/dist/src/permissions/audit.d.ts.map +1 -0
  145. package/dist/src/permissions/audit.js +127 -0
  146. package/dist/src/permissions/audit.js.map +1 -0
  147. package/dist/src/plugins/store/search.js +1 -1
  148. package/dist/src/plugins/store/search.js.map +1 -1
  149. package/dist/src/redact/redact.d.ts.map +1 -1
  150. package/dist/src/redact/redact.js +22 -5
  151. package/dist/src/redact/redact.js.map +1 -1
  152. package/dist/src/redact/sarif.d.ts +28 -0
  153. package/dist/src/redact/sarif.d.ts.map +1 -0
  154. package/dist/src/redact/sarif.js +53 -0
  155. package/dist/src/redact/sarif.js.map +1 -0
  156. package/dist/src/release/release.d.ts +19 -0
  157. package/dist/src/release/release.d.ts.map +1 -1
  158. package/dist/src/release/release.js +29 -1
  159. package/dist/src/release/release.js.map +1 -1
  160. package/dist/src/sbom/sbom.d.ts +2 -0
  161. package/dist/src/sbom/sbom.d.ts.map +1 -1
  162. package/dist/src/sbom/sbom.js +5 -0
  163. package/dist/src/sbom/sbom.js.map +1 -1
  164. package/dist/src/swarmvector/semantic-router.d.ts.map +1 -1
  165. package/dist/src/swarmvector/semantic-router.js +8 -3
  166. package/dist/src/swarmvector/semantic-router.js.map +1 -1
  167. package/dist/src/testreport/testreport.d.ts.map +1 -1
  168. package/dist/src/testreport/testreport.js +9 -2
  169. package/dist/src/testreport/testreport.js.map +1 -1
  170. package/dist/src/transcript/export.d.ts.map +1 -1
  171. package/dist/src/transcript/export.js +8 -4
  172. package/dist/src/transcript/export.js.map +1 -1
  173. package/dist/src/update/checker.d.ts +1 -0
  174. package/dist/src/update/checker.d.ts.map +1 -1
  175. package/dist/src/update/checker.js +7 -2
  176. package/dist/src/update/checker.js.map +1 -1
  177. package/dist/src/usage/model-efficiency.d.ts +29 -0
  178. package/dist/src/usage/model-efficiency.d.ts.map +1 -0
  179. package/dist/src/usage/model-efficiency.js +28 -0
  180. package/dist/src/usage/model-efficiency.js.map +1 -0
  181. package/dist/src/usage/reflect-html.d.ts.map +1 -1
  182. package/dist/src/usage/reflect-html.js +1 -0
  183. package/dist/src/usage/reflect-html.js.map +1 -1
  184. package/dist/src/usage/reflect.d.ts +35 -1
  185. package/dist/src/usage/reflect.d.ts.map +1 -1
  186. package/dist/src/usage/reflect.js +58 -4
  187. package/dist/src/usage/reflect.js.map +1 -1
  188. package/dist/src/usage/transcript-usage.d.ts +8 -1
  189. package/dist/src/usage/transcript-usage.d.ts.map +1 -1
  190. package/dist/src/usage/transcript-usage.js +21 -1
  191. package/dist/src/usage/transcript-usage.js.map +1 -1
  192. package/dist/src/util/since.d.ts +13 -0
  193. package/dist/src/util/since.d.ts.map +1 -0
  194. package/dist/src/util/since.js +21 -0
  195. package/dist/src/util/since.js.map +1 -0
  196. package/dist/src/util/stdout.d.ts +23 -1
  197. package/dist/src/util/stdout.d.ts.map +1 -1
  198. package/dist/src/util/stdout.js +34 -2
  199. package/dist/src/util/stdout.js.map +1 -1
  200. package/dist/tsconfig.tsbuildinfo +1 -1
  201. package/package.json +1 -1
@@ -8,6 +8,9 @@ import { callMCPTool, MCPClientError } from '../mcp-client.js';
8
8
  import { storeCommand } from './transfer-store.js';
9
9
  import { existsSync, readFileSync, statSync } from 'node:fs';
10
10
  import { join } from 'node:path';
11
+ import { bridgeSearchEntries } from '../memory/memory-bridge.js';
12
+ import { selectInjectionMemories, mapSearchResultsToCandidates, extractPromptFromPayload, } from '../memory-inject/select.js';
13
+ import { classifyCommand, extractBashCommand, denyOutput } from '../hooks-recipe/command-guard.js';
11
14
  /**
12
15
  * #1686 — `?? 0` only defaults null/undefined; NaN slips through and
13
16
  * surfaces as `"NaN"` (or earlier crashed `.toFixed`) in the metrics
@@ -731,6 +734,174 @@ const routeCommand = {
731
734
  }
732
735
  }
733
736
  };
737
+ /**
738
+ * Read a Claude Code hook payload from stdin (JSON on UserPromptSubmit), with a
739
+ * short timeout so it never hangs when stdin isn't closed (a known Windows
740
+ * failure mode). Returns '' for a TTY or on any error — the caller then simply
741
+ * injects nothing.
742
+ */
743
+ async function readHookStdin(timeoutMs = 1500) {
744
+ if (process.stdin.isTTY)
745
+ return '';
746
+ return new Promise((resolve) => {
747
+ let data = '';
748
+ const finish = (v) => {
749
+ try {
750
+ process.stdin.pause();
751
+ }
752
+ catch { /* noop */ }
753
+ resolve(v);
754
+ };
755
+ const timer = setTimeout(() => finish(data), timeoutMs);
756
+ try {
757
+ process.stdin.setEncoding('utf8');
758
+ process.stdin.on('data', (c) => { data += c; });
759
+ process.stdin.on('end', () => { clearTimeout(timer); finish(data); });
760
+ process.stdin.on('error', () => { clearTimeout(timer); finish(data); });
761
+ process.stdin.resume();
762
+ }
763
+ catch {
764
+ clearTimeout(timer);
765
+ finish('');
766
+ }
767
+ });
768
+ }
769
+ const DEFAULT_INJECT_NAMESPACES = ['claude-memories', 'auto-memory', 'patterns'];
770
+ /**
771
+ * memory-inject — the #43 prompt-time semantic memory injection hook. Embeds the
772
+ * submitted prompt, vector-searches AgentDB across the memory namespaces, and
773
+ * emits the most relevant stored memories as UserPromptSubmit `additionalContext`
774
+ * under a strict token budget. Wired for the hook path: it NEVER errors out
775
+ * (that would break the user's prompt) and stays silent when nothing is relevant.
776
+ */
777
+ const memoryInjectCommand = {
778
+ name: 'memory-inject',
779
+ description: 'Inject relevant stored memories into the prompt (UserPromptSubmit hook)',
780
+ options: [
781
+ { name: 'prompt', short: 'p', description: 'Prompt text (else read the hook JSON from stdin)', type: 'string' },
782
+ { name: 'namespaces', description: 'Comma-separated namespaces to search', type: 'string', default: DEFAULT_INJECT_NAMESPACES.join(',') },
783
+ { name: 'budget', description: 'Max tokens of injected context', type: 'number', default: 800 },
784
+ { name: 'min-relevance', description: 'Minimum relevance score (0..1)', type: 'number', default: 0.35 },
785
+ { name: 'top-k', short: 'K', description: 'Max memories to inject', type: 'number', default: 5 },
786
+ { name: 'limit', description: 'Max search results to rank', type: 'number', default: 20 },
787
+ { name: 'preview', description: 'Print the block for humans instead of emitting hook JSON', type: 'boolean' },
788
+ { name: 'db', description: 'Path to the memory database', type: 'string' },
789
+ ],
790
+ examples: [
791
+ { command: 'swarmdo hooks memory-inject -p "refactor the auth module" --preview', description: 'Preview relevant memories for a prompt' },
792
+ { command: "echo '{\"prompt\":\"fix bug\"}' | swarmdo hooks memory-inject", description: 'Run as a UserPromptSubmit hook' },
793
+ ],
794
+ action: async (ctx) => {
795
+ const preview = Boolean(ctx.flags.preview);
796
+ // A hook must NEVER break the user's prompt: swallow every error and emit nothing.
797
+ try {
798
+ if (process.env.SWARMDO_MEMORY_INJECT_DISABLE === '1')
799
+ return { success: true };
800
+ let prompt = (ctx.flags.prompt || ctx.args.join(' ')).trim();
801
+ if (!prompt)
802
+ prompt = extractPromptFromPayload(await readHookStdin());
803
+ if (!prompt)
804
+ return { success: true }; // nothing to search on
805
+ const namespaces = String(ctx.flags.namespaces ?? DEFAULT_INJECT_NAMESPACES.join(','))
806
+ .split(',').map((s) => s.trim()).filter(Boolean);
807
+ const minRelevance = ctx.flags.minRelevance ?? 0.35;
808
+ const topK = ctx.flags.topK ?? 5;
809
+ const limit = Math.max(ctx.flags.limit ?? 20, topK);
810
+ const search = await bridgeSearchEntries({
811
+ query: prompt,
812
+ namespace: 'all',
813
+ limit,
814
+ threshold: minRelevance,
815
+ fullContent: true,
816
+ dbPath: ctx.flags.db,
817
+ });
818
+ const rows = (search && search.success ? search.results : []);
819
+ const candidates = mapSearchResultsToCandidates(rows, namespaces);
820
+ const result = selectInjectionMemories(candidates, {
821
+ budgetTokens: ctx.flags.budget ?? 800,
822
+ minRelevance,
823
+ topK,
824
+ });
825
+ if (preview) {
826
+ if (!result.block) {
827
+ output.printInfo('No relevant memories to inject.');
828
+ }
829
+ else {
830
+ output.writeln(result.block);
831
+ output.writeln();
832
+ output.printInfo(`Injected ${result.used.length} memor${result.used.length === 1 ? 'y' : 'ies'} ` +
833
+ `(~${result.tokensUsed} tokens${result.skipped ? `, ${result.skipped} skipped` : ''}).`);
834
+ }
835
+ return { success: true, data: { used: result.used.length, skipped: result.skipped, tokensUsed: result.tokensUsed } };
836
+ }
837
+ // Hook mode: emit additionalContext JSON ONLY when there's something to add,
838
+ // so an irrelevant prompt leaves Claude's context untouched.
839
+ if (result.block) {
840
+ process.stdout.write(JSON.stringify({
841
+ hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: result.block },
842
+ }) + '\n');
843
+ }
844
+ return { success: true };
845
+ }
846
+ catch (err) {
847
+ if (preview)
848
+ output.printError(`Memory injection failed: ${String(err)}`);
849
+ return { success: true }; // never fail the hook
850
+ }
851
+ },
852
+ };
853
+ /**
854
+ * guard-bash — the #83 PreToolUse/Bash guardrail. Reads the hook payload from
855
+ * stdin (or --command), classifies the bash command against a conservative
856
+ * destructive-command denylist, and on a match emits Claude Code's
857
+ * `permissionDecision: "deny"` to hard-block it. This is the only safety net
858
+ * that still fires under headless `claude -p --dangerously-skip-permissions`.
859
+ * Hook-safe: it NEVER errors and stays silent (allow) for anything not on the
860
+ * denylist, so it can't break a legitimate command.
861
+ */
862
+ const guardBashCommand = {
863
+ name: 'guard-bash',
864
+ description: 'Block dangerous bash commands (PreToolUse hook): emits permissionDecision:deny on a destructive-command denylist',
865
+ options: [
866
+ { name: 'command', short: 'c', description: 'Command to classify (else read the hook JSON from stdin)', type: 'string' },
867
+ { name: 'preview', description: 'Print the human-readable verdict instead of emitting hook JSON', type: 'boolean' },
868
+ ],
869
+ examples: [
870
+ { command: 'swarmdo hooks guard-bash -c "rm -rf /" --preview', description: 'Check whether a command would be blocked' },
871
+ { command: 'echo \'{"tool_input":{"command":"rm -rf /"}}\' | swarmdo hooks guard-bash', description: 'Run as a PreToolUse/Bash hook' },
872
+ ],
873
+ action: async (ctx) => {
874
+ const preview = Boolean(ctx.flags.preview);
875
+ // A hook must NEVER break a tool call by crashing: swallow everything, default to allow.
876
+ try {
877
+ if (process.env.SWARMDO_GUARD_BASH_DISABLE === '1')
878
+ return { success: true };
879
+ let command = (ctx.flags.command || ctx.args.join(' ')).trim();
880
+ if (!command)
881
+ command = extractBashCommand(await readHookStdin());
882
+ if (!command)
883
+ return { success: true }; // nothing to classify → allow
884
+ const verdict = classifyCommand(command);
885
+ if (preview) {
886
+ if (verdict.block)
887
+ output.printError(`BLOCK: ${verdict.reason} [${verdict.rule}]`);
888
+ else
889
+ output.printSuccess('allow — no destructive pattern matched');
890
+ return { success: true, data: verdict };
891
+ }
892
+ // Hook mode: emit the deny JSON only on a block; silence = allow.
893
+ if (verdict.block) {
894
+ process.stdout.write(JSON.stringify(denyOutput(verdict.reason)) + '\n');
895
+ }
896
+ return { success: true };
897
+ }
898
+ catch (err) {
899
+ if (preview)
900
+ output.printError(`guard-bash failed: ${String(err)}`);
901
+ return { success: true }; // never fail the hook (fail-open: a broken guard must not block work)
902
+ }
903
+ },
904
+ };
734
905
  // Explain subcommand
735
906
  const explainCommand = {
736
907
  name: 'explain',
@@ -4447,7 +4618,7 @@ const notifyCommand = {
4447
4618
  const recipeCommand = {
4448
4619
  name: 'recipe',
4449
4620
  aliases: ['recipes'],
4450
- description: 'Install ready-made Claude Code hook recipes (desktop notify) into settings — dry-run unless --apply',
4621
+ description: 'Install ready-made Claude Code hook recipes (desktop notify, memory injection) into settings — dry-run unless --apply',
4451
4622
  options: [
4452
4623
  { name: 'apply', type: 'boolean', description: 'write the change (default: preview only)', default: false },
4453
4624
  { name: 'shared', type: 'boolean', description: 'target .claude/settings.json (shared/committed) instead of settings.local.json', default: false },
@@ -4553,6 +4724,8 @@ export const hooksCommand = {
4553
4724
  sessionEndCommand,
4554
4725
  sessionRestoreCommand,
4555
4726
  routeCommand,
4727
+ memoryInjectCommand,
4728
+ guardBashCommand,
4556
4729
  explainCommand,
4557
4730
  pretrainCommand,
4558
4731
  buildAgentsCommand,
@@ -4610,6 +4783,8 @@ export const hooksCommand = {
4610
4783
  `${output.highlight('session-end')} - End current session and persist state`,
4611
4784
  `${output.highlight('session-restore')} - Restore a previous session`,
4612
4785
  `${output.highlight('route')} - Route tasks to optimal agents`,
4786
+ `${output.highlight('memory-inject')} - Inject relevant memories into the prompt (UserPromptSubmit)`,
4787
+ `${output.highlight('guard-bash')} - Block dangerous bash commands (PreToolUse deny)`,
4613
4788
  `${output.highlight('explain')} - Explain routing decisions`,
4614
4789
  `${output.highlight('pretrain')} - Bootstrap intelligence from repository`,
4615
4790
  `${output.highlight('build-agents')} - Generate optimized agent configs`,