brainclaw 1.24.0 → 1.25.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.
@@ -179,16 +179,22 @@ export async function pushPending(options = {}) {
179
179
  throw new Error('Adresse du cloud inconnue : passez --url. Elle n\'est pas conservée par l\'appairage ' +
180
180
  '(état de connexion sans champ d\'URL), et aucune adresse n\'est devinée.');
181
181
  }
182
+ const pending = list('pending', cwd);
183
+ // File vide = rien à signer : sortir AVANT de résoudre l'identité. Le signataire est
184
+ // déduit de la première entrée pending ; le chercher sur une file vide transformait
185
+ // « tout est déjà parti » en erreur d'identité — vécu le 2026-08-10, juste après un
186
+ // envoi complet dont il ne restait que des conflits.
187
+ if (pending.length === 0)
188
+ return result;
182
189
  // L'identité SIGNATAIRE du transport : celle de l'agent qui a produit les enveloppes.
183
190
  // Elle est portée par l'entrée d'outbox, donc le transport n'a pas à deviner qui signe.
184
- const agentId = options.agentId ?? list('pending', cwd)[0]?.origin_agent_id;
191
+ const agentId = options.agentId ?? pending[0]?.origin_agent_id;
185
192
  const identity = agentId ? loadAgentSigningKey(agentId) : undefined;
186
193
  if (!identity) {
187
194
  throw new Error('Identité de signature introuvable : le cloud vérifie une signature de TRANSPORT ' +
188
195
  'liant envelope_id, rev et base_rev. Sans elle, chaque envoi est refusé en 422.');
189
196
  }
190
197
  const identityPem = identity.privateKeyPem;
191
- const pending = list('pending', cwd);
192
198
  const batch = options.limit ? pending.slice(0, options.limit) : pending;
193
199
  result.attempted = batch.length;
194
200
  if (options.dryRun)
