brainclaw 1.9.0 → 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.
Files changed (149) hide show
  1. package/README.md +631 -499
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli.js +18 -1
  4. package/dist/commands/code-map.js +129 -0
  5. package/dist/commands/codev.js +7 -0
  6. package/dist/commands/harvest.js +1 -1
  7. package/dist/commands/hooks.js +73 -73
  8. package/dist/commands/init.js +1 -1
  9. package/dist/commands/install-hooks.js +78 -78
  10. package/dist/commands/mcp-read-handlers.js +57 -14
  11. package/dist/commands/mcp.js +200 -13
  12. package/dist/commands/run-profile.js +3 -2
  13. package/dist/commands/switch.js +125 -93
  14. package/dist/commands/version.js +1 -1
  15. package/dist/core/agent-capability.js +19 -4
  16. package/dist/core/agent-files.js +131 -119
  17. package/dist/core/code-map/backend.js +123 -0
  18. package/dist/core/code-map/core.js +81 -0
  19. package/dist/core/code-map/drafts.js +2 -0
  20. package/dist/core/code-map/extractor.js +29 -0
  21. package/dist/core/code-map/finalizer.js +191 -0
  22. package/dist/core/code-map/freshness.js +108 -0
  23. package/dist/core/code-map/ids.js +0 -0
  24. package/dist/core/code-map/importable.js +35 -0
  25. package/dist/core/code-map/indexes.js +197 -0
  26. package/dist/core/code-map/lang/java/imports.scm +17 -0
  27. package/dist/core/code-map/lang/java/index.js +254 -0
  28. package/dist/core/code-map/lang/java/tags.scm +48 -0
  29. package/dist/core/code-map/lang/php/imports.scm +21 -0
  30. package/dist/core/code-map/lang/php/index.js +251 -0
  31. package/dist/core/code-map/lang/php/tags.scm +44 -0
  32. package/dist/core/code-map/lang/provider.js +9 -0
  33. package/dist/core/code-map/lang/providers.js +24 -0
  34. package/dist/core/code-map/lang/python/imports.scm +90 -0
  35. package/dist/core/code-map/lang/python/index.js +364 -0
  36. package/dist/core/code-map/lang/python/tags.scm +81 -0
  37. package/dist/core/code-map/lang/query-runtime.js +374 -0
  38. package/dist/core/code-map/lang/registry.js +125 -0
  39. package/dist/core/code-map/lang/typescript/imports.scm +90 -0
  40. package/dist/core/code-map/lang/typescript/index.js +306 -0
  41. package/dist/core/code-map/lang/typescript/tags.js.scm +106 -0
  42. package/dist/core/code-map/lang/typescript/tags.scm +151 -0
  43. package/dist/core/code-map/lock.js +210 -0
  44. package/dist/core/code-map/materialized.js +51 -0
  45. package/dist/core/code-map/memory-reader.js +59 -0
  46. package/dist/core/code-map/paths.js +53 -0
  47. package/dist/core/code-map/query.js +568 -0
  48. package/dist/core/code-map/refresh.js +0 -0
  49. package/dist/core/code-map/resolve.js +177 -0
  50. package/dist/core/code-map/store.js +206 -0
  51. package/dist/core/code-map/types.js +288 -0
  52. package/dist/core/code-map/vocabulary.js +57 -0
  53. package/dist/core/code-map/wasm-loader.js +294 -0
  54. package/dist/core/code-map/work-section.js +206 -0
  55. package/dist/core/codev-prompts.js +38 -38
  56. package/dist/core/codev-rounds.js +4 -0
  57. package/dist/core/default-profiles/doctor.yaml +11 -11
  58. package/dist/core/default-profiles/janitor.yaml +11 -11
  59. package/dist/core/default-profiles/onboarder.yaml +11 -11
  60. package/dist/core/default-profiles/reviewer.yaml +13 -13
  61. package/dist/core/dispatcher.js +1 -1
  62. package/dist/core/entity-operations.js +29 -3
  63. package/dist/core/execution-adapters.js +11 -10
  64. package/dist/core/execution-profile.js +58 -0
  65. package/dist/core/execution.js +1 -1
  66. package/dist/core/facade-schema.js +9 -0
  67. package/dist/core/instruction-templates.js +2 -0
  68. package/dist/core/loops/verbs.js +0 -1
  69. package/dist/core/mcp-command-resolution.js +3 -1
  70. package/dist/core/messaging.js +2 -2
  71. package/dist/core/protocol-skills.js +164 -164
  72. package/dist/core/runtime-signals.js +1 -1
  73. package/dist/core/search.js +19 -2
  74. package/dist/core/security-guard.js +207 -207
  75. package/dist/core/spawn-check.js +16 -2
  76. package/dist/core/staleness.js +1 -1
  77. package/dist/core/store-resolution.js +67 -11
  78. package/dist/core/worktree.js +18 -18
  79. package/dist/facts.js +9 -5
  80. package/dist/facts.json +8 -4
  81. package/dist/vendor/web-tree-sitter/tree-sitter.js +3980 -0
  82. package/dist/vendor/web-tree-sitter/tree-sitter.wasm +0 -0
  83. package/dist/wasm/tree-sitter-java.wasm +0 -0
  84. package/dist/wasm/tree-sitter-javascript.wasm +0 -0
  85. package/dist/wasm/tree-sitter-php.wasm +0 -0
  86. package/dist/wasm/tree-sitter-python.wasm +0 -0
  87. package/dist/wasm/tree-sitter-tsx.wasm +0 -0
  88. package/dist/wasm/tree-sitter-typescript.wasm +0 -0
  89. package/dist/wasm/tree-sitter.wasm +0 -0
  90. package/docs/PROTOCOL.md +1 -1
  91. package/docs/adapters/openclaw.md +43 -43
  92. package/docs/architecture/project-refs.md +328 -328
  93. package/docs/cli.md +2131 -2093
  94. package/docs/code-map.md +198 -0
  95. package/docs/concepts/coordination.md +52 -52
  96. package/docs/concepts/coordinator-runbook.md +129 -129
  97. package/docs/concepts/dispatch-lifecycle.md +245 -245
  98. package/docs/concepts/event-log-store.md +928 -928
  99. package/docs/concepts/ideation-loop.md +317 -317
  100. package/docs/concepts/loop-engine.md +520 -511
  101. package/docs/concepts/mcp-governance.md +268 -268
  102. package/docs/concepts/memory.md +84 -84
  103. package/docs/concepts/multi-agent-workflows.md +167 -167
  104. package/docs/concepts/observer-protocol.md +361 -361
  105. package/docs/concepts/plans-and-claims.md +217 -217
  106. package/docs/concepts/project-md-convention.md +35 -35
  107. package/docs/concepts/runtime-notes.md +38 -38
  108. package/docs/concepts/troubleshooting.md +254 -254
  109. package/docs/concepts/workspace-bootstrapping.md +142 -142
  110. package/docs/context-format-changelog.md +35 -35
  111. package/docs/context-format.md +48 -48
  112. package/docs/index.md +65 -65
  113. package/docs/integrations/agents.md +158 -158
  114. package/docs/integrations/claude-code.md +23 -23
  115. package/docs/integrations/cline.md +77 -77
  116. package/docs/integrations/continue.md +55 -55
  117. package/docs/integrations/copilot.md +68 -68
  118. package/docs/integrations/cursor.md +23 -23
  119. package/docs/integrations/kilocode.md +72 -72
  120. package/docs/integrations/mcp.md +385 -378
  121. package/docs/integrations/mistral-vibe.md +122 -122
  122. package/docs/integrations/openclaw.md +92 -92
  123. package/docs/integrations/opencode.md +84 -84
  124. package/docs/integrations/overview.md +115 -115
  125. package/docs/integrations/roo.md +71 -71
  126. package/docs/integrations/windsurf.md +77 -77
  127. package/docs/mcp-schema-changelog.md +364 -356
  128. package/docs/playbooks/integration/index.md +121 -121
  129. package/docs/playbooks/orchestration.md +37 -0
  130. package/docs/playbooks/productivity/index.md +99 -99
  131. package/docs/playbooks/team/index.md +117 -117
  132. package/docs/product/agent-first-model.md +184 -184
  133. package/docs/product/entity-model-audit.md +462 -462
  134. package/docs/product/positioning.md +86 -86
  135. package/docs/quickstart-existing-project.md +107 -107
  136. package/docs/quickstart.md +183 -183
  137. package/docs/release-maintenance.md +79 -79
  138. package/docs/reputation.md +52 -52
  139. package/docs/review.md +45 -45
  140. package/docs/security.md +212 -212
  141. package/docs/server-operations.md +118 -118
  142. package/docs/storage.md +106 -106
  143. package/package.json +86 -66
  144. package/docs/concepts/event-log-store-critique-A.md +0 -333
  145. package/docs/concepts/event-log-store-critique-B.md +0 -353
  146. package/docs/concepts/event-log-store-phase0-measurements.md +0 -58
  147. package/docs/concepts/event-log-store-proposal-A.md +0 -365
  148. package/docs/concepts/event-log-store-proposal-B.md +0 -404
  149. package/docs/concepts/identity-model-proposal.md +0 -371
