@kinqs/brainrouter-cli 0.3.5 → 0.3.7

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 (125) hide show
  1. package/README.md +29 -52
  2. package/agents/architect.json +18 -0
  3. package/agents/explorer.json +18 -0
  4. package/agents/reviewer.json +18 -0
  5. package/agents/verifier.json +18 -0
  6. package/agents/worker.json +18 -0
  7. package/bin/cli.cjs +71 -0
  8. package/dist/agent/agent.d.ts +224 -3
  9. package/dist/agent/agent.js +561 -55
  10. package/dist/cli/banner.d.ts +80 -0
  11. package/dist/cli/banner.js +232 -0
  12. package/dist/cli/cliPrompt.d.ts +106 -0
  13. package/dist/cli/cliPrompt.js +314 -0
  14. package/dist/cli/commands/_context.d.ts +3 -1
  15. package/dist/cli/commands/_helpers.d.ts +1 -1
  16. package/dist/cli/commands/_helpers.js +6 -6
  17. package/dist/cli/commands/config.d.ts +46 -0
  18. package/dist/cli/commands/config.js +1042 -0
  19. package/dist/cli/commands/guard.js +75 -10
  20. package/dist/cli/commands/init.d.ts +20 -0
  21. package/dist/cli/commands/init.js +64 -0
  22. package/dist/cli/commands/login.d.ts +13 -0
  23. package/dist/cli/commands/login.js +179 -0
  24. package/dist/cli/commands/mcp.d.ts +19 -0
  25. package/dist/cli/commands/mcp.js +286 -0
  26. package/dist/cli/commands/memory.js +2 -2
  27. package/dist/cli/commands/obs.js +22 -22
  28. package/dist/cli/commands/orchestration.js +18 -0
  29. package/dist/cli/commands/session.js +13 -5
  30. package/dist/cli/commands/ui.js +202 -91
  31. package/dist/cli/commands/workflow.d.ts +20 -0
  32. package/dist/cli/commands/workflow.js +368 -51
  33. package/dist/cli/ink/ChatApp.d.ts +206 -0
  34. package/dist/cli/ink/ChatApp.js +493 -0
  35. package/dist/cli/ink/Frame.d.ts +26 -0
  36. package/dist/cli/ink/Frame.js +5 -0
  37. package/dist/cli/ink/Picker.d.ts +65 -0
  38. package/dist/cli/ink/Picker.js +133 -0
  39. package/dist/cli/ink/SlashPalette.d.ts +51 -0
  40. package/dist/cli/ink/SlashPalette.js +136 -0
  41. package/dist/cli/ink/TextField.d.ts +34 -0
  42. package/dist/cli/ink/TextField.js +47 -0
  43. package/dist/cli/ink/WizardApp.d.ts +7 -0
  44. package/dist/cli/ink/WizardApp.js +422 -0
  45. package/dist/cli/ink/ambientChat.d.ts +34 -0
  46. package/dist/cli/ink/ambientChat.js +7 -0
  47. package/dist/cli/ink/consoleCapture.d.ts +11 -0
  48. package/dist/cli/ink/consoleCapture.js +33 -0
  49. package/dist/cli/ink/markdownRender.d.ts +41 -0
  50. package/dist/cli/ink/markdownRender.js +278 -0
  51. package/dist/cli/ink/renderWithResizeClear.d.ts +14 -0
  52. package/dist/cli/ink/renderWithResizeClear.js +33 -0
  53. package/dist/cli/ink/runChat.d.ts +34 -0
  54. package/dist/cli/ink/runChat.js +571 -0
  55. package/dist/cli/ink/runPicker.d.ts +31 -0
  56. package/dist/cli/ink/runPicker.js +139 -0
  57. package/dist/cli/ink/runSlashPalette.d.ts +23 -0
  58. package/dist/cli/ink/runSlashPalette.js +33 -0
  59. package/dist/cli/ink/runWizard.d.ts +22 -0
  60. package/dist/cli/ink/runWizard.js +133 -0
  61. package/dist/cli/ink/stdinHandoff.d.ts +51 -0
  62. package/dist/cli/ink/stdinHandoff.js +78 -0
  63. package/dist/cli/ink/toolFormat.d.ts +73 -0
  64. package/dist/cli/ink/toolFormat.js +180 -0
  65. package/dist/cli/ink/useTerminalSize.d.ts +35 -0
  66. package/dist/cli/ink/useTerminalSize.js +26 -0
  67. package/dist/cli/repl.d.ts +25 -3
  68. package/dist/cli/repl.js +64 -646
  69. package/dist/cli/slashSuggest.d.ts +32 -0
  70. package/dist/cli/slashSuggest.js +146 -0
  71. package/dist/cli/spinner.d.ts +34 -0
  72. package/dist/cli/spinner.js +36 -0
  73. package/dist/cli/statusline.d.ts +67 -0
  74. package/dist/cli/statusline.js +204 -0
  75. package/dist/cli/theme.d.ts +79 -0
  76. package/dist/cli/theme.js +106 -0
  77. package/dist/cli/whereView.d.ts +81 -0
  78. package/dist/cli/whereView.js +245 -0
  79. package/dist/cli/wizard/modelsApi.d.ts +72 -0
  80. package/dist/cli/wizard/modelsApi.js +166 -0
  81. package/dist/cli/wizard/picker.d.ts +202 -0
  82. package/dist/cli/wizard/picker.js +547 -0
  83. package/dist/cli/wizard/providers.d.ts +86 -0
  84. package/dist/cli/wizard/providers.js +190 -0
  85. package/dist/cli/wizard/runner.d.ts +13 -0
  86. package/dist/cli/wizard/runner.js +488 -0
  87. package/dist/cli/wizard/types.d.ts +122 -0
  88. package/dist/cli/wizard/types.js +109 -0
  89. package/dist/config/config.d.ts +52 -0
  90. package/dist/config/config.js +89 -75
  91. package/dist/index.js +215 -206
  92. package/dist/memory/briefing.d.ts +11 -1
  93. package/dist/memory/briefing.js +69 -1
  94. package/dist/memory/consolidation.d.ts +1 -1
  95. package/dist/orchestration/agentRegistry.d.ts +36 -0
  96. package/dist/orchestration/agentRegistry.js +64 -0
  97. package/dist/orchestration/orchestrator.d.ts +7 -0
  98. package/dist/orchestration/orchestrator.js +2 -0
  99. package/dist/orchestration/tools.d.ts +10 -1
  100. package/dist/orchestration/tools.js +48 -4
  101. package/dist/prompt/breadthHint.d.ts +5 -0
  102. package/dist/prompt/breadthHint.js +44 -0
  103. package/dist/prompt/skillCatalog.d.ts +11 -0
  104. package/dist/prompt/skillCatalog.js +134 -0
  105. package/dist/prompt/skillRunner.d.ts +2 -2
  106. package/dist/prompt/skillRunner.js +2 -31
  107. package/dist/prompt/systemPrompt.d.ts +34 -0
  108. package/dist/prompt/systemPrompt.js +128 -108
  109. package/dist/runtime/dangerousCommand.d.ts +53 -0
  110. package/dist/runtime/dangerousCommand.js +105 -0
  111. package/dist/runtime/mcpClient.d.ts +38 -1
  112. package/dist/runtime/mcpClient.js +104 -13
  113. package/dist/runtime/mcpPool.d.ts +162 -0
  114. package/dist/runtime/mcpPool.js +423 -0
  115. package/dist/runtime/mcpUtils.d.ts +3 -1
  116. package/dist/state/goalStore.d.ts +98 -17
  117. package/dist/state/goalStore.js +132 -42
  118. package/dist/state/preferencesStore.d.ts +67 -3
  119. package/dist/state/preferencesStore.js +84 -1
  120. package/dist/state/workflowArtifacts.d.ts +63 -2
  121. package/dist/state/workflowArtifacts.js +120 -8
  122. package/dist/tests/_helpers.d.ts +31 -0
  123. package/dist/tests/_helpers.js +91 -0
  124. package/package.json +12 -5
  125. package/.env.example +0 -109
