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.
- package/LICENSE +74 -0
- package/README.md +226 -0
- package/dist/cli.js +1037 -0
- package/dist/commands/accept.js +149 -0
- package/dist/commands/adapter-openclaw-import.js +75 -0
- package/dist/commands/add-step.js +35 -0
- package/dist/commands/agent-board.js +106 -0
- package/dist/commands/audit.js +35 -0
- package/dist/commands/bootstrap.js +34 -0
- package/dist/commands/capability.js +104 -0
- package/dist/commands/changes.js +112 -0
- package/dist/commands/check-constraints.js +63 -0
- package/dist/commands/claim-resource.js +54 -0
- package/dist/commands/claim.js +92 -0
- package/dist/commands/complete-step.js +34 -0
- package/dist/commands/constraint.js +44 -0
- package/dist/commands/context-diff.js +32 -0
- package/dist/commands/context.js +63 -0
- package/dist/commands/decision.js +45 -0
- package/dist/commands/delete-plan.js +20 -0
- package/dist/commands/diff.js +99 -0
- package/dist/commands/doctor.js +1275 -0
- package/dist/commands/enable-agent.js +63 -0
- package/dist/commands/env.js +46 -0
- package/dist/commands/estimation-report.js +167 -0
- package/dist/commands/explore.js +47 -0
- package/dist/commands/export.js +381 -0
- package/dist/commands/handoff.js +63 -0
- package/dist/commands/history.js +22 -0
- package/dist/commands/hooks.js +123 -0
- package/dist/commands/init.js +356 -0
- package/dist/commands/install-hooks.js +115 -0
- package/dist/commands/instruction.js +56 -0
- package/dist/commands/list-agents.js +44 -0
- package/dist/commands/list-claims.js +45 -0
- package/dist/commands/list-instructions.js +50 -0
- package/dist/commands/list-plans.js +48 -0
- package/dist/commands/mcp-worker.js +12 -0
- package/dist/commands/mcp.js +2272 -0
- package/dist/commands/memory.js +283 -0
- package/dist/commands/metrics.js +175 -0
- package/dist/commands/plan-resource.js +62 -0
- package/dist/commands/plan.js +76 -0
- package/dist/commands/prune-candidates.js +36 -0
- package/dist/commands/prune.js +48 -0
- package/dist/commands/pull.js +25 -0
- package/dist/commands/push.js +28 -0
- package/dist/commands/rebuild.js +14 -0
- package/dist/commands/reflect-runtime-note.js +74 -0
- package/dist/commands/reflect.js +286 -0
- package/dist/commands/register-agent.js +29 -0
- package/dist/commands/reject.js +52 -0
- package/dist/commands/release-claim.js +41 -0
- package/dist/commands/release-claims.js +67 -0
- package/dist/commands/review.js +242 -0
- package/dist/commands/rollback.js +156 -0
- package/dist/commands/runtime-note.js +144 -0
- package/dist/commands/runtime-status.js +49 -0
- package/dist/commands/search.js +36 -0
- package/dist/commands/session-end.js +187 -0
- package/dist/commands/session-start.js +147 -0
- package/dist/commands/set-trust.js +92 -0
- package/dist/commands/setup.js +446 -0
- package/dist/commands/show-candidate.js +31 -0
- package/dist/commands/star-candidate.js +28 -0
- package/dist/commands/status.js +133 -0
- package/dist/commands/sync.js +159 -0
- package/dist/commands/tool.js +126 -0
- package/dist/commands/trap.js +74 -0
- package/dist/commands/update-handoff.js +23 -0
- package/dist/commands/update-plan.js +37 -0
- package/dist/commands/upgrade.js +382 -0
- package/dist/commands/use-candidate.js +35 -0
- package/dist/commands/version.js +96 -0
- package/dist/commands/watch.js +215 -0
- package/dist/commands/whoami.js +104 -0
- package/dist/core/agent-context.js +340 -0
- package/dist/core/agent-files.js +874 -0
- package/dist/core/agent-integrations.js +135 -0
- package/dist/core/agent-inventory.js +401 -0
- package/dist/core/agent-registry.js +420 -0
- package/dist/core/ai-agent-detection.js +140 -0
- package/dist/core/audit.js +85 -0
- package/dist/core/bootstrap.js +658 -0
- package/dist/core/brainclaw-version.js +433 -0
- package/dist/core/candidates.js +137 -0
- package/dist/core/circuit-breaker.js +118 -0
- package/dist/core/claims.js +72 -0
- package/dist/core/config.js +86 -0
- package/dist/core/context-diff.js +122 -0
- package/dist/core/context.js +1212 -0
- package/dist/core/contradictions.js +270 -0
- package/dist/core/coordination.js +86 -0
- package/dist/core/cross-project.js +99 -0
- package/dist/core/duplicates.js +72 -0
- package/dist/core/event-log.js +152 -0
- package/dist/core/events.js +56 -0
- package/dist/core/execution-context.js +204 -0
- package/dist/core/freshness.js +87 -0
- package/dist/core/global-registry.js +182 -0
- package/dist/core/host.js +10 -0
- package/dist/core/identity.js +151 -0
- package/dist/core/ids.js +56 -0
- package/dist/core/input-validation.js +81 -0
- package/dist/core/instructions.js +117 -0
- package/dist/core/io.js +191 -0
- package/dist/core/json-store.js +63 -0
- package/dist/core/lifecycle.js +45 -0
- package/dist/core/lock.js +129 -0
- package/dist/core/logger.js +49 -0
- package/dist/core/machine-profile.js +332 -0
- package/dist/core/markdown.js +120 -0
- package/dist/core/memory-git.js +133 -0
- package/dist/core/migration.js +247 -0
- package/dist/core/project-registry.js +64 -0
- package/dist/core/reflection-safety.js +21 -0
- package/dist/core/repo-analysis.js +133 -0
- package/dist/core/reputation.js +409 -0
- package/dist/core/runtime.js +134 -0
- package/dist/core/schema.js +580 -0
- package/dist/core/search.js +115 -0
- package/dist/core/security.js +66 -0
- package/dist/core/setup-state.js +50 -0
- package/dist/core/state.js +83 -0
- package/dist/core/store-resolution.js +119 -0
- package/dist/core/sync-remote.js +83 -0
- package/dist/core/traps.js +86 -0
- 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
|