@@ -1,11 +1,11 @@
1
- name: doctor
2
- description: Run diagnostics and fix issues automatically
3
- trust_level: trusted
4
- trigger: manual
5
- prompt: >-
6
- Run bclaw_doctor, analyze the findings, and fix what can be fixed
7
- automatically. For issues requiring human judgment, create plans.
8
- invoke: codex exec --full-auto "{prompt}"
9
- tags:
10
- - diagnostics
11
- - built-in
1
+ name: doctor
2
+ description: Run diagnostics and fix issues automatically
3
+ trust_level: trusted
4
+ trigger: manual
5
+ prompt: >-
6
+ Run bclaw_doctor, analyze the findings, and fix what can be fixed
7
+ automatically. For issues requiring human judgment, create plans.
8
+ invoke: codex exec --full-auto "{prompt}"
9
+ tags:
10
+ - diagnostics
11
+ - built-in
@@ -1,11 +1,11 @@
1
- name: janitor
2
- description: Clean up stale claims, archive old notes, check for orphaned files
3
- trust_level: contributor
4
- trigger: manual
5
- prompt: >-
6
- Clean up this project: prune stale claims, archive old runtime notes, check
7
- for orphaned files. Use bclaw_doctor, prune, and release-claims.
8
- invoke: codex exec --full-auto "{prompt}"
9
- tags:
10
- - maintenance
11
- - built-in
1
+ name: janitor
2
+ description: Clean up stale claims, archive old notes, check for orphaned files
3
+ trust_level: contributor
4
+ trigger: manual
5
+ prompt: >-
6
+ Clean up this project: prune stale claims, archive old runtime notes, check
7
+ for orphaned files. Use bclaw_doctor, prune, and release-claims.
8
+ invoke: codex exec --full-auto "{prompt}"
9
+ tags:
10
+ - maintenance
11
+ - built-in
@@ -1,11 +1,11 @@
1
- name: onboarder
2
- description: Generate a project summary for new team members
3
- trust_level: observer
4
- trigger: manual
5
- prompt: >-
6
- Generate a project summary for a new team member. Read the board, plans,
7
- decisions, constraints, and produce a concise onboarding document.
8
- invoke: claude -p "{prompt}" --allowedTools "Edit,Write,Bash,Read,Glob,Grep"
9
- tags:
10
- - onboarding
11
- - built-in
1
+ name: onboarder
2
+ description: Generate a project summary for new team members
3
+ trust_level: observer
4
+ trigger: manual
5
+ prompt: >-
6
+ Generate a project summary for a new team member. Read the board, plans,
7
+ decisions, constraints, and produce a concise onboarding document.
8
+ invoke: claude -p "{prompt}" --allowedTools "Edit,Write,Bash,Read,Glob,Grep"
9
+ tags:
10
+ - onboarding
11
+ - built-in
@@ -1,13 +1,13 @@
1
- name: reviewer
2
- description: Review pending candidates and give structured opinions
3
- trust_level: trusted
4
- trigger: manual
5
- prompt: >-
6
- Review pending candidates in this project. For each, give a structured
7
- opinion (approve/reject with reasons). Use
8
- bclaw_find(entity: "candidate", filter: {status: "pending"}) and
9
- bclaw_transition(entity: "candidate", id, to: "accepted" | "rejected").
10
- invoke: claude -p "{prompt}" --allowedTools "Edit,Write,Bash,Read,Glob,Grep"
11
- tags:
12
- - review
13
- - built-in
1
+ name: reviewer
2
+ description: Review pending candidates and give structured opinions
3
+ trust_level: trusted
4
+ trigger: manual
5
+ prompt: >-
6
+ Review pending candidates in this project. For each, give a structured
7
+ opinion (approve/reject with reasons). Use
8
+ bclaw_find(entity: "candidate", filter: {status: "pending"}) and
9
+ bclaw_transition(entity: "candidate", id, to: "accepted" | "rejected").
10
+ invoke: claude -p "{prompt}" --allowedTools "Edit,Write,Bash,Read,Glob,Grep"
11
+ tags:
12
+ - review
13
+ - built-in
@@ -469,7 +469,7 @@ export function generateBrief(plan, item, cwd, briefMode, options) {
469
469
  if (mode === 'full') {
470
470
  parts.push(buildProtocolSection(options));
471
471
  }
472
- // pln#528 — transport-aware addendum (debrief LeaseUp P1#2). When the agent is
472
+ // pln#528 — transport-aware addendum (field debrief P1#2). When the agent is
473
473
  // spawned sandboxed (no MCP + no git commit — e.g. codex --sandbox
474
474
  // workspace-write), the MCP lifecycle lines in the Protocol section do NOT
475
475
  // apply. Say so explicitly and make the FILE protocol authoritative, so the
@@ -62,6 +62,18 @@ function passesProvenanceFilter(item, filter) {
62
62
  }
63
63
  return true;
64
64
  }