package/README.md CHANGED
@@ -6,8 +6,6 @@ recall, skills, and capture.
6
6
 
7
7
  Ships the `brainrouter` binary.
8
8
 
9
- ---
10
-
11
9
  ## Install
12
10
 
13
11
  ```bash
@@ -23,52 +21,44 @@ command not found`.
23
21
 
24
22
  | How Node is installed | Use `sudo`? |
25
23
  |---|---|
26
- | Homebrew (`brew install node`) | No — global prefix is user-writable |
27
- | nvm / asdf / fnm | No — same reason |
28
- | System Node on macOS / Linux | Yes — global prefix is `/usr/local/...` |
29
-
30
- Check yours:
31
-
32
- ```bash
33
- npm config get prefix
34
- ```
24
+ | Homebrew (`brew install node`) | No — global prefix is user-writable |
25
+ | nvm / asdf / fnm | No — same reason |
26
+ | System Node on macOS / Linux | Yes — global prefix is `/usr/local/...` |
35
27
 
36
- If the path is under `/Users/...`, `/opt/homebrew/...`, or your home dir
37
- no sudo. If it's `/usr/local/...` use sudo.
28
+ Check yours: `npm config get prefix`. If the path is under `/Users/...`,
29
+ `/opt/homebrew/...`, or your home dir — no sudo. If it's `/usr/local/...` use sudo.
38
30
 
