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,874 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { spawnSync } from 'node:child_process';
4
+ export const BRAINCLAW_SECTION_START = '<!-- brainclaw:start -->';
5
+ export const BRAINCLAW_SECTION_END = '<!-- brainclaw:end -->';
6
+ export function buildBrainclawSection(storageDir) {
7
+ return `${BRAINCLAW_SECTION_START}
8
+ ## Brainclaw — shared project memory
9
+
10
+ This project uses brainclaw for shared coordination between humans and agents.
11
+
12
+ ### Session start (required)
13
+
14
+ 1. Run \`brainclaw context\` to load shared state (constraints, decisions, traps, plans, handoffs)
15
+ 2. Check **Your open work** for active claims and in-progress plans assigned to you
16
+ 3. Respect active claims from other agents — check \`brainclaw claim list\` before editing a claimed scope
17
+
18
+ ### Before finishing (required)
19
+
20
+ 1. Release claims you opened: \`brainclaw claim release <id>\` — or \`brainclaw session-end --auto-release\`
21
+ 2. Update completed plan items: \`brainclaw plan update <id> --status done\`
22
+
23
+ ### Recording work
24
+
25
+ \`\`\`bash
26
+ brainclaw memory create decision "<text>" # record a decision
27
+ brainclaw memory create constraint "<text>" # record an active constraint
28
+ brainclaw memory create trap "<text>" # record a known trap
29
+ brainclaw claim create "<text>" --scope <path> # claim a scope before editing
30
+ brainclaw plan create "<text>" # add a shared work item
31
+ \`\`\`
32
+
33
+ Memory is stored in \`${storageDir}/\`. Run \`brainclaw doctor\` to verify health.
34
+ ${BRAINCLAW_SECTION_END}`;
35
+ }
36
+ export function buildHygieneSection() {
37
+ return `## Brainclaw — required coordination
38
+
39
+ Before starting work:
40
+ 1. Run \`brainclaw context\` (or \`brainclaw context --json\`) to load shared memory
41
+ 2. Run \`brainclaw claim list\` — do not edit a file claimed by another agent
42
+ 3. Create a plan for significant work: \`brainclaw plan create "<description>"\`
43
+ 4. Claim files you will modify: \`brainclaw claim create "<description>" --scope <path>\`
44
+
45
+ Before finishing:
46
+ 1. Run \`brainclaw session-end --auto-release\` — releases claims and updates plans
47
+ 2. Or manually: \`brainclaw claim release <id>\` and \`brainclaw plan update <id> --status done\``;
48
+ }
49
+ export function hasBrainclawSection(content) {
50
+ return content.includes(BRAINCLAW_SECTION_START) && content.includes(BRAINCLAW_SECTION_END);
51
+ }
52
+ export function upsertBrainclawSection(existingContent, section) {
53
+ const start = existingContent.indexOf(BRAINCLAW_SECTION_START);
54
+ const end = existingContent.indexOf(BRAINCLAW_SECTION_END);
55
+ if (start !== -1 && end !== -1) {
56
+ const before = existingContent.slice(0, start);
57
+ const after = existingContent.slice(end + BRAINCLAW_SECTION_END.length);
58
+ return before + section + after;
59
+ }
60
+ const trimmed = existingContent.trimEnd();
61
+ return trimmed.length > 0 ? `${trimmed}\n\n${section}\n` : `${section}\n`;
62
+ }
63
+ export function ensureAgentFiles(cwd, storageDir, options = {}) {
64
+ const section = buildBrainclawSection(storageDir);
65
+ const result = {
66
+ agentsMdCreated: false,
67
+ agentsMdUpdated: false,
68
+ copilotInstructionsCreated: false,
69
+ copilotInstructionsUpdated: false,
70
+ };
71
+ // AGENTS.md
72
+ const agentsMdPath = path.join(cwd, 'AGENTS.md');
73
+ const agentsMdExists = fs.existsSync(agentsMdPath);
74
+ if (!options.onlyExisting || agentsMdExists) {
75
+ const agentsMdContent = agentsMdExists
76
+ ? fs.readFileSync(agentsMdPath, 'utf-8')
77
+ : '# AGENTS\n\nProject guidelines for AI coding agents.\n';
78
+ if (!options.requireExistingSection || !agentsMdExists || hasBrainclawSection(agentsMdContent)) {
79
+ const newAgentsMd = upsertBrainclawSection(agentsMdContent, section);
80
+ if (newAgentsMd !== agentsMdContent) {
81
+ fs.writeFileSync(agentsMdPath, newAgentsMd, 'utf-8');
82
+ if (agentsMdExists) {
83
+ result.agentsMdUpdated = true;
84
+ }
85
+ else {
86
+ result.agentsMdCreated = true;
87
+ }
88
+ }
89
+ }
90
+ }
91
+ // .github/copilot-instructions.md
92
+ const copilotPath = path.join(cwd, '.github', 'copilot-instructions.md');
93
+ const copilotExists = fs.existsSync(copilotPath);
94
+ if (!options.onlyExisting || copilotExists) {
95
+ const copilotContent = copilotExists
96
+ ? fs.readFileSync(copilotPath, 'utf-8')
97
+ : '# Copilot Instructions\n';
98
+ if (!options.requireExistingSection || !copilotExists || hasBrainclawSection(copilotContent)) {
99
+ if (!copilotExists) {
100
+ fs.mkdirSync(path.dirname(copilotPath), { recursive: true });
101
+ }
102
+ const newCopilot = upsertBrainclawSection(copilotContent, section);
103
+ if (newCopilot !== copilotContent) {
104
+ fs.writeFileSync(copilotPath, newCopilot, 'utf-8');
105
+ if (copilotExists) {
106
+ result.copilotInstructionsUpdated = true;
107
+ }
108
+ else {
109
+ result.copilotInstructionsCreated = true;
110
+ }
111
+ }
112
+ }
113
+ }
114
+ return result;
115
+ }
116
+ export function ensureGitignoreEntries(cwd, entries) {
117
+ const gitignorePath = path.join(cwd, '.gitignore');
118
+ const current = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
119
+ const lines = new Set(current.split(/\r?\n/).map((l) => l.trim()));
120
+ const toAdd = entries.filter((e) => !lines.has(e));
121
+ if (toAdd.length === 0)
122
+ return;
123
+ const separator = current.trimEnd().length > 0 ? '\n' : '';
124
+ const next = `${current.trimEnd()}${separator}\n# Agent instruction files (generated by brainclaw)\n${toAdd.join('\n')}\n`;
125
+ fs.writeFileSync(gitignorePath, next, 'utf-8');
126
+ }
127
+ export function collectWorkspaceGitignoreEntries(cwd, results) {
128
+ const workspaceRoot = path.resolve(cwd);
129
+ const collected = new Set();
130
+ for (const result of results) {
131
+ if (!result.relativePath)
132
+ continue;
133
+ if (result.relativePath === 'package.json')
134
+ continue;
135
+ const expectedWorkspacePath = path.resolve(workspaceRoot, result.relativePath);
136
+ const actualPath = path.resolve(result.filePath);
137
+ if (actualPath !== expectedWorkspacePath)
138
+ continue;
139
+ collected.add(result.relativePath.replace(/\\/g, '/'));
140
+ }
141
+ return [...collected];
142
+ }
143
+ export function collectExportGitignoreEntries(cwd, targetRelativePath, results, options = {}) {
144
+ const collected = new Set();
145
+ if (options.includeTarget !== false) {
146
+ collected.add(targetRelativePath.replace(/\\/g, '/'));
147
+ }
148
+ for (const entry of collectWorkspaceGitignoreEntries(cwd, results)) {
149
+ collected.add(entry);
150
+ }
151
+ return [...collected];
152
+ }
153
+ export const AGENT_EXPORT_REGISTRY = [
154
+ { agentName: 'github-copilot', format: 'copilot-instructions', relativePath: '.github/copilot-instructions.md' },
155
+ { agentName: 'claude-code', format: 'claude-md', relativePath: 'CLAUDE.md' },
156
+ { agentName: 'cursor', format: 'cursor-rules', relativePath: '.cursor/rules/brainclaw.md' },
157
+ { agentName: 'windsurf', format: 'windsurf', relativePath: '.windsurfrules' },
158
+ { agentName: 'cline', format: 'cline', relativePath: '.clinerules/brainclaw.md' },
159
+ { agentName: 'codex', format: 'agents-md', relativePath: 'AGENTS.md' },
160
+ { agentName: 'continue', format: 'continue', relativePath: '.continue/rules/brainclaw.md' },
161
+ { agentName: 'roo', format: 'roo', relativePath: '.roo/rules/brainclaw.md' },
162
+ { agentName: 'opencode', format: 'agents-md', relativePath: 'AGENTS.md' },
163
+ { agentName: 'antigravity', format: 'gemini-md', relativePath: 'GEMINI.md' },
164
+ ];
165
+ export const FALLBACK_EXPORT_TARGET = {
166
+ agentName: 'unknown',
167
+ format: 'agents-md',
168
+ relativePath: 'AGENTS.md',
169
+ };
170
+ export function resolveExportTarget(agentName) {
171
+ return AGENT_EXPORT_REGISTRY.find((t) => t.agentName === agentName) ?? FALLBACK_EXPORT_TARGET;
172
+ }
173
+ export function resolveExportTargetByFormat(format) {
174
+ return AGENT_EXPORT_REGISTRY.find((t) => t.format === format) ?? FALLBACK_EXPORT_TARGET;
175
+ }
176
+ export function writeExportFile(content, relativePath, cwd) {
177
+ const fullPath = path.join(cwd, relativePath);
178
+ const dir = path.dirname(fullPath);
179
+ if (!fs.existsSync(dir))
180
+ fs.mkdirSync(dir, { recursive: true });
181
+ const existed = fs.existsSync(fullPath);
182
+ const section = `${BRAINCLAW_SECTION_START}\n${content}\n${BRAINCLAW_SECTION_END}`;
183
+ const existing = existed ? fs.readFileSync(fullPath, 'utf-8') : '';
184
+ const next = upsertBrainclawSection(existing, section);
185
+ if (next === existing) {
186
+ return { created: false, updated: false, filePath: fullPath };
187
+ }
188
+ fs.writeFileSync(fullPath, next, 'utf-8');
189
+ return { created: !existed, updated: existed, filePath: fullPath };
190
+ }
191
+ const ALL_BCLAW_TOOLS = [
192
+ 'bclaw_get_context', 'bclaw_bootstrap', 'bclaw_get_execution_context',
193
+ 'bclaw_read_handoff', 'bclaw_get_agent_board', 'bclaw_search', 'bclaw_estimation_report',
194
+ 'bclaw_list_plans', 'bclaw_list_claims', 'bclaw_list_agents', 'bclaw_list_instructions', 'bclaw_list_candidates',
195
+ 'bclaw_write_note', 'bclaw_create_candidate', 'bclaw_accept', 'bclaw_reject',
196
+ 'bclaw_claim', 'bclaw_release_claim', 'bclaw_session_start', 'bclaw_session_end',
197
+ 'bclaw_create_plan', 'bclaw_update_plan', 'bclaw_add_step', 'bclaw_complete_step',
198
+ ];
199
+ const CLINE_MCP_RELATIVE_PATH = '.vscode/cline_mcp_settings.json';
200
+ const CURSOR_MDC_RELATIVE_PATH = '.cursor/rules/brainclaw-mcp-shim.mdc';
201
+ const COPILOT_SKILL_RELATIVE_PATH = '.github/skills/brainclaw-context/SKILL.md';
202
+ const WINDSURF_MCP_RELATIVE_PATH = '.codeium/windsurf/mcp_config.json';
203
+ const CLAUDE_CODE_MCP_RELATIVE_PATH = '.mcp.json';
204
+ const CLAUDE_CODE_COMMAND_RELATIVE_PATH = '.claude/commands/brainclaw.md';
205
+ const CLAUDE_CODE_SETTINGS_RELATIVE_PATH = '.claude/settings.local.json';
206
+ const CLAUDE_CODE_SESSION_MARKER_RELATIVE_PATH = '.claude/.bclaw-session';
207
+ const CURSOR_MCP_RELATIVE_PATH = '.cursor/mcp.json';
208
+ const ROO_MCP_RELATIVE_PATH = '.roo/mcp.json';
209
+ const CONTINUE_CONFIG_RELATIVE_PATH = '.continue/config.json';
210
+ const OPENCODE_CONFIG_RELATIVE_PATH = 'opencode.json';
211
+ const ANTIGRAVITY_MCP_RELATIVE_PATH = '.gemini/antigravity/mcp_config.json';
212
+ export const LOCAL_ONLY_AGENT_WORKSPACE_FILES = [
213
+ CLINE_MCP_RELATIVE_PATH,
214
+ CURSOR_MDC_RELATIVE_PATH,
215
+ COPILOT_SKILL_RELATIVE_PATH,
216
+ CLAUDE_CODE_MCP_RELATIVE_PATH,
217
+ CLAUDE_CODE_COMMAND_RELATIVE_PATH,
218
+ CLAUDE_CODE_SETTINGS_RELATIVE_PATH,
219
+ CLAUDE_CODE_SESSION_MARKER_RELATIVE_PATH,
220
+ ROO_MCP_RELATIVE_PATH,
221
+ CONTINUE_CONFIG_RELATIVE_PATH,
222
+ OPENCODE_CONFIG_RELATIVE_PATH,
223
+ ];
224
+ function isJsonObject(value) {
225
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
226
+ }
227
+ function readJsonObject(filePath) {
228
+ if (!fs.existsSync(filePath)) {
229
+ return {};
230
+ }
231
+ try {
232
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
233
+ return isJsonObject(parsed) ? parsed : {};
234
+ }
235
+ catch {
236
+ return {};
237
+ }
238
+ }
239
+ function writeTextFileIfChanged(filePath, content) {
240
+ const existed = fs.existsSync(filePath);
241
+ const current = existed ? fs.readFileSync(filePath, 'utf-8') : undefined;
242
+ if (current === content) {
243
+ return { created: false, updated: false };
244
+ }
245
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
246
+ fs.writeFileSync(filePath, content, 'utf-8');
247
+ return { created: !existed, updated: existed };
248
+ }
249
+ function writeJsonFileIfChanged(filePath, next) {
250
+ const serialized = `${JSON.stringify(next, null, 2)}\n`;
251
+ return writeTextFileIfChanged(filePath, serialized);
252
+ }
253
+ function resolveHomeDir(env) {
254
+ return env.HOME?.trim() || env.USERPROFILE?.trim() || undefined;
255
+ }
256
+ function runGit(cwd, args, input) {
257
+ const result = spawnSync('git', args, {
258
+ cwd,
259
+ encoding: 'utf-8',
260
+ input,
261
+ });
262
+ if (result.status !== 0) {
263
+ return {
264
+ ok: false,
265
+ stdout: result.stdout ?? '',
266
+ };
267
+ }
268
+ return {
269
+ ok: true,
270
+ stdout: result.stdout ?? '',
271
+ };
272
+ }
273
+ export function auditLocalAgentWorkspaceFiles(cwd) {
274
+ const auditedPaths = [...LOCAL_ONLY_AGENT_WORKSPACE_FILES];
275
+ const presentPaths = auditedPaths
276
+ .filter((relativePath) => fs.existsSync(path.join(cwd, relativePath)))
277
+ .map((relativePath) => relativePath.replace(/\\/g, '/'));
278
+ const gitRepoCheck = runGit(cwd, ['rev-parse', '--is-inside-work-tree']);
279
+ if (!gitRepoCheck.ok || gitRepoCheck.stdout.trim() !== 'true') {
280
+ return {
281
+ isGitRepo: false,
282
+ auditedPaths,
283
+ presentPaths,
284
+ ignoredPaths: [],
285
+ missingGitignorePaths: [],
286
+ trackedPaths: [],
287
+ hasIssues: false,
288
+ };
289
+ }
290
+ if (presentPaths.length === 0) {
291
+ return {
292
+ isGitRepo: true,
293
+ auditedPaths,
294
+ presentPaths,
295
+ ignoredPaths: [],
296
+ missingGitignorePaths: [],
297
+ trackedPaths: [],
298
+ hasIssues: false,
299
+ };
300
+ }
301
+ const ignoredResult = runGit(cwd, ['check-ignore', '--no-index', '--stdin'], `${presentPaths.join('\n')}\n`);
302
+ const ignoredPaths = ignoredResult.ok
303
+ ? ignoredResult.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
304
+ : [];
305
+ const ignoredSet = new Set(ignoredPaths);
306
+ const trackedResult = runGit(cwd, ['ls-files', '--', ...presentPaths]);
307
+ const trackedPaths = trackedResult.ok
308
+ ? trackedResult.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/\\/g, '/'))
309
+ : [];
310
+ const missingGitignorePaths = presentPaths.filter((relativePath) => !ignoredSet.has(relativePath));
311
+ return {
312
+ isGitRepo: true,
313
+ auditedPaths,
314
+ presentPaths,
315
+ ignoredPaths,
316
+ missingGitignorePaths,
317
+ trackedPaths,
318
+ hasIssues: missingGitignorePaths.length > 0 || trackedPaths.length > 0,
319
+ };
320
+ }
321
+ export function describeAutoConfigWrite(result) {
322
+ if (!result.created && !result.updated) {
323
+ return undefined;
324
+ }
325
+ const verb = result.created ? 'Created' : 'Updated';
326
+ const displayPath = result.relativePath ?? result.filePath;
327
+ return `✔ ${verb} ${result.label} at ${displayPath}`;
328
+ }
329
+ export function buildClaudeCodeCommandText() {
330
+ return `Load brainclaw project memory and prepare for coordinated work.
331
+
332
+ Steps:
333
+ 1. Run \`brainclaw context --json\` — load constraints, decisions, traps, plans, handoffs
334
+ 2. Run \`brainclaw claim list\` — check what files other agents have claimed
335
+ 3. Before editing any file, run \`brainclaw claim create "<description>" --scope <path>\`
336
+ 4. Before finishing, run \`brainclaw session-end --auto-release\`
337
+ `;
338
+ }
339
+ export function ensureClineMcpConfig(cwd) {
340
+ const filePath = path.join(cwd, '.vscode', 'cline_mcp_settings.json');
341
+ const existing = readJsonObject(filePath);
342
+ const mcpServers = isJsonObject(existing.mcpServers) ? { ...existing.mcpServers } : {};
343
+ mcpServers.brainclaw = {
344
+ command: 'npx',
345
+ args: ['brainclaw', 'mcp'],
346
+ disabled: false,
347
+ autoApprove: ALL_BCLAW_TOOLS,
348
+ };
349
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
350
+ ...existing,
351
+ mcpServers,
352
+ });
353
+ return {
354
+ kind: 'mcp',
355
+ label: 'Cline MCP settings',
356
+ created,
357
+ updated,
358
+ filePath,
359
+ relativePath: CLINE_MCP_RELATIVE_PATH,
360
+ };
361
+ }
362
+ export function ensureWindsurfMcpConfig(homeDir) {
363
+ if (!homeDir) {
364
+ return undefined;
365
+ }
366
+ const filePath = path.join(homeDir, '.codeium', 'windsurf', 'mcp_config.json');
367
+ const existing = readJsonObject(filePath);
368
+ const mcpServers = isJsonObject(existing.mcpServers) ? { ...existing.mcpServers } : {};
369
+ mcpServers.brainclaw = {
370
+ command: 'npx',
371
+ args: ['brainclaw', 'mcp'],
372
+ };
373
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
374
+ ...existing,
375
+ mcpServers,
376
+ });
377
+ return {
378
+ kind: 'mcp',
379
+ label: 'Windsurf MCP settings',
380
+ created,
381
+ updated,
382
+ filePath,
383
+ relativePath: WINDSURF_MCP_RELATIVE_PATH,
384
+ };
385
+ }
386
+ export function ensureCopilotSkill(cwd) {
387
+ const filePath = path.join(cwd, '.github', 'skills', 'brainclaw-context', 'SKILL.md');
388
+ const content = `---
389
+ name: brainclaw-context
390
+ description: "Use this skill when you need the latest Brainclaw context, active plans, constraints, traps, or handoffs before coding. Trigger phrases: refresh project memory, load brainclaw context, inspect active plans, inspect constraints."
391
+ ---
392
+
393
+ # Brainclaw Context
394
+
395
+ Use this skill to fetch live project memory before significant edits or when asked about repository rules.
396
+
397
+ ## Steps
398
+
399
+ 1. Run \`brainclaw context --json\`.
400
+ 2. Read active plans, constraints, decisions, traps, and handoffs from the result.
401
+ 3. Prefer Brainclaw state over stale assumptions from older instructions or prior sessions.
402
+ `;
403
+ const { created, updated } = writeTextFileIfChanged(filePath, content);
404
+ return {
405
+ kind: 'skill',
406
+ label: 'Copilot Brainclaw skill',
407
+ created,
408
+ updated,
409
+ filePath,
410
+ relativePath: COPILOT_SKILL_RELATIVE_PATH,
411
+ };
412
+ }
413
+ export function ensureCursorMdc(cwd) {
414
+ const filePath = path.join(cwd, '.cursor', 'rules', 'brainclaw-mcp-shim.mdc');
415
+ const content = `---
416
+ description: Use this rule when work depends on live Brainclaw memory or active project rules.
417
+ globs: "**/*"
418
+ alwaysApply: true
419
+ ---
420
+
421
+ Before significant edits or when asked about project rules, run:
422
+ <run_command>
423
+ brainclaw context --json
424
+ </run_command>
425
+
426
+ If Brainclaw reports active claims or in-progress plans, follow them before editing.
427
+ `;
428
+ const { created, updated } = writeTextFileIfChanged(filePath, content);
429
+ return {
430
+ kind: 'rule',
431
+ label: 'Cursor imperative Brainclaw rule',
432
+ created,
433
+ updated,
434
+ filePath,
435
+ relativePath: CURSOR_MDC_RELATIVE_PATH,
436
+ };
437
+ }
438
+ function buildCommandHookEntry(command) {
439
+ return {
440
+ matcher: '',
441
+ hooks: [{ type: 'command', command }],
442
+ };
443
+ }
444
+ function containsCommandHook(entries, command) {
445
+ return entries.some((entry) => isJsonObject(entry) &&
446
+ Array.isArray(entry.hooks) &&
447
+ entry.hooks.some((h) => isJsonObject(h) && h.command === command));
448
+ }
449
+ export function ensureProjectDevDependency(cwd) {
450
+ const filePath = path.join(cwd, 'package.json');
451
+ if (!fs.existsSync(filePath))
452
+ return undefined;
453
+ let pkg;
454
+ try {
455
+ pkg = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
456
+ }
457
+ catch {
458
+ return undefined;
459
+ }
460
+ // Skip if this IS the brainclaw package itself
461
+ if (pkg.name === 'brainclaw')
462
+ return undefined;
463
+ const devDeps = isJsonObject(pkg.devDependencies) ? { ...pkg.devDependencies } : {};
464
+ if (devDeps['brainclaw'])
465
+ return undefined;
466
+ devDeps['brainclaw'] = 'latest';
467
+ const next = { ...pkg, devDependencies: devDeps };
468
+ fs.writeFileSync(filePath, JSON.stringify(next, null, 2) + '\n', 'utf-8');
469
+ return {
470
+ kind: 'rule',
471
+ label: 'brainclaw devDependency (enables npx brainclaw without global PATH)',
472
+ created: true,
473
+ updated: false,
474
+ filePath,
475
+ relativePath: 'package.json',
476
+ };
477
+ }
478
+ export function ensureClaudeCodeMcpConfig(cwd) {
479
+ const filePath = path.join(cwd, '.mcp.json');
480
+ const existing = readJsonObject(filePath);
481
+ const mcpServers = isJsonObject(existing.mcpServers) ? { ...existing.mcpServers } : {};
482
+ mcpServers.brainclaw = {
483
+ command: 'npx',
484
+ args: ['brainclaw', 'mcp'],
485
+ };
486
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
487
+ ...existing,
488
+ mcpServers,
489
+ });
490
+ return {
491
+ kind: 'mcp',
492
+ label: 'Claude Code MCP server',
493
+ created,
494
+ updated,
495
+ filePath,
496
+ relativePath: CLAUDE_CODE_MCP_RELATIVE_PATH,
497
+ };
498
+ }
499
+ export function ensureClaudeCodeCommand(cwd) {
500
+ const filePath = path.join(cwd, '.claude', 'commands', 'brainclaw.md');
501
+ const content = buildClaudeCodeCommandText();
502
+ const { created, updated } = writeTextFileIfChanged(filePath, content);
503
+ return {
504
+ kind: 'skill',
505
+ label: 'Claude Code brainclaw command',
506
+ created,
507
+ updated,
508
+ filePath,
509
+ relativePath: CLAUDE_CODE_COMMAND_RELATIVE_PATH,
510
+ };
511
+ }
512
+ export function ensureClaudeCodeUserSettings(homeDir, env = process.env) {
513
+ if (!homeDir)
514
+ return undefined;
515
+ const filePath = path.join(homeDir, '.claude', 'settings.json');
516
+ const existing = readJsonObject(filePath);
517
+ // MCP server
518
+ const mcpServers = isJsonObject(existing.mcpServers) ? { ...existing.mcpServers } : {};
519
+ mcpServers.brainclaw = {
520
+ command: 'npx',
521
+ args: ['brainclaw', 'mcp'],
522
+ };
523
+ // Permissions
524
+ const permissions = isJsonObject(existing.permissions) ? { ...existing.permissions } : {};
525
+ const allow = Array.isArray(permissions.allow) ? [...permissions.allow] : [];
526
+ if (!allow.includes('Bash(npx brainclaw:*)'))
527
+ allow.push('Bash(npx brainclaw:*)');
528
+ if (!allow.includes('mcp__brainclaw__*'))
529
+ allow.push('mcp__brainclaw__*');
530
+ permissions.allow = allow;
531
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
532
+ ...existing,
533
+ mcpServers,
534
+ permissions,
535
+ });
536
+ return {
537
+ kind: 'mcp',
538
+ label: 'Claude Code user settings — MCP + permissions (global, all projects)',
539
+ created,
540
+ updated,
541
+ filePath,
542
+ };
543
+ }
544
+ export function ensureClaudeCodeUserCommand(homeDir) {
545
+ if (!homeDir)
546
+ return undefined;
547
+ const filePath = path.join(homeDir, '.claude', 'commands', 'brainclaw.md');
548
+ const content = buildClaudeCodeCommandText();
549
+ const { created, updated } = writeTextFileIfChanged(filePath, content);
550
+ return {
551
+ kind: 'skill',
552
+ label: 'Claude Code brainclaw command (global, all projects)',
553
+ created,
554
+ updated,
555
+ filePath,
556
+ };
557
+ }
558
+ export function ensureClaudeCodeSettings(cwd) {
559
+ const filePath = path.join(cwd, '.claude', 'settings.local.json');
560
+ const existing = readJsonObject(filePath);
561
+ // Merge permissions.allow
562
+ const permissions = isJsonObject(existing.permissions) ? { ...existing.permissions } : {};
563
+ const allow = Array.isArray(permissions.allow) ? [...permissions.allow] : [];
564
+ if (!allow.includes('Bash(npx brainclaw:*)')) {
565
+ allow.push('Bash(npx brainclaw:*)');
566
+ }
567
+ if (!allow.includes('mcp__brainclaw__*')) {
568
+ allow.push('mcp__brainclaw__*');
569
+ }
570
+ permissions.allow = allow;
571
+ // Merge hooks — UserPromptSubmit injects full context on first prompt, diff on subsequent
572
+ const hooks = isJsonObject(existing.hooks) ? { ...existing.hooks } : {};
573
+ const contextCommand = 'f=.claude/.bclaw-session; if [ ! -f "$f" ]; then touch "$f"; npx brainclaw context 2>/dev/null; else npx brainclaw context-diff 2>/dev/null; fi';
574
+ const stopCommand = 'rm -f .claude/.bclaw-session; npx brainclaw session-end --auto-release --dry-run 2>/dev/null';
575
+ const userPromptHooks = Array.isArray(hooks.UserPromptSubmit) ? [...hooks.UserPromptSubmit] : [];
576
+ if (!containsCommandHook(userPromptHooks, contextCommand)) {
577
+ userPromptHooks.push(buildCommandHookEntry(contextCommand));
578
+ }
579
+ hooks.UserPromptSubmit = userPromptHooks;
580
+ const stopHooks = Array.isArray(hooks.Stop) ? [...hooks.Stop] : [];
581
+ if (!containsCommandHook(stopHooks, stopCommand)) {
582
+ stopHooks.push(buildCommandHookEntry(stopCommand));
583
+ }
584
+ hooks.Stop = stopHooks;
585
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
586
+ ...existing,
587
+ permissions,
588
+ hooks,
589
+ });
590
+ return {
591
+ kind: 'rule',
592
+ label: 'Claude Code settings (permissions + session hooks)',
593
+ created,
594
+ updated,
595
+ filePath,
596
+ relativePath: CLAUDE_CODE_SETTINGS_RELATIVE_PATH,
597
+ };
598
+ }
599
+ export function ensureCursorMcpConfig(homeDir) {
600
+ if (!homeDir) {
601
+ return undefined;
602
+ }
603
+ const filePath = path.join(homeDir, '.cursor', 'mcp.json');
604
+ const existing = readJsonObject(filePath);
605
+ const mcpServers = isJsonObject(existing.mcpServers) ? { ...existing.mcpServers } : {};
606
+ mcpServers.brainclaw = {
607
+ command: 'npx',
608
+ args: ['brainclaw', 'mcp'],
609
+ };
610
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
611
+ ...existing,
612
+ mcpServers,
613
+ });
614
+ return {
615
+ kind: 'mcp',
616
+ label: 'Cursor MCP settings',
617
+ created,
618
+ updated,
619
+ filePath,
620
+ relativePath: CURSOR_MCP_RELATIVE_PATH,
621
+ };
622
+ }
623
+ export function ensureRooMcpConfig(cwd) {
624
+ const filePath = path.join(cwd, '.roo', 'mcp.json');
625
+ const existing = readJsonObject(filePath);
626
+ const mcpServers = isJsonObject(existing.mcpServers) ? { ...existing.mcpServers } : {};
627
+ mcpServers.brainclaw = {
628
+ command: 'npx',
629
+ args: ['brainclaw', 'mcp'],
630
+ alwaysAllow: ALL_BCLAW_TOOLS,
631
+ };
632
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
633
+ ...existing,
634
+ mcpServers,
635
+ });
636
+ return {
637
+ kind: 'mcp',
638
+ label: 'Roo Code MCP settings',
639
+ created,
640
+ updated,
641
+ filePath,
642
+ relativePath: ROO_MCP_RELATIVE_PATH,
643
+ };
644
+ }
645
+ export function ensureCodexMcpConfig(homeDir, env = process.env) {
646
+ const codexHome = env.CODEX_HOME?.trim() || (homeDir ? path.join(homeDir, '.codex') : null);
647
+ if (!codexHome)
648
+ return null;
649
+ const filePath = path.join(codexHome, 'config.toml');
650
+ const brainclawBlock = [
651
+ '\n[mcp_servers.brainclaw]',
652
+ 'command = "npx"',
653
+ 'args = ["brainclaw", "mcp"]',
654
+ ].join('\n');
655
+ let existing = '';
656
+ let fileExisted = false;
657
+ if (fs.existsSync(filePath)) {
658
+ existing = fs.readFileSync(filePath, 'utf-8');
659
+ fileExisted = true;
660
+ }
661
+ if (existing.includes('[mcp_servers.brainclaw]')) {
662
+ return { kind: 'mcp', label: 'Codex MCP config', created: false, updated: false, filePath };
663
+ }
664
+ const newContent = existing + brainclawBlock + '\n';
665
+ if (!fs.existsSync(path.dirname(filePath))) {
666
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
667
+ }
668
+ fs.writeFileSync(filePath, newContent, 'utf-8');
669
+ return {
670
+ kind: 'mcp',
671
+ label: 'Codex MCP config',
672
+ created: !fileExisted,
673
+ updated: fileExisted,
674
+ filePath,
675
+ };
676
+ }
677
+ export function ensureContinueMcpConfig(cwd) {
678
+ const filePath = path.join(cwd, '.continue', 'config.json');
679
+ const existing = readJsonObject(filePath);
680
+ // Continue uses an array for mcpServers, not a keyed object
681
+ const mcpServers = Array.isArray(existing.mcpServers) ? [...existing.mcpServers] : [];
682
+ const alreadyPresent = mcpServers.some((entry) => isJsonObject(entry) && entry.name === 'brainclaw');
683
+ if (!alreadyPresent) {
684
+ mcpServers.push({ name: 'brainclaw', command: 'npx', args: ['brainclaw', 'mcp'] });
685
+ }
686
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
687
+ ...existing,
688
+ mcpServers,
689
+ });
690
+ return {
691
+ kind: 'mcp',
692
+ label: 'Continue MCP settings',
693
+ created,
694
+ updated,
695
+ filePath,
696
+ relativePath: CONTINUE_CONFIG_RELATIVE_PATH,
697
+ };
698
+ }
699
+ export function ensureContinueUserMcpConfig(homeDir) {
700
+ if (!homeDir)
701
+ return undefined;
702
+ const filePath = path.join(homeDir, '.continue', 'config.json');
703
+ const existing = readJsonObject(filePath);
704
+ const mcpServers = Array.isArray(existing.mcpServers) ? [...existing.mcpServers] : [];
705
+ const alreadyPresent = mcpServers.some((entry) => isJsonObject(entry) && entry.name === 'brainclaw');
706
+ if (!alreadyPresent) {
707
+ mcpServers.push({ name: 'brainclaw', command: 'npx', args: ['brainclaw', 'mcp'] });
708
+ }
709
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
710
+ ...existing,
711
+ mcpServers,
712
+ });
713
+ return {
714
+ kind: 'mcp',
715
+ label: 'Continue MCP settings (global, all projects)',
716
+ created,
717
+ updated,
718
+ filePath,
719
+ };
720
+ }
721
+ export function ensureOpenCodeMcpConfig(cwd) {
722
+ const filePath = path.join(cwd, 'opencode.json');
723
+ const existing = readJsonObject(filePath);
724
+ const mcp = isJsonObject(existing.mcp) ? { ...existing.mcp } : {};
725
+ mcp.brainclaw = {
726
+ type: 'local',
727
+ command: ['npx', 'brainclaw', 'mcp'],
728
+ };
729
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
730
+ ...existing,
731
+ mcp,
732
+ });
733
+ return {
734
+ kind: 'mcp',
735
+ label: 'OpenCode MCP config',
736
+ created,
737
+ updated,
738
+ filePath,
739
+ relativePath: OPENCODE_CONFIG_RELATIVE_PATH,
740
+ };
741
+ }
742
+ export function ensureAntigravityMcpConfig(homeDir) {
743
+ if (!homeDir) {
744
+ return undefined;
745
+ }
746
+ const filePath = path.join(homeDir, '.gemini', 'antigravity', 'mcp_config.json');
747
+ const existing = readJsonObject(filePath);
748
+ const mcpServers = isJsonObject(existing.mcpServers) ? { ...existing.mcpServers } : {};
749
+ mcpServers.brainclaw = {
750
+ command: 'npx',
751
+ args: ['brainclaw', 'mcp'],
752
+ };
753
+ const { created, updated } = writeJsonFileIfChanged(filePath, {
754
+ ...existing,
755
+ mcpServers,
756
+ });
757
+ return {
758
+ kind: 'mcp',
759
+ label: 'Antigravity MCP config',
760
+ created,
761
+ updated,
762
+ filePath,
763
+ relativePath: ANTIGRAVITY_MCP_RELATIVE_PATH,
764
+ };
765
+ }
766
+ export function writeDetectedAgentAutoConfig(agentName, cwd, env = process.env) {
767
+ switch (agentName) {
768
+ case 'claude-code': {
769
+ const results = [
770
+ ensureClaudeCodeMcpConfig(cwd),
771
+ ensureClaudeCodeCommand(cwd),
772
+ ensureClaudeCodeSettings(cwd),
773
+ ];
774
+ const userSettings = ensureClaudeCodeUserSettings(resolveHomeDir(env));
775
+ if (userSettings)
776
+ results.push(userSettings);
777
+ const userCmd = ensureClaudeCodeUserCommand(resolveHomeDir(env));
778
+ if (userCmd)
779
+ results.push(userCmd);
780
+ const dep = ensureProjectDevDependency(cwd);
781
+ if (dep)
782
+ results.push(dep);
783
+ return results;
784
+ }
785
+ case 'cline':
786
+ return [ensureClineMcpConfig(cwd)];
787
+ case 'windsurf': {
788
+ const result = ensureWindsurfMcpConfig(resolveHomeDir(env));
789
+ return result ? [result] : [];
790
+ }
791
+ case 'github-copilot':
792
+ return [ensureCopilotSkill(cwd)];
793
+ case 'cursor': {
794
+ const results = [ensureCursorMdc(cwd)];
795
+ const mcp = ensureCursorMcpConfig(resolveHomeDir(env));
796
+ if (mcp)
797
+ results.push(mcp);
798
+ return results;
799
+ }
800
+ case 'roo':
801
+ return [ensureRooMcpConfig(cwd)];
802
+ case 'codex': {
803
+ const result = ensureCodexMcpConfig(resolveHomeDir(env), env);
804
+ return result ? [result] : [];
805
+ }
806
+ case 'continue': {
807
+ const results = [ensureContinueMcpConfig(cwd)];
808
+ const userMcp = ensureContinueUserMcpConfig(resolveHomeDir(env));
809
+ if (userMcp)
810
+ results.push(userMcp);
811
+ return results;
812
+ }
813
+ case 'opencode':
814
+ return [ensureOpenCodeMcpConfig(cwd)];
815
+ case 'antigravity': {
816
+ const result = ensureAntigravityMcpConfig(resolveHomeDir(env));
817
+ return result ? [result] : [];
818
+ }
819
+ default:
820
+ return [];
821
+ }
822
+ }
823
+ export function writeExportCompanionFiles(format, cwd, env = process.env) {
824
+ switch (format) {
825
+ case 'claude-md': {
826
+ const results = [
827
+ ensureClaudeCodeMcpConfig(cwd),
828
+ ensureClaudeCodeCommand(cwd),
829
+ ensureClaudeCodeSettings(cwd),
830
+ ];
831
+ const userSettings = ensureClaudeCodeUserSettings(resolveHomeDir(env));
832
+ if (userSettings)
833
+ results.push(userSettings);
834
+ const userCmd = ensureClaudeCodeUserCommand(resolveHomeDir(env));
835
+ if (userCmd)
836
+ results.push(userCmd);
837
+ const dep = ensureProjectDevDependency(cwd);
838
+ if (dep)
839
+ results.push(dep);
840
+ return results;
841
+ }
842
+ case 'cline':
843
+ return [ensureClineMcpConfig(cwd)];
844
+ case 'windsurf': {
845
+ const result = ensureWindsurfMcpConfig(resolveHomeDir(env));
846
+ return result ? [result] : [];
847
+ }
848
+ case 'copilot-instructions':
849
+ return [ensureCopilotSkill(cwd)];
850
+ case 'cursor-rules': {
851
+ const results = [ensureCursorMdc(cwd)];
852
+ const mcp = ensureCursorMcpConfig(resolveHomeDir(env));
853
+ if (mcp)
854
+ results.push(mcp);
855
+ return results;
856
+ }
857
+ case 'roo':
858
+ return [ensureRooMcpConfig(cwd)];
859
+ case 'continue': {
860
+ const results = [ensureContinueMcpConfig(cwd)];
861
+ const userMcp = ensureContinueUserMcpConfig(resolveHomeDir(env));
862
+ if (userMcp)
863
+ results.push(userMcp);
864
+ return results;
865
+ }
866
+ case 'gemini-md': {
867
+ const result = ensureAntigravityMcpConfig(resolveHomeDir(env));
868
+ return result ? [result] : [];
869
+ }
870
+ default:
871
+ return [];
872
+ }
873
+ }
874
+ //# sourceMappingURL=agent-files.js.map