brainclaw 1.7.1 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -94
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli.js +25 -3
- package/dist/commands/dispatch.js +2 -0
- package/dist/commands/doctor.js +17 -0
- package/dist/commands/harvest.js +124 -1
- package/dist/commands/mcp.js +32 -8
- package/dist/core/agent-capability.js +67 -0
- package/dist/core/agent-inventory.js +54 -7
- package/dist/core/agentrun-reconciler.js +126 -52
- package/dist/core/coordination.js +10 -9
- package/dist/core/dirty-scope.js +11 -5
- package/dist/core/dispatcher.js +109 -29
- package/dist/core/entity-operations.js +54 -1
- package/dist/core/execution-adapters.js +32 -51
- package/dist/core/execution.js +14 -8
- package/dist/core/instruction-templates.js +5 -4
- package/dist/core/runtime-signals.js +102 -0
- package/dist/core/schema.js +18 -0
- package/dist/core/spawn-check.js +125 -0
- package/dist/core/worktree.js +146 -7
- package/dist/facts.js +3 -3
- package/dist/facts.json +2 -2
- package/docs/cli.md +8 -4
- package/docs/integrations/mcp.md +48 -15
- package/docs/mcp-schema-changelog.md +16 -5
- package/docs/playbooks/team/index.md +7 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ If you've ever:
|
|
|
14
14
|
- watched two coworkers (or two agents) **edit the same files** without knowing it,
|
|
15
15
|
- or **gave up running multiple agents in parallel** because keeping them in sync was a pain,
|
|
16
16
|
|
|
17
|
-
brainclaw gives you durable shared state across sessions, agents, and teammates. Plans, claims, handoffs, decisions, and traps live in `.brainclaw/`, work identically across any compatible agent (Claude Code, Codex, Copilot, Cline, OpenCode, Cursor, Windsurf, Kilocode, Roo Code, Continue, Mistral Vibe, Hermes, Antigravity/Gemini CLI, …), and stay accessible whether you orchestrate them in parallel or pick them up one after another.
|
|
17
|
+
brainclaw gives you durable shared state across sessions, agents, and teammates. Plans, claims, handoffs, decisions, and traps live in `.brainclaw/`, work identically across any compatible agent (Claude Code, Codex, Copilot, Cline, OpenCode, Cursor, Windsurf, Kilocode, Roo Code, Continue, Mistral Vibe, Hermes, Antigravity/Gemini CLI, …), and stay accessible whether you orchestrate them in parallel or pick them up one after another.
|
|
18
18
|
|
|
19
19
|
Use it two ways — **together or separately**:
|
|
20
20
|
|
|
@@ -81,8 +81,8 @@ brainclaw is designed to sit alongside the coding agents teams are already using
|
|
|
81
81
|
|
|
82
82
|
| Logo | Agent | Tier | What brainclaw configures |
|
|
83
83
|
|---|---|---|---|
|
|
84
|
-
| [](https://github.com/openclaw/openclaw) | **[OpenClaw](https://github.com/openclaw/openclaw)** | B | MCP + brainclaw skill (SKILL.md) for structured project memory |
|
|
85
|
-
| [](https://github.com/NousResearch/hermes-agent) | **[Hermes Agent](https://github.com/NousResearch/hermes-agent)** | B | MCP + universal `.agents/skills/brainclaw/SKILL.md` |
|
|
84
|
+
| [](https://github.com/openclaw/openclaw) | **[OpenClaw](https://github.com/openclaw/openclaw)** | B | MCP + brainclaw skill (SKILL.md) for structured project memory |
|
|
85
|
+
| [](https://github.com/NousResearch/hermes-agent) | **[Hermes Agent](https://github.com/NousResearch/hermes-agent)** | B | MCP + universal `.agents/skills/brainclaw/SKILL.md` |
|
|
86
86
|
| [](https://github.com/qwibitai/nanoclaw) | **[NanoClaw](https://github.com/qwibitai/nanoclaw)** | C | brainclaw skill — messaging agent (WhatsApp, Telegram, Slack) |
|
|
87
87
|
| [](https://github.com/NVIDIA/NemoClaw) | **[NemoClaw](https://github.com/NVIDIA/NemoClaw)** | C | brainclaw skill — NVIDIA enterprise agent stack |
|
|
88
88
|
| [](https://github.com/sipeed/picoclaw) | **[PicoClaw](https://github.com/sipeed/picoclaw)** | C | brainclaw skill — edge/IoT agent (Go, <10MB RAM) |
|
|
@@ -109,52 +109,52 @@ If you want the least surprising setup today, use Linux first. If you are on Win
|
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
112
|
-
## Get Started
|
|
113
|
-
|
|
114
|
-
### 1. Let your coding agent lead
|
|
115
|
-
|
|
116
|
-
The smoothest first-run path is agent-first:
|
|
117
|
-
|
|
118
|
-
1. ask your coding agent to inspect the package and explain what brainclaw does
|
|
119
|
-
2. ask it to install brainclaw and initialize or join the project you're working on
|
|
120
|
-
3. use the CLI yourself when you need an explicit operator or fallback path
|
|
121
|
-
|
|
122
|
-
If you want to drive setup manually, use the steps below.
|
|
123
|
-
|
|
124
|
-
### 2. Install
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
npm install -g brainclaw
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 3. Bootstrap this machine
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
brainclaw setup-machine --yes
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
This detects the installed coding agents on the current machine, writes the machine-level MCP and user config Brainclaw manages for that detected set, and does **not** scan or initialize repositories.
|
|
137
|
-
|
|
138
|
-
### 4. Initialize or refresh the current project
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
cd your-project
|
|
142
|
-
brainclaw init
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
`brainclaw init` is now safe to rerun. It creates `.brainclaw/` when the project is new, or refreshes the managed Brainclaw and agent integration files when the project already has memory.
|
|
146
|
-
|
|
147
|
-
If you are explicitly adding another agent to an existing Brainclaw project, use:
|
|
148
|
-
|
|
149
|
-
```bash
|
|
150
|
-
brainclaw enable-agent <agent-name>
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### 5. Restart your agent
|
|
154
|
-
|
|
155
|
-
Restart your coding agent (or reload MCP servers) so it picks up the new configuration. After that, brainclaw tools are available.
|
|
156
|
-
|
|
157
|
-
### 6. Start working
|
|
112
|
+
## Get Started
|
|
113
|
+
|
|
114
|
+
### 1. Let your coding agent lead
|
|
115
|
+
|
|
116
|
+
The smoothest first-run path is agent-first:
|
|
117
|
+
|
|
118
|
+
1. ask your coding agent to inspect the package and explain what brainclaw does
|
|
119
|
+
2. ask it to install brainclaw and initialize or join the project you're working on
|
|
120
|
+
3. use the CLI yourself when you need an explicit operator or fallback path
|
|
121
|
+
|
|
122
|
+
If you want to drive setup manually, use the steps below.
|
|
123
|
+
|
|
124
|
+
### 2. Install
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm install -g brainclaw
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 3. Bootstrap this machine
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
brainclaw setup-machine --yes
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This detects the installed coding agents on the current machine, writes the machine-level MCP and user config Brainclaw manages for that detected set, and does **not** scan or initialize repositories.
|
|
137
|
+
|
|
138
|
+
### 4. Initialize or refresh the current project
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
cd your-project
|
|
142
|
+
brainclaw init
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`brainclaw init` is now safe to rerun. It creates `.brainclaw/` when the project is new, or refreshes the managed Brainclaw and agent integration files when the project already has memory.
|
|
146
|
+
|
|
147
|
+
If you are explicitly adding another agent to an existing Brainclaw project, use:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
brainclaw enable-agent <agent-name>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 5. Restart your agent
|
|
154
|
+
|
|
155
|
+
Restart your coding agent (or reload MCP servers) so it picks up the new configuration. After that, brainclaw tools are available.
|
|
156
|
+
|
|
157
|
+
### 6. Start working
|
|
158
158
|
|
|
159
159
|
Pick one of the canonical entry points depending on what you're doing:
|
|
160
160
|
|
|
@@ -186,7 +186,7 @@ For agents without MCP (e.g. Copilot reads `.github/copilot-instructions.md`), r
|
|
|
186
186
|
brainclaw export --detect --write
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
### 7. Verify it works
|
|
189
|
+
### 7. Verify it works
|
|
190
190
|
|
|
191
191
|
```bash
|
|
192
192
|
brainclaw status # see active sessions, claims, plans
|
|
@@ -197,11 +197,11 @@ brainclaw agent-board # see what each agent is doing
|
|
|
197
197
|
|
|
198
198
|
To configure brainclaw for all your repos and agents at once:
|
|
199
199
|
|
|
200
|
-
```bash
|
|
201
|
-
brainclaw setup --yes
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
This is the broader multi-repo wizard. It bootstraps the machine, scans your project roots, and initializes selected repositories in one pass.
|
|
200
|
+
```bash
|
|
201
|
+
brainclaw setup --yes
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
This is the broader multi-repo wizard. It bootstraps the machine, scans your project roots, and initializes selected repositories in one pass.
|
|
205
205
|
|
|
206
206
|
### Existing projects
|
|
207
207
|
|
|
@@ -232,7 +232,7 @@ Still sharp:
|
|
|
232
232
|
1. **Same-checkout concurrent edits** — running two agents in the *same* working tree (no per-claim worktree) is still the wrong answer. Use the dispatch path (auto-worktree per claim) instead of raw concurrent CLI sessions.
|
|
233
233
|
2. **Cross-machine sync** — federation across machines is on the roadmap, not in v1.x. Today brainclaw's store is local and one-machine-per-project.
|
|
234
234
|
3. **Spawn-and-forget assumptions** — spawned workers don't always commit their work cleanly. The brief-ack file confirms the spawn started; in the worst case the coordinator harvests open changes.
|
|
235
|
-
4. **Live state for hook-less agents** — Tier B/C agents without lifecycle hooks (Cursor, Cline, Windsurf, Copilot, Continue, Kilocode, Mistral Vibe, Hermes) get live context via `.live.md` companions regenerated on session-end and handoff, not via real-time push.
|
|
235
|
+
4. **Live state for hook-less agents** — Tier B/C agents without lifecycle hooks (Cursor, Cline, Windsurf, Copilot, Continue, Kilocode, Mistral Vibe, Hermes) get live context via `.live.md` companions regenerated on session-end and handoff, not via real-time push.
|
|
236
236
|
|
|
237
237
|
Recommended use today:
|
|
238
238
|
|
|
@@ -343,34 +343,56 @@ npm run test:coverage # with coverage report
|
|
|
343
343
|
|
|
344
344
|
## Changelog
|
|
345
345
|
|
|
346
|
-
For older releases (v0.x and the early v1.0 launch series), `git log` on `master` is the source of truth — every release commit follows the `chore(release): bump version to <semver>` convention, and the matching feature/fix commits reference their plan id (e.g. `feat(mcp): self-heal ... (pln#478)`).
|
|
347
|
-
|
|
348
|
-
### v1.7.
|
|
349
|
-
|
|
350
|
-
- **
|
|
351
|
-
|
|
352
|
-
the
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
`
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
-
|
|
346
|
+
For older releases (v0.x and the early v1.0 launch series), `git log` on `master` is the source of truth — every release commit follows the `chore(release): bump version to <semver>` convention, and the matching feature/fix commits reference their plan id (e.g. `feat(mcp): self-heal ... (pln#478)`).
|
|
347
|
+
|
|
348
|
+
### v1.7.3
|
|
349
|
+
|
|
350
|
+
- **Multi-agent dispatch hardening for JS/TS monorepos** — dispatched worktrees
|
|
351
|
+
junction-link per-package `node_modules` (npm / yarn / pnpm workspaces), not
|
|
352
|
+
just the root, and surface failed links instead of swallowing them; `brainclaw
|
|
353
|
+
worktree clean` now garbage-collects merged worktrees past birth-noise instead
|
|
354
|
+
of skipping them all; the agent inventory reports an agent `spawnable` when its
|
|
355
|
+
binary is on PATH even if `--version` is slow to start; dispatch-verification
|
|
356
|
+
guidance leads with `bclaw_dispatch_status` (not the untrustworthy Windows
|
|
357
|
+
wrapper pid); and a new `LANE-RESULT.json` convention + `brainclaw harvest
|
|
358
|
+
<assignment_id>` give workers a standard, MCP-free result channel. The dispatch
|
|
359
|
+
dirty-guard also ignores `.claude/`, `.cursor/`, and `.codex/` agent-local
|
|
360
|
+
config. (pln#523, pln#524, pln#525, pln#526, trp#371, trp#427, trp#428)
|
|
361
|
+
|
|
362
|
+
### v1.7.2
|
|
363
|
+
|
|
364
|
+
- **Sequence MCP tools are agent-first by default** — sequence creation,
|
|
365
|
+
listing, update, and deletion tools are now in the default MCP catalog, with
|
|
366
|
+
explicit lane item schemas (`planId`, optional `stepId`, `rank`,
|
|
367
|
+
dependencies, lane metadata) and matching canonical CRUD validation for
|
|
368
|
+
`entity="sequence"`.
|
|
369
|
+
|
|
370
|
+
### v1.7.1
|
|
371
|
+
|
|
372
|
+
- **MCP project context isolation fix** — `bclaw_switch` now keeps MCP switches
|
|
373
|
+
session-scoped even when the agent session has to be resolved or created on
|
|
374
|
+
the fly. Session lookup honors explicit session IDs, avoids adopting another
|
|
375
|
+
live process's session, detects Codex via native `CODEX_*` runtime variables,
|
|
376
|
+
and `bclaw_switch(list=true)` reports the session active project with
|
|
377
|
+
`active_source`.
|
|
378
|
+
|
|
379
|
+
### v1.7.0
|
|
380
|
+
|
|
381
|
+
- **Dispatch reliability + scope-aware dirty guard** — evidence-first
|
|
382
|
+
`agent_run` reconciliation avoids false terminal states, `bclaw_coordinate`
|
|
383
|
+
accepts pinned refs and a scope-aware `allow_dirty` guard, and the Hermes
|
|
384
|
+
agent integration joins the supported surfaces.
|
|
385
|
+
|
|
386
|
+
### v1.6.0
|
|
387
|
+
|
|
388
|
+
- **Bootstrap loop + cross-project agent workflow** — the bootstrap ideation
|
|
389
|
+
preset can materialize `PROJECT.md`, `bclaw_init_project` initializes and links
|
|
390
|
+
arbitrary project paths, and `project=` routing reaches `bclaw_work` /
|
|
391
|
+
`bclaw_loop` for linked-project operations.
|
|
392
|
+
|
|
393
|
+
### v1.5.3
|
|
394
|
+
|
|
395
|
+
- **Cross-project canonical grammar + CLI parity** (pln#359, all phases) — the canonical grammar (`bclaw_find / get / create / update / remove / transition`), `bclaw_context`, and `bclaw_coordinate` now accept an optional `project: <name>` argument that routes the operation to a linked project. Two link kinds are recognised: `cross_project_links` (sibling/peer projects in `config.yaml`, `brainclaw link list`) and workspace store-chain children. Arbitrary directory paths are rejected — adoption requires an explicit link, which gives the user a single point of control over what an agent can reach. Identity is sourced from the caller's home registry; entity writes + audit log entries land in the target. Unknown project names throw `validation_error` with a hint listing the configured links — no silent fallback. Cross-project `bclaw_coordinate` is **inbox-only**: claim/assignment/message all land in the target, the target agent picks the brief up async via its own `bclaw_work`, and auto-spawn from the source process is force-disabled because the spawn cwd / worktree are tied to the target's git repo (a warning surfaces in `FacadeResponse.warnings`). The CLI exposes the same as a global `--project <name>` flag, mutually exclusive with `--cwd`. Refs: helper `resolveProjectCwd` in `src/core/cross-project.ts`, MCP write/read handler dispatch in `src/commands/mcp.ts` and `src/commands/mcp-read-handlers.ts`, `--project` plumbing in `src/cli.ts` preAction, surface advertisement in `src/core/instruction-templates.ts`, plus tests in `tests/unit/cross-project.test.ts` (10 unit cases on the helper), `tests/unit/bclaw-coordinate.test.ts` (4 cross-project routing cases), and `tests/cli-cross-project.test.ts` (5 e2e cases). Closes the `--cwd` workaround pattern that had been the day-to-day shape of multi-project sessions.
|
|
374
396
|
- **Site facts contract** (umbrella `pln_7fdfd70d` sprint 0) — new `scripts/emit-site-facts.mjs` emits `dist/facts.{js,json}` from `MCP_TOOL_NAMES` + `ENTITY_NAMES` so the brainclaw-site (and any consumer) can pull live tool/entity counts at build time without forking the values into a hand-maintained config. The package `files` list ships `dist/facts.json`; build:cli runs the emitter as part of the chain.
|
|
375
397
|
|
|
376
398
|
### v1.5.2
|
|
@@ -428,15 +450,15 @@ For older releases (v0.x and the early v1.0 launch series), `git log` on `master
|
|
|
428
450
|
|
|
429
451
|
---
|
|
430
452
|
|
|
431
|
-
## License
|
|
432
|
-
|
|
433
|
-
brainclaw core is published under the [MIT License](LICENSE) — (c) 2024-2026 Juan Berdah.
|
|
434
|
-
|
|
435
|
-
The licensing split is simple:
|
|
436
|
-
|
|
437
|
-
- the local-first brainclaw core is MIT
|
|
438
|
-
- cloud shared-memory, remote collaboration services, advanced dashboards, and related hosted add-ons will live in separate commercial products
|
|
439
|
-
|
|
440
|
-
The MIT core covers what makes brainclaw useful inside a repo today: local project memory, local MCP and CLI coordination, onboarding and bootstrap, plans, claims, handoffs, runtime notes, and local agent integrations.
|
|
453
|
+
## License
|
|
454
|
+
|
|
455
|
+
brainclaw core is published under the [MIT License](LICENSE) — (c) 2024-2026 Juan Berdah.
|
|
456
|
+
|
|
457
|
+
The licensing split is simple:
|
|
458
|
+
|
|
459
|
+
- the local-first brainclaw core is MIT
|
|
460
|
+
- cloud shared-memory, remote collaboration services, advanced dashboards, and related hosted add-ons will live in separate commercial products
|
|
461
|
+
|
|
462
|
+
The MIT core covers what makes brainclaw useful inside a repo today: local project memory, local MCP and CLI coordination, onboarding and bootstrap, plans, claims, handoffs, runtime notes, and local agent integrations.
|
|
441
463
|
|
|
442
464
|
The goal is not to close brainclaw down. The goal is to keep the local-first core open and genuinely useful on its own, while keeping hosted collaboration features separate.
|
|
Binary file
|
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ import { runInstruction } from './commands/instruction.js';
|
|
|
30
30
|
import { runListAgents } from './commands/list-agents.js';
|
|
31
31
|
import { runSurfaceTaskResource } from './commands/surface-task-resource.js';
|
|
32
32
|
import { runListInstructions } from './commands/list-instructions.js';
|
|
33
|
-
import { runDoctor } from './commands/doctor.js';
|
|
33
|
+
import { runDoctor, runDoctorSpawnCheck } from './commands/doctor.js';
|
|
34
34
|
import { runRepair } from './commands/repair.js';
|
|
35
35
|
import { runStale } from './commands/stale.js';
|
|
36
36
|
import { runRebuild } from './commands/rebuild.js';
|
|
@@ -104,7 +104,7 @@ import { runDiscover } from './commands/discover.js';
|
|
|
104
104
|
import { runMigrate } from './commands/migrate.js';
|
|
105
105
|
import { runRunProfile } from './commands/run-profile.js';
|
|
106
106
|
import { runCompact } from './commands/compact.js';
|
|
107
|
-
import { runHarvestCandidates } from './commands/harvest.js';
|
|
107
|
+
import { runHarvestCandidates, runHarvestLane } from './commands/harvest.js';
|
|
108
108
|
import { runQuestionsCommand } from './commands/questions.js';
|
|
109
109
|
import { runReplyCommand } from './commands/reply.js';
|
|
110
110
|
import { requireRegisteredAgentIdentity } from './core/agent-registry.js';
|
|
@@ -681,7 +681,13 @@ program
|
|
|
681
681
|
.option('--repair', 'Rebuild dist/ when the MCP runtime is missing or stale')
|
|
682
682
|
.option('--after-migration', 'Run the v1.0 post-migration health check only (exits non-zero on any failure)')
|
|
683
683
|
.option('--dispatch', 'Run dispatch-health diagnostic only: reconcile open agent_runs and report stuck/unverified/silent failures (pln#496 step stp_8c072d75)')
|
|
684
|
-
.
|
|
684
|
+
.option('--spawn-check', 'Real spawn round-trip per installed agent before dispatch (pln#520 step 2): validates delivery + handshake on this host, exits non-zero on any installed-agent failure')
|
|
685
|
+
.option('--spawn-check-timeout <ms>', 'Per-agent timeout for --spawn-check (default 15000)', parseInt)
|
|
686
|
+
.action(async (options) => {
|
|
687
|
+
if (options.spawnCheck) {
|
|
688
|
+
await runDoctorSpawnCheck({ cwd: options.cwd, json: options.json, timeoutMs: options.spawnCheckTimeout });
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
685
691
|
runDoctor({ ...options, afterMigration: options.afterMigration, dispatch: options.dispatch });
|
|
686
692
|
});
|
|
687
693
|
// --- repair (Phase 4 Sprint 2 Lane C / pln#397) ---
|
|
@@ -1003,6 +1009,18 @@ program
|
|
|
1003
1009
|
const globalOpts = program.opts();
|
|
1004
1010
|
runHarvestCandidates({ ...options, cwd: globalOpts.cwd });
|
|
1005
1011
|
});
|
|
1012
|
+
// --- harvest (lane results, pln#526) ---
|
|
1013
|
+
program
|
|
1014
|
+
.command('harvest [assignment_id]')
|
|
1015
|
+
.description('Harvest a worker LANE-RESULT.json from its worktree into the project (pass an assignment id, or --all)')
|
|
1016
|
+
.option('--all', 'Harvest every lane result found across worktrees')
|
|
1017
|
+
.option('--dry-run', 'Preview without writing events/markers')
|
|
1018
|
+
.option('--worktree <path>', 'Explicit worktree path to scan (repeatable)', collect, [])
|
|
1019
|
+
.option('--json', 'Output as JSON')
|
|
1020
|
+
.action((assignmentId, options) => {
|
|
1021
|
+
const globalOpts = program.opts();
|
|
1022
|
+
runHarvestLane(assignmentId, { ...options, cwd: globalOpts.cwd });
|
|
1023
|
+
});
|
|
1006
1024
|
// --- prune-candidates ---
|
|
1007
1025
|
program
|
|
1008
1026
|
.command('prune-candidates')
|
|
@@ -1441,6 +1459,8 @@ dispatchCmd
|
|
|
1441
1459
|
.option('--agents <names>', 'Comma-separated list of agents to dispatch to')
|
|
1442
1460
|
.option('--lanes <names>', 'Comma-separated list of lanes to dispatch')
|
|
1443
1461
|
.option('--max <n>', 'Maximum assignments', parseInt)
|
|
1462
|
+
.option('--max-concurrency <n>', 'Opt-in cap on concurrent instances per host-binary (default: unlimited)', parseInt)
|
|
1463
|
+
.option('--model <name>', 'Model to run, decoupled from agent identity (e.g. --model sonnet)')
|
|
1444
1464
|
.option('--dry', 'Preview assignments without sending messages')
|
|
1445
1465
|
.option('--spawn', 'Autonomously launch CLI agents with invoke templates')
|
|
1446
1466
|
.option('--agent <name>', 'Dispatcher agent name')
|
|
@@ -1450,6 +1470,8 @@ dispatchCmd
|
|
|
1450
1470
|
agents: options.agents,
|
|
1451
1471
|
lanes: options.lanes,
|
|
1452
1472
|
max: options.max,
|
|
1473
|
+
maxConcurrency: options.maxConcurrency,
|
|
1474
|
+
model: options.model,
|
|
1453
1475
|
dry: options.dry,
|
|
1454
1476
|
spawn: options.spawn,
|
|
1455
1477
|
agent: options.agent,
|
|
@@ -87,6 +87,8 @@ export async function runDispatch(options) {
|
|
|
87
87
|
dryRun: options.dry,
|
|
88
88
|
dispatcherAgent,
|
|
89
89
|
autoExecute: options.spawn,
|
|
90
|
+
maxConcurrency: options.maxConcurrency,
|
|
91
|
+
model: options.model,
|
|
90
92
|
}, effectiveCwd);
|
|
91
93
|
if (!result) {
|
|
92
94
|
console.log('No active sequence found.');
|
package/dist/commands/doctor.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import * as childProcess from 'node:child_process';
|
|
5
5
|
import { reconcileAllOpenRuns } from '../core/agentrun-reconciler.js';
|
|
6
|
+
import { runSpawnCheck, renderSpawnCheckReport } from '../core/spawn-check.js';
|
|
6
7
|
import { loadAgentRun } from '../core/agentruns.js';
|
|
7
8
|
import { listAgentIdentities, resolveCurrentAgentIdentity } from '../core/agent-registry.js';
|
|
8
9
|
import { listCapabilities as listRegistryCapabilities, listTools as listRegistryTools } from '../core/registries.js';
|
|
@@ -565,6 +566,22 @@ function renderDispatchHealthHumanReport(report) {
|
|
|
565
566
|
}
|
|
566
567
|
return lines.join('\n');
|
|
567
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* pln#520 step 2 — `brainclaw doctor --spawn-check`. Real spawn round-trip per
|
|
571
|
+
* installed agent on the current host. Exits non-zero if any installed agent
|
|
572
|
+
* fails (so it gates CI / a pre-dispatch pre-flight).
|
|
573
|
+
*/
|
|
574
|
+
export async function runDoctorSpawnCheck(options = {}) {
|
|
575
|
+
const report = await runSpawnCheck(options);
|
|
576
|
+
if (options.json) {
|
|
577
|
+
console.log(JSON.stringify(report, null, 2));
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
console.log(renderSpawnCheckReport(report));
|
|
581
|
+
}
|
|
582
|
+
if (report.exit_code !== 0)
|
|
583
|
+
process.exit(report.exit_code);
|
|
584
|
+
}
|
|
568
585
|
export function runDoctor(options = {}) {
|
|
569
586
|
if (options.dispatch) {
|
|
570
587
|
const report = runDispatchHealthCheck(options);
|
package/dist/commands/harvest.js
CHANGED
|
@@ -14,7 +14,7 @@ import fs from 'node:fs';
|
|
|
14
14
|
import os from 'node:os';
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import crypto from 'node:crypto';
|
|
17
|
-
import { CandidateSchema } from '../core/schema.js';
|
|
17
|
+
import { CandidateSchema, LaneResultSchema } from '../core/schema.js';
|
|
18
18
|
import { listCandidates, listArchivedCandidates, saveCandidate } from '../core/candidates.js';
|
|
19
19
|
import { createRuntimeEvent } from '../core/events.js';
|
|
20
20
|
import { memoryExists } from '../core/io.js';
|
|
@@ -186,4 +186,127 @@ export function runHarvestCandidates(options = {}) {
|
|
|
186
186
|
}
|
|
187
187
|
console.log(`\n✔ Harvest complete${dryTag}: ${result.harvested.length} imported, ${result.skipped.length} skipped, ${result.errors.length} error(s).`);
|
|
188
188
|
}
|
|
189
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
190
|
+
// pln#526 — LANE-RESULT convention
|
|
191
|
+
//
|
|
192
|
+
// A dispatched worker writes a single `LANE-RESULT.json` at its worktree root
|
|
193
|
+
// as its final step. This is the standard, brief-boilerplate-free channel for a
|
|
194
|
+
// worker (especially a sandboxed one that cannot reach MCP) to report its
|
|
195
|
+
// outcome. The coordinator ingests it with `brainclaw harvest <assignment_id>`.
|
|
196
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
197
|
+
/** Conventional path of a worker's lane-result file at the worktree root. */
|
|
198
|
+
export function getLaneResultPath(worktreePath) {
|
|
199
|
+
return path.join(worktreePath, 'LANE-RESULT.json');
|
|
200
|
+
}
|
|
201
|
+
/** Idempotency marker so a lane-result is harvested once. */
|
|
202
|
+
function laneHarvestedMarkerPath(cwd, assignmentId) {
|
|
203
|
+
return path.join(cwd, '.brainclaw', 'coordination', 'runtime', 'result', `${assignmentId}.harvested`);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Scan worktrees for `LANE-RESULT.json`, validate, and ingest each: emit a
|
|
207
|
+
* `lane_result_harvested` runtime event (durable + queryable) and drop an
|
|
208
|
+
* idempotency marker so re-runs skip it. The worker's actual code lives in the
|
|
209
|
+
* worktree/branch; this surfaces the structured outcome (status + summary) the
|
|
210
|
+
* coordinator needs to converge the lane.
|
|
211
|
+
*/
|
|
212
|
+
export function harvestLaneResults(options = {}) {
|
|
213
|
+
const cwd = options.cwd ?? process.cwd();
|
|
214
|
+
const agent = options.agent ?? 'coordinator';
|
|
215
|
+
const result = { harvested: [], skipped: [], errors: [] };
|
|
216
|
+
const worktreePaths = (options.worktreePaths && options.worktreePaths.length > 0)
|
|
217
|
+
? options.worktreePaths
|
|
218
|
+
: autoDetectWorktreePaths(cwd);
|
|
219
|
+
for (const worktreePath of worktreePaths) {
|
|
220
|
+
const file = getLaneResultPath(worktreePath);
|
|
221
|
+
if (!fs.existsSync(file))
|
|
222
|
+
continue;
|
|
223
|
+
let lane;
|
|
224
|
+
try {
|
|
225
|
+
lane = LaneResultSchema.parse(JSON.parse(fs.readFileSync(file, 'utf-8')));
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
result.errors.push(`Failed to parse ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
// Assignment filter (when harvesting a specific lane).
|
|
232
|
+
if (options.assignmentId && lane.assignment_id !== options.assignmentId)
|
|
233
|
+
continue;
|
|
234
|
+
const marker = laneHarvestedMarkerPath(cwd, lane.assignment_id);
|
|
235
|
+
if (fs.existsSync(marker)) {
|
|
236
|
+
result.skipped.push(lane.assignment_id);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (!options.dryRun) {
|
|
240
|
+
try {
|
|
241
|
+
createRuntimeEvent({
|
|
242
|
+
agent,
|
|
243
|
+
event_type: 'lane_result_harvested',
|
|
244
|
+
text: `Lane result for ${lane.assignment_id}: ${lane.status} — ${lane.summary.slice(0, 120)}`,
|
|
245
|
+
tags: ['harvest', 'lane-result', lane.status],
|
|
246
|
+
assignment_id: lane.assignment_id,
|
|
247
|
+
metadata: {
|
|
248
|
+
assignment_id: lane.assignment_id,
|
|
249
|
+
status: lane.status,
|
|
250
|
+
artifacts: lane.artifacts ?? [],
|
|
251
|
+
files_changed: lane.files_changed ?? [],
|
|
252
|
+
source_worktree: worktreePath,
|
|
253
|
+
},
|
|
254
|
+
}, cwd);
|
|
255
|
+
fs.mkdirSync(path.dirname(marker), { recursive: true });
|
|
256
|
+
fs.writeFileSync(marker, new Date(0).toISOString(), 'utf-8');
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
result.errors.push(`Failed to ingest lane result for ${lane.assignment_id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
result.harvested.push(lane);
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
export function runHarvestLane(assignmentId, options = {}) {
|
|
268
|
+
const cwd = options.cwd ?? process.cwd();
|
|
269
|
+
if (!memoryExists(cwd)) {
|
|
270
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
if (!assignmentId && !options.all) {
|
|
274
|
+
console.error('Error: provide an <assignment_id>, or pass --all to harvest every lane result.');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
const result = harvestLaneResults({
|
|
278
|
+
assignmentId: options.all ? undefined : assignmentId,
|
|
279
|
+
worktreePaths: options.worktree,
|
|
280
|
+
dryRun: options.dryRun,
|
|
281
|
+
cwd,
|
|
282
|
+
});
|
|
283
|
+
if (options.json) {
|
|
284
|
+
console.log(JSON.stringify({
|
|
285
|
+
harvested: result.harvested,
|
|
286
|
+
skipped: result.skipped,
|
|
287
|
+
errors: result.errors,
|
|
288
|
+
}, null, 2));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const dryTag = options.dryRun ? ' (dry-run)' : '';
|
|
292
|
+
if (result.harvested.length === 0 && result.skipped.length === 0 && result.errors.length === 0) {
|
|
293
|
+
console.log(assignmentId ? `No LANE-RESULT.json found for ${assignmentId}.` : 'No lane results found in any worktree.');
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
for (const lane of result.harvested) {
|
|
297
|
+
const verb = options.dryRun ? ' (dry-run) Would harvest' : ' ✔ Harvested';
|
|
298
|
+
console.log(`${verb} [${lane.assignment_id}] ${lane.status}: ${lane.summary.slice(0, 100)}`);
|
|
299
|
+
if (lane.files_changed?.length)
|
|
300
|
+
console.log(` files: ${lane.files_changed.slice(0, 8).join(', ')}`);
|
|
301
|
+
if (lane.notes)
|
|
302
|
+
console.log(` notes: ${lane.notes.slice(0, 120)}`);
|
|
303
|
+
}
|
|
304
|
+
for (const id of result.skipped) {
|
|
305
|
+
console.log(` ⟳ Skipped (already harvested): ${id}`);
|
|
306
|
+
}
|
|
307
|
+
for (const err of result.errors) {
|
|
308
|
+
console.error(` ✗ ${err}`);
|
|
309
|
+
}
|
|
310
|
+
console.log(`\n✔ Lane harvest complete${dryTag}: ${result.harvested.length} harvested, ${result.skipped.length} skipped, ${result.errors.length} error(s).`);
|
|
311
|
+
}
|
|
189
312
|
//# sourceMappingURL=harvest.js.map
|
package/dist/commands/mcp.js
CHANGED
|
@@ -57,6 +57,30 @@ export const SCHEMA_VERSION = '1.0.0';
|
|
|
57
57
|
export const MCP_PROTOCOL_VERSIONS = ['2025-11-25', '2024-11-05'];
|
|
58
58
|
export const MCP_SERVER_NOT_INITIALIZED = -32002;
|
|
59
59
|
const MCP_RUNTIME_REPAIR_COMMAND = 'brainclaw doctor --repair';
|
|
60
|
+
const SEQUENCE_ITEM_INPUT_SCHEMA = {
|
|
61
|
+
type: 'object',
|
|
62
|
+
description: 'Sequence lane item. planId is required; stepId optionally narrows dispatch/readiness to a specific plan step.',
|
|
63
|
+
properties: {
|
|
64
|
+
planId: { type: 'string', minLength: 1, description: 'Plan item ID referenced by this sequence item.' },
|
|
65
|
+
stepId: { type: 'string', minLength: 1, description: 'Optional plan step ID inside planId for step-level dispatch/readiness.' },
|
|
66
|
+
rank: { type: 'number', minimum: 1, description: 'Positive integer ordering key. Ranks must be unique within a sequence.' },
|
|
67
|
+
hard_after: {
|
|
68
|
+
type: 'array',
|
|
69
|
+
items: { type: 'string' },
|
|
70
|
+
description: 'Sequence item planId values that must complete before this item becomes ready.',
|
|
71
|
+
},
|
|
72
|
+
soft_after: {
|
|
73
|
+
type: 'array',
|
|
74
|
+
items: { type: 'string' },
|
|
75
|
+
description: 'Advisory predecessor planId values; they inform ordering but do not block readiness.',
|
|
76
|
+
},
|
|
77
|
+
lane: { type: 'string', description: 'Optional lane label used for parallel dispatch grouping and filtering.' },
|
|
78
|
+
scope_hint: { type: 'string', description: 'Optional file/path scope hint for claim and brief generation.' },
|
|
79
|
+
rationale: { type: 'string', description: 'Optional explanation for this item or dependency placement.' },
|
|
80
|
+
},
|
|
81
|
+
required: ['planId', 'rank'],
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
};
|
|
60
84
|
const { $defs: loopPhaseDefs, ...loopPhaseItemSchema } = generatedSchemas.LoopPhase;
|
|
61
85
|
const loopSlotInputItemSchema = generatedSchemas.LoopSlotInput;
|
|
62
86
|
export const MCP_READ_TOOLS = [
|
|
@@ -160,7 +184,7 @@ export const MCP_READ_TOOLS = [
|
|
|
160
184
|
{
|
|
161
185
|
name: 'bclaw_list_sequences',
|
|
162
186
|
description: 'List coordination sequences with optional filters on status and id.',
|
|
163
|
-
annotations: { tier: '
|
|
187
|
+
annotations: { tier: 'standard', category: 'coordination', headlessApproval: 'auto' },
|
|
164
188
|
inputSchema: {
|
|
165
189
|
type: 'object',
|
|
166
190
|
properties: {
|
|
@@ -624,7 +648,7 @@ const MCP_WRITE_TOOLS = [
|
|
|
624
648
|
{
|
|
625
649
|
name: 'bclaw_create_sequence',
|
|
626
650
|
description: 'Create a coordination sequence shared by agents.',
|
|
627
|
-
annotations: { tier: '
|
|
651
|
+
annotations: { tier: 'standard', category: 'coordination', headlessApproval: 'prompt' },
|
|
628
652
|
inputSchema: {
|
|
629
653
|
type: 'object',
|
|
630
654
|
properties: {
|
|
@@ -632,7 +656,7 @@ const MCP_WRITE_TOOLS = [
|
|
|
632
656
|
description: { type: 'string', description: 'Optional sequence description.' },
|
|
633
657
|
status: { type: 'string', description: 'Status: draft, active, archived.' },
|
|
634
658
|
owner: { type: 'string', description: 'Optional sequence owner.' },
|
|
635
|
-
items: { type: 'array', description: 'Sequence items in rank order.', items:
|
|
659
|
+
items: { type: 'array', description: 'Sequence items in rank order.', items: SEQUENCE_ITEM_INPUT_SCHEMA },
|
|
636
660
|
tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags.' },
|
|
637
661
|
agent: { type: 'string', description: 'Agent name.' },
|
|
638
662
|
agentId: { type: 'string', description: 'Registered agent id.' },
|
|
@@ -643,7 +667,7 @@ const MCP_WRITE_TOOLS = [
|
|
|
643
667
|
{
|
|
644
668
|
name: 'bclaw_update_sequence',
|
|
645
669
|
description: 'Update a coordination sequence status, metadata, or items.',
|
|
646
|
-
annotations: { tier: '
|
|
670
|
+
annotations: { tier: 'standard', category: 'coordination', headlessApproval: 'prompt' },
|
|
647
671
|
inputSchema: {
|
|
648
672
|
type: 'object',
|
|
649
673
|
properties: {
|
|
@@ -652,7 +676,7 @@ const MCP_WRITE_TOOLS = [
|
|
|
652
676
|
description: { type: 'string', description: 'Optional new description.' },
|
|
653
677
|
status: { type: 'string', description: 'Status: draft, active, archived.' },
|
|
654
678
|
owner: { type: 'string', description: 'Optional sequence owner.' },
|
|
655
|
-
items: { type: 'array', description: 'Optional replacement items array.', items:
|
|
679
|
+
items: { type: 'array', description: 'Optional replacement items array.', items: SEQUENCE_ITEM_INPUT_SCHEMA },
|
|
656
680
|
tags: { type: 'array', items: { type: 'string' }, description: 'Optional replacement tags.' },
|
|
657
681
|
agent: { type: 'string', description: 'Agent name.' },
|
|
658
682
|
agentId: { type: 'string', description: 'Registered agent id.' },
|
|
@@ -754,7 +778,7 @@ const MCP_WRITE_TOOLS = [
|
|
|
754
778
|
{
|
|
755
779
|
name: 'bclaw_delete_sequence',
|
|
756
780
|
description: 'Delete a sequence by ID. Requires trusted or curator trust level.',
|
|
757
|
-
annotations: { tier: '
|
|
781
|
+
annotations: { tier: 'standard', category: 'coordination', headlessApproval: 'prompt' },
|
|
758
782
|
inputSchema: {
|
|
759
783
|
type: 'object',
|
|
760
784
|
properties: {
|
|
@@ -932,7 +956,7 @@ const MCP_WRITE_TOOLS = [
|
|
|
932
956
|
},
|
|
933
957
|
{
|
|
934
958
|
name: 'bclaw_coordinate',
|
|
935
|
-
description: 'Multi-agent coordination facade: assign tasks to agents (with claims), consult agents (no claim), create a review candidate, open an ideation loop, reroute an active claim to another agent, or summarize a thread. Returns a FacadeResponse with selected_targets, delivery_plan, artifacts, side_effects, and execution_status. IMPORTANT — execution_status semantics: `delivered_and_started` means the spawn wrapper touched the brief-ack sentinel (`.brainclaw/coordination/runtime/ack/<assignment_id>.ack`) — NOT that the worker is doing useful work. Spawned workers may still die silently before consuming the brief (cf. trap trp_38f63ea4). To verify a dispatch is actually alive,
|
|
959
|
+
description: 'Multi-agent coordination facade: assign tasks to agents (with claims), consult agents (no claim), create a review candidate, open an ideation loop, reroute an active claim to another agent, or summarize a thread. Returns a FacadeResponse with selected_targets, delivery_plan, artifacts, side_effects, and execution_status. IMPORTANT — execution_status semantics: `delivered_and_started` means the spawn wrapper touched the brief-ack sentinel (`.brainclaw/coordination/runtime/ack/<assignment_id>.ack`) — NOT that the worker is doing useful work. Spawned workers may still die silently before consuming the brief (cf. trap trp_38f63ea4). To verify a dispatch is actually alive, call `bclaw_dispatch_status(target_id=<asgn_…>)` — it reads the runtime sentinels (ack/heartbeat/completed/failed) plus captured stdout/stderr tails and returns a single health verdict + recommended next action (this is the `verify_with` target attached to the response). Do NOT diagnose liveness from the tracked pid: on Windows an ack-wrapped spawn runs under cmd.exe, so `agent_run.pid` is the wrapper (which exits early by design), NOT the real worker — `Get-Process -Id <pid>` reads it dead while the worker is alive and committing. The reconciler trusts the sentinels and infers `completed` from a post-start commit even when the worker never called bclaw_assignment_update. See docs/concepts/dispatch-lifecycle.md for the full FSM + diagnostic decision tree, and docs/integrations/<agent>.md for per-agent spawn semantics (notably codex.md re sandbox MCP availability).',
|
|
936
960
|
annotations: { tier: 'facade', category: 'coordination', headlessApproval: 'auto' },
|
|
937
961
|
inputSchema: {
|
|
938
962
|
type: 'object',
|
|
@@ -1092,7 +1116,7 @@ const MCP_WRITE_TOOLS = [
|
|
|
1092
1116
|
inputSchema: {
|
|
1093
1117
|
type: 'object',
|
|
1094
1118
|
properties: {
|
|
1095
|
-
entity: { type: 'string', description: 'Entity name: plan | decision | constraint | trap | handoff | runtime_note | candidate | claim | action | assignment | agent_run | cross_project_link. Others not yet wired.' },
|
|
1119
|
+
entity: { type: 'string', description: 'Entity name: plan | decision | constraint | trap | handoff | runtime_note | candidate | sequence | claim | action | assignment | agent_run | cross_project_link. Others not yet wired.' },
|
|
1096
1120
|
filter: { type: 'object', description: 'Filter keys: status, tag (single tag), tags (array, any-match), author, plan_id, source, auto_generated, limit, offset, includeLegacy (bool, default false), minAutoReflectConfidence (0-1, default 0.6). entity=agent_run also accepts assignment_id, claim_id, message_id.' },
|
|
1097
1121
|
project: { type: 'string', description: 'Optional: name (or path/basename) of a linked project to query. Defaults to the current project. Only cross_project_links (config.yaml) and workspace store-chain children are accepted — list with `brainclaw link list`.' },
|
|
1098
1122
|
},
|