39
31
  Verify the install:
40
32
 
41
33
  ```bash
42
34
  which brainrouter # prints the path to the binary
43
- brainrouter --version # prints 0.3.5
35
+ brainrouter --version # prints 0.3.7
44
36
  ```
45
37
 
46
- ---
47
-
48
38
  ## Configure
49
39
 
50
- Two configuration surfaces, both one-time:
51
-
52
- ### 1. The chat LLM and MCP server profile
40
+ Run `brainrouter` for the first time and the **setup wizard** starts
41
+ automatically:
53
42
 
54
- ```bash
55
- brainrouter config # interactive set LLM provider, model, key, endpoint
56
- brainrouter login # interactive — set MCP server URL + API key
43
+ ```
44
+ Welcome Theme Provider API key → Model → MCP → AGENT.md → Done
57
45
  ```
58
46
 
59
- Both write to `~/.config/brainrouter/config.json`.
47
+ It writes everything to `~/.config/brainrouter/config.json` — no manual
48
+ file editing needed.
60
49
 
61
- For local-model setups (LM Studio / Ollama), point the LLM endpoint at
62
- `http://localhost:1234/v1/chat/completions` or `http://localhost:11434/v1/chat/completions`.
50
+ To re-run the wizard later: type `/init` inside the REPL.
63
51
 
64
- ### 2. (Optional) Runtime knobs `~/.config/brainrouter/cli.env` or `./brainrouter-cli.env`
52
+ To change a single setting: use `/config <key> <value>` or the `/config`
53
+ home panel. To re-configure the MCP server connection: use `/login`.
65
54
 
66
- Only needed if you want to tune sandbox, tool-loop limits, trace logging,
67
- or web-search backend. See the [`.env.example`](.env.example) bundled with
68
- this package for the full list. LLM credentials do **not** go here — they
69
- live in `config.json`.
55
+ For local-model setups (LM Studio / Ollama), point the LLM endpoint at
56
+ `http://localhost:1234/v1/chat/completions` or `http://localhost:11434/v1/chat/completions`.
70
57
 
