brainclaw 0.19.2

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 (128) hide show
  1. package/LICENSE +74 -0
  2. package/README.md +226 -0
  3. package/dist/cli.js +1037 -0
  4. package/dist/commands/accept.js +149 -0
  5. package/dist/commands/adapter-openclaw-import.js +75 -0
  6. package/dist/commands/add-step.js +35 -0
  7. package/dist/commands/agent-board.js +106 -0
  8. package/dist/commands/audit.js +35 -0
  9. package/dist/commands/bootstrap.js +34 -0
  10. package/dist/commands/capability.js +104 -0
  11. package/dist/commands/changes.js +112 -0
  12. package/dist/commands/check-constraints.js +63 -0
  13. package/dist/commands/claim-resource.js +54 -0
  14. package/dist/commands/claim.js +92 -0
  15. package/dist/commands/complete-step.js +34 -0
  16. package/dist/commands/constraint.js +44 -0
  17. package/dist/commands/context-diff.js +32 -0
  18. package/dist/commands/context.js +63 -0
  19. package/dist/commands/decision.js +45 -0
  20. package/dist/commands/delete-plan.js +20 -0
  21. package/dist/commands/diff.js +99 -0
  22. package/dist/commands/doctor.js +1275 -0
  23. package/dist/commands/enable-agent.js +63 -0
  24. package/dist/commands/env.js +46 -0
  25. package/dist/commands/estimation-report.js +167 -0
  26. package/dist/commands/explore.js +47 -0
  27. package/dist/commands/export.js +381 -0
  28. package/dist/commands/handoff.js +63 -0
  29. package/dist/commands/history.js +22 -0
  30. package/dist/commands/hooks.js +123 -0
  31. package/dist/commands/init.js +356 -0
  32. package/dist/commands/install-hooks.js +115 -0
  33. package/dist/commands/instruction.js +56 -0
  34. package/dist/commands/list-agents.js +44 -0
  35. package/dist/commands/list-claims.js +45 -0
  36. package/dist/commands/list-instructions.js +50 -0
  37. package/dist/commands/list-plans.js +48 -0
  38. package/dist/commands/mcp-worker.js +12 -0
  39. package/dist/commands/mcp.js +2272 -0
  40. package/dist/commands/memory.js +283 -0
  41. package/dist/commands/metrics.js +175 -0
  42. package/dist/commands/plan-resource.js +62 -0
  43. package/dist/commands/plan.js +76 -0
  44. package/dist/commands/prune-candidates.js +36 -0
  45. package/dist/commands/prune.js +48 -0
  46. package/dist/commands/pull.js +25 -0
  47. package/dist/commands/push.js +28 -0
  48. package/dist/commands/rebuild.js +14 -0
  49. package/dist/commands/reflect-runtime-note.js +74 -0
  50. package/dist/commands/reflect.js +286 -0
  51. package/dist/commands/register-agent.js +29 -0
  52. package/dist/commands/reject.js +52 -0
  53. package/dist/commands/release-claim.js +41 -0
  54. package/dist/commands/release-claims.js +67 -0
  55. package/dist/commands/review.js +242 -0
  56. package/dist/commands/rollback.js +156 -0
  57. package/dist/commands/runtime-note.js +144 -0
  58. package/dist/commands/runtime-status.js +49 -0
  59. package/dist/commands/search.js +36 -0
  60. package/dist/commands/session-end.js +187 -0
  61. package/dist/commands/session-start.js +147 -0
  62. package/dist/commands/set-trust.js +92 -0
  63. package/dist/commands/setup.js +446 -0
  64. package/dist/commands/show-candidate.js +31 -0
  65. package/dist/commands/star-candidate.js +28 -0
  66. package/dist/commands/status.js +133 -0
  67. package/dist/commands/sync.js +159 -0
  68. package/dist/commands/tool.js +126 -0
  69. package/dist/commands/trap.js +74 -0
  70. package/dist/commands/update-handoff.js +23 -0
  71. package/dist/commands/update-plan.js +37 -0
  72. package/dist/commands/upgrade.js +382 -0
  73. package/dist/commands/use-candidate.js +35 -0
  74. package/dist/commands/version.js +96 -0
  75. package/dist/commands/watch.js +215 -0
  76. package/dist/commands/whoami.js +104 -0
  77. package/dist/core/agent-context.js +340 -0
  78. package/dist/core/agent-files.js +874 -0
  79. package/dist/core/agent-integrations.js +135 -0
  80. package/dist/core/agent-inventory.js +401 -0
  81. package/dist/core/agent-registry.js +420 -0
  82. package/dist/core/ai-agent-detection.js +140 -0
  83. package/dist/core/audit.js +85 -0
  84. package/dist/core/bootstrap.js +658 -0
  85. package/dist/core/brainclaw-version.js +433 -0
  86. package/dist/core/candidates.js +137 -0
  87. package/dist/core/circuit-breaker.js +118 -0
  88. package/dist/core/claims.js +72 -0
  89. package/dist/core/config.js +86 -0
  90. package/dist/core/context-diff.js +122 -0
  91. package/dist/core/context.js +1212 -0
  92. package/dist/core/contradictions.js +270 -0
  93. package/dist/core/coordination.js +86 -0
  94. package/dist/core/cross-project.js +99 -0
  95. package/dist/core/duplicates.js +72 -0
  96. package/dist/core/event-log.js +152 -0
  97. package/dist/core/events.js +56 -0
  98. package/dist/core/execution-context.js +204 -0
  99. package/dist/core/freshness.js +87 -0
  100. package/dist/core/global-registry.js +182 -0
  101. package/dist/core/host.js +10 -0
  102. package/dist/core/identity.js +151 -0
  103. package/dist/core/ids.js +56 -0
  104. package/dist/core/input-validation.js +81 -0
  105. package/dist/core/instructions.js +117 -0
  106. package/dist/core/io.js +191 -0
  107. package/dist/core/json-store.js +63 -0
  108. package/dist/core/lifecycle.js +45 -0
  109. package/dist/core/lock.js +129 -0
  110. package/dist/core/logger.js +49 -0
  111. package/dist/core/machine-profile.js +332 -0
  112. package/dist/core/markdown.js +120 -0
  113. package/dist/core/memory-git.js +133 -0
  114. package/dist/core/migration.js +247 -0
  115. package/dist/core/project-registry.js +64 -0
  116. package/dist/core/reflection-safety.js +21 -0
  117. package/dist/core/repo-analysis.js +133 -0
  118. package/dist/core/reputation.js +409 -0
  119. package/dist/core/runtime.js +134 -0
  120. package/dist/core/schema.js +580 -0
  121. package/dist/core/search.js +115 -0
  122. package/dist/core/security.js +66 -0
  123. package/dist/core/setup-state.js +50 -0
  124. package/dist/core/state.js +83 -0
  125. package/dist/core/store-resolution.js +119 -0
  126. package/dist/core/sync-remote.js +83 -0
  127. package/dist/core/traps.js +86 -0
  128. package/package.json +60 -0
