@bike4mind/cli 0.18.5 → 0.20.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.
- package/LICENSE +1 -1
- package/README.md +204 -35
- package/bin/bike4mind-cli.mjs +137 -24
- package/bin/hearth-hook.mjs +292 -0
- package/dist/AgentHistoryStore-C8uUKjjC.mjs +35512 -0
- package/dist/ApiClient-B_CQrUiF.mjs +277 -0
- package/dist/{ConfigStore-D39UqFnY.mjs → ConfigStore-DD3DcC3-.mjs} +6199 -3954
- package/dist/{ImageStore-BVmEG1xc.mjs → ImageStore-kVo-oHoS.mjs} +2 -2
- package/dist/PluginStore-DwvOJ-G3.mjs +206 -0
- package/dist/ProxyManager-Bqr7Lmsd.mjs +3 -0
- package/dist/{ProxyManager-CV94yZUW.mjs → ProxyManager-C5H0pUyK.mjs} +2 -2
- package/dist/{SandboxOrchestrator-BS6gALNq.mjs → SandboxOrchestrator-BFPVpmB5.mjs} +1 -1
- package/dist/{SandboxOrchestrator-BoINxbX4.mjs → SandboxOrchestrator-C8uleDn2.mjs} +7 -7
- package/dist/ShellSessionManager-6o8KZzl1-vrbPAUTq.mjs +252 -0
- package/dist/{ViolationLogStore-B-plqJfn.mjs → ViolationLogStore-byEhxa2A.mjs} +1 -1
- package/dist/WorkItemsClient-Cow6nXx7.mjs +382 -0
- package/dist/{bashExecute-B1N1lMOS-TZVDbcQ4.mjs → bashExecute-CrdPpBqk-DCATrE-D.mjs} +116 -16
- package/dist/buildAgent-mVuXU_H4.mjs +824 -0
- package/dist/commands/acpCommand.mjs +798 -0
- package/dist/commands/apiCommand.mjs +14 -16
- package/dist/commands/doctorCommand.mjs +5 -5
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +272 -76
- package/dist/commands/mcpCommand.mjs +14 -1
- package/dist/commands/pluginCommand.mjs +232 -0
- package/dist/commands/updateCommand.mjs +10 -9
- package/dist/{grepSearch-DJs-cubo-Bm0Y8oS3.mjs → grepSearch-BaYUfIYs-C-fxWc9G.mjs} +3 -3
- package/dist/index.mjs +3281 -2322
- package/dist/{package-I_v_WFUn.mjs → package-BqKSCbso.mjs} +1 -1
- package/dist/serve-CuF0I5en.mjs +772 -0
- package/dist/store-BG3e54c8.mjs +3 -0
- package/dist/{store-DV5s-qni.mjs → store-CvjTpQPs.mjs} +70 -3
- package/dist/{terminalSetup-BbJt04ZG.mjs → terminalSetup-DjXAwpDy.mjs} +2 -3
- package/dist/{treeSitterEngine-BRbQ9b7I.mjs → treeSitterEngine-QBE3YkmG.mjs} +51 -1
- package/dist/{updateChecker-C8xsNY2L.mjs → updateChecker-CQW8bxo6.mjs} +10 -10
- package/package.json +48 -43
- package/dist/BackgroundAgentManager-D-xsWd3C.mjs +0 -27303
- package/dist/ProxyManager-ByuAHFMq.mjs +0 -3
- package/dist/store-DgzCTRkN.mjs +0 -3
- package/dist/utils-Cdktpk_k.mjs +0 -158
- package/dist/utils-DEizxshI.mjs +0 -3
package/LICENSE
CHANGED
|
@@ -9,7 +9,7 @@ Parameters
|
|
|
9
9
|
|
|
10
10
|
Licensor: Bike4Mind, Inc.
|
|
11
11
|
|
|
12
|
-
Licensed Work: Bike4Mind (
|
|
12
|
+
Licensed Work: Bike4Mind (this repository and the components
|
|
13
13
|
distributed under this License).
|
|
14
14
|
The Licensed Work is (c) 2026 Bike4Mind, Inc.
|
|
15
15
|
|
package/README.md
CHANGED
|
@@ -70,26 +70,74 @@ The CLI will prompt you to authenticate with your Bike4Mind account on first run
|
|
|
70
70
|
### CLI Flags
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
|
-
b4m [options]
|
|
73
|
+
b4m [options] ["initial prompt"]
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
A bare positional argument seeds **and** submits the first turn while keeping the interactive UI open (e.g. `b4m "summarize the git log"`).
|
|
77
|
+
|
|
78
|
+
**Environment / API** (see [API Configuration](#api-configuration)):
|
|
79
|
+
|
|
80
|
+
| Flag | Effect |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `--dev` | Point the CLI at the local dev server (`http://localhost:3001`) and remember it. Aliases: `--local`, single-dash forms. |
|
|
83
|
+
| `--prod` | Point the CLI at Bike4Mind production and remember it. Alias: `--production`. |
|
|
84
|
+
| `--api-url <url>` | Point the CLI at **any** instance — a self-hosted stack, an AWS deployment, `http://localhost:3000`, etc. Clears cached auth (bound to the old origin) and exits; run `b4m` again to sign in. |
|
|
85
|
+
| `--reset-api` | Reset the API URL to the built-in default and clear auth, then exit. |
|
|
86
|
+
|
|
87
|
+
**Session / behavior:**
|
|
88
|
+
|
|
89
|
+
| Flag | Effect |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `--verbose`, `-v` | Show debug logs in the console (also always written to file — see [Debug Logs](#debug-logs)). |
|
|
92
|
+
| `--debug-stream` | Ultra-verbose: log every SSE event (stream-parser debugging). Implies `--verbose`. |
|
|
93
|
+
| `--no-project-config` | Skip loading project-specific config (`.bike4mind/`). |
|
|
94
|
+
| `--add-dir <dir>` | Grant file access to an additional directory. Repeatable. |
|
|
95
|
+
| `--ollama-host <url>` | Add a local Ollama endpoint's models to the picker (e.g. `http://localhost:11434`). |
|
|
96
|
+
| `--no-remote-skills` | Skip fetching remote B4M-web skills for this run (local files only). |
|
|
97
|
+
|
|
98
|
+
**Headless / non-interactive** (for scripts and CI):
|
|
99
|
+
|
|
100
|
+
| Flag | Effect |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `-p`, `--prompt <query>` | Run a single query non-interactively and exit. |
|
|
103
|
+
| `--output-format <fmt>` | Output when using `-p`: `text` (default), `json`, or `stream-json` (NDJSON of thoughts/actions/observations). |
|
|
104
|
+
| `--dangerously-skip-permissions` | With `-p`, auto-allow all tool permission prompts. Use with caution in CI/CD. |
|
|
105
|
+
|
|
106
|
+
**Info:**
|
|
107
|
+
|
|
108
|
+
| Flag | Effect |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `--help`, `-h` | Show help. |
|
|
111
|
+
| `--version`, `-V` | Show CLI version. |
|
|
112
|
+
|
|
113
|
+
> **Host / `claude`-drop-in flags** (`--mcp-config`, `--strict-mcp-config`, `--append-system-prompt`, `--allowedTools`, `--settings`, `--session-id`, `--resume`) are documented under [Running as a `claude` engine inside a host app](#running-as-a-claude-engine-inside-a-host-app).
|
|
82
114
|
|
|
83
115
|
**Examples:**
|
|
84
116
|
```bash
|
|
117
|
+
# Point at a self-hosted stack, then sign in
|
|
118
|
+
b4m --api-url http://localhost:3000
|
|
119
|
+
b4m
|
|
120
|
+
|
|
121
|
+
# One-shot headless query, JSON output
|
|
122
|
+
b4m -p "What is 2+2?" --output-format json
|
|
123
|
+
|
|
85
124
|
# Run with debug logs visible
|
|
86
125
|
b4m --verbose
|
|
87
126
|
|
|
88
|
-
#
|
|
89
|
-
b4m
|
|
127
|
+
# Seed and submit the first turn, stay interactive
|
|
128
|
+
b4m "review the recent changes"
|
|
129
|
+
```
|
|
90
130
|
|
|
91
|
-
|
|
92
|
-
|
|
131
|
+
### Subcommands
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
b4m mcp list # List configured MCP servers
|
|
135
|
+
b4m mcp add <name> -- <command...> # Add an MCP server
|
|
136
|
+
b4m mcp remove <name> # Remove an MCP server
|
|
137
|
+
b4m mcp enable <name> | disable <name> # Toggle a server on/off
|
|
138
|
+
b4m mcp serve [--http] [--port <n>] # Expose Bike4Mind itself as an MCP server
|
|
139
|
+
b4m update # Check for and install CLI updates
|
|
140
|
+
b4m doctor # Diagnose the CLI installation (Node, registry, ripgrep, native modules)
|
|
93
141
|
```
|
|
94
142
|
|
|
95
143
|
### Switching environments (`--dev` / `--prod`)
|
|
@@ -116,31 +164,64 @@ shown in the startup banner (`🌍 API Environment: …`).
|
|
|
116
164
|
|
|
117
165
|
## Commands
|
|
118
166
|
|
|
119
|
-
While in interactive mode
|
|
167
|
+
While in interactive mode. This mirrors the registry in `src/config/commands.ts` — run `/help` in a session for the live list (which also includes any custom and project commands).
|
|
120
168
|
|
|
121
169
|
**Authentication:**
|
|
122
170
|
- `/login` - Authenticate with your B4M account
|
|
123
171
|
- `/logout` - Clear authentication and sign out
|
|
124
172
|
- `/whoami` - Show current authenticated user
|
|
125
173
|
|
|
126
|
-
**Session
|
|
174
|
+
**Session management:**
|
|
127
175
|
- `/save <name>` - Save current session
|
|
128
|
-
- `/resume` - List and resume saved sessions
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
- `/
|
|
132
|
-
- `/
|
|
176
|
+
- `/resume` (alias `/sessions`) - List and resume saved sessions
|
|
177
|
+
- `/clear` (alias `/new`) - Start a new session
|
|
178
|
+
- `/compact [instructions]` - Compact the conversation into a new session
|
|
179
|
+
- `/context` - Show context-window usage
|
|
180
|
+
- `/usage` - Show credit usage and balance
|
|
181
|
+
|
|
182
|
+
**API configuration:**
|
|
183
|
+
- `/set-api <url>` - Connect to a self-hosted / custom Bike4Mind instance
|
|
184
|
+
- `/reset-api` - Reset to the Bike4Mind main service
|
|
133
185
|
- `/api-info` - Show current API configuration
|
|
134
186
|
|
|
135
|
-
**
|
|
136
|
-
- `/
|
|
137
|
-
- `/
|
|
138
|
-
- `/
|
|
187
|
+
**Files & checkpoints:**
|
|
188
|
+
- `/add-dir <path>` / `/remove-dir <path>` / `/dirs` - Manage accessible directories
|
|
189
|
+
- `/undo` - Undo the last file change
|
|
190
|
+
- `/checkpoints` - List file restore points
|
|
191
|
+
- `/restore <number>` - Restore files to a checkpoint
|
|
192
|
+
- `/diff [number]` - Diff current state against a checkpoint
|
|
193
|
+
- `/rewind` - Rewind the conversation to a previous point
|
|
194
|
+
|
|
195
|
+
**Tool permissions:**
|
|
196
|
+
- `/trust <tool-name>` / `/untrust <tool-name>` / `/trusted` - Manage auto-approved tools
|
|
197
|
+
|
|
198
|
+
**Sandbox** (OS-level isolation for `bash`):
|
|
199
|
+
- `/sandbox` - Show sandbox status and configuration
|
|
200
|
+
- `/sandbox:enable` / `/sandbox:disable` - Toggle the sandbox
|
|
201
|
+
- `/sandbox:mode <auto-allow|permissions>` - Set enforcement mode
|
|
202
|
+
- `/sandbox:trust-domain <domain> [...]` / `/sandbox:domains` - Manage the network allowlist
|
|
203
|
+
- `/sandbox:violations [count]` / `/sandbox:violations:clear` - Inspect / clear violations
|
|
204
|
+
|
|
205
|
+
**MCP & agents:**
|
|
206
|
+
- `/mcp` (alias `/mcp:list`) - Show MCP server status and connected tools
|
|
207
|
+
- `/agents` (alias `/agents:list`) - List available agents
|
|
208
|
+
- `/agents:new <name>` / `/agents:reload` - Create / reload agent definitions
|
|
209
|
+
|
|
210
|
+
**Custom commands:**
|
|
211
|
+
- `/commands` - List all custom commands
|
|
212
|
+
- `/commands:new <name>` / `/commands:reload` - Create / reload custom commands
|
|
213
|
+
|
|
214
|
+
**Durable workflow** (cross-session continuity):
|
|
215
|
+
- `/workflow [decisions|blockers|handoff|review-gates]` - Workflow overview or a section
|
|
216
|
+
- `/decisions` / `/blockers` / `/review-gates` - Shortcuts for the sections above
|
|
217
|
+
- `/handoff [generate|--local]` - Show or generate a session handoff (`--local` = LLM-free snapshot)
|
|
139
218
|
|
|
140
219
|
**General:**
|
|
141
220
|
- `/help` - Show help
|
|
142
|
-
- `/
|
|
143
|
-
- `/config` - Show configuration
|
|
221
|
+
- `/config` - Open the interactive configuration editor
|
|
222
|
+
- `/project-config` - Show merged project configuration
|
|
223
|
+
- `/terminal-setup` - Configure Shift+Enter for multi-line input
|
|
224
|
+
- `/exit` (alias `/quit`) - Exit the CLI
|
|
144
225
|
|
|
145
226
|
## Configuration
|
|
146
227
|
|
|
@@ -164,31 +245,43 @@ The default is baked in at build time from the hosted build environment — ther
|
|
|
164
245
|
fallback in source** (open-core, #9306/#9392), so a fresh clone ships empty. A fork publishes its
|
|
165
246
|
own CLI by setting `B4M_DEFAULT_API_URL` when building — see `tsdown.config.ts`.
|
|
166
247
|
|
|
248
|
+
Because that default is injected only at build time, the endpoint is resolved like this:
|
|
249
|
+
|
|
250
|
+
1. A **custom URL** you set (`--api-url` / `--dev` / `--prod` / `/set-api`) always wins.
|
|
251
|
+
2. Otherwise, the **build-time default** baked into a published binary.
|
|
252
|
+
3. Otherwise, when running **from source** (a `pnpm link --global` checkout, `pnpm dev` — no
|
|
253
|
+
`dist/` built), the CLI defaults to the local dev server `http://localhost:3001`. Use
|
|
254
|
+
`--prod` / `--api-url` to point it elsewhere.
|
|
255
|
+
4. Otherwise (a published, unbranded fork with no baked default), the first `b4m` **prompts you
|
|
256
|
+
to choose a backend**.
|
|
257
|
+
|
|
167
258
|
**Quick switch between local dev and production:** use the `b4m --dev` / `b4m --prod`
|
|
168
259
|
launch flags (see [Switching environments](#switching-environments---dev----prod)).
|
|
169
260
|
They persist your choice and cache auth per-environment. The `/set-api`, `/reset-api`,
|
|
170
261
|
and `/api-info` commands below operate on the same setting from inside a session.
|
|
171
262
|
|
|
172
|
-
**For
|
|
173
|
-
|
|
174
|
-
If your organization runs a self-hosted Bike4Mind instance, connect to it using:
|
|
263
|
+
**For self-hosted / custom instances:**
|
|
175
264
|
|
|
176
|
-
|
|
177
|
-
/set-api https://app.your-instance.example.com
|
|
178
|
-
```
|
|
265
|
+
The CLI can point at **any** Bike4Mind deployment — a self-hosted Docker stack, an AWS deployment, or `http://localhost:3000` — because auth (the OAuth device flow) and the chat API ship in the open core and are served by that instance directly.
|
|
179
266
|
|
|
180
|
-
|
|
267
|
+
From the launch line (clears auth, then exit — run `b4m` again to sign in):
|
|
181
268
|
|
|
182
269
|
```bash
|
|
183
|
-
|
|
270
|
+
b4m --api-url http://localhost:3000 # local self-host Docker stack
|
|
271
|
+
b4m --api-url https://app.your-instance.example.com
|
|
272
|
+
b4m --reset-api # back to the built-in default
|
|
184
273
|
```
|
|
185
274
|
|
|
186
|
-
|
|
275
|
+
Or from inside a session:
|
|
187
276
|
|
|
188
277
|
```bash
|
|
189
|
-
/api-
|
|
278
|
+
/set-api https://app.your-instance.example.com
|
|
279
|
+
/reset-api # return to the default service
|
|
280
|
+
/api-info # show the current API configuration
|
|
190
281
|
```
|
|
191
282
|
|
|
283
|
+
Auth tokens are cached **per environment**, so switching between hosted and self-host does not force a re-login. For an end-to-end walkthrough (hosted **and** self-host, sign-in, credits, troubleshooting), see the top-level [**BIKE4MIND_CLI.md**](../../BIKE4MIND_CLI.md).
|
|
284
|
+
|
|
192
285
|
### Tool API Keys
|
|
193
286
|
|
|
194
287
|
Some built-in tools require API keys to function. Add them to `~/.bike4mind/config.json`:
|
|
@@ -297,6 +390,77 @@ You can also run any MCP server via npx or custom executables:
|
|
|
297
390
|
|
|
298
391
|
**Note:** Internal MCP servers must be built and available in the `b4m-core/packages/mcp/dist/src/` directory. The CLI will automatically find them if you're running from the monorepo. For Docker-based servers, ensure Docker is installed and the image is accessible.
|
|
299
392
|
|
|
393
|
+
#### Serve Bike4Mind itself as an MCP server (`b4m mcp serve`)
|
|
394
|
+
|
|
395
|
+
The reverse of adding a server: `b4m mcp serve` exposes your Bike4Mind backend to any MCP client (Claude Desktop, editors, other agents). It advertises eight tools (`list_notebooks`, `get_notebook`, `create_notebook`, `send_message`, `search_knowledge_base`, `list_files`, `get_file`, `generate_sound_effect`) plus four resource templates (`b4m://notebook/{id}`, `b4m://file/{id}`, `b4m://project/{id}`, `b4m://artifact/{id}`), each listable and readable as JSON, and `b4m://agent-quest` - the credential-free manifest for The Open Door, the onboarding quest for agents.
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
b4m mcp serve # stdio (default) - for local clients that spawn the process
|
|
399
|
+
b4m mcp serve --http --port 7000 # stateless streamable HTTP at http://127.0.0.1:7000/mcp
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Auth precedence is `--api-key` > `B4M_API_KEY` > the stored login; the endpoint is `B4M_API_URL` > `--api-url` > the configured backend. Tool *listing* needs no credentials; tool *calls* authenticate. Prefer `B4M_API_KEY` over `--api-key` (a flag value shows up in process listings). HTTP mode binds loopback only (`127.0.0.1`, `/mcp` path) and has no per-request auth, so do not expose it off-host. The advertised scopes are the recommended key configuration, not per-route hard gates. See [Serve Bike4Mind as an MCP server](../../BIKE4MIND_CLI.md#serve-bike4mind-as-an-mcp-server-b4m-mcp-serve) for the Claude Desktop config and details.
|
|
403
|
+
|
|
404
|
+
## Plugins
|
|
405
|
+
|
|
406
|
+
The CLI can load external feature modules at runtime. A plugin is an npm package that adds tools, a system-prompt section, slash commands, and (optionally) WebSocket handlers to the interactive session, without any change to the CLI itself.
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
b4m plugin add @someone/b4m-plugin-foo # install from npm
|
|
410
|
+
b4m plugin add github:user/b4m-plugin-foo # install from GitHub
|
|
411
|
+
b4m plugin add file:/path/to/local/plugin # install a local directory (development/QA)
|
|
412
|
+
b4m plugin list # show installed plugins and their state
|
|
413
|
+
b4m plugin remove foo # uninstall and disable
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Plugins install to `~/.bike4mind/plugins/` and are gated by a `features` toggle in your config: `add` enables it for you, and every installed plugin also shows up automatically in the `/config` editor. A newly installed plugin loads the next time you start `b4m`; toggling an already-installed plugin in `/config` hot-reloads it in place.
|
|
417
|
+
|
|
418
|
+
### Writing a plugin
|
|
419
|
+
|
|
420
|
+
A plugin declares itself with a `b4m-plugin` field in its `package.json`:
|
|
421
|
+
|
|
422
|
+
```json
|
|
423
|
+
{
|
|
424
|
+
"name": "@someone/b4m-plugin-foo",
|
|
425
|
+
"type": "module",
|
|
426
|
+
"b4m-plugin": { "entry": "./dist/index.js", "configKey": "foo" }
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
`entry` (required) is the module the CLI imports, resolved inside the package. `configKey` (optional, defaults to the package name) is the `features` key that gates the plugin.
|
|
431
|
+
|
|
432
|
+
The entry default-exports a factory. It receives a context (currently `{ logger }`, with `debug/info/warn/error` methods that write to the CLI debug log) and returns the module object:
|
|
433
|
+
|
|
434
|
+
```js
|
|
435
|
+
export default ctx => ({
|
|
436
|
+
name: 'foo', // unique module name
|
|
437
|
+
description: 'What this plugin does',
|
|
438
|
+
getTools: () => [
|
|
439
|
+
{
|
|
440
|
+
toolFn: async args => 'result', // invoked when the agent calls the tool
|
|
441
|
+
toolSchema: {
|
|
442
|
+
name: 'foo_tool',
|
|
443
|
+
description: 'What the tool does',
|
|
444
|
+
parameters: { type: 'object', properties: {} }, // JSON Schema
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
getSystemPromptSection: () => 'Extra system-prompt text (or an empty string)',
|
|
449
|
+
getCommands: () => [{ name: 'foo', description: '...', execute: args => {} }], // optional
|
|
450
|
+
registerWsHandlers: wsManager => {}, // optional
|
|
451
|
+
dispose: () => {}, // optional, runs on exit/disable
|
|
452
|
+
});
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Tool objects are written out structurally as shown - plugin packages cannot import `@bike4mind/*` packages at runtime (those are bundled into the CLI binary and are not on npm in a compatible form). See `test/fixtures/b4m-plugin-example/` in this package for a complete working plugin.
|
|
456
|
+
|
|
457
|
+
### Limitations and trust
|
|
458
|
+
|
|
459
|
+
- **Plugins run in-process with your privileges.** Installing one runs its npm install scripts, and enabling it executes its code inside the CLI, same as configuring an MCP server command. Only install plugins you trust.
|
|
460
|
+
- **Interactive mode only.** Headless (`b4m -p`) and ACP sessions do not load feature modules or plugins today.
|
|
461
|
+
- **Downgrade caveat:** CLI versions without plugin support strip plugin keys from the config `features` map on their next save; re-enable after upgrading again.
|
|
462
|
+
- A plugin that fails to load (bad manifest, import error, malformed tools) is skipped with a warning at startup - it never blocks the CLI. `b4m plugin list` shows the reason.
|
|
463
|
+
|
|
300
464
|
## Git-Aware Code Search
|
|
301
465
|
|
|
302
466
|
The CLI includes a `recent_changes` tool that uses git history to find recently modified files. This significantly speeds up debugging by narrowing the search space to recently changed code.
|
|
@@ -459,6 +623,11 @@ b4m --version
|
|
|
459
623
|
|
|
460
624
|
After step 4, running `b4m` anywhere on your system executes this working tree. Re-run `pnpm link --global` if you move or rename the repo.
|
|
461
625
|
|
|
626
|
+
> **Which backend does a linked checkout talk to?** A source/linked run has no build-time
|
|
627
|
+
> default baked in, so `b4m` defaults to the local dev server (`http://localhost:3001`) — start
|
|
628
|
+
> your local stack, or point it elsewhere with `b4m --prod` / `b4m --api-url <url>`. See
|
|
629
|
+
> [API Configuration](#api-configuration) for the full resolution order.
|
|
630
|
+
|
|
462
631
|
### Editing CLI source (`packages/cli/src/`)
|
|
463
632
|
|
|
464
633
|
Just run `b4m`. The bin's `tsx` fallback picks up your edits on the next invocation — no build step needed.
|
|
@@ -547,7 +716,7 @@ packages/cli/
|
|
|
547
716
|
│ │ └── ConfigStore.ts
|
|
548
717
|
│ └── index.tsx # Main entry point
|
|
549
718
|
└── bin/
|
|
550
|
-
└── bike4mind-cli.
|
|
719
|
+
└── bike4mind-cli.mjs # Executable (source/dist auto-detect + flag parsing)
|
|
551
720
|
```
|
|
552
721
|
|
|
553
722
|
## Dependencies
|
package/bin/bike4mind-cli.mjs
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// In production, it will run the compiled JavaScript
|
|
4
|
+
// Executable entry point for the CLI: tsx runs the TypeScript source in dev,
|
|
5
|
+
// the compiled JavaScript in production.
|
|
7
6
|
|
|
8
|
-
// Suppress punycode deprecation warning from
|
|
9
|
-
// This is a known issue with older npm packages using Node's built-in punycode
|
|
7
|
+
// Suppress punycode deprecation warning from older deps using Node's built-in punycode.
|
|
10
8
|
process.removeAllListeners('warning');
|
|
11
9
|
process.on('warning', (warning) => {
|
|
12
|
-
// Only suppress punycode deprecation warnings
|
|
13
10
|
if (warning.name === 'DeprecationWarning' && warning.message.includes('punycode')) {
|
|
14
11
|
return;
|
|
15
12
|
}
|
|
16
|
-
// Show all other warnings
|
|
17
13
|
console.warn(warning);
|
|
18
14
|
});
|
|
19
15
|
|
|
@@ -37,7 +33,7 @@ const { version: cliVersion } = require('../package.json');
|
|
|
37
33
|
// Intercept these BEFORE yargs parses argv. They're accepted with either a
|
|
38
34
|
// single or double dash (e.g. `b4m -prod` and `b4m --prod` both work), but
|
|
39
35
|
// single-dash multi-char tokens would otherwise be split into clustered short
|
|
40
|
-
// flags by yargs (`-prod`
|
|
36
|
+
// flags by yargs (`-prod` -> `-p -r -o -d`, colliding with -p/--prompt). We pull
|
|
41
37
|
// them out here, record the target, and strip them so yargs sees a clean argv.
|
|
42
38
|
const ENV_FLAG_MAP = {
|
|
43
39
|
'--dev': 'dev', '-dev': 'dev', '--local': 'dev', '-local': 'dev',
|
|
@@ -57,14 +53,13 @@ let envTarget = null;
|
|
|
57
53
|
process.argv = [process.argv[0], process.argv[1], ...cleaned];
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
// Parse CLI arguments
|
|
61
56
|
const argv = await yargs(hideBin(process.argv))
|
|
62
57
|
// --dev / --prod are declared here ONLY so they appear in `--help`. The actual
|
|
63
|
-
// handling is the pre-yargs argv interception above
|
|
58
|
+
// handling is the pre-yargs argv interception above - these yargs-side values
|
|
64
59
|
// (`argv.dev` / `argv.prod`) are never read.
|
|
65
60
|
.option('dev', {
|
|
66
61
|
type: 'boolean',
|
|
67
|
-
description: 'Point the CLI at the local dev server (http://localhost:
|
|
62
|
+
description: 'Point the CLI at the local dev server (http://localhost:3000) and remember it',
|
|
68
63
|
})
|
|
69
64
|
.option('prod', {
|
|
70
65
|
type: 'boolean',
|
|
@@ -107,13 +102,17 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
107
102
|
description: 'When using -p, auto-allow all tool permission prompts (use with caution in CI/CD)',
|
|
108
103
|
default: false,
|
|
109
104
|
})
|
|
105
|
+
.option('permission-policy', {
|
|
106
|
+
type: 'string',
|
|
107
|
+
description: 'When using -p, path to a JSON permission policy for unattended runs (allow/deny/risk rules)',
|
|
108
|
+
})
|
|
110
109
|
.option('ollama-host', {
|
|
111
110
|
type: 'string',
|
|
112
111
|
description: 'Add local Ollama models to the model picker (e.g. http://localhost:11434)',
|
|
113
112
|
})
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
113
|
+
// claude-compatible flags (host drop-in masquerade). Declared so a host's
|
|
114
|
+
// claude launch flags parse into named options instead of leaking into the
|
|
115
|
+
// positional `argv._` task. See packages/cli README "host app".
|
|
117
116
|
.option('mcp-config', {
|
|
118
117
|
type: 'string',
|
|
119
118
|
description: 'Path to a JSON file of MCP servers to inject ({ "mcpServers": {...} })',
|
|
@@ -198,10 +197,62 @@ const argv = await yargs(hideBin(process.argv))
|
|
|
198
197
|
}, async () => {
|
|
199
198
|
// Handled by external command handler
|
|
200
199
|
})
|
|
201
|
-
.
|
|
200
|
+
.command('serve', 'Run Bike4Mind as an MCP server (stdio by default, or --http)', (yargs) => {
|
|
201
|
+
return yargs
|
|
202
|
+
.option('http', {
|
|
203
|
+
type: 'boolean',
|
|
204
|
+
description: 'Serve over streamable HTTP instead of stdio',
|
|
205
|
+
default: false,
|
|
206
|
+
})
|
|
207
|
+
.option('port', {
|
|
208
|
+
type: 'number',
|
|
209
|
+
description: 'Port for --http mode',
|
|
210
|
+
default: 7000,
|
|
211
|
+
})
|
|
212
|
+
.option('api-key', {
|
|
213
|
+
type: 'string',
|
|
214
|
+
description: 'Bike4Mind API key (overrides B4M_API_KEY and the stored login). Visible in process listings; prefer B4M_API_KEY',
|
|
215
|
+
})
|
|
216
|
+
.option('api-url', {
|
|
217
|
+
type: 'string',
|
|
218
|
+
description: 'Bike4Mind API URL (overrides B4M_API_URL and the configured backend)',
|
|
219
|
+
})
|
|
220
|
+
.example('b4m mcp serve', 'Serve over stdio for Claude Desktop and other local clients')
|
|
221
|
+
.example('b4m mcp serve --http --port 7000', 'Serve over streamable HTTP on port 7000');
|
|
222
|
+
}, async () => {
|
|
223
|
+
// Handled by external command handler
|
|
224
|
+
})
|
|
225
|
+
.demandCommand(1, 'You must provide a subcommand (list, add, remove, enable, disable, serve)');
|
|
226
|
+
})
|
|
227
|
+
.command('plugin', 'Manage B4M feature-module plugins', (yargs) => {
|
|
228
|
+
return yargs
|
|
229
|
+
.command('list', 'List installed plugins', {}, async () => {
|
|
230
|
+
// Handled by external command handler
|
|
231
|
+
})
|
|
232
|
+
.command('add <spec>', 'Install a plugin package', (yargs) => {
|
|
233
|
+
return yargs
|
|
234
|
+
.positional('spec', {
|
|
235
|
+
type: 'string',
|
|
236
|
+
describe: 'npm package, @scope/package, github:user/repo, or file:<path>',
|
|
237
|
+
})
|
|
238
|
+
.example('b4m plugin add @someone/b4m-plugin-foo', 'Install from npm')
|
|
239
|
+
.example('b4m plugin add github:user/b4m-plugin-foo', 'Install from GitHub');
|
|
240
|
+
}, async () => {
|
|
241
|
+
// Handled by external command handler
|
|
242
|
+
})
|
|
243
|
+
.command('remove <name>', 'Uninstall a plugin', (yargs) => {
|
|
244
|
+
return yargs.positional('name', {
|
|
245
|
+
type: 'string',
|
|
246
|
+
describe: 'Plugin name (config key, package name, or short name)',
|
|
247
|
+
});
|
|
248
|
+
}, async () => {
|
|
249
|
+
// Handled by external command handler
|
|
250
|
+
})
|
|
251
|
+
.demandCommand(1, 'You must provide a subcommand (list, add, remove)');
|
|
202
252
|
})
|
|
203
253
|
.command('update', 'Check for and install CLI updates')
|
|
204
254
|
.command('doctor', 'Run diagnostic checks on CLI installation')
|
|
255
|
+
.command('acp', 'Run as an Agent Client Protocol (ACP) stdio server for editors like Zed')
|
|
205
256
|
.help()
|
|
206
257
|
.alias('help', 'h')
|
|
207
258
|
.version(cliVersion)
|
|
@@ -238,8 +289,8 @@ if (argv['no-remote-skills']) {
|
|
|
238
289
|
process.env.B4M_NO_REMOTE_SKILLS = '1';
|
|
239
290
|
}
|
|
240
291
|
|
|
241
|
-
//
|
|
242
|
-
// Mirrors the established bin
|
|
292
|
+
// claude-compatible flags -> B4M_* env (read by src/index.tsx init).
|
|
293
|
+
// Mirrors the established bin->env->init channel (cf. --add-dir -> B4M_ADDITIONAL_DIRS).
|
|
243
294
|
if (argv['mcp-config']) {
|
|
244
295
|
process.env.B4M_MCP_CONFIG_FILE = resolve(argv['mcp-config']);
|
|
245
296
|
}
|
|
@@ -266,7 +317,7 @@ if (argv.resume) {
|
|
|
266
317
|
}
|
|
267
318
|
// Positional task (claude `<prompt>` form): seeds AND submits turn 1, stays interactive.
|
|
268
319
|
// Only when it's not a known subcommand and headless -p wasn't used.
|
|
269
|
-
const KNOWN_SUBCOMMANDS = new Set(['mcp', 'update', 'doctor']);
|
|
320
|
+
const KNOWN_SUBCOMMANDS = new Set(['mcp', 'plugin', 'update', 'doctor', 'acp']);
|
|
270
321
|
if (argv.prompt === undefined && argv._.length > 0 && !KNOWN_SUBCOMMANDS.has(String(argv._[0]))) {
|
|
271
322
|
process.env.B4M_INITIAL_PROMPT = String(argv._[0]);
|
|
272
323
|
}
|
|
@@ -284,10 +335,21 @@ const hasDist = existsSync(distPath);
|
|
|
284
335
|
const isDev = process.env.NODE_ENV === 'development' ||
|
|
285
336
|
(!hasDist && hasSource);
|
|
286
337
|
|
|
338
|
+
// Signal source mode to the app so endpoint resolution can default an
|
|
339
|
+
// otherwise-unconfigured run to the local dev server (build-time brand
|
|
340
|
+
// defaults are never injected into a source run). Set before any command
|
|
341
|
+
// dispatch below so --reset-api messaging and the app both observe it.
|
|
342
|
+
// See resolveApiEndpoint() / isSourceMode() in src/utils/apiUrl.ts.
|
|
343
|
+
if (isDev) {
|
|
344
|
+
process.env.B4M_SOURCE_MODE = '1';
|
|
345
|
+
}
|
|
346
|
+
|
|
287
347
|
// Handle --api-url / --reset-api flags
|
|
288
348
|
// These mutate ~/.bike4mind/config.json and exit before any auth flow runs,
|
|
289
349
|
// so devs can recover from a misconfigured customUrl without editing JSON.
|
|
290
|
-
|
|
350
|
+
// `mcp serve` has its own ephemeral --api-url (points that one server at a
|
|
351
|
+
// backend without touching config), so skip the mutate-and-exit path for it.
|
|
352
|
+
if ((argv['reset-api'] || argv['api-url'] !== undefined) && argv._[0] !== 'mcp') {
|
|
291
353
|
try {
|
|
292
354
|
let handleApiCommand;
|
|
293
355
|
|
|
@@ -360,6 +422,7 @@ if (argv.prompt !== undefined) {
|
|
|
360
422
|
dangerouslySkipPermissions: argv['dangerously-skip-permissions'] || false,
|
|
361
423
|
verbose: argv.verbose || false,
|
|
362
424
|
addDirs: rawAddDirs.map(d => resolve(d)),
|
|
425
|
+
permissionPolicyPath: argv['permission-policy'] ? resolve(argv['permission-policy']) : undefined,
|
|
363
426
|
});
|
|
364
427
|
// handleHeadlessCommand calls process.exit internally, but handle the case it doesn't
|
|
365
428
|
process.exit(0);
|
|
@@ -397,6 +460,31 @@ if (argv._[0] === 'mcp') {
|
|
|
397
460
|
}
|
|
398
461
|
}
|
|
399
462
|
|
|
463
|
+
// Handle plugin subcommands (external commands)
|
|
464
|
+
if (argv._[0] === 'plugin') {
|
|
465
|
+
const pluginSubcommand = argv._[1];
|
|
466
|
+
|
|
467
|
+
try {
|
|
468
|
+
let handlePluginCommand;
|
|
469
|
+
|
|
470
|
+
if (isDev) {
|
|
471
|
+
const { register } = require('tsx/esm/api');
|
|
472
|
+
register();
|
|
473
|
+
const module = await import('../src/commands/pluginCommand.ts');
|
|
474
|
+
handlePluginCommand = module.handlePluginCommand;
|
|
475
|
+
} else {
|
|
476
|
+
const module = await import('../dist/commands/pluginCommand.mjs');
|
|
477
|
+
handlePluginCommand = module.handlePluginCommand;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
await handlePluginCommand(pluginSubcommand, argv);
|
|
481
|
+
process.exit(0);
|
|
482
|
+
} catch (error) {
|
|
483
|
+
console.error('Error:', error.message);
|
|
484
|
+
process.exit(1);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
400
488
|
// Handle update command (external command)
|
|
401
489
|
if (argv._[0] === 'update') {
|
|
402
490
|
try {
|
|
@@ -420,6 +508,31 @@ if (argv._[0] === 'update') {
|
|
|
420
508
|
}
|
|
421
509
|
}
|
|
422
510
|
|
|
511
|
+
// Handle ACP server command (external command)
|
|
512
|
+
// stdio is the JSON-RPC transport, so this never returns until the client
|
|
513
|
+
// disconnects. Errors go to stderr to keep stdout as a clean protocol stream.
|
|
514
|
+
if (argv._[0] === 'acp') {
|
|
515
|
+
try {
|
|
516
|
+
let handleAcpCommand;
|
|
517
|
+
|
|
518
|
+
if (isDev) {
|
|
519
|
+
const { register } = require('tsx/esm/api');
|
|
520
|
+
register();
|
|
521
|
+
const module = await import('../src/commands/acpCommand.ts');
|
|
522
|
+
handleAcpCommand = module.handleAcpCommand;
|
|
523
|
+
} else {
|
|
524
|
+
const module = await import('../dist/commands/acpCommand.mjs');
|
|
525
|
+
handleAcpCommand = module.handleAcpCommand;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
await handleAcpCommand({ verbose: argv.verbose || false, version: cliVersion });
|
|
529
|
+
process.exit(0);
|
|
530
|
+
} catch (error) {
|
|
531
|
+
process.stderr.write(`Error: ${error.message}\n`);
|
|
532
|
+
process.exit(1);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
423
536
|
// Handle doctor command (external command)
|
|
424
537
|
if (argv._[0] === 'doctor') {
|
|
425
538
|
try {
|
|
@@ -444,14 +557,14 @@ if (argv._[0] === 'doctor') {
|
|
|
444
557
|
}
|
|
445
558
|
|
|
446
559
|
if (isDev) {
|
|
447
|
-
//
|
|
448
|
-
|
|
560
|
+
// Note: this is about *how the CLI runs* (unbuilt TypeScript source), which is
|
|
561
|
+
// distinct from the `--dev` flag (which selects the local dev *backend*).
|
|
562
|
+
console.log('🔧 Running from TypeScript source (no dist build)\n');
|
|
449
563
|
// Development: use tsx to run TypeScript
|
|
450
564
|
try {
|
|
451
565
|
const { register } = require('tsx/esm/api');
|
|
452
566
|
register();
|
|
453
567
|
|
|
454
|
-
// Import and run the main app
|
|
455
568
|
await import(join(__dirname, '../src/index.tsx'));
|
|
456
569
|
} catch (error) {
|
|
457
570
|
console.error('Failed to start CLI in development mode:', error);
|
|
@@ -463,7 +576,7 @@ if (isDev) {
|
|
|
463
576
|
try {
|
|
464
577
|
// Auto-update on launch (consent-first): when a newer version is available
|
|
465
578
|
// on a writable global prefix, install + re-exec into it BEFORE importing
|
|
466
|
-
// the code-split app
|
|
579
|
+
// the code-split app - running an install while dist/index.mjs is loaded
|
|
467
580
|
// would crash it. On the default 'ask' preference this prompts the user
|
|
468
581
|
// (Update once / Always / Skip / Never); 'auto' installs silently, 'never'
|
|
469
582
|
// does nothing. Wrapped so the updater can never block launching the CLI.
|
|
@@ -471,7 +584,7 @@ if (isDev) {
|
|
|
471
584
|
const { maybeAutoUpdateOnLaunch } = await import('../dist/commands/updateCommand.mjs');
|
|
472
585
|
await maybeAutoUpdateOnLaunch();
|
|
473
586
|
} catch {
|
|
474
|
-
// Updater is best-effort
|
|
587
|
+
// Updater is best-effort - fall through to the current version.
|
|
475
588
|
}
|
|
476
589
|
|
|
477
590
|
await import(join(__dirname, '../dist/index.mjs'));
|