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
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
@@ -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();
@@ -299,7 +299,7 @@ export function harvestLaneResults(options = {}) {
299
299
  // ─────────────────────────────────────────────────────────────────────────────
300
300
  // pln#534 — worktree-as-contract: integrate a worker's lane on its behalf.
301
301
  //
302
- // LEVER #1 from the LeaseUp frontier (can_100f1e8c). The worker's contract is
302
+ // LEVER #1 from a cross-project field session. The worker's contract is
303
303
  // reduced to "edit files in this worktree + drop LANE-RESULT.json". brainclaw
304
304
  // carries the rest for a worker that cannot (a sandboxed agent whose root
305
305
  // excludes `.git`, i.e. dispatchCanCommit=false): it COMMITS the worktree diff
@@ -9,33 +9,33 @@ import { BRAINCLAW_SECTION_START, BRAINCLAW_SECTION_END, upsertBrainclawSection,
9
9
  * deterministically into every agent conversation.
10
10
  */
11
11
  export function generateCursorHook(projectName) {
12
- return `---
13
- description: brainclaw session bootstrap for ${projectName}
14
- alwaysApply: true
15
- ---
16
-
17
- # Brainclaw session bootstrap
18
-
19
- Brainclaw is the shared coordination layer for this project. Use its MCP facades first — the CLI is only a fallback when MCP is unavailable.
20
-
21
- ## At the start of every session
22
-
23
- Call \`bclaw_work(intent)\` — it handles session setup, context load, and scope claim in a single call.
24
-
25
- - \`bclaw_work(intent: "resume")\` when continuing an existing task.
26
- - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` when starting new work on a specific scope.
27
- - \`bclaw_work(intent: "consult")\` to read context without claiming.
28
-
29
- ## To coordinate with other agents
30
-
31
- \`bclaw_coordinate(intent)\` — \`assign\`, \`consult\`, \`review\`, or \`reroute\`.
32
-
33
- ## Before finishing
34
-
35
- - Release your claims: \`bclaw_release_claim(id)\`.
36
- - Close out the session: \`bclaw_session_end\` (or let it auto-release via the session hook).
37
-
38
- CLI fallback only: \`brainclaw context\` / \`brainclaw session-end --auto-release\` if MCP is not reachable.
12
+ return `---
13
+ description: brainclaw session bootstrap for ${projectName}
14
+ alwaysApply: true
15
+ ---
16
+
17
+ # Brainclaw session bootstrap
18
+
19
+ Brainclaw is the shared coordination layer for this project. Use its MCP facades first — the CLI is only a fallback when MCP is unavailable.
20
+
21
+ ## At the start of every session
22
+
23
+ Call \`bclaw_work(intent)\` — it handles session setup, context load, and scope claim in a single call.
24
+
25
+ - \`bclaw_work(intent: "resume")\` when continuing an existing task.
26
+ - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` when starting new work on a specific scope.
27
+ - \`bclaw_work(intent: "consult")\` to read context without claiming.
28
+
29
+ ## To coordinate with other agents
30
+
31
+ \`bclaw_coordinate(intent)\` — \`assign\`, \`consult\`, \`review\`, or \`reroute\`.
32
+
33
+ ## Before finishing
34
+
35
+ - Release your claims: \`bclaw_release_claim(id)\`.
36
+ - Close out the session: \`bclaw_session_end\` (or let it auto-release via the session hook).
37
+
38
+ CLI fallback only: \`brainclaw context\` / \`brainclaw session-end --auto-release\` if MCP is not reachable.
39
39
  `;
40
40
  }
41
41
  /**
@@ -44,58 +44,58 @@ CLI fallback only: \`brainclaw context\` / \`brainclaw session-end --auto-releas
44
44
  * delimited "SESSION START" block acts as a deterministic trigger.
45
45
  */
46
46
  export function generateWindsurfHook(projectName) {
47
- return `# Brainclaw session trigger — ${projectName}
48
- # This block is always active. Windsurf Cascade reads .windsurfrules on every activation.
49
-
50
- ## SESSION START (always run first)
51
-
52
- Call the Brainclaw MCP facade before responding to any request:
53
-
54
- - \`bclaw_work(intent: "resume")\` when continuing an existing task.
55
- - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` when starting new work on a specific scope.
56
- - \`bclaw_work(intent: "consult")\` to read context without claiming.
57
-
58
- This returns active constraints, recent decisions, known traps, open plans, active claims, and the last handoff. Do not skip.
59
-
60
- ## COORDINATION
61
-
62
- \`bclaw_coordinate(intent)\` to assign, consult, review, or reroute other agents.
63
-
64
- ## SESSION END (before finishing)
65
-
66
- - \`bclaw_release_claim(id)\` for each claim you hold.
67
- - \`bclaw_session_end\` to close the session (auto-releases remaining claims).
68
-
69
- CLI fallback only when MCP is unavailable: \`brainclaw context\` / \`brainclaw session-end --auto-release\`.
47
+ return `# Brainclaw session trigger — ${projectName}
48
+ # This block is always active. Windsurf Cascade reads .windsurfrules on every activation.
49
+
50
+ ## SESSION START (always run first)
51
+
52
+ Call the Brainclaw MCP facade before responding to any request:
53
+
54
+ - \`bclaw_work(intent: "resume")\` when continuing an existing task.
55
+ - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` when starting new work on a specific scope.
56
+ - \`bclaw_work(intent: "consult")\` to read context without claiming.
57
+
58
+ This returns active constraints, recent decisions, known traps, open plans, active claims, and the last handoff. Do not skip.
59
+
60
+ ## COORDINATION
61
+
62
+ \`bclaw_coordinate(intent)\` to assign, consult, review, or reroute other agents.
63
+
64
+ ## SESSION END (before finishing)
65
+
66
+ - \`bclaw_release_claim(id)\` for each claim you hold.
67
+ - \`bclaw_session_end\` to close the session (auto-releases remaining claims).
68
+
69
+ CLI fallback only when MCP is unavailable: \`brainclaw context\` / \`brainclaw session-end --auto-release\`.
70
70
  `;
71
71
  }
72
72
  /**
73
73
  * Generate a generic markdown session-trigger section.
74
74
  */
75
75
  export function generateMarkdownHook(agentName, projectName) {
76
- return `# Brainclaw session trigger — ${projectName}
77
- # This block is always active. ${agentName} reads this file on every activation.
78
-
79
- ## SESSION START (always run first)
80
-
81
- Call the Brainclaw MCP facade before responding to any request:
82
-
83
- - \`bclaw_work(intent: "resume")\` when continuing an existing task.
84
- - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` when starting new work on a specific scope.
85
- - \`bclaw_work(intent: "consult")\` to read context without claiming.
86
-
87
- This returns active constraints, recent decisions, known traps, open plans, active claims, and the last handoff. Do not skip.
88
-
89
- ## COORDINATION
90
-
91
- \`bclaw_coordinate(intent)\` to assign, consult, review, or reroute other agents.
92
-
93
- ## SESSION END (before finishing)
94
-
95
- - \`bclaw_release_claim(id)\` for each claim you hold.
96
- - \`bclaw_session_end\` to close the session (auto-releases remaining claims).
97
-
98
- CLI fallback only when MCP is unavailable: \`brainclaw context\` / \`brainclaw session-end --auto-release\`.
76
+ return `# Brainclaw session trigger — ${projectName}
77
+ # This block is always active. ${agentName} reads this file on every activation.
78
+
79
+ ## SESSION START (always run first)
80
+
81
+ Call the Brainclaw MCP facade before responding to any request:
82
+
83
+ - \`bclaw_work(intent: "resume")\` when continuing an existing task.
84
+ - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` when starting new work on a specific scope.
85
+ - \`bclaw_work(intent: "consult")\` to read context without claiming.
86
+
87
+ This returns active constraints, recent decisions, known traps, open plans, active claims, and the last handoff. Do not skip.
88
+
89
+ ## COORDINATION
90
+
91
+ \`bclaw_coordinate(intent)\` to assign, consult, review, or reroute other agents.
92
+
93
+ ## SESSION END (before finishing)
94
+
95
+ - \`bclaw_release_claim(id)\` for each claim you hold.
96
+ - \`bclaw_session_end\` to close the session (auto-releases remaining claims).
97
+
98
+ CLI fallback only when MCP is unavailable: \`brainclaw context\` / \`brainclaw session-end --auto-release\`.
99
99
  `;
100
100
  }
101
101
  export function writeHook(content, relativePath, cwd) {
@@ -117,7 +117,7 @@ export async function runInit(options = {}) {
117
117
  });
118
118
  // Auto-detect and register the AI coding agent running in this environment
119
119
  const detectedAi = skipAgentBootstrap ? undefined : detectAiAgent();
120
- let registeredAiAgent = detectedAi
120
+ const registeredAiAgent = detectedAi
121
121
  ? registerAgentIdentity({
122
122
  agentName: detectedAi.name,
123
123
  kind: detectedAi.kind,
@@ -40,63 +40,63 @@ export function runInstallHooks(options = {}) {
40
40
  }
41
41
  }
42
42
  function generateClaudePreToolScript() {
43
- return `#!/bin/sh
44
- # brainclaw Claude Code preToolUse hook
45
- # Generated by: brainclaw install-hooks
46
- exec node -e "
47
- const fs = require('fs');
48
- const path = require('path');
49
- const { execSync } = require('child_process');
50
-
51
- const toolName = process.env.CLAUDE_TOOL_NAME || process.argv[2] || process.env.TOOL_NAME || '';
52
- const isWrite = /edit|write|replace|bash|str_replace/i.test(toolName);
53
-
54
- if (!isWrite || !toolName) process.exit(0);
55
-
56
- try {
57
- const BCLAW_CMD = fs.existsSync(path.join(process.cwd(), 'node_modules', '.bin', 'brainclaw'))
58
- ? 'npx brainclaw' : 'brainclaw';
59
- const out = execSync(BCLAW_CMD + ' claim list --json', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
60
- const claims = JSON.parse(out);
61
- if (claims && claims.length > 0) process.exit(0);
62
- } catch (e) {
63
- // ignore if brainclaw not found or fails
64
- process.exit(0);
65
- }
66
-
67
- const sessionMark = path.join(process.cwd(), '.brainclaw', 'tmp', 'claude_warned');
68
- if (!fs.existsSync(path.dirname(sessionMark))) fs.mkdirSync(path.dirname(sessionMark), { recursive: true });
69
-
70
- if (fs.existsSync(sessionMark)) {
71
- const mtime = fs.statSync(sessionMark).mtimeMs;
72
- if (Date.now() - mtime < 2 * 60 * 60 * 1000) {
73
- process.exit(0); // already warned this session
74
- }
75
- }
76
- fs.writeFileSync(sessionMark, Date.now().toString());
77
-
78
- process.stderr.write('\\n[Brainclaw] ⚠️ WARNING: You are about to use an Edit/Write tool (' + toolName + ') but you have NO active claim.\\n');
79
- process.stderr.write('[Brainclaw] Consider running \\\`brainclaw claim create <scope>\\\` to lock your work and avoid conflicts.\\n\\n');
80
- " 2>&1 || exit 0
43
+ return `#!/bin/sh
44
+ # brainclaw Claude Code preToolUse hook
45
+ # Generated by: brainclaw install-hooks
46
+ exec node -e "
47
+ const fs = require('fs');
48
+ const path = require('path');
49
+ const { execSync } = require('child_process');
50
+
51
+ const toolName = process.env.CLAUDE_TOOL_NAME || process.argv[2] || process.env.TOOL_NAME || '';
52
+ const isWrite = /edit|write|replace|bash|str_replace/i.test(toolName);
53
+
54
+ if (!isWrite || !toolName) process.exit(0);
55
+
56
+ try {
57
+ const BCLAW_CMD = fs.existsSync(path.join(process.cwd(), 'node_modules', '.bin', 'brainclaw'))
58
+ ? 'npx brainclaw' : 'brainclaw';
59
+ const out = execSync(BCLAW_CMD + ' claim list --json', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
60
+ const claims = JSON.parse(out);
61
+ if (claims && claims.length > 0) process.exit(0);
62
+ } catch (e) {
63
+ // ignore if brainclaw not found or fails
64
+ process.exit(0);
65
+ }
66
+
67
+ const sessionMark = path.join(process.cwd(), '.brainclaw', 'tmp', 'claude_warned');
68
+ if (!fs.existsSync(path.dirname(sessionMark))) fs.mkdirSync(path.dirname(sessionMark), { recursive: true });
69
+
70
+ if (fs.existsSync(sessionMark)) {
71
+ const mtime = fs.statSync(sessionMark).mtimeMs;
72
+ if (Date.now() - mtime < 2 * 60 * 60 * 1000) {
73
+ process.exit(0); // already warned this session
74
+ }
75
+ }
76
+ fs.writeFileSync(sessionMark, Date.now().toString());
77
+
78
+ process.stderr.write('\\n[Brainclaw] ⚠️ WARNING: You are about to use an Edit/Write tool (' + toolName + ') but you have NO active claim.\\n');
79
+ process.stderr.write('[Brainclaw] Consider running \\\`brainclaw claim create <scope>\\\` to lock your work and avoid conflicts.\\n\\n');
80
+ " 2>&1 || exit 0
81
81
  `;
82
82
  }
83
83
  function generatePostMergeScript() {
84
- return `#!/bin/sh
85
- # brainclaw post-merge hook
86
- # Auto-releases claims whose scope overlaps with files changed by the merge
87
- # Generated by: brainclaw install-hooks
88
-
89
- BCLAW_CMD=""
90
- if command -v brainclaw >/dev/null 2>&1; then
91
- BCLAW_CMD="brainclaw"
92
- elif command -v bclaw >/dev/null 2>&1; then
93
- BCLAW_CMD="bclaw"
94
- else
95
- BCLAW_CMD="npx --no brainclaw"
96
- fi
97
-
98
- $BCLAW_CMD release-claims --from-git-diff 2>/dev/null || true
99
- $BCLAW_CMD worktree clean 2>/dev/null || true
84
+ return `#!/bin/sh
85
+ # brainclaw post-merge hook
86
+ # Auto-releases claims whose scope overlaps with files changed by the merge
87
+ # Generated by: brainclaw install-hooks
88
+
89
+ BCLAW_CMD=""
90
+ if command -v brainclaw >/dev/null 2>&1; then
91
+ BCLAW_CMD="brainclaw"
92
+ elif command -v bclaw >/dev/null 2>&1; then
93
+ BCLAW_CMD="bclaw"
94
+ else
95
+ BCLAW_CMD="npx --no brainclaw"
96
+ fi
97
+
98
+ $BCLAW_CMD release-claims --from-git-diff 2>/dev/null || true
99
+ $BCLAW_CMD worktree clean 2>/dev/null || true
100
100
  `;
101
101
  }
102
102
  function findGitRoot(cwd) {
@@ -113,30 +113,30 @@ function findGitRoot(cwd) {
113
113
  function generateHookScript() {
114
114
  // Use node directly to avoid sh.exe pipe issues on Windows (SIGPIPE).
115
115
  // The script is a self-contained node -e that runs both checks.
116
- return `#!/bin/sh
117
- # brainclaw pre-commit hook — generated by brainclaw install-hooks
118
- # Runs via node to avoid sh.exe SIGPIPE issues on Windows.
119
- exec node -e "
120
- const { execSync } = require('child_process');
121
- const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' }).trim();
122
- if (!staged) process.exit(0);
123
-
124
- // Check 1: reject staged .brainclaw/ files
125
- const brainclawFiles = staged.split('\\\\n').filter(f => f.startsWith('.brainclaw/'));
126
- if (brainclawFiles.length > 0) {
127
- process.stderr.write('\\\\nbrainclaw: .brainclaw/ files are staged — blocked.\\\\n');
128
- brainclawFiles.forEach(f => process.stderr.write(' ' + f + '\\\\n'));
129
- process.stderr.write(' Fix: git reset HEAD .brainclaw/\\\\n\\\\n');
130
- process.exit(1);
131
- }
132
-
133
- // Check 2: active constraint violations
134
- try {
135
- execSync('brainclaw check-constraints --staged', { stdio: 'inherit' });
136
- } catch (e) {
137
- if (e.status) process.exit(e.status);
138
- }
139
- " 2>&1 || exit $?
116
+ return `#!/bin/sh
117
+ # brainclaw pre-commit hook — generated by brainclaw install-hooks
118
+ # Runs via node to avoid sh.exe SIGPIPE issues on Windows.
119
+ exec node -e "
120
+ const { execSync } = require('child_process');
121
+ const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' }).trim();
122
+ if (!staged) process.exit(0);
123
+
124
+ // Check 1: reject staged .brainclaw/ files
125
+ const brainclawFiles = staged.split('\\\\n').filter(f => f.startsWith('.brainclaw/'));
126
+ if (brainclawFiles.length > 0) {
127
+ process.stderr.write('\\\\nbrainclaw: .brainclaw/ files are staged — blocked.\\\\n');
128
+ brainclawFiles.forEach(f => process.stderr.write(' ' + f + '\\\\n'));
129
+ process.stderr.write(' Fix: git reset HEAD .brainclaw/\\\\n\\\\n');
130
+ process.exit(1);
131
+ }
132
+
133
+ // Check 2: active constraint violations
134
+ try {
135
+ execSync('brainclaw check-constraints --staged', { stdio: 'inherit' });
136
+ } catch (e) {
137
+ if (e.status) process.exit(e.status);
138
+ }
139
+ " 2>&1 || exit $?
140
140
  `;
141
141
  }
142
142
  //# sourceMappingURL=install-hooks.js.map