@@ -0,0 +1,381 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { memoryExists } from '../core/io.js';
4
+ import { loadState } from '../core/state.js';
5
+ import { loadConfig, saveConfig } from '../core/config.js';
6
+ import { isAgentIntegrationName, upsertAgentIntegrationDeclaration } from '../core/agent-integrations.js';
7
+ import { resolveInstructions, loadInstructions } from '../core/instructions.js';
8
+ import { detectAiAgent } from '../core/ai-agent-detection.js';
9
+ import { resolveExportTarget, resolveExportTargetByFormat, writeExportFile, buildHygieneSection, describeAutoConfigWrite, writeExportCompanionFiles, collectExportGitignoreEntries, ensureGitignoreEntries, } from '../core/agent-files.js';
10
+ import { logger } from '../core/logger.js';
11
+ export function runExport(options) {
12
+ const cwd = options.cwd ?? process.cwd();
13
+ if (!memoryExists(cwd)) {
14
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
15
+ process.exit(1);
16
+ }
17
+ if (options.detect) {
18
+ if (options.shared) {
19
+ console.error('Error: --shared cannot be used with --detect. Use `brainclaw export --format <format> --write --shared` to publish a specific instruction file.');
20
+ process.exit(1);
21
+ }
22
+ runExportDetect(cwd, options);
23
+ return;
24
+ }
25
+ if (!options.format) {
26
+ console.error('Error: --format or --detect is required.');
27
+ process.exit(1);
28
+ }
29
+ const content = generateExport(options.format, options, cwd);
30
+ if (options.write) {
31
+ const target = resolveExportTargetByFormat(options.format);
32
+ const result = writeExportFile(content, target.relativePath, cwd);
33
+ const autoConfigs = writeExportCompanionFiles(options.format, cwd);
34
+ const gitignoreEntries = collectExportGitignoreEntries(cwd, target.relativePath, autoConfigs, {
35
+ includeTarget: !options.shared,
36
+ });
37
+ if (gitignoreEntries.length > 0) {
38
+ ensureGitignoreEntries(cwd, gitignoreEntries);
39
+ }
40
+ declareAgentIntegrationFromTarget(cwd, target.agentName, 'manual');
41
+ console.log(`✔ Written to ${target.relativePath} (${result.created ? 'created' : 'updated'})`);
42
+ if (options.shared) {
43
+ console.log(`✔ Left ${target.relativePath} versionable (--shared); local companion config remains gitignored`);
44
+ }
45
+ else if (gitignoreEntries.length > 0) {
46
+ console.log('✔ Added generated local agent files to .gitignore');
47
+ }
48
+ for (const autoConfig of autoConfigs) {
49
+ const message = describeAutoConfigWrite(autoConfig);
50
+ if (message) {
51
+ console.log(message);
52
+ }
53
+ }
54
+ }
55
+ else if (options.output) {
56
+ const dir = path.dirname(options.output);
57
+ if (dir && !fs.existsSync(dir))
58
+ fs.mkdirSync(dir, { recursive: true });
59
+ fs.writeFileSync(options.output, content, 'utf-8');
60
+ console.log(`✔ Exported to ${options.output}`);
61
+ }
62
+ else {
63
+ console.log(content);
64
+ }
65
+ }
66
+ function runExportDetect(cwd, options) {
67
+ const detected = detectAiAgent();
68
+ const target = detected ? resolveExportTarget(detected.name) : resolveExportTarget('unknown');
69
+ const content = generateExport(target.format, options, cwd);
70
+ const result = writeExportFile(content, target.relativePath, cwd);
71
+ const autoConfigs = writeExportCompanionFiles(target.format, cwd);
72
+ const gitignoreEntries = collectExportGitignoreEntries(cwd, target.relativePath, autoConfigs);
73
+ if (gitignoreEntries.length > 0) {
74
+ ensureGitignoreEntries(cwd, gitignoreEntries);
75
+ }
76
+ declareAgentIntegrationFromTarget(cwd, target.agentName, detected ? 'detected' : 'manual');
77
+ const source = detected ? `${detected.name} [${detected.detection_source}]` : 'fallback (no agent detected)';
78
+ console.log(`✔ Detected: ${source}`);
79
+ console.log(`✔ Written to ${target.relativePath} (${result.created ? 'created' : 'updated'})`);
80
+ if (gitignoreEntries.length > 0) {
81
+ console.log('✔ Added generated local agent files to .gitignore');
82
+ }
83
+ for (const autoConfig of autoConfigs) {
84
+ const message = describeAutoConfigWrite(autoConfig);
85
+ if (message) {
86
+ console.log(message);
87
+ }
88
+ }
89
+ }
90
+ export function writeAgentExportForAgent(agentName, cwd) {
91
+ const rendered = renderAgentExportForAgent(agentName, cwd);
92
+ if (!rendered) {
93
+ return undefined;
94
+ }
95
+ const result = writeExportFile(rendered.content, rendered.relativePath, cwd);
96
+ declareAgentIntegrationFromTarget(cwd, rendered.agentName, 'manual');
97
+ return { relativePath: rendered.relativePath, created: result.created, updated: result.updated };
98
+ }
99
+ export function renderAgentExportForAgent(agentName, cwd) {
100
+ const target = resolveExportTarget(agentName);
101
+ if (target.agentName === 'unknown') {
102
+ return undefined;
103
+ }
104
+ const content = generateExport(target.format, {}, cwd);
105
+ return { agentName: target.agentName, relativePath: target.relativePath, content };
106
+ }
107
+ export function writeDetectedAgentExport(detectedAgentName, cwd) {
108
+ const rendered = renderAgentExportForAgent(detectedAgentName, cwd);
109
+ if (!rendered) {
110
+ return undefined;
111
+ }
112
+ // Skip if this is the fallback and AGENTS.md is already handled by ensureAgentFiles
113
+ if (rendered.relativePath === 'AGENTS.md' || rendered.relativePath === '.github/copilot-instructions.md') {
114
+ return undefined;
115
+ }
116
+ const result = writeExportFile(rendered.content, rendered.relativePath, cwd);
117
+ return { relativePath: rendered.relativePath, created: result.created };
118
+ }
119
+ function declareAgentIntegrationFromTarget(cwd, agentName, declarationSource) {
120
+ if (!isAgentIntegrationName(agentName)) {
121
+ return;
122
+ }
123
+ const config = loadConfig(cwd);
124
+ if (upsertAgentIntegrationDeclaration(config, agentName, declarationSource)) {
125
+ saveConfig(config, cwd);
126
+ }
127
+ }
128
+ function generateExport(format, options, cwd) {
129
+ switch (format) {
130
+ case 'copilot-instructions': return generateCopilotInstructions(options, cwd);
131
+ case 'cursor-rules': return generateCursorRules(options, cwd);
132
+ case 'agents-md': return generateAgentsMd(options, cwd);
133
+ case 'claude-md': return generateClaudeMd(options, cwd);
134
+ case 'windsurf': return generateWindsurf(options, cwd);
135
+ case 'cline': return generateCline(options, cwd);
136
+ case 'roo': return generateRoo(options, cwd);
137
+ case 'continue': return generateContinueRules(options, cwd);
138
+ case 'gemini-md': return generateGeminiMd(options, cwd);
139
+ default:
140
+ throw new Error(`Unknown export format: ${format}`);
141
+ }
142
+ }
143
+ function getInstructionText(options, cwd) {
144
+ try {
145
+ const all = loadInstructions(cwd);
146
+ const resolved = resolveInstructions(all, {
147
+ project: options.project,
148
+ agent: options.agent,
149
+ });
150
+ return resolved.filter(i => i.active).map(i => i.text);
151
+ }
152
+ catch (err) {
153
+ logger.debug('Failed to resolve instructions for export:', err);
154
+ return [];
155
+ }
156
+ }
157
+ function getConstraintsSummary(cwd) {
158
+ try {
159
+ const state = loadState(cwd);
160
+ const active = state.active_constraints.filter(c => c.status === 'active');
161
+ if (active.length === 0)
162
+ return '';
163
+ return '## Active Constraints\n\n' + active.map(c => `- ${c.text}`).join('\n');
164
+ }
165
+ catch (err) {
166
+ logger.debug('Failed to load constraints for export:', err);
167
+ return '';
168
+ }
169
+ }
170
+ function getDecisionsSummary(cwd) {
171
+ try {
172
+ const state = loadState(cwd);
173
+ if (state.recent_decisions.length === 0)
174
+ return '';
175
+ return '## Key Decisions\n\n' + state.recent_decisions.slice(-10).map(d => `- ${d.text}`).join('\n');
176
+ }
177
+ catch {
178
+ return '';
179
+ }
180
+ }
181
+ function getTrapsSummary(cwd) {
182
+ try {
183
+ const state = loadState(cwd);
184
+ const traps = state.known_traps.filter(t => t.visibility === 'shared');
185
+ if (traps.length === 0)
186
+ return '';
187
+ return '## Known Traps\n\n' + traps.map(t => `- [${t.severity}] ${t.text}`).join('\n');
188
+ }
189
+ catch {
190
+ return '';
191
+ }
192
+ }
193
+ // buildHygieneSection is imported from ../core/agent-files.js
194
+ function generateCopilotInstructions(options, cwd) {
195
+ const config = loadConfig(cwd);
196
+ const lines = [
197
+ `# Copilot Instructions — ${config.project_name}`,
198
+ '',
199
+ '> Generated by brainclaw. Do not edit manually — run `brainclaw export --format copilot-instructions --write` to regenerate.',
200
+ '',
201
+ ];
202
+ const instructions = getInstructionText(options, cwd);
203
+ if (instructions.length > 0) {
204
+ lines.push('## Instructions\n');
205
+ for (const inst of instructions)
206
+ lines.push(`- ${inst}`);
207
+ lines.push('');
208
+ }
209
+ const constraints = getConstraintsSummary(cwd);
210
+ if (constraints) {
211
+ lines.push(constraints);
212
+ lines.push('');
213
+ }
214
+ const decisions = getDecisionsSummary(cwd);
215
+ if (decisions) {
216
+ lines.push(decisions);
217
+ lines.push('');
218
+ }
219
+ const traps = getTrapsSummary(cwd);
220
+ if (traps) {
221
+ lines.push(traps);
222
+ lines.push('');
223
+ }
224
+ lines.push(buildHygieneSection());
225
+ return lines.join('\n');
226
+ }
227
+ function generateCursorRules(options, cwd) {
228
+ const instructions = getInstructionText(options, cwd);
229
+ const state = loadState(cwd);
230
+ const lines = ['# Cursor rules — generated by brainclaw', ''];
231
+ if (instructions.length > 0) {
232
+ lines.push('## Instructions');
233
+ for (const inst of instructions)
234
+ lines.push(`- ${inst}`);
235
+ lines.push('');
236
+ }
237
+ const constraints = state.active_constraints.filter(c => c.status === 'active');
238
+ if (constraints.length > 0) {
239
+ lines.push('## Constraints');
240
+ for (const c of constraints)
241
+ lines.push(`- ${c.text}`);
242
+ lines.push('');
243
+ }
244
+ lines.push(buildHygieneSection());
245
+ return lines.join('\n');
246
+ }
247
+ function generateAgentsMd(options, cwd) {
248
+ const config = loadConfig(cwd);
249
+ const instructions = getInstructionText(options, cwd);
250
+ const state = loadState(cwd);
251
+ const lines = [
252
+ `# AGENTS.md — ${config.project_name}`,
253
+ '',
254
+ '> Generated by brainclaw.',
255
+ '',
256
+ '## Project Instructions',
257
+ '',
258
+ ];
259
+ if (instructions.length > 0) {
260
+ for (const inst of instructions)
261
+ lines.push(`- ${inst}`);
262
+ }
263
+ else {
264
+ lines.push('_No project instructions defined._');
265
+ }
266
+ lines.push('');
267
+ const activePlans = state.plan_items.filter(p => p.status === 'todo' || p.status === 'in_progress');
268
+ if (activePlans.length > 0) {
269
+ lines.push('## Active Plan Items\n');
270
+ for (const p of activePlans.slice(0, 10)) {
271
+ lines.push(`- [${p.status}] ${p.text}${p.assignee ? ` (@${p.assignee})` : ''}`);
272
+ }
273
+ lines.push('');
274
+ }
275
+ const traps = state.known_traps.filter(t => t.visibility === 'shared');
276
+ if (traps.length > 0) {
277
+ lines.push('## Known Traps\n');
278
+ for (const t of traps.slice(0, 10)) {
279
+ lines.push(`- [${t.severity}] ${t.text}`);
280
+ }
281
+ lines.push('');
282
+ }
283
+ lines.push(buildHygieneSection());
284
+ return lines.join('\n');
285
+ }
286
+ function generateClaudeMd(options, cwd) {
287
+ const config = loadConfig(cwd);
288
+ const instructions = getInstructionText(options, cwd);
289
+ const state = loadState(cwd);
290
+ const lines = [
291
+ `# ${config.project_name}`,
292
+ '',
293
+ '> This file is generated by brainclaw. Run `brainclaw export --format claude-md --write` to regenerate.',
294
+ '',
295
+ ];
296
+ if (instructions.length > 0) {
297
+ lines.push('## Project Instructions\n');
298
+ for (const inst of instructions)
299
+ lines.push(`- ${inst}`);
300
+ lines.push('');
301
+ }
302
+ const constraints = state.active_constraints.filter(c => c.status === 'active');
303
+ if (constraints.length > 0) {
304
+ lines.push('## Active Constraints\n');
305
+ for (const c of constraints)
306
+ lines.push(`- ${c.text}`);
307
+ lines.push('');
308
+ }
309
+ const traps = state.known_traps.filter(t => t.visibility === 'shared');
310
+ if (traps.length > 0) {
311
+ lines.push('## Known Traps\n');
312
+ for (const t of traps)
313
+ lines.push(`- [${t.severity}] ${t.text}`);
314
+ lines.push('');
315
+ }
316
+ lines.push(buildHygieneSection());
317
+ return lines.join('\n');
318
+ }
319
+ function generateWindsurf(options, cwd) {
320
+ const instructions = getInstructionText(options, cwd);
321
+ const state = loadState(cwd);
322
+ const lines = ['# Windsurf rules — generated by brainclaw', ''];
323
+ if (instructions.length > 0) {
324
+ lines.push('## Instructions');
325
+ for (const inst of instructions)
326
+ lines.push(`- ${inst}`);
327
+ lines.push('');
328
+ }
329
+ const constraints = state.active_constraints.filter(c => c.status === 'active');
330
+ if (constraints.length > 0) {
331
+ lines.push('## Constraints');
332
+ for (const c of constraints)
333
+ lines.push(`- ${c.text}`);
334
+ lines.push('');
335
+ }
336
+ lines.push(buildHygieneSection());
337
+ return lines.join('\n');
338
+ }
339
+ function generateCline(options, cwd) {
340
+ return generateWindsurf(options, cwd).replace('Windsurf rules', 'Cline rules');
341
+ }
342
+ function generateRoo(options, cwd) {
343
+ return generateWindsurf(options, cwd).replace('Windsurf rules', 'Roo rules');
344
+ }
345
+ function generateContinueRules(options, cwd) {
346
+ return generateWindsurf(options, cwd).replace('Windsurf rules', 'Continue rules');
347
+ }
348
+ function generateGeminiMd(options, cwd) {
349
+ const config = loadConfig(cwd);
350
+ const instructions = getInstructionText(options, cwd);
351
+ const state = loadState(cwd);
352
+ const lines = [
353
+ `# ${config.project_name}`,
354
+ '',
355
+ '> Generated by brainclaw. Run `brainclaw export --format gemini-md --write` to regenerate.',
356
+ '',
357
+ ];
358
+ if (instructions.length > 0) {
359
+ lines.push('## Project Instructions\n');
360
+ for (const inst of instructions)
361
+ lines.push(`- ${inst}`);
362
+ lines.push('');
363
+ }
364
+ const constraints = state.active_constraints.filter(c => c.status === 'active');
365
+ if (constraints.length > 0) {
366
+ lines.push('## Active Constraints\n');
367
+ for (const c of constraints)
368
+ lines.push(`- ${c.text}`);
369
+ lines.push('');
370
+ }
371
+ const traps = state.known_traps.filter(t => t.visibility === 'shared');
372
+ if (traps.length > 0) {
373
+ lines.push('## Known Traps\n');
374
+ for (const t of traps)
375
+ lines.push(`- [${t.severity}] ${t.text}`);
376
+ lines.push('');
377
+ }
378
+ lines.push(buildHygieneSection());
379
+ return lines.join('\n');
380
+ }
381
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1,63 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { loadState, saveState } from '../core/state.js';
3
+ import { resolveCurrentAgentName } from '../core/agent-registry.js';
4
+ import { loadConfig } from '../core/config.js';
5
+ import { generateMarkdown } from '../core/markdown.js';
6
+ import { generateIdWithLabel, nowISO } from '../core/ids.js';
7
+ import { scanText } from '../core/security.js';
8
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
9
+ import { validateCliInput } from '../core/input-validation.js';
10
+ import { resolveTargetStore } from '../core/store-resolution.js';
11
+ export function runHandoff(text, options) {
12
+ const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
13
+ if (!memoryExists(cwd)) {
14
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
15
+ process.exit(1);
16
+ }
17
+ validateCliInput(text, options.tag);
18
+ const config = loadConfig(cwd);
19
+ const warnings = scanText(text, config);
20
+ for (const w of warnings) {
21
+ console.warn(`⚠ ${w.message}`);
22
+ if (w.level === 'block') {
23
+ console.error('Blocked: strict redaction is enabled. Entry not added.');
24
+ process.exit(1);
25
+ }
26
+ }
27
+ const state = loadState(cwd);
28
+ const plan = options.plan ? state.plan_items.find((item) => item.id === options.plan) : undefined;
29
+ if (options.plan && !plan) {
30
+ console.error(`Error: Plan item '${options.plan}' not found.`);
31
+ process.exit(1);
32
+ }
33
+ const { id, short_label } = generateIdWithLabel('open_handoffs');
34
+ let diff;
35
+ if (options.captureDiff) {
36
+ try {
37
+ diff = execSync('git diff HEAD', { encoding: 'utf-8' });
38
+ }
39
+ catch {
40
+ diff = "Could not capture git diff.";
41
+ }
42
+ }
43
+ const entry = {
44
+ id,
45
+ short_label,
46
+ from: options.from,
47
+ to: options.to,
48
+ text,
49
+ created_at: nowISO(),
50
+ author: options.author ?? resolveCurrentAgentName(cwd),
51
+ status: 'open',
52
+ project: options.project ?? plan?.project,
53
+ plan_id: options.plan,
54
+ tags: options.tag ?? [],
55
+ related_paths: options.path,
56
+ snapshot: diff ? { diff } : undefined,
57
+ };
58
+ state.open_handoffs.push(entry);
59
+ saveState(state, cwd);
60
+ writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
61
+ console.log(`✔ Handoff added: [${id}] ${options.from} → ${options.to}: ${text}`);
62
+ }
63
+ //# sourceMappingURL=handoff.js.map
@@ -0,0 +1,22 @@
1
+ import { readAuditLog } from '../core/audit.js';
2
+ import { memoryExists } from '../core/io.js';
3
+ export function runHistory(id) {
4
+ if (!memoryExists()) {
5
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
6
+ process.exit(1);
7
+ }
8
+ const entries = readAuditLog({ itemId: id });
9
+ if (entries.length === 0) {
10
+ console.log(`No history found for item '${id}'.`);
11
+ return;
12
+ }
13
+ console.log(`History for ${id} — ${entries.length} event(s):`);
14
+ console.log('');
15
+ for (const entry of entries) {
16
+ const parts = [` ${entry.timestamp}`, `[${entry.actor}]`, entry.action];
17
+ if (entry.reason)
18
+ parts.push(`| ${entry.reason}`);
19
+ console.log(parts.join(' '));
20
+ }
21
+ }
22
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1,123 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { memoryExists } from '../core/io.js';
4
+ import { loadConfig } from '../core/config.js';
5
+ import { BRAINCLAW_SECTION_START, BRAINCLAW_SECTION_END, upsertBrainclawSection } from '../core/agent-files.js';
6
+ /**
7
+ * Generate the Cursor MDC hook file content.
8
+ * Uses MDC frontmatter with `alwaysApply: true` so Cursor injects it
9
+ * deterministically into every agent conversation.
10
+ */
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
+ **At the start of every session, before doing anything else:**
20
+
21
+ \`\`\`bash
22
+ brainclaw context
23
+ \`\`\`
24
+
25
+ This loads the shared project memory: active constraints, recent decisions, known traps,
26
+ open plan items, active claims, and the last handoff.
27
+
28
+ **Before finishing any session:**
29
+
30
+ \`\`\`bash
31
+ brainclaw claim release <id> # release claims you opened
32
+ brainclaw plan update <id> --status done # close plan items you completed
33
+ # or in one shot:
34
+ brainclaw session-end --auto-release
35
+ \`\`\`
36
+ `;
37
+ }
38
+ /**
39
+ * Generate the Windsurf session-trigger section.
40
+ * Windsurf reads .windsurfrules on every Cascade activation — a clearly
41
+ * delimited "SESSION START" block acts as a deterministic trigger.
42
+ */
43
+ export function generateWindsurfHook(projectName) {
44
+ return `# Brainclaw session trigger — ${projectName}
45
+ # This block is always active. Windsurf Cascade reads .windsurfrules on every activation.
46
+
47
+ ## SESSION START (always run first)
48
+
49
+ Before responding to any request, execute:
50
+
51
+ \`\`\`bash
52
+ brainclaw context
53
+ \`\`\`
54
+
55
+ This gives you: active constraints, recent decisions, known traps, open plans, active claims,
56
+ and the last handoff note. Do not skip this step.
57
+
58
+ ## SESSION END (before finishing)
59
+
60
+ \`\`\`bash
61
+ brainclaw claim release <id> # for each claim you hold
62
+ brainclaw plan update <id> --status done # for each plan item you completed
63
+ # or:
64
+ brainclaw session-end --auto-release
65
+ \`\`\`
66
+ `;
67
+ }
68
+ export function writeHook(content, relativePath, cwd) {
69
+ const fullPath = path.join(cwd, relativePath);
70
+ const dir = path.dirname(fullPath);
71
+ if (!fs.existsSync(dir))
72
+ fs.mkdirSync(dir, { recursive: true });
73
+ const existed = fs.existsSync(fullPath);
74
+ if (relativePath.endsWith('.mdc')) {
75
+ // MDC files: write as-is (frontmatter must be preserved verbatim)
76
+ fs.writeFileSync(fullPath, content, 'utf-8');
77
+ }
78
+ else {
79
+ // Plain markdown rules files: use brainclaw sentinel markers
80
+ const section = `${BRAINCLAW_SECTION_START}\n${content}\n${BRAINCLAW_SECTION_END}`;
81
+ const existing = existed ? fs.readFileSync(fullPath, 'utf-8') : '';
82
+ fs.writeFileSync(fullPath, upsertBrainclawSection(existing, section), 'utf-8');
83
+ }
84
+ return { target: path.basename(relativePath, path.extname(relativePath)), relativePath, created: !existed };
85
+ }
86
+ export function runHooks(options = {}) {
87
+ const cwd = options.cwd ?? process.cwd();
88
+ if (!memoryExists(cwd)) {
89
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
90
+ process.exit(1);
91
+ }
92
+ const config = loadConfig(cwd);
93
+ const target = options.target ?? 'all';
94
+ const results = [];
95
+ if (target === 'cursor' || target === 'all') {
96
+ const content = generateCursorHook(config.project_name);
97
+ results.push(writeHook(content, '.cursor/rules/brainclaw-session.mdc', cwd));
98
+ }
99
+ if (target === 'windsurf' || target === 'all') {
100
+ const content = generateWindsurfHook(config.project_name);
101
+ results.push(writeHook(content, '.windsurfrules', cwd));
102
+ }
103
+ for (const r of results) {
104
+ console.log(`✔ Hook written to ${r.relativePath} (${r.created ? 'created' : 'updated'})`);
105
+ }
106
+ }
107
+ /**
108
+ * Called from `brainclaw init` when an agent is detected.
109
+ * Writes hooks relevant to the detected agent, silently on success.
110
+ */
111
+ export function writeDetectedAgentHooks(agentName, projectName, cwd) {
112
+ const results = [];
113
+ if (agentName === 'cursor') {
114
+ const content = generateCursorHook(projectName);
115
+ results.push(writeHook(content, '.cursor/rules/brainclaw-session.mdc', cwd));
116
+ }
117
+ if (agentName === 'windsurf') {
118
+ const content = generateWindsurfHook(projectName);
119
+ results.push(writeHook(content, '.windsurfrules', cwd));
120
+ }
121
+ return results;
122
+ }
123
+ //# sourceMappingURL=hooks.js.map