71
- ---
58
+ **Runtime knobs** (sandbox, trace log, web-search backend, tool-loop limits)
59
+ are set as shell environment variables. See
60
+ [`brainrouter-docs/configuration.md`](https://github.com/kinqsradiollc/BrainRouter/blob/main/brainrouter-docs/configuration.md)
61
+ for the full list.
72
62
 
73
63
  ## Run
74
64
 
@@ -83,22 +73,15 @@ Inside the REPL, type `/help` for the full slash-command list (60+
83
73
  commands across session / memory / workflow / orchestration / observability
84
74
  surfaces).
85
75
 
86
- ### Offline mode
87
-
88
- If the MCP server isn't reachable, the CLI still boots — but only local
89
- tools (file edits, shell, web fetch, `spawn_agent`) work. Memory recall,
90
- capture, and skills are disabled until the server is back. The startup
91
- banner shows `⚠️ OFFLINE MODE` when this happens. Pass `--strict-mcp` to
76
+ **Offline mode** — if the MCP server isn't reachable, the CLI still boots
77
+ with only local tools (file edits, shell, web fetch, `spawn_agent`). Memory
78
+ recall, capture, and skills are disabled until the server is back. The
79
+ startup banner shows `offline` when this happens. Pass `--strict-mcp` to
92
80
  make the CLI exit instead of degrading.
93
81
 
94
- ### Stdio mode
95
-
96
- If you'd rather have the CLI spawn the MCP server as a child process
97
- instead of running it separately, use `brainrouter config` → "Set Active
98
- Server Profile" → `default` (the bundled stdio profile). You don't need
99
- to run anything else — the CLI manages the server's lifecycle.
100
-
101
- ---
82
+ **Stdio mode** — to have the CLI spawn the MCP server as a child process
83
+ instead of running it separately: open `/config`, go to MCP settings, and
84
+ pick the bundled `stdio` profile. The CLI manages the server's lifecycle.
102
85
 
103
86
  ## Workspace detection
104
87
 
@@ -113,8 +96,6 @@ BRAINROUTER_WORKSPACE=/absolute/path/to/project brainrouter
113
96
 
114
97
  Inside the REPL, run `/workspace` to confirm the active root and session key.
115
98
 
116
- ---
117
-
118
99
  ## What you also probably want
119
100
 
120
101
  A BrainRouter MCP server for the cognitive memory. The CLI works without
@@ -127,9 +108,7 @@ $EDITOR ~/.config/brainrouter/server.env # set BRAINROUTER_LLM_API_KEY,
127
108
  brainrouter-mcp --http --port 3747 # in a separate terminal
128
109
  ```
129
110
 
130
- Then `brainrouter login` and point at `http://localhost:3747/mcp`.
131
-
132
- ---
111
+ Then run `/login` inside the REPL and point at `http://localhost:3747/mcp`.
133
112
 
134
113
  ## Docs
135
114
 
@@ -138,8 +117,6 @@ Then `brainrouter login` and point at `http://localhost:3747/mcp`.
138
117
  - **Maintainer runbook**: [SETUP.md](https://github.com/kinqsradiollc/BrainRouter/blob/main/SETUP.md)
139
118
  - **Bugs / requests**: <https://github.com/kinqsradiollc/BrainRouter/issues>
140
119
 
141
- ---
142
-
143
120
  ## License
144
121
 
145
122
  MIT
@@ -0,0 +1,18 @@
1
+ {
2
+ "id": "architect",
3
+ "displayName": "Architect",
4
+ "whenToUse": "Design alternatives and tradeoffs for a feature or system change. No file writes.",
5
+ "prompt": "## Role: Architect\nYou design solutions; you do not write production code.\n\n### Memory-first opening (mandatory)\n- `memory_search` and `memory_graph_query` for the feature/domain — past architecture decisions often constrain new ones.\n- `memory_contradictions` (action: list) — if prior designs contradict the proposed change, flag it.\n- Cite any architecture_decision records you find with their recordId.\n\nAlways present at least two design alternatives with explicit tradeoffs (complexity, blast radius, reversibility, test cost).\nEnd with a clear recommendation and the smallest first vertical slice.",
6
+ "model": null,
7
+ "effort": null,
8
+ "defaultAccess": "read",
9
+ "toolScope": { "local": ["*"], "mcp": ["memory_*"] },
10
+ "disallowedTools": [],
11
+ "maxIterations": 30,
12
+ "timeoutMs": 120000,
13
+ "maxResultChars": 8000,
14
+ "subagents": [],
15
+ "delegateName": "delegate_architect",
16
+ "tier": "reasoning",
17
+ "outputContract": null
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "id": "explorer",
3
+ "displayName": "Explorer",
4
+ "whenToUse": "Read-only codebase investigation. Returns concrete file paths, line ranges, and a short list of facts.",
5
+ "prompt": "## Role: Explorer\nYou are a read-only investigator. Do not edit files or run shell commands.\nGoal: map the relevant code, return concrete file paths with line ranges, and surface the few facts the parent needs to decide.\n\n### Memory-first opening (mandatory)\n- Step 1: `memory_search` for the topic of investigation. Past explorers may have mapped this already — do not re-discover what BrainRouter already knows.\n- Step 2: `memory_graph_query` with the dominant feature/entity name to surface related memories across 2 hops.\n- Step 3: `memory_file_history` for any file the parent specifically mentions.\n- Cite every recordId you build on. Your output begins with a `### Memory consulted` block listing the record IDs and what they told you.\n\nOutput structure: 1) Memory consulted, 2) Summary (3-5 bullets), 3) Key files with line ranges, 4) Open questions, 5) Suggested next probe.\nNever claim work is complete without naming actual files you read AND showing the memory you consulted.",
6
+ "model": null,
7
+ "effort": null,
8
+ "defaultAccess": "read",
9
+ "toolScope": { "local": ["*"], "mcp": ["memory_*"] },
10
+ "disallowedTools": [],
11
+ "maxIterations": 30,
12
+ "timeoutMs": 120000,
13
+ "maxResultChars": 8000,
14
+ "subagents": [],
15
+ "delegateName": "delegate_explorer",
16
+ "tier": "reasoning",
17
+ "outputContract": null
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "id": "reviewer",
3
+ "displayName": "Reviewer",
4
+ "whenToUse": "Code review stance; findings first, severity-ordered. Read-only.",
5
+ "prompt": "## Role: Reviewer\nYou review changes critically. Findings first; severity-ordered (blocker, major, minor, nit).\n\n### Memory-first opening (mandatory)\n- `memory_search` for prior reviews on the same files or feature — never re-flag an issue another reviewer already decided is acceptable.\n- `memory_file_history` for each file in the diff — known regressions and prior bug fixes inform your verdict.\n- Cite related recordIds inline in each finding so the parent can see the precedent.\n\nFor each finding: file:line, what is wrong, why it matters, suggested fix.\nDo not make edits. The parent will decide what to apply.",
6
+ "model": null,
7
+ "effort": null,
8
+ "defaultAccess": "read",
9
+ "toolScope": { "local": ["*"], "mcp": ["memory_*"] },
10
+ "disallowedTools": [],
11
+ "maxIterations": 30,
12
+ "timeoutMs": 120000,
13
+ "maxResultChars": 8000,
14
+ "subagents": [],
15
+ "delegateName": "delegate_reviewer",
16
+ "tier": "reasoning",
17
+ "outputContract": null
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "id": "verifier",
3
+ "displayName": "Verifier",
4
+ "whenToUse": "Runs tests and checks; reports pass/fail with evidence.",
5
+ "prompt": "## Role: Verifier\nYou verify that recent changes work. Run the smallest useful set of tests/typechecks.\n\n### Memory-first opening (mandatory)\n- `memory_search` for prior failure modes on these tests — flaky tests, environment caveats, and known-bad commands live in memory.\n- `memory_file_history` for any test file involved — past fixes for the same suite are highly relevant.\n\nReport: which command(s) you ran, exit codes, failing output (trimmed), and a clear PASS/FAIL verdict.\nNever claim PASS without actually executing a check. On failure, call `memory_task_update` with the blocker so the next worker can pick it up.",
6
+ "model": null,
7
+ "effort": null,
8
+ "defaultAccess": "shell",
9
+ "toolScope": { "local": ["*"], "mcp": ["memory_*"] },
10
+ "disallowedTools": [],
11
+ "maxIterations": 30,
12
+ "timeoutMs": 120000,
13
+ "maxResultChars": 8000,
14
+ "subagents": [],
15
+ "delegateName": "delegate_verifier",
16
+ "tier": "worker",
17
+ "outputContract": null
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "id": "worker",
3
+ "displayName": "Worker",
4
+ "whenToUse": "Implementation-focused. May edit files when granted write access.",
5
+ "prompt": "## Role: Worker\nYou implement a single bounded task. Keep edits minimal and scoped.\n\n### Memory-first opening (mandatory)\n- `memory_recall` for the task topic — past instructions, conventions, and tool_preference records often dictate HOW to implement.\n- `memory_file_history` for the files you intend to touch — known fragility lives there.\n- If the parent gave you `seedRecordIds`, treat those as authoritative context.\n- `memory_task_state` if this looks like a continuation — pick up where prior work left off.\n\nRead before editing. Prefer edit_file over write_file when possible. Prefer apply_patch for multi-file edits.\nOn completion call `memory_task_update` with the outcome, then report exactly which files you changed and any follow-ups the verifier should run.",
6
+ "model": null,
7
+ "effort": null,
8
+ "defaultAccess": "write",
9
+ "toolScope": { "local": ["*"], "mcp": ["memory_*"] },
10
+ "disallowedTools": [],
11
+ "maxIterations": 30,
12
+ "timeoutMs": 120000,
13
+ "maxResultChars": 8000,
14
+ "subagents": [],
15
+ "delegateName": "delegate_worker",
16
+ "tier": "worker",
17
+ "outputContract": null
18
+ }
package/bin/cli.cjs ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Thin CommonJS shim that runs BEFORE the real ESM CLI entrypoint.
5
+ *
6
+ * Why CJS for the bin: ESM hoists all `import` statements above any
7
+ * top-level code in the module that owns them. The CLI imports
8
+ * `node:sqlite` transitively via `config/config.ts`, which triggers
9
+ * Node's `ExperimentalWarning` the FIRST time the module is touched —
10
+ * and that happens during import resolution, before any line of code
11
+ * in `src/index.ts` runs. So a warning filter installed inside that
12
+ * file always fires too late.
13
+ *
14
+ * This shim does three things synchronously, with zero ESM imports
15
+ * blocking it, and only then hands off:
16
+ *
17
+ * 1. Remove Node's default "warning" printer.
18
+ * 2. Install a filtered listener that drops `ExperimentalWarning`
19
+ * (sqlite, ESM in older Node) and dotenv self-promotion lines.
20
+ * 3. Override `process.emitWarning` so future direct callers also
21
+ * route through the same filter.
22
+ *
23
+ * Anything BrainRouter itself emits via `process.emitWarning('…',
24
+ * 'BrainRouterWarning')` (or any non-suppressible type) flows through
25
+ * unchanged. NODE_NO_WARNINGS=1 would silence those too, which is why
26
+ * we don't just set that env.
27
+ *
28
+ * The shim then dynamically imports the ESM entry. Dynamic `import()`
29
+ * is the only way to load ESM from CJS; it returns a promise we await
30
+ * so an unhandled rejection during boot still surfaces as an error.
31
+ */
32
+
33
+ function isSuppressibleWarning(message, type) {
34
+ const looksExperimental =
35
+ type === 'ExperimentalWarning' ||
36
+ /experimental feature|SQLite is an experimental/i.test(message);
37
+ const looksDotenvNoise = /dotenv@\d|dotenvx|dotenv\.org/i.test(message);
38
+ return looksExperimental || looksDotenvNoise;
39
+ }
40
+
41
+ for (const listener of process.listeners('warning')) {
42
+ process.removeListener('warning', listener);
43
+ }
44
+ process.on('warning', (warning) => {
45
+ const message = (warning && warning.message) || '';
46
+ const type = (warning && warning.name) || '';
47
+ if (isSuppressibleWarning(message, type)) return;
48
+ process.stderr.write(`(node:${process.pid}) ${type || 'Warning'}: ${message || warning}\n`);
49
+ });
50
+
51
+ const originalEmitWarning = process.emitWarning.bind(process);
52
+ process.emitWarning = function emitWarning(warning, ...rest) {
53
+ const message = typeof warning === 'string' ? warning : (warning && warning.message) || '';
54
+ const type =
55
+ typeof rest[0] === 'string' ? rest[0] :
56
+ (rest[0] && typeof rest[0] === 'object' && 'type' in rest[0]) ? rest[0].type :
57
+ (warning && warning.name) || '';
58
+ if (isSuppressibleWarning(message, type)) return;
59
+ return originalEmitWarning(warning, ...rest);
60
+ };
61
+
62
+ // Path to the compiled ESM entry, resolved relative to this shim.
63
+ const path = require('node:path');
64
+ const url = require('node:url');
65
+ const entry = path.resolve(__dirname, '..', 'dist', 'index.js');
66
+ import(url.pathToFileURL(entry).href).catch((err) => {
67
+ // Surface boot-time errors verbatim — a silent exit would just look like
68
+ // the CLI never started.
69
+ process.stderr.write(`brainrouter: failed to load CLI entrypoint: ${(err && err.stack) || err}\n`);
70
+ process.exit(1);
71
+ });