65
+ function isLegacyProvenance(item) {
66
+ return item.provenance?.kind === 'legacy';
67
+ }
68
+ function isLowConfidenceAutoReflect(item, filter) {
69
+ const provenance = item.provenance;
70
+ if (provenance?.kind !== 'auto_reflect')
71
+ return false;
72
+ const threshold = typeof filter.minAutoReflectConfidence === 'number'
73
+ ? filter.minAutoReflectConfidence
74
+ : DEFAULT_MIN_AUTO_REFLECT_CONFIDENCE;
75
+ return (provenance.confidence ?? 0) < threshold;
76
+ }
65
77
  /** Thrown when a verb is not yet wired for a given entity. */
66
78
  export class EntityOperationUnsupportedError extends Error {
67
79
  constructor(entity, verb, hint) {
@@ -129,9 +141,21 @@ function loadAgentRunsWithReconciliation(cwd) {
129
141
  }
130
142
  export function listEntities(name, cwd, filter = {}) {
131
143
  const all = loadAll(name, cwd);
132
- const filtered = applyFilter(all, filter);
144
+ const fieldFiltered = applyFieldFilter(all, filter);
145
+ const excludedLegacy = filter.includeLegacy === true
146
+ ? 0
147
+ : fieldFiltered.filter((item) => isLegacyProvenance(item)).length;
148
+ const excludedLowConfidenceAutoReflect = fieldFiltered.filter((item) => isLowConfidenceAutoReflect(item, filter)).length;
149
+ const filtered = fieldFiltered.filter((item) => passesProvenanceFilter(item, filter));
133
150
  const paged = applyPaging(filtered, filter);
134
- return { entity: name, total: filtered.length, items: paged };
151
+ return {
152
+ entity: name,
153
+ total: filtered.length,
154
+ items: paged,
155
+ excluded_legacy: excludedLegacy,
156
+ excluded_low_confidence_auto_reflect: excludedLowConfidenceAutoReflect,
157
+ total_before_provenance_filter: fieldFiltered.length,
158
+ };
135
159
  }
136
160
  /** Default serialized-items budget (chars) — keeps a bclaw_find payload well under the ~25k-token MCP cap (trp#449). */
137
161
  export const DEFAULT_FIND_CHAR_BUDGET = 40000;
@@ -189,8 +213,10 @@ function loadAll(name, cwd) {
189
213
  }
190
214
  }
191
215
  function applyFilter(items, filter) {
216
+ return applyFieldFilter(items, filter).filter((item) => passesProvenanceFilter(item, filter));
217
+ }
218
+ function applyFieldFilter(items, filter) {
192
219
  let result = items;
193
- result = result.filter((item) => passesProvenanceFilter(item, filter));
194
220
  if (filter.status) {
195
221
  result = result.filter((item) => item.status === filter.status);
196
222
  }
@@ -1,7 +1,7 @@
1
1
  import { spawn, execFileSync } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { buildClaimEnvPrefix } from './execution-profile.js';
4
+ import { buildClaimEnvPrefix, buildWorkerIdentityEnv } from './execution-profile.js';
5
5
  import { getCapabilityProfile } from './agent-capability.js';
6
6
  import { nowISO } from './ids.js';
7
7
  import { ensureRuntimeDirs, getRuntimeLogPath, getRuntimeSignalPath, } from './runtime-signals.js';
@@ -97,15 +97,16 @@ export class CliExecutionAdapter {
97
97
  }
98
98
  start(invoke, options) {
99
99
  const isWin32 = process.platform === 'win32';
100
- const env = {
101
- ...process.env,
102
- // pln#562 step 5 truthful attribution: every commit a dispatched
103
- // worker makes is authored as the AGENT, not as the human whose
104
- // git config happens to be on the machine. invoke.env may override.
105
- ...buildGitAttributionEnv(options.agent),
106
- ...(invoke.env ?? {}),
107
- ...(options.claimId ? { BRAINCLAW_CLAIM_ID: options.claimId } : {}),
108
- };
100
+ // F7 (trp_0e5150d3): route worker env through buildWorkerIdentityEnv so the
101
+ // worker is an independent agent — coordinator identity (BRAINCLAW_AGENT*,
102
+ // SESSION_ID, PROJECT) is scrubbed LAST and cannot be reintroduced by
103
+ // invoke.env. pln#562 step 5 truthful git attribution (worker authors its
104
+ // own commits) is merged before the scrub. BRAINCLAW_CWD is preserved (D1a).
105
+ const env = buildWorkerIdentityEnv(process.env, {
106
+ agent: options.agent,
107
+ claimId: options.claimId,
108
+ extraEnv: { ...buildGitAttributionEnv(options.agent), ...(invoke.env ?? {}) },
109
+ });
109
110
  if (invoke.promptDelivery === 'temp_file' && invoke.tempFilePath && invoke.promptText) {
110
111
  const dir = path.dirname(invoke.tempFilePath);
111
112
  if (!fs.existsSync(dir))
@@ -247,6 +247,64 @@ export function buildClaimEnvPrefix(claimId, options) {
247
247
  default: return `BRAINCLAW_CLAIM_ID=${claimId} `;
248
248
  }
249
249
  }
250
+ /**
251
+ * F7 (trp_0e5150d3): build the env for a SPAWNED worker so it is an INDEPENDENT
252
+ * agent, not a clone of the coordinator. The coordinator's process.env carries
253
+ * identity-bearing state (BRAINCLAW_AGENT*, BRAINCLAW_SESSION_ID, BRAINCLAW_PROJECT);
254
+ * inherited by an MCP-capable worker it would make the worker read/mutate the
255
+ * coordinator's session-scoped active project and identify AS the coordinator.
256
+ *
257
+ * Merge order (Codex cadrage review — batch 2): baseEnv → extraEnv (git
258
+ * attribution + invoke.env) → worker identity (agent name, claim) → SCRUB the
259
+ * forbidden coordinator keys LAST, so a deliberate or accidental extraEnv cannot
260
+ * reintroduce them. This helper is the SINGLE place worker env is built; every
261
+ * agent-spawn site must route through it.
262
+ *
263
+ * BRAINCLAW_CWD is intentionally PRESERVED (D1 = (a)): a worker's worktree lives
264
+ * outside the monorepo and has no .brainclaw, so it must anchor to the shared
265
+ * monorepo-root store the coordinator pointed at; the claim scopes the work. A
266
+ * caller override via extraEnv.BRAINCLAW_CWD is honoured (deliberate, not leaked).
267
+ */
268
+ export function buildWorkerIdentityEnv(baseEnv, options = {}) {
269
+ const env = {};
270
+ for (const [key, value] of Object.entries(baseEnv)) {
271
+ if (typeof value === 'string')
272
+ env[key] = value;
273
+ }
274
+ if (options.extraEnv) {
275
+ for (const [key, value] of Object.entries(options.extraEnv)) {
276
+ if (typeof value === 'string')
277
+ env[key] = value;
278
+ }
279
+ }
280
+ // Worker identity — truthful attribution, mirrors buildGitAttributionEnv.
281
+ const agent = options.agent?.trim();
282
+ if (agent) {
283
+ env.BRAINCLAW_AGENT = agent;
284
+ env.BRAINCLAW_AGENT_NAME = agent;
285
+ }
286
+ else {
287
+ // No target agent → don't leak the coordinator's identity either.
288
+ delete env.BRAINCLAW_AGENT;
289
+ delete env.BRAINCLAW_AGENT_NAME;
290
+ }
291
+ // Claim ownership is explicit: adopt the supplied claim, otherwise DELETE any
292
+ // inherited parent BRAINCLAW_CLAIM_ID so an unclaimed/dry-run worker (e.g. the
293
+ // codev/codev-rounds call sites that pass only { agent }) never adopts the
294
+ // coordinator's claim (Codex final review F7 finding).
295
+ if (options.claimId && options.claimId !== '(dry-run)') {
296
+ env.BRAINCLAW_CLAIM_ID = options.claimId;
297
+ }
298
+ else {
299
+ delete env.BRAINCLAW_CLAIM_ID;
300
+ }
301
+ // SCRUB LAST — coordinator identity must never survive into the worker, even if
302
+ // baseEnv or extraEnv set it. (BRAINCLAW_CWD is deliberately NOT scrubbed.)
303
+ delete env.BRAINCLAW_AGENT_ID;
304
+ delete env.BRAINCLAW_SESSION_ID;
305
+ delete env.BRAINCLAW_PROJECT;
306
+ return env;
307
+ }
250
308
  // ── Verification helper (used by setup / doctor) ───────────────────────────
251
309
  /**
252
310
  * Try `node --version` from the resolved profile's `node_path` to confirm
@@ -161,7 +161,7 @@ export async function attemptExecution(invoke, options) {
161
161
  // claim was reused/re-dispatched without one), REFUSE to spawn instead of
162
162
  // falling back to options.cwd — which is the integration repo, where the
163
163
  // worker would edit the main tree directly (dangerous for an autonomous fleet,
164
- // debrief LeaseUp). Return the command for manual, isolated execution.
164
+ // a cross-project field debrief). Return the command for manual, isolated execution.
165
165
  if (options.requireWorktree && !options.worktreePath) {
166
166
  appendAuditEntry({
167
167
  actor: options.dispatcherAgent,
@@ -196,5 +196,14 @@ export const FacadeResponseSchema = z.object({
196
196
  * remains for the bootstrap hint; new consumers should read this array.
197
197
  */
198
198
  next_actions: z.array(NextActionSchema).optional(),
199
+ /**
200
+ * Code Map P0 (spec §10): opt-in, present ONLY when the project's Code Map
201
+ * manifest carries `code_map_enabled: true`. Absent for every project that
202
+ * has not turned Code Map on (the P0 default), so existing bclaw_work callers
203
+ * are unaffected and the off-path adds no work. Shape mirrors
204
+ * CodeMapWorkSection: { enabled, matches, freshness_badge, missing_index?,
205
+ * lock_wait_ms? }. Passthrough so the section can evolve without a schema bump.
206
+ */
207
+ code_map: z.object({}).passthrough().optional(),
199
208
  });
200
209
  //# sourceMappingURL=facade-schema.js.map
@@ -233,6 +233,7 @@ function renderSessionProtocol() {
233
233
  '1. Call `bclaw_work(intent)` (consult|execute|resume|review) — one call handles session, context, and claim (execute). Every response carries `next_actions` with the exact follow-up calls: follow those instead of memorizing the API.',
234
234
  '2. Canonical grammar for memory objects: `bclaw_find` / `bclaw_get` / `bclaw_create` / `bclaw_update` / `bclaw_remove` / `bclaw_transition` (entity, …).',
235
235
  '3. Do not assume project state without reading brainclaw context first.',
236
+ '4. Before editing unfamiliar code, orient with the **Code Map**: `bclaw_code_brief(target=<symbol|path>)` for a ranked reading list + related decisions/traps, `bclaw_code_find(query=<name>)` to locate a symbol/class/component. Don\'t grep the repo blind when a symbol index already answers "where is X / what should I read first". A `missing_index`/stale badge means run `bclaw_code_refresh` first.',
236
237
  '',
237
238
  'Escalation (only when orchestrating other agents): `bclaw_coordinate(intent=review|consult|assign)`. Verify any dispatch with `bclaw_dispatch_status(target_id)` — trust its sentinel-based verdict, not the tracked pid. Details: `docs/concepts/dispatch-lifecycle.md`.',
238
239
  ].join('\n');
@@ -322,6 +323,7 @@ function renderAvailableTools() {
322
323
  '**Session/claims:** `bclaw_session_start`, `bclaw_session_end`, `bclaw_claim`, `bclaw_release_claim` · **steps:** `bclaw_add_step`, `bclaw_complete_step`, `bclaw_update_step`, `bclaw_delete_step` · **sequences:** `bclaw_list_sequences`, `bclaw_create_sequence`, `bclaw_update_sequence`, `bclaw_delete_sequence`',
323
324
  '**Inbox:** `bclaw_read_inbox`, `bclaw_ack_message`, `bclaw_send_message`, `bclaw_correct_handoff` · **capture:** `bclaw_write_note`, `bclaw_quick_capture(text, type?)` · **search:** `bclaw_search` · **setup:** `bclaw_setup`, `bclaw_bootstrap`, `bclaw_switch`, `bclaw_release_notes`',
324
325
  '**Escalation (orchestrators):** `bclaw_coordinate(intent=review|consult|assign|ideate)` · `bclaw_dispatch(intent=execute)` on an active sequence · `bclaw_loop(intent=turn|complete_turn|advance|close)` to drive turns · `bclaw_dispatch_status(target_id)` to verify',
326
+ '**Code discovery (Code Map):** `bclaw_code_find(query)` locate a symbol/class/component · `bclaw_code_brief(target)` ranked reading list + related memory before editing · `bclaw_code_status` / `bclaw_code_refresh` check freshness / rebuild the symbol+import index. Use it before grepping unfamiliar code — see `docs/code-map.md`.',
325
327
  '',
326
328
  'Responses are self-teaching — follow their `next_actions`. Full catalog + stability contract: `docs/integrations/mcp.md`, `docs/concepts/mcp-governance.md`.',
327
329
  ].join('\n');
@@ -653,7 +653,6 @@ function assertOpenQuestionsInvariant(thread, intent) {
653
653
  const message = `[brainclaw/loops/fsm] ${intent}: open_questions drift on loop ${thread.id} ` +
654
654
  `— persisted=${JSON.stringify([...persisted])} canonical=${JSON.stringify([...canonical])}`;
655
655
  // Log in prod so drift is visible without taking down the engine.
656
- // eslint-disable-next-line no-console
657
656
  console.warn(message);
658
657
  if (fsmAssertsEnabled()) {
659
658
  throw new Error(message);
@@ -216,7 +216,9 @@ export function brainclawMcpEntry(agentName, existing, workspacePath) {
216
216
  export function quoteShellArg(arg) {
217
217
  if (/^[A-Za-z0-9_./:=+-]+$/.test(arg))
218
218
  return arg;
219
- return `"${arg.replace(/"/g, '\\"')}"`;
219
+ // Escape backslashes before quotes — otherwise an input like `\"` would
220
+ // collapse to a quote that breaks out of the quoted argument.
221
+ return `"${arg.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
220
222
  }
221
223
  /**
222
224
  * Resolve the brainclaw CLI invocation for hook configs.
@@ -108,7 +108,7 @@ export function readInbox(input, cwd) {
108
108
  if (input.markAsRead) {
109
109
  return mutate({ cwd }, () => {
110
110
  // Fresh read inside lock
111
- let messages = applyInboxFilters(loadMessagesFromDir(dir), input);
111
+ const messages = applyInboxFilters(loadMessagesFromDir(dir), input);
112
112
  const total = messages.length;
113
113
  const offset = input.offset ?? 0;
114
114
  const limit = input.limit ?? 20;
@@ -127,7 +127,7 @@ export function readInbox(input, cwd) {
127
127
  });
128
128
  }
129
129
  // Read-only path: no lock needed
130
- let messages = applyInboxFilters(loadMessagesFromDir(dir), input);
130
+ const messages = applyInboxFilters(loadMessagesFromDir(dir), input);
131
131
  const total = messages.length;
132
132
  const offset = input.offset ?? 0;
133
133
  const limit = input.limit ?? 20;