brainclaw 1.9.1 → 1.10.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/README.md +47 -1
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli.js +18 -1
- package/dist/commands/code-map.js +129 -0
- package/dist/commands/codev.js +7 -0
- package/dist/commands/mcp.js +121 -0
- package/dist/commands/run-profile.js +3 -2
- package/dist/commands/switch.js +100 -89
- package/dist/core/agent-files.js +12 -0
- package/dist/core/code-map/backend.js +123 -0
- package/dist/core/code-map/core.js +81 -0
- package/dist/core/code-map/drafts.js +2 -0
- package/dist/core/code-map/extractor.js +29 -0
- package/dist/core/code-map/finalizer.js +191 -0
- package/dist/core/code-map/freshness.js +108 -0
- package/dist/core/code-map/ids.js +0 -0
- package/dist/core/code-map/importable.js +35 -0
- package/dist/core/code-map/indexes.js +197 -0
- package/dist/core/code-map/lang/java/imports.scm +17 -0
- package/dist/core/code-map/lang/java/index.js +254 -0
- package/dist/core/code-map/lang/java/tags.scm +48 -0
- package/dist/core/code-map/lang/php/imports.scm +21 -0
- package/dist/core/code-map/lang/php/index.js +251 -0
- package/dist/core/code-map/lang/php/tags.scm +44 -0
- package/dist/core/code-map/lang/provider.js +9 -0
- package/dist/core/code-map/lang/providers.js +24 -0
- package/dist/core/code-map/lang/python/imports.scm +90 -0
- package/dist/core/code-map/lang/python/index.js +364 -0
- package/dist/core/code-map/lang/python/tags.scm +81 -0
- package/dist/core/code-map/lang/query-runtime.js +374 -0
- package/dist/core/code-map/lang/registry.js +125 -0
- package/dist/core/code-map/lang/typescript/imports.scm +90 -0
- package/dist/core/code-map/lang/typescript/index.js +306 -0
- package/dist/core/code-map/lang/typescript/tags.js.scm +106 -0
- package/dist/core/code-map/lang/typescript/tags.scm +151 -0
- package/dist/core/code-map/lock.js +210 -0
- package/dist/core/code-map/materialized.js +51 -0
- package/dist/core/code-map/memory-reader.js +59 -0
- package/dist/core/code-map/paths.js +53 -0
- package/dist/core/code-map/query.js +568 -0
- package/dist/core/code-map/refresh.js +0 -0
- package/dist/core/code-map/resolve.js +177 -0
- package/dist/core/code-map/store.js +206 -0
- package/dist/core/code-map/types.js +288 -0
- package/dist/core/code-map/vocabulary.js +57 -0
- package/dist/core/code-map/wasm-loader.js +294 -0
- package/dist/core/code-map/work-section.js +206 -0
- package/dist/core/codev-rounds.js +4 -0
- package/dist/core/execution-adapters.js +11 -10
- package/dist/core/execution-profile.js +58 -0
- package/dist/core/facade-schema.js +9 -0
- package/dist/core/instruction-templates.js +2 -0
- package/dist/core/mcp-command-resolution.js +3 -1
- package/dist/core/store-resolution.js +41 -4
- package/dist/facts.js +9 -5
- package/dist/facts.json +8 -4
- package/dist/vendor/web-tree-sitter/tree-sitter.js +3980 -0
- package/dist/vendor/web-tree-sitter/tree-sitter.wasm +0 -0
- package/dist/wasm/tree-sitter-java.wasm +0 -0
- package/dist/wasm/tree-sitter-javascript.wasm +0 -0
- package/dist/wasm/tree-sitter-php.wasm +0 -0
- package/dist/wasm/tree-sitter-python.wasm +0 -0
- package/dist/wasm/tree-sitter-tsx.wasm +0 -0
- package/dist/wasm/tree-sitter-typescript.wasm +0 -0
- package/dist/wasm/tree-sitter.wasm +0 -0
- package/docs/cli.md +46 -8
- package/docs/code-map.md +198 -0
- package/docs/integrations/mcp.md +13 -6
- package/docs/mcp-schema-changelog.md +7 -3
- package/docs/quickstart.md +1 -1
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center"><strong>Local-first coordination and shared memory for coding agents.</strong></p>
|
|
8
8
|
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/jberdah/brainclaw/actions/workflows/ci.yml"><img src="https://github.com/jberdah/brainclaw/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/brainclaw"><img src="https://img.shields.io/npm/v/brainclaw?logo=npm" alt="npm version"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/brainclaw"><img src="https://img.shields.io/npm/dm/brainclaw" alt="npm downloads"></a>
|
|
13
|
+
<img src="https://img.shields.io/node/v/brainclaw" alt="node version">
|
|
14
|
+
<a href="./LICENSE"><img src="https://img.shields.io/npm/l/brainclaw" alt="MIT license"></a>
|
|
15
|
+
<img src="https://img.shields.io/badge/MCP-stdio-blue" alt="MCP">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
9
18
|
---
|
|
10
19
|
|
|
11
20
|
If you've ever:
|
|
@@ -35,6 +44,7 @@ It sits alongside your coding agents and gives them a shared state layer they ca
|
|
|
35
44
|
| **Project memory** | constraints, decisions, traps, handoffs, and layered instructions agents can resume from |
|
|
36
45
|
| **Coordination state** | shared plans, file claims (dispatched work isolated in Git Worktrees), runtime notes, and board views for active work |
|
|
37
46
|
| **Agent-ready context** | compact, prompt-sized context built from real workspace state instead of stale instructions |
|
|
47
|
+
| **Code Map** | a Tree-sitter symbol + import index (JS/TS, Python, PHP, Java) so agents ask "where is X / what should I read first" before editing, with related decisions/traps attached — `bclaw_code_find` / `bclaw_code_brief`, see [code map](docs/code-map.md) |
|
|
38
48
|
| **Native agent files** | auto-writes `CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.cursor/rules/`, `.windsurfrules`, and similar local guidance |
|
|
39
49
|
| **Multi-turn loops** | review and ideation loops with structured phases, iteration semantics, and per-phase memory filters — see[loop engine](docs/concepts/loop-engine.md) and [ideation loop](docs/concepts/ideation-loop.md) |
|
|
40
50
|
| **Machine AI surface discovery** | detects local coding agents plus desktop AI work surfaces such as ChatGPT Desktop and Gemini CLI |
|
|
@@ -43,6 +53,19 @@ It sits alongside your coding agents and gives them a shared state layer they ca
|
|
|
43
53
|
|
|
44
54
|
---
|
|
45
55
|
|
|
56
|
+
## Code Map
|
|
57
|
+
|
|
58
|
+
When an agent (or you) is about to edit unfamiliar code, the first question is *"where is this, and what should I read first?"* Code Map answers it without grepping: a per-project [Tree-sitter](https://tree-sitter.github.io/) index of the symbols each file defines (functions, classes, types, React components/hooks), what it imports/exports, and how files relate — across **JS / TS / TSX, Python, PHP, and Java**.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
brainclaw code-map find useAuth # locate a symbol / component / hook by name
|
|
62
|
+
brainclaw code-map brief src/App.tsx # ranked "read these first" list + related decisions/traps
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Capable agents use the MCP equivalents `bclaw_code_find` / `bclaw_code_brief`, each carrying a freshness badge. Code Map is a **discovery aid, not a build artifact**: it never changes your code, never blocks `bclaw_work`, and degrades gracefully — a stale or missing index says so instead of answering wrong. Pull-based (no daemon) and monorepo-aware. Full guide: **[Code Map](docs/code-map.md)**.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
46
69
|
## Agent Surfaces
|
|
47
70
|
|
|
48
71
|
brainclaw exposes the same collaboration state through three surfaces, but they do not have the same role in an agent-first workflow.
|
|
@@ -97,7 +120,7 @@ brainclaw is most effective today when one agent works at a time in a given chec
|
|
|
97
120
|
|
|
98
121
|
## Platform Support
|
|
99
122
|
|
|
100
|
-
brainclaw
|
|
123
|
+
brainclaw requires Node.js 22.12+ (`engines.node = ">=22.12.0"`). CI exercises Node 22 (Active LTS) and Node 24 (current LTS) on Linux; Windows runs on Node 24. Node 20 reached EOL in April 2026 and is no longer supported — the commander 15 upgrade requires Node ≥22.12. The recommended runtime is Node 22 LTS or Node 24 LTS.
|
|
101
124
|
|
|
102
125
|
| Logo | Platform | Status today | Notes |
|
|
103
126
|
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
@@ -359,6 +382,7 @@ If you are integrating Brainclaw into an agent workflow, start with the agent-fa
|
|
|
359
382
|
| `docs/quickstart-existing-project.md` | Joining a project that already has `.brainclaw/` |
|
|
360
383
|
| `docs/server-operations.md` | Operator and remote-server workflow guide |
|
|
361
384
|
| `docs/cli.md` | CLI reference for operators, scripts, and fallback use |
|
|
385
|
+
| `docs/code-map.md` | Code Map — symbol/import index, freshness model, monorepo behavior |
|
|
362
386
|
| `docs/concepts/memory.md` | What "memory" means in brainclaw |
|
|
363
387
|
| `docs/concepts/plans-and-claims.md` | Coordination layer |
|
|
364
388
|
| `docs/concepts/runtime-notes.md` | Ephemeral observations |
|
|
@@ -393,6 +417,28 @@ npm run test:coverage # with coverage report
|
|
|
393
417
|
|
|
394
418
|
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)`).
|
|
395
419
|
|
|
420
|
+
### v1.10.0
|
|
421
|
+
|
|
422
|
+
- **Code Map** — a new per-project [Tree-sitter](docs/code-map.md) symbol + import
|
|
423
|
+
index for **JS / TS / TSX, Python, PHP, and Java**. Ask *"where is X / what should I
|
|
424
|
+
read first"* before editing: `bclaw_code_find`, `bclaw_code_brief`,
|
|
425
|
+
`bclaw_code_status`, `bclaw_code_refresh` (CLI: `brainclaw code-map …`). Pull-based
|
|
426
|
+
freshness with a per-response badge; never blocks `bclaw_work`; monorepo-aware. See
|
|
427
|
+
[code map](docs/code-map.md).
|
|
428
|
+
- **Monorepo multi-project safety** — agents working in different child projects of a
|
|
429
|
+
monorepo are now genuinely independent:
|
|
430
|
+
- an anchored agent working *inside* a child project resolves **that** child, not the
|
|
431
|
+
repo root — no more "plans / index target the repo root";
|
|
432
|
+
- CLI `brainclaw switch` is **session-scoped by default** (two agents no longer clobber
|
|
433
|
+
a shared pointer); the new `--global` flag is the only path that sets the shared
|
|
434
|
+
workspace default; `switch --list` / show are session-aware;
|
|
435
|
+
- a session-less agent physically inside a child project beats a stale shared global
|
|
436
|
+
pointer (resolves the child it is in);
|
|
437
|
+
- dispatched / CoDev workers spawn with a **clean identity** — the coordinator's
|
|
438
|
+
session / project / agent env no longer leaks into a worker.
|
|
439
|
+
- Internal — MCP public surface fingerprint re-pinned for the Code Map tools (additive;
|
|
440
|
+
no tool removed or renamed).
|
|
441
|
+
|
|
396
442
|
### v1.9.1
|
|
397
443
|
|
|
398
444
|
- **Multi-project scoping fixes for monorepos** — MCP reads/writes resolve the
|
|
Binary file
|
package/dist/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ import { runListPlans } from './commands/list-plans.js';
|
|
|
19
19
|
import { runUpdatePlan } from './commands/update-plan.js';
|
|
20
20
|
import { runDeletePlan } from './commands/delete-plan.js';
|
|
21
21
|
import { runPlanResource } from './commands/plan-resource.js';
|
|
22
|
+
import { runCodeMap } from './commands/code-map.js';
|
|
22
23
|
import { runSequenceResource } from './commands/sequence.js';
|
|
23
24
|
import { runAddStep } from './commands/add-step.js';
|
|
24
25
|
import { runDeleteStep } from './commands/delete-step.js';
|
|
@@ -598,6 +599,20 @@ program
|
|
|
598
599
|
.action((subcommand, args, options) => {
|
|
599
600
|
runPlanResource(subcommand, args, { ...options, actualEffort: options.actualEffort, localOnly: options.localOnly });
|
|
600
601
|
});
|
|
602
|
+
// --- code-map ---
|
|
603
|
+
program
|
|
604
|
+
.command('code-map <subcommand> [args...]')
|
|
605
|
+
.description('Query the per-project Code Map (status, refresh, find, brief)')
|
|
606
|
+
.option('--json', 'Output as JSON')
|
|
607
|
+
.option('--all', 'For refresh: enumerate all supported files (full refresh)')
|
|
608
|
+
.option('--changed', 'For refresh: only changed files (default)')
|
|
609
|
+
.option('--limit <n>', 'Max results for find/brief', (v) => parseInt(v, 10))
|
|
610
|
+
.action((subcommand, args, options) => {
|
|
611
|
+
void runCodeMap(subcommand, args, options).catch((err) => {
|
|
612
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
613
|
+
process.exit(1);
|
|
614
|
+
});
|
|
615
|
+
});
|
|
601
616
|
// --- list-plans ---
|
|
602
617
|
program
|
|
603
618
|
.command('list-plans')
|
|
@@ -1806,15 +1821,17 @@ program
|
|
|
1806
1821
|
});
|
|
1807
1822
|
program
|
|
1808
1823
|
.command('switch [project]')
|
|
1809
|
-
.description('Set the active project for subsequent commands')
|
|
1824
|
+
.description('Set the active project for subsequent commands (session-scoped by default)')
|
|
1810
1825
|
.option('--list', 'List available projects in the workspace')
|
|
1811
1826
|
.option('--clear', 'Clear the active project (revert to cwd)')
|
|
1827
|
+
.option('--global', 'Set/clear the SHARED workspace default for ALL agents (writes active-project.json). Without it, switch is session-scoped and isolated.')
|
|
1812
1828
|
.option('--json', 'Output as JSON')
|
|
1813
1829
|
.action((project, options) => {
|
|
1814
1830
|
const globalOpts = options.parent?.parent ? program.opts() : {};
|
|
1815
1831
|
runSwitch(project, {
|
|
1816
1832
|
list: options.list,
|
|
1817
1833
|
clear: options.clear,
|
|
1834
|
+
global: options.global,
|
|
1818
1835
|
json: options.json,
|
|
1819
1836
|
cwd: globalOpts.cwd,
|
|
1820
1837
|
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `brainclaw code-map <subcommand>` — CLI surface over the Code Map backend
|
|
3
|
+
* (spec §9). Mirrors plan-resource.ts: a switch over the subcommand delegating
|
|
4
|
+
* to a JsonlBackend (status | refresh | find | brief). The backend owns all
|
|
5
|
+
* query logic; this file only adapts it to argv + stdout (text or --json), and
|
|
6
|
+
* every output carries the freshness_badge.
|
|
7
|
+
*/
|
|
8
|
+
import { JsonlBackend } from '../core/code-map/backend.js';
|
|
9
|
+
const KNOWN_SUBCOMMANDS = new Set(['status', 'refresh', 'find', 'brief']);
|
|
10
|
+
function backend() {
|
|
11
|
+
return new JsonlBackend();
|
|
12
|
+
}
|
|
13
|
+
function badgeLine(badge) {
|
|
14
|
+
const detailKeys = Object.keys(badge.details ?? {}).filter((k) => badge.details[k] !== null && badge.details[k] !== undefined);
|
|
15
|
+
const detail = detailKeys.length
|
|
16
|
+
? ` (${detailKeys.map((k) => `${k}=${JSON.stringify(badge.details[k])}`).join(', ')})`
|
|
17
|
+
: '';
|
|
18
|
+
return `Freshness: ${badge.status}${detail}`;
|
|
19
|
+
}
|
|
20
|
+
export async function runCodeMap(subcommand, args, options = {}) {
|
|
21
|
+
const normalized = (subcommand ?? '').trim().toLowerCase();
|
|
22
|
+
const be = backend();
|
|
23
|
+
const cwd = options.cwd;
|
|
24
|
+
if (normalized === 'status') {
|
|
25
|
+
const status = await be.status({ cwd });
|
|
26
|
+
printStatus(status, options);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (normalized === 'refresh') {
|
|
30
|
+
const scope = options.all ? 'all' : 'changed';
|
|
31
|
+
const result = await be.refresh({ scope, cwd });
|
|
32
|
+
printRefresh(result, options);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (normalized === 'find') {
|
|
36
|
+
const query = args.join(' ').trim();
|
|
37
|
+
if (!query) {
|
|
38
|
+
console.error('Error: code-map find requires <query>.');
|
|
39
|
+
console.error(' Usage: brainclaw code-map find <query>');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const result = await be.find({ query, limit: options.limit, cwd });
|
|
43
|
+
printFind(result, options);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (normalized === 'brief') {
|
|
47
|
+
const target = args.join(' ').trim();
|
|
48
|
+
if (!target) {
|
|
49
|
+
console.error('Error: code-map brief requires <symbol-or-path>.');
|
|
50
|
+
console.error(' Usage: brainclaw code-map brief <symbol-or-path>');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const result = await be.brief({ target, limit: options.limit, cwd });
|
|
54
|
+
printBrief(result, options);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
console.error(`Error: unknown code-map subcommand "${subcommand}".`);
|
|
58
|
+
console.error(` Available: ${[...KNOWN_SUBCOMMANDS].join(', ')}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
function printStatus(status, options) {
|
|
62
|
+
if (options.json) {
|
|
63
|
+
console.log(JSON.stringify(status, null, 2));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log('Code Map status');
|
|
67
|
+
console.log(` Store: ${status.store_exists ? 'present' : 'absent'}`);
|
|
68
|
+
console.log(` ${badgeLine(status.freshness_badge)}`);
|
|
69
|
+
if (status.stats) {
|
|
70
|
+
console.log(` Files: ${status.stats.files_indexed}`);
|
|
71
|
+
console.log(` Nodes: ${status.stats.nodes}`);
|
|
72
|
+
console.log(` Edges: ${status.stats.edges}`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log(' Stats: (none — index not built)');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function printRefresh(result, options) {
|
|
79
|
+
if (options.json) {
|
|
80
|
+
console.log(JSON.stringify(result, null, 2));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
console.log(`Code Map refresh [${result.scope}]`);
|
|
84
|
+
console.log(` Ran: ${result.ran}`);
|
|
85
|
+
console.log(` Lock: ${result.lock_acquired ? 'acquired' : 'not acquired'}`);
|
|
86
|
+
if (result.lock_status)
|
|
87
|
+
console.log(` Status: ${result.lock_status}`);
|
|
88
|
+
console.log(` ${badgeLine(result.freshness_badge)}`);
|
|
89
|
+
}
|
|
90
|
+
function printFind(result, options) {
|
|
91
|
+
if (options.json) {
|
|
92
|
+
console.log(JSON.stringify(result, null, 2));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(`Code Map find: "${result.query}"`);
|
|
96
|
+
console.log(` ${badgeLine(result.freshness_badge)}`);
|
|
97
|
+
if (result.matches.length === 0) {
|
|
98
|
+
console.log(' (no matches)');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
for (const m of result.matches) {
|
|
102
|
+
const sub = m.subtype ? ` ${m.subtype}` : '';
|
|
103
|
+
console.log(` [${m.score.toFixed(1)}] ${m.name}${sub} — ${m.path}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function printBrief(result, options) {
|
|
107
|
+
if (options.json) {
|
|
108
|
+
console.log(JSON.stringify(result, null, 2));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
console.log(`Code Map brief: "${result.target}"`);
|
|
112
|
+
console.log(` ${badgeLine(result.freshness_badge)}`);
|
|
113
|
+
if (result.suggested_files_to_read.length === 0) {
|
|
114
|
+
console.log(' Suggested files: (none)');
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.log(' Suggested files to read:');
|
|
118
|
+
for (const f of result.suggested_files_to_read) {
|
|
119
|
+
console.log(` [${f.score.toFixed(1)}] ${f.path} — ${f.reason}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (result.related_memory.length > 0) {
|
|
123
|
+
console.log(' Related memory:');
|
|
124
|
+
for (const mem of result.related_memory) {
|
|
125
|
+
console.log(` ${mem.id} (${mem.kind}): ${mem.text.slice(0, 80)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=code-map.js.map
|
package/dist/commands/codev.js
CHANGED
|
@@ -26,6 +26,7 @@ import { buildContext } from '../core/context.js';
|
|
|
26
26
|
import { buildCoordinationSnapshot } from '../core/coordination.js';
|
|
27
27
|
import { getDefaultInvokeTemplate, getSpawnableAgents } from '../core/agent-capability.js';
|
|
28
28
|
import { executeRound } from '../core/codev-rounds.js';
|
|
29
|
+
import { buildWorkerIdentityEnv } from '../core/execution-profile.js';
|
|
29
30
|
import { loadIdeationRound } from '../core/ideation.js';
|
|
30
31
|
import { summarizeMetrics, summarizeMetricsByRound } from '../core/codev-metrics.js';
|
|
31
32
|
import { generatePlansFromConvergence, generateSummaryNote } from '../core/codev-plan-gen.js';
|
|
@@ -422,12 +423,16 @@ function spawnConsultant(brief, threadId, personaName, cwd, agent) {
|
|
|
422
423
|
console.warn(` ⚠ Spawn error for ${agentName}/${personaName}: ${err.message}`);
|
|
423
424
|
});
|
|
424
425
|
};
|
|
426
|
+
// F7 (trp_0e5150d3): scrub coordinator identity so a consultant worker is an
|
|
427
|
+
// independent agent — these spawns previously inherited the full parent env.
|
|
428
|
+
const workerEnv = buildWorkerIdentityEnv(process.env, { agent: agentName });
|
|
425
429
|
if (agentName === 'codex') {
|
|
426
430
|
// Codex: use temp file via shell to avoid Windows .cmd ENOENT issues
|
|
427
431
|
const child = spawn('sh', ['-c', `cat "${briefFile}" | "${binaryPath}" exec --full-auto - ; rm -f "${briefFile}"`], {
|
|
428
432
|
detached: true,
|
|
429
433
|
stdio: 'ignore',
|
|
430
434
|
cwd,
|
|
435
|
+
env: workerEnv,
|
|
431
436
|
});
|
|
432
437
|
attachErrorHandler(child);
|
|
433
438
|
child.unref();
|
|
@@ -438,6 +443,7 @@ function spawnConsultant(brief, threadId, personaName, cwd, agent) {
|
|
|
438
443
|
detached: true,
|
|
439
444
|
stdio: 'ignore',
|
|
440
445
|
cwd,
|
|
446
|
+
env: workerEnv,
|
|
441
447
|
});
|
|
442
448
|
attachErrorHandler(child);
|
|
443
449
|
child.unref();
|
|
@@ -448,6 +454,7 @@ function spawnConsultant(brief, threadId, personaName, cwd, agent) {
|
|
|
448
454
|
detached: true,
|
|
449
455
|
stdio: 'ignore',
|
|
450
456
|
cwd,
|
|
457
|
+
env: workerEnv,
|
|
451
458
|
});
|
|
452
459
|
attachErrorHandler(child);
|
|
453
460
|
child.unref();
|
package/dist/commands/mcp.js
CHANGED
|
@@ -48,6 +48,7 @@ import { dispatch, dispatchReview, generateDispatchBrief } from '../core/dispatc
|
|
|
48
48
|
import { deleteMemoryItem, updateMemoryItem } from '../core/operations/memory-mutation.js';
|
|
49
49
|
import { assessMemoryPressure, buildCompactionTemplate, applyCompaction } from '../core/gc-semantic.js';
|
|
50
50
|
import { WorkRequestSchema, CoordinateRequestSchema } from '../core/facade-schema.js';
|
|
51
|
+
import { codeMapWorkSection, codeMapRefreshNextActions } from '../core/code-map/work-section.js';
|
|
51
52
|
import { getSpawnableAgents, getCapabilityProfile, buildInvokeCommand, validateAgentForDispatch } from '../core/agent-capability.js';
|
|
52
53
|
import { attemptExecution } from '../core/execution.js';
|
|
53
54
|
import { createAgentRun, transitionAgentRun } from '../core/agentruns.js';
|
|
@@ -449,8 +450,54 @@ export const MCP_READ_TOOLS = [
|
|
|
449
450
|
required: ['target_id'],
|
|
450
451
|
},
|
|
451
452
|
},
|
|
453
|
+
{
|
|
454
|
+
name: 'bclaw_code_status',
|
|
455
|
+
description: 'Code Map status for this project: store presence, freshness badge (fresh / stale_changed_files / stale_extractor / stale_grammar / partial / missing_index), and index stats (files, nodes, edges). Read-only; never refreshes. Pair with bclaw_code_refresh when freshness is missing_index or stale.',
|
|
456
|
+
annotations: { tier: 'standard', category: 'discovery', headlessApproval: 'auto' },
|
|
457
|
+
inputSchema: {
|
|
458
|
+
type: 'object',
|
|
459
|
+
properties: {},
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: 'bclaw_code_find',
|
|
464
|
+
description: 'Search the Code Map symbol index for a query (function/class/component/hook/type names). Returns ranked matches with path + score, plus a freshness_badge from the lazy read-path check. Read-only; never refreshes — a missing_index badge means run bclaw_code_refresh first.',
|
|
465
|
+
annotations: { tier: 'standard', category: 'discovery', headlessApproval: 'auto' },
|
|
466
|
+
inputSchema: {
|
|
467
|
+
type: 'object',
|
|
468
|
+
properties: {
|
|
469
|
+
query: { type: 'string', description: 'Symbol or token to search for (e.g. "App", "useAuth", "dispatch").' },
|
|
470
|
+
limit: { type: 'number', description: 'Max matches to return.' },
|
|
471
|
+
},
|
|
472
|
+
required: ['query'],
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: 'bclaw_code_brief',
|
|
477
|
+
description: 'Before editing, ask Code Map what to read: returns a ranked suggested_files_to_read list (cap 12) for a symbol or path, related brainclaw memory (cap 5), and a freshness_badge. Read-only; never refreshes.',
|
|
478
|
+
annotations: { tier: 'standard', category: 'discovery', headlessApproval: 'auto' },
|
|
479
|
+
inputSchema: {
|
|
480
|
+
type: 'object',
|
|
481
|
+
properties: {
|
|
482
|
+
target: { type: 'string', description: 'Symbol name or file path to build a reading brief for.' },
|
|
483
|
+
limit: { type: 'number', description: 'Max suggested files (hard-capped at 12 by the spec).' },
|
|
484
|
+
},
|
|
485
|
+
required: ['target'],
|
|
486
|
+
},
|
|
487
|
+
},
|
|
452
488
|
];
|
|
453
489
|
const MCP_WRITE_TOOLS = [
|
|
490
|
+
{
|
|
491
|
+
name: 'bclaw_code_refresh',
|
|
492
|
+
description: 'Rebuild the Code Map index for this project (Tree-sitter parse + shards + indexes, behind the per-project lock). scope="changed" (default) reparses changed files; scope="all" does a full refresh + compaction. A live competing lock fails fast with a clear status — refresh never blocks. Returns the resulting freshness_badge.',
|
|
493
|
+
annotations: { tier: 'standard', category: 'discovery', headlessApproval: 'prompt' },
|
|
494
|
+
inputSchema: {
|
|
495
|
+
type: 'object',
|
|
496
|
+
properties: {
|
|
497
|
+
scope: { type: 'string', enum: ['changed', 'all'], description: 'changed (default) reparses changed files only; all does a full refresh with orphan compaction.' },
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
},
|
|
454
501
|
{
|
|
455
502
|
name: 'bclaw_dispatch',
|
|
456
503
|
description: 'Unified dispatch entry for sequence-lane parallelization (parallelize plans across lanes). To open a NEW review of a commit/branch, use `bclaw_coordinate(intent="review", open_loop=true, targetAgents=[…])` instead — bclaw_dispatch is for sequence-driven execution, NOT for opening new reviews. `intent` discriminator: analysis (sequence lane status, read-only), execute (default — analyze + generate briefs + send to agents), review (routes an EXISTING already-reflected handoff to a reviewer — only for handoffs produced by `session-end --reflect-handoff` or similar). Consolidates the legacy bclaw_dispatch_analysis / bclaw_dispatch / bclaw_dispatch_review. Returns FacadeResponse; for verification semantics see the same response-validation guidance documented on `bclaw_coordinate`.',
|
|
@@ -2599,6 +2646,59 @@ async function _executeMcpToolCallInner(payload) {
|
|
|
2599
2646
|
const { handleCheckSecurity } = await import('./check-security-mcp.js');
|
|
2600
2647
|
return { response: toolResponse(await handleCheckSecurity(args, cwd)) };
|
|
2601
2648
|
}
|
|
2649
|
+
// Code Map tools (spec §9). These delegate to the async JsonlBackend, so
|
|
2650
|
+
// they are handled here rather than via the synchronous read-tool path.
|
|
2651
|
+
// status/find/brief are reads; refresh is a write (prompt approval).
|
|
2652
|
+
if (name === 'bclaw_code_status' || name === 'bclaw_code_find' || name === 'bclaw_code_brief' || name === 'bclaw_code_refresh') {
|
|
2653
|
+
const { JsonlBackend } = await import('../core/code-map/backend.js');
|
|
2654
|
+
const be = new JsonlBackend();
|
|
2655
|
+
if (name === 'bclaw_code_status') {
|
|
2656
|
+
const status = await be.status({ cwd });
|
|
2657
|
+
return {
|
|
2658
|
+
response: toolResponse({
|
|
2659
|
+
content: [{ type: 'text', text: `Code Map: ${status.store_exists ? 'store present' : 'no store'} — freshness=${status.freshness_badge.status}` }],
|
|
2660
|
+
structuredContent: { ...status, freshness_badge: status.freshness_badge },
|
|
2661
|
+
}),
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
if (name === 'bclaw_code_refresh') {
|
|
2665
|
+
const scope = args.scope === 'all' ? 'all' : 'changed';
|
|
2666
|
+
const result = await be.refresh({ scope, cwd });
|
|
2667
|
+
return {
|
|
2668
|
+
response: toolResponse({
|
|
2669
|
+
content: [{ type: 'text', text: `Code Map refresh [${result.scope}]: ran=${result.ran} freshness=${result.freshness_badge.status}${result.lock_status ? ` (${result.lock_status})` : ''}` }],
|
|
2670
|
+
structuredContent: { ...result, freshness_badge: result.freshness_badge },
|
|
2671
|
+
}),
|
|
2672
|
+
};
|
|
2673
|
+
}
|
|
2674
|
+
if (name === 'bclaw_code_find') {
|
|
2675
|
+
const query = typeof args.query === 'string' ? args.query : '';
|
|
2676
|
+
if (!query.trim()) {
|
|
2677
|
+
return { response: createToolErrorResponse('validation_error', 'bclaw_code_find requires a non-empty query.') };
|
|
2678
|
+
}
|
|
2679
|
+
const limit = typeof args.limit === 'number' ? args.limit : undefined;
|
|
2680
|
+
const result = await be.find({ query, limit, cwd });
|
|
2681
|
+
return {
|
|
2682
|
+
response: toolResponse({
|
|
2683
|
+
content: [{ type: 'text', text: `Code Map find "${result.query}": ${result.matches.length} match(es), freshness=${result.freshness_badge.status}` }],
|
|
2684
|
+
structuredContent: { ...result, freshness_badge: result.freshness_badge },
|
|
2685
|
+
}),
|
|
2686
|
+
};
|
|
2687
|
+
}
|
|
2688
|
+
// bclaw_code_brief
|
|
2689
|
+
const target = typeof args.target === 'string' ? args.target : '';
|
|
2690
|
+
if (!target.trim()) {
|
|
2691
|
+
return { response: createToolErrorResponse('validation_error', 'bclaw_code_brief requires a non-empty target.') };
|
|
2692
|
+
}
|
|
2693
|
+
const limit = typeof args.limit === 'number' ? args.limit : undefined;
|
|
2694
|
+
const result = await be.brief({ target, limit, cwd });
|
|
2695
|
+
return {
|
|
2696
|
+
response: toolResponse({
|
|
2697
|
+
content: [{ type: 'text', text: `Code Map brief "${result.target}": ${result.suggested_files_to_read.length} file(s) to read, freshness=${result.freshness_badge.status}` }],
|
|
2698
|
+
structuredContent: { ...result, freshness_badge: result.freshness_badge },
|
|
2699
|
+
}),
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2602
2702
|
if (MCP_READ_TOOLS.some((tool) => tool.name === name) || LEGACY_READ_TOOL_HANDLERS.has(name)) {
|
|
2603
2703
|
return {
|
|
2604
2704
|
response: appendLegacyMcpToolWarning(toolResponse(handleMcpReadToolCall(name, args, { cwd, connectionSessionId, effectiveScope: scopeInfo })), name),
|
|
@@ -4854,6 +4954,26 @@ async function _executeMcpToolCallInner(payload) {
|
|
|
4854
4954
|
nextActions.push({ tool: 'bclaw_context', args: { kind: 'memory' }, when: 'to read the full changed items behind context_diff' });
|
|
4855
4955
|
}
|
|
4856
4956
|
nextActions.push({ tool: 'bclaw_quick_capture', args: { text: '<finding>', type: '<decision|trap|constraint|note>' }, when: 'capture decisions/traps as you work' });
|
|
4957
|
+
// Code Map opt-in section (spec §10). Single live seam: returns null
|
|
4958
|
+
// (and does no work) unless the project's Code Map manifest carries
|
|
4959
|
+
// code_map_enabled:true. Never refreshes; bounded ≤2500ms on an active
|
|
4960
|
+
// lock; surfaces a missing_index hint or stale results with the badge.
|
|
4961
|
+
let codeMapSection = null;
|
|
4962
|
+
try {
|
|
4963
|
+
codeMapSection = await codeMapWorkSection(targetCwd, {
|
|
4964
|
+
query: workReq.scope ?? workReq.contextTarget,
|
|
4965
|
+
});
|
|
4966
|
+
}
|
|
4967
|
+
catch {
|
|
4968
|
+
// Best-effort: Code Map must never break or block bclaw_work.
|
|
4969
|
+
}
|
|
4970
|
+
// Code Map onboarding/freshness nudge (pln#588): promote the actionable
|
|
4971
|
+
// refresh to a first-class next_action so a fresh or stale project's first
|
|
4972
|
+
// bclaw_work TELLS the agent to build/update the index — the passive
|
|
4973
|
+
// missing_index hint alone was easy to skip. Still never refreshes here.
|
|
4974
|
+
for (const action of codeMapRefreshNextActions(codeMapSection)) {
|
|
4975
|
+
nextActions.push(action);
|
|
4976
|
+
}
|
|
4857
4977
|
const facadeResponse = {
|
|
4858
4978
|
status: 'ok',
|
|
4859
4979
|
intent: workReq.intent,
|
|
@@ -4868,6 +4988,7 @@ async function _executeMcpToolCallInner(payload) {
|
|
|
4868
4988
|
bootstrap_verdict: bootstrapVerdict,
|
|
4869
4989
|
next_action: nextAction,
|
|
4870
4990
|
next_actions: nextActions,
|
|
4991
|
+
...(codeMapSection ? { code_map: codeMapSection } : {}),
|
|
4871
4992
|
};
|
|
4872
4993
|
const summaryParts = [`✔ bclaw_work [${workReq.intent}] session=${sessionResult.session_id}`];
|
|
4873
4994
|
if (claimId)
|
|
@@ -37,8 +37,9 @@ export function runRunProfile(profileName, options = {}) {
|
|
|
37
37
|
console.error(`Unknown agent: ${options.agent}. Using profile invoke template.`);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
// Replace {prompt} placeholder with the profile prompt
|
|
41
|
-
|
|
40
|
+
// Replace {prompt} placeholder with the profile prompt. Escape backslashes
|
|
41
|
+
// before quotes so a backslash in the prompt can't break out of the quoting.
|
|
42
|
+
const command = invoke.replace(/\{prompt\}/g, profile.prompt.replace(/\\/g, '\\\\').replace(/"/g, '\\"'));
|
|
42
43
|
if (options.dry) {
|
|
43
44
|
console.log(`[dry-run] Profile: ${profile.name}`);
|
|
44
45
|
console.log(`[dry-run] Command: ${command}`);
|