@@ -214,7 +220,14 @@ export async function pushPending(options = {}) {
214
220
  // réel en écrasement silencieux du travail d'un autre appareil.
215
221
  if (res.status === 409) {
216
222
  const detail = (await res.clone().json().catch(() => ({})));
217
- const expected = detail['expected_base_rev'] ?? detail['expected'];
223
+ // `current_head_rev` est le nom que le serveur DÉPLOYÉ répond (projection.ts,
224
+ // REV_CONFLICT). Les deux autres sont des noms historiques gardés en repli.
225
+ // Dérive constatée le 2026-08-10 : le client lisait `expected_base_rev` sur une
226
+ // réponse qui ne l'a jamais porté — le recalage ne se déclenchait donc JAMAIS et
227
+ // chaque mise à jour finissait en conflit. Le test unitaire rejoue depuis la
228
+ // forme de réponse RÉELLE du serveur ; leçon dec#160/162, un contrat inter-
229
+ // services ne se vérifie que contre le service.
230
+ const expected = detail['current_head_rev'] ?? detail['expected_base_rev'] ?? detail['expected'];
218
231
  if (typeof expected === 'string' || typeof expected === 'number') {
219
232
  // On RESIGNE avec la nouvelle base_rev : c'est précisément ce que la signature
220
233
  // au niveau du transport rend possible, et qu'une signature figée à l'émission
@@ -48,6 +48,9 @@ export const MCP_HEADLESS_AUTO_TOOL_NAMES = [
48
48
  'bclaw_code_status',
49
49
  'bclaw_code_find',
50
50
  'bclaw_code_brief',
51
+ 'bclaw_code_impact',
52
+ 'bclaw_code_export',
53
+ 'bclaw_code_outline',
51
54
  'bclaw_send_message',
52
55
  'bclaw_ack_message',
53
56
  'bclaw_write_note',
@@ -7,6 +7,7 @@ import yaml from 'yaml';
7
7
  import { logger } from './logger.js';
8
8
  import { loadConfig } from './config.js';
9
9
  import { parsePorcelainZ, isSystemDirtyPath } from './dirty-scope.js';
10
+ import { entityRecordDirs } from './io.js';
10
11
  /** Normalizes a path for use in git CLI arguments (forward slashes on Windows). */
11
12
  function gitPath(p) {
12
13
  return p.replace(/\\/g, '/');
@@ -1548,6 +1549,68 @@ export function probeLocalBranch(mainWorktreePath, branchName) {
1548
1549
  export function isGitRepo(cwd) {
1549
1550
  return runGit(['rev-parse', '--is-inside-work-tree'], cwd).ok;
1550
1551
  }
1552
+ /**
1553
+ * Comparison key for worktree paths: PHYSICAL identity when the path exists
1554
+ * (realpath expands Windows 8.3 short names — `RUNNER~1` and `runneradmin`
1555
+ * are the same directory but different strings, and git always reports the
1556
+ * long form while a claim may carry the short one), else plain resolution.
1557
+ * Forward slashes, case-folded on win32.
1558
+ */
1559
+ function worktreePathKey(p) {
1560
+ let resolved;
1561
+ try {
1562
+ resolved = fs.realpathSync.native(p);
1563
+ }
1564
+ catch {
1565
+ resolved = path.resolve(p);
1566
+ }
1567
+ resolved = resolved.replace(/\\/g, '/').replace(/\/+$/, '');
1568
+ return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
1569
+ }
1570
+ /**
1571
+ * Worktree paths referenced by an ACTIVE, non-expired claim — the set the GC
1572
+ * must never touch.
1573
+ *
1574
+ * Read directly from the claims record dirs (both layouts, pln#649) instead of
1575
+ * claims.ts: claims.ts imports worktree.ts, so the dependency can only point
1576
+ * this way. The parse is deliberately lenient — an unreadable claim simply does
1577
+ * not protect anything; it never blocks the GC of OTHER worktrees.
1578
+ *
1579
+ * Scope note: dispatch claims are project-local, so the project store is the
1580
+ * right authority here; workspace-level claims (cross-project) never carry a
1581
+ * lane worktree_path.
1582
+ */
1583
+ function activeClaimWorktreePaths(cwd) {
1584
+ const out = new Set();
1585
+ const now = new Date();
1586
+ for (const dir of entityRecordDirs('claims', cwd)) {
1587
+ let files;
1588
+ try {
1589
+ files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
1590
+ }
1591
+ catch {
1592
+ continue;
1593
+ }
1594
+ for (const f of files) {
1595
+ try {
1596
+ const claim = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));
1597
+ if (claim.status !== 'active')
1598
+ continue;
1599
+ if (typeof claim.worktree_path !== 'string' || !claim.worktree_path)
1600
+ continue;
1601
+ // Mirror isClaimExpired: a zombie claim past its expiry must not make a
1602
+ // worktree un-GC-able forever.
1603
+ if (claim.expires_at && new Date(claim.expires_at) < now)
1604
+ continue;
1605
+ out.add(worktreePathKey(claim.worktree_path));
1606
+ }
1607
+ catch {
1608
+ /* lenient by design — see above */
1609
+ }
1610
+ }
1611
+ }
1612
+ return out;
1613
+ }
1551
1614
  /**
1552
1615
  * Removes worktrees whose branch has been fully merged into the current branch
1553
1616
  * (typically master/main after a merge). Also removes brainclaw-managed
@@ -1561,6 +1624,16 @@ export function isGitRepo(cwd) {
1561
1624
  * - content (`git cherry HEAD <branch>`, patch-id): catches squash merges,
1562
1625
  * which is GitHub's default merge strategy on this repo and previously left
1563
1626
  * every squashed lane un-GC-able forever.
1627
+ *
1628
+ * ACTIVE-CLAIM GATE (incident 2026-08-10): a freshly-dispatched lane worktree
1629
+ * has no commits of its own — its branch IS an ancestor of HEAD, so both merged
1630
+ * probes say "merged" — and before the agent's first write it has no uncommitted
1631
+ * changes either. Both historical gates therefore pass during a lane's startup
1632
+ * window, and the post-merge hook destroyed a live codex lane 7 minutes after
1633
+ * spawn (worktree emptied under the running agent). The coordination store is
1634
+ * the authority on liveness: a worktree referenced by an active claim is
1635
+ * untouchable — merged or not, clean or not, force or not. The escape hatch is
1636
+ * releasing the claim, never bypassing it.
1564
1637
  */
1565
1638
  export function cleanMergedWorktrees(mainWorktreePath, options = {}) {
1566
1639
  const result = { removed: [], skipped: [], pruned: false };
@@ -1580,9 +1653,16 @@ export function cleanMergedWorktrees(mainWorktreePath, options = {}) {
1580
1653
  .filter(Boolean)
1581
1654
  : []);
1582
1655
  const worktrees = listWorktrees(mainWorktreePath);
1656
+ const protectedPaths = activeClaimWorktreePaths(mainWorktreePath);
1583
1657
  for (const wt of worktrees) {
1584
1658
  if (wt.is_main)
1585
1659
  continue;
1660
+ // Active-claim gate — see the function doc. Checked BEFORE the merged
1661
+ // probes and BEFORE `force`: a live dispatched lane is never GC-able.
1662
+ if (protectedPaths.has(worktreePathKey(wt.path))) {
1663
+ result.skipped.push({ path: wt.path, reason: 'active claim' });
1664
+ continue;
1665
+ }
1586
1666
  // trp#926 — a lane's branch is "merged" if EITHER git says its commits are
1587
1667
  // ancestors of HEAD (fast-forward / merge-commit) OR every commit's patch
1588
1668
  // is already on HEAD (squash-merge, catching GitHub's default strategy).
@@ -1621,7 +1701,7 @@ export function cleanMergedWorktrees(mainWorktreePath, options = {}) {
1621
1701
  }
1622
1702
  }
1623
1703
  // Clean orphan brainclaw worktree directories (no matching git worktree)
1624
- cleanOrphanWorktreeDirs(mainWorktreePath, worktrees, result, options.dryRun);
1704
+ cleanOrphanWorktreeDirs(mainWorktreePath, worktrees, result, options.dryRun, protectedPaths);
1625
1705
  return result;
1626
1706
  }
1627
1707
  /** A worker whose heartbeat file was touched within this window looks alive. */
@@ -1722,7 +1802,7 @@ export function gcWorktreeIfHarvested(mainWorktreePath, worktreePath, options =
1722
1802
  * Removes brainclaw-managed worktree directories under ~/.brainclaw/worktrees/
1723
1803
  * that no longer have a corresponding git worktree entry.
1724
1804
  */
1725
- function cleanOrphanWorktreeDirs(mainWorktreePath, activeWorktrees, result, dryRun) {
1805
+ function cleanOrphanWorktreeDirs(mainWorktreePath, activeWorktrees, result, dryRun, protectedPaths = new Set()) {
1726
1806
  const base = worktreesBaseDir(mainWorktreePath);
1727
1807
  if (!fs.existsSync(base))
1728
1808
  return;
@@ -1740,6 +1820,13 @@ function cleanOrphanWorktreeDirs(mainWorktreePath, activeWorktrees, result, dryR
1740
1820
  const dirPath = path.resolve(path.join(base, entry.name));
1741
1821
  if (activePaths.has(dirPath))
1742
1822
  continue;
1823
+ // Active-claim gate: a dir whose git admin entry vanished can still host a
1824
+ // LIVE agent (the 2026-08-10 incident left exactly this state behind). If a
1825
+ // claim still points here, it is not debris.
1826
+ if (protectedPaths.has(worktreePathKey(dirPath))) {
1827
+ result.skipped.push({ path: dirPath, reason: 'active claim' });
1828
+ continue;
1829
+ }
1743
1830
  // This directory is not referenced by any git worktree — it's orphaned
1744
1831
  if (dryRun) {
1745
1832
  result.removed.push(dirPath);
package/dist/facts.js CHANGED
@@ -1,11 +1,11 @@
1
1
  // Generated by scripts/emit-site-facts.mjs at build time. Do not edit manually.
2
- // Source: brainclaw v1.24.0 on 2026-08-10T18:03:29.240Z
2
+ // Source: brainclaw v1.25.0 on 2026-08-10T23:31:26.947Z
3
3
  export const FACTS = {
4
- "version": "1.24.0",
5
- "generated_at": "2026-08-10T18:03:29.240Z",
4
+ "version": "1.25.0",
5
+ "generated_at": "2026-08-10T23:31:26.947Z",
6
6
  "tools": {
7
- "count": 67,
8
- "published_count": 65,
7
+ "count": 70,
8
+ "published_count": 68,
9
9
  "names": [
10
10
  "bclaw_bootstrap",
11
11
  "bclaw_release_notes",
@@ -34,6 +34,9 @@ export const FACTS = {
34
34
  "bclaw_code_status",
35
35
  "bclaw_code_find",
36
36
  "bclaw_code_brief",
37
+ "bclaw_code_impact",
38
+ "bclaw_code_export",
39
+ "bclaw_code_outline",
37
40
  "bclaw_code_refresh",
38
41
  "bclaw_dispatch",
39
42
  "bclaw_send_message",
@@ -474,7 +477,7 @@ export const FACTS = {
474
477
  },
475
478
  "bench": {
476
479
  "schema": "brainclaw.bench.v1",
477
- "generated_at": "2026-08-10T18:03:27.087Z",
480
+ "generated_at": "2026-08-10T23:31:25.464Z",
478
481
  "node_version": "v24.18.0",
479
482
  "platform": "linux-x64",
480
483
  "repeats": 3,
@@ -483,7 +486,7 @@ export const FACTS = {
483
486
  "name": "cold_onboard",
484
487
  "volume": "empty",
485
488
  "description": "fresh machine → init → first useful context. Baseline for time-to-first-value.",
486
- "duration_ms_median": 71,
489
+ "duration_ms_median": 59,
487
490
  "payload_chars_median": 1640,
488
491
  "payload_tokens_est_median": 410
489
492
  },
@@ -491,9 +494,9 @@ export const FACTS = {
491
494
  "name": "warm_work",
492
495
  "volume": "medium",
493
496
  "description": "bclaw_work consult over a real-shaped store (~200 plans / 500 handoffs / 450 claims).",
494
- "duration_ms_median": 107,
495
- "payload_chars_median": 2626,
496
- "payload_tokens_est_median": 657
497
+ "duration_ms_median": 88,
498
+ "payload_chars_median": 2625,
499
+ "payload_tokens_est_median": 656
497
500
  },
498
501
  {
499
502
  "name": "first_edit",
package/dist/facts.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
- "version": "1.24.0",
3
- "generated_at": "2026-08-10T18:03:29.240Z",
2
+ "version": "1.25.0",
3
+ "generated_at": "2026-08-10T23:31:26.947Z",
4
4
  "tools": {
5
- "count": 67,
6
- "published_count": 65,
5
+ "count": 70,
6
+ "published_count": 68,
7
7
  "names": [
8
8
  "bclaw_bootstrap",
9
9
  "bclaw_release_notes",
@@ -32,6 +32,9 @@
32
32
  "bclaw_code_status",
33
33
  "bclaw_code_find",
34
34
  "bclaw_code_brief",
35
+ "bclaw_code_impact",
36
+ "bclaw_code_export",
37
+ "bclaw_code_outline",
35
38
  "bclaw_code_refresh",
36
39
  "bclaw_dispatch",
37
40
  "bclaw_send_message",
@@ -472,7 +475,7 @@
472
475
  },
473
476
  "bench": {
474
477
  "schema": "brainclaw.bench.v1",
475
- "generated_at": "2026-08-10T18:03:27.087Z",
478
+ "generated_at": "2026-08-10T23:31:25.464Z",
476
479
  "node_version": "v24.18.0",
477
480
  "platform": "linux-x64",
478
481
  "repeats": 3,
@@ -481,7 +484,7 @@
481
484
  "name": "cold_onboard",
482
485
  "volume": "empty",
483
486
  "description": "fresh machine → init → first useful context. Baseline for time-to-first-value.",
484
- "duration_ms_median": 71,
487
+ "duration_ms_median": 59,
485
488
  "payload_chars_median": 1640,
486
489
  "payload_tokens_est_median": 410
487
490
  },
@@ -489,9 +492,9 @@
489
492
  "name": "warm_work",
490
493
  "volume": "medium",
491
494
  "description": "bclaw_work consult over a real-shaped store (~200 plans / 500 handoffs / 450 claims).",
492
- "duration_ms_median": 107,
493
- "payload_chars_median": 2626,
494
- "payload_tokens_est_median": 657
495
+ "duration_ms_median": 88,
496
+ "payload_chars_median": 2625,
497
+ "payload_tokens_est_median": 656
495
498
  },
496
499
  {
497
500
  "name": "first_edit",
package/docs/cli.md CHANGED
@@ -652,6 +652,14 @@ Search the symbol index by name (function / class / component / hook / type). Re
652
652
  ### `brainclaw code-map brief <target> [--limit <n>]`
653
653
 
654
654
  Given a symbol or path, return a ranked reading list (`suggested_files_to_read`) plus related memory (decisions/traps/constraints) — what to read before editing.
655
+ ### `brainclaw code-map export <symbol-or-path> [--direction outgoing|incoming|both]`
656
+
657
+ Export a compact **local** Code Map subgraph around one symbol or file. The default
658
+ is one hop in both directions; hard caps always apply (depth 4, 100 nodes, 200
659
+ edges), so the command never defaults to a whole-graph export. `--max-nodes`,
660
+ `--max-edges`, and `--depth` only tighten the result; `--min-confidence` has a
661
+ hard floor of 0.5. JSON keeps each edge's `kind`, `source`, and `confidence`.
662
+ Use `--format mermaid` for a diagram projected from that same JSON model.
655
663
 
656
664
  ```bash
657
665
  brainclaw code-map refresh --all
package/docs/code-map.md CHANGED
@@ -24,6 +24,7 @@ rebuilds it.
24
24
  brainclaw memory.
25
25
  - **To locate** a function/class/component/hook by name without grepping:
26
26
  `code-map find <query>` (or `bclaw_code_find`).
27
+ - **To inspect a bounded local dependency neighborhood**: `code-map export <symbol-or-path>` (or `bclaw_code_export`) returns compact nodes and edges, not a repository graph dump.
27
28
  - **To check coverage / staleness**: `code-map status` (or `bclaw_code_status`).
28
29
  - **After pulling changes or doing work**: `code-map refresh` to bring the index
29
30
  back to `fresh`.
@@ -95,9 +96,30 @@ Read-only. Builds a reading brief for a symbol or file: a ranked
95
96
  brainclaw code-map brief App
96
97
  ```
97
98
 
99
+ ### `brainclaw code-map export <symbol-or-path>`
100
+
101
+ Read-only export of a **local** persisted subgraph around one symbol or file. It
102
+ never refreshes, reparses, calls a service, or silently turns into a whole-project
103
+ graph export. The default is one hop in both directions; limits are always
104
+ reported and hard-capped at depth 4, 100 nodes, and 200 edges.
105
+
106
+ ```bash
107
+ brainclaw code-map export useAuth --direction incoming --depth 2 --json
108
+ brainclaw code-map export src/hooks/useAuth.ts --format mermaid
109
+ ```
110
+
111
+ `--direction` is `outgoing`, `incoming`, or `both` (default). `--max-nodes` and
112
+ `--max-edges` can tighten the response only; `--min-confidence` cannot be set
113
+ below 0.5. JSON is canonical and includes compact `nodes`, `edges`, root IDs,
114
+ limits, truncation flags, and a freshness badge. Every edge retains `kind`,
115
+ `source`, and `confidence`; low-confidence nodes/relations are excluded so an
116
+ extraction heuristic cannot appear indistinguishable from a high-confidence
117
+ relation. `--format mermaid` adds a Mermaid rendering projected from those exact
118
+ JSON nodes and edges—never from a second traversal.
119
+
98
120
  ## MCP tools
99
121
 
100
- Capable agents should prefer the MCP surface. The four tools mirror the CLI and
122
+ Capable agents should prefer the MCP surface. The read tools mirror the CLI and
101
123
  all return a `freshness_badge`:
102
124
 
103
125
  | Tool | Kind | Purpose |
@@ -105,6 +127,7 @@ all return a `freshness_badge`:
105
127
  | `bclaw_code_status` | read | Store presence, freshness badge, index stats. Never refreshes. |
106
128
  | `bclaw_code_find` | read | Ranked symbol-index search (`query`, optional `limit`). Never refreshes. |
107
129
  | `bclaw_code_brief` | read | Reading brief for a symbol/path (`target`, optional `limit`, files capped at 12). Never refreshes. |
130
+ | `bclaw_code_export` | read | Bounded local subgraph around required `target`; direction/depth/node/edge caps, confidence filtering, and optional Mermaid projection. Never refreshes. |
108
131
  | `bclaw_code_refresh` | write | Rebuild the index. `scope` = `"changed"` (default) or `"all"`. Fails fast on a live lock. |
109
132
 
110
133
  The read tools never trigger a parse — if `bclaw_code_status` /
@@ -25,7 +25,7 @@ The default dynamic workflow is:
25
25
  1. `bclaw_work` to start the session and load the relevant context in one call (returns compact payload by default — pass `compact: false` for the full context result)
26
26
  2. `bclaw_context({ kind: "execution" })` early when the agent needs local tooling signals or package update visibility
27
27
  3. `bclaw_context({ kind: "memory" })`, `bclaw_context({ kind: "board" })`, or `bclaw_context({ kind: "delta" })` when the target path changes or full memory is needed beyond the compact summary
28
- 4. `bclaw_code_brief({ target })` / `bclaw_code_find({ query })` before editing unfamiliar code — get a ranked reading list (with related decisions/traps) and locate symbols from the Code Map instead of grepping blind. A `missing_index` badge means run `bclaw_code_refresh` first. See [code map](../code-map.md)
28
+ 4. `bclaw_code_brief({ target })` / `bclaw_code_find({ query })` before editing unfamiliar code — get a ranked reading list (with related decisions/traps) and locate symbols from the Code Map instead of grepping blind. Use `bclaw_code_impact({ target, depth: 2 })` when you need an explainable local blast radius; its `tests_for` separates resolved imports from low-confidence filename suggestions. Use `bclaw_code_export({ target, direction, depth, maxNodes, maxEdges })` when you need a compact bounded subgraph; every returned edge keeps `kind`, `source`, and `confidence`, and `format: 'mermaid'` is projected from that same JSON model. A `missing_index` badge means run `bclaw_code_refresh` first. See [code map](../code-map.md)
29
29
  5. `bclaw_find` / `bclaw_get` / `bclaw_create` / `bclaw_update` / `bclaw_remove` / `bclaw_transition` for entity reads and writes
30
30
  6. `bclaw_coordinate`, `bclaw_dispatch`, or `bclaw_loop` for assign, consult, review, reroute, summarize, dispatch, or multi-turn loop flows
31
31
  7. `bclaw_read_inbox` when resuming delegated work
@@ -47,7 +47,7 @@ Every tool has one of three tiers in its `annotations.tier` field:
47
47
  - **standard** — Day-to-day coordination tools: plans, claims, messaging, sequences, dispatch, review, memory. Returned by default alongside facades.
48
48
  - **advanced** — Specialized governance, audit, registry, and power tools.
49
49
 
50
- By default, `tools/list` returns **facade + standard** tools (46 tools). To get all tools including advanced, pass `{ "catalog": "all" }`, `{ "include": "all" }`, or `{ "advanced": true }`. To filter by a single tier, pass `{ "tier": "facade" }`, `{ "tier": "standard" }`, or `{ "tier": "advanced" }`.
50
+ By default, `tools/list` returns **facade + standard** tools (49 tools). To get all tools including advanced, pass `{ "catalog": "all" }`, `{ "include": "all" }`, or `{ "advanced": true }`. To filter by a single tier, pass `{ "tier": "facade" }`, `{ "tier": "standard" }`, or `{ "tier": "advanced" }`.
51
51
 
52
52
  Published tools remain callable regardless of catalog filtering — the tier only affects discovery via `tools/list`.
53
53
 
@@ -111,6 +111,9 @@ Each tool also has an `annotations.category` field: `session`, `context`, `memor
111
111
  | `bclaw_code_status` | discovery | Code Map freshness badge + index stats (store presence, files/nodes/edges) |
112
112
  | `bclaw_code_find` | discovery | Search the Code Map symbol index by name (function/class/component/hook/type) |
113
113
  | `bclaw_code_brief` | discovery | Ranked reading list + related decisions/traps before editing a symbol or path |
114
+ | `bclaw_code_impact` | discovery | Explainable local blast radius from resolved imports: definition, direct dependents, opt-in bounded transitives, tests, and count-based risk |
115
+ | `bclaw_code_export` | discovery | Compact bounded local nodes/edges around one symbol or file; preserves edge kind/source/confidence, with optional Mermaid projection |
116
+ | `bclaw_code_outline` | discovery | Source-ordered symbols of one indexed file (span, exported, confidence) — no reparse |
114
117
  | `bclaw_code_refresh` | discovery | Rebuild the Code Map index (`scope: changed \| all`) |
115
118
 
116
119
  See [code map](../code-map.md) for the full Code Map reference (CLI, freshness model, supported languages).
@@ -408,7 +408,17 @@ will still succeed. A follow-up PR will strip the dead handler code.
408
408
  changelog records the published MCP surface fingerprint. When a tool
409
409
  name, tier, category, or input schema changes, the test fails until
410
410
  this section is updated.
411
- - MCP public surface fingerprint: `sha256:8241fa50b8cb4805`
411
+ - MCP public surface fingerprint: `sha256:b8dbb80bae8f6e36`
412
+ (updated 2026-08-10 for pln#665: `bclaw_code_export` — additive Tier-B read tool for a required, bounded local Code Map subgraph. Its target, direction, depth, node/edge caps, confidence threshold, and optional Mermaid projection are explicit; JSON retains each relation's kind/source/confidence and never defaults to a whole-graph export.)
413
+ Previous: `sha256:9ed35ed6cc49ea9a`
414
+ (updated 2026-08-10 for pln#661: `bclaw_code_impact` — additive Tier-B read tool
415
+ for local, resolved-import impact analysis. It exposes definition, direct causes,
416
+ optional bounded transitives, tests, and a count-based risk score; its required
417
+ `target` plus optional `depth` and `limit` input surface are explicitly bounded.)
418
+ Previous: `sha256:2a9f7d4cd72609df`
419
+ (updated 2026-08-10 for pln#660: `bclaw_code_outline` — new Tier-B read tool,
420
+ source-ordered symbols of one indexed file from the existing shard; no reparse,
421
+ no mutation, bounded output. Purely additive.)
412
422
  (updated 2026-07-25 for pln#632: `bclaw_loop` gains the `bind` intent — an
413
423
  implementation loop dispatches its linked sequence and advances bind→execute — plus
414
424
  its typed inputSchema properties `dry_run`, `lanes`, `auto_execute`, `model`, and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainclaw",
3
- "version": "1.24.0",
3
+ "version": "1.25.0",
4
4
  "description": "Shared project memory for humans and coding agents.",
5
5
  "type": "module",
6
6
  "repository": {