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,356 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import readline from 'node:readline/promises';
4
+ import { registerAgentIdentity, resolveDefaultAgentName, resolveExistingCurrentAgent } from '../core/agent-registry.js';
5
+ import { MEMORY_DIR, memoryExists, ensureMemoryDir, memoryPath, writeFileAtomic } from '../core/io.js';
6
+ import { emptyState, loadState, saveState } from '../core/state.js';
7
+ import { defaultConfig, saveConfig } from '../core/config.js';
8
+ import { generateMarkdown } from '../core/markdown.js';
9
+ import { initMemoryRepo } from '../core/memory-git.js';
10
+ import { buildProjectIdentity, resolveExistingProjectIdentity, saveProjectIdentity } from '../core/project-registry.js';
11
+ import { scanProject, upsertProject } from '../core/global-registry.js';
12
+ import { analyzeRepository, scanWorkspaceBoundaries } from '../core/repo-analysis.js';
13
+ import { isAgentIntegrationName, upsertAgentIntegrationDeclaration } from '../core/agent-integrations.js';
14
+ import { describeAutoConfigWrite, ensureAgentFiles, ensureGitignoreEntries, writeDetectedAgentAutoConfig } from '../core/agent-files.js';
15
+ import { detectAiAgent, detectWslEnvironment } from '../core/ai-agent-detection.js';
16
+ import { hasCompletedSetup } from '../core/setup-state.js';
17
+ import { writeDetectedAgentExport } from './export.js';
18
+ import { writeDetectedAgentHooks } from './hooks.js';
19
+ export async function runInit(options = {}) {
20
+ const cwd = options.cwd ?? process.cwd();
21
+ const containingMemoryStore = resolveContainingMemoryStore(cwd);
22
+ const skipSetupRequirement = options.skipSetupRequirement === true || process.env.BRAINCLAW_SKIP_SETUP_REQUIREMENT === '1';
23
+ if (!skipSetupRequirement && !hasCompletedSetup()) {
24
+ console.error('Error: global brainclaw setup has not been completed on this machine.');
25
+ console.error('Run `brainclaw setup` first, then retry `brainclaw init` from your project root.');
26
+ process.exit(1);
27
+ }
28
+ if (containingMemoryStore) {
29
+ console.error(`Error: cannot run \`brainclaw init\` from inside an existing project memory store (${containingMemoryStore}).`);
30
+ console.error('Run `brainclaw init` from the project root directory instead.');
31
+ process.exit(1);
32
+ }
33
+ // --scan: detect service boundaries and suggest init targets, then exit
34
+ if (options.scan) {
35
+ const { suggestions, alreadyInitialised } = scanWorkspaceBoundaries(cwd);
36
+ if (alreadyInitialised.length > 0) {
37
+ console.log(`Already initialised (${alreadyInitialised.length}):`);
38
+ for (const { relativePath } of alreadyInitialised) {
39
+ console.log(` ✔ ./${relativePath}`);
40
+ }
41
+ console.log('');
42
+ }
43
+ if (suggestions.length === 0) {
44
+ console.log('No service boundaries detected in subdirectories.');
45
+ }
46
+ else {
47
+ console.log(`Detected ${suggestions.length} service boundary candidate(s):`);
48
+ for (const { relativePath, markers } of suggestions) {
49
+ console.log(` → ./${relativePath} [${markers.join(', ')}]`);
50
+ console.log(` cd ${relativePath} && brainclaw init -y`);
51
+ }
52
+ }
53
+ return;
54
+ }
55
+ const existingIdentity = resolveExistingProjectIdentity(cwd);
56
+ const existingCurrentAgent = resolveExistingCurrentAgent(cwd);
57
+ const storageDir = resolveStorageDir(options.storageDir);
58
+ const topology = resolveTopology(options.topology);
59
+ const ignoreStrategy = topology === 'embedded' ? 'none' : 'project-gitignore';
60
+ const skipAgentBootstrap = options.skipAgentBootstrap === true || process.env.BRAINCLAW_SKIP_AGENT_BOOTSTRAP === '1';
61
+ if (memoryExists(cwd) && !options.force) {
62
+ console.error('Error: project memory already exists. Use --force to overwrite.');
63
+ process.exit(1);
64
+ }
65
+ // Derive project name from directory
66
+ const projectName = path.basename(cwd);
67
+ const shouldAnalyzeRepo = options.analyzeRepo !== false
68
+ && process.env.BRAINCLAW_SKIP_REPO_ANALYSIS !== '1';
69
+ const analysis = shouldAnalyzeRepo ? analyzeRepository(cwd) : undefined;
70
+ const projectMode = await resolveProjectMode(options, analysis);
71
+ const projectStrategy = await resolveProjectStrategy(options, projectMode);
72
+ ensureMemoryDir(cwd, storageDir);
73
+ const currentAgent = registerAgentIdentity({
74
+ agentName: existingCurrentAgent?.agent_name ?? resolveDefaultAgentName(),
75
+ kind: existingCurrentAgent?.kind ?? 'human',
76
+ trustLevel: 'curator',
77
+ cwd,
78
+ preferredDirName: storageDir,
79
+ });
80
+ // Auto-detect and register the AI coding agent running in this environment
81
+ const detectedAi = skipAgentBootstrap ? undefined : detectAiAgent();
82
+ let registeredAiAgent = detectedAi
83
+ ? registerAgentIdentity({
84
+ agentName: detectedAi.name,
85
+ kind: detectedAi.kind,
86
+ trustLevel: detectedAi.trust_level,
87
+ cwd,
88
+ preferredDirName: storageDir,
89
+ })
90
+ : undefined;
91
+ // Only write empty state if no data exists yet.
92
+ // When --force is used on an existing project, preserve the data
93
+ // and only refresh config/agent registration/directory structure.
94
+ const existingState = loadState(cwd);
95
+ const hasExistingData = existingState.active_constraints.length > 0 ||
96
+ existingState.recent_decisions.length > 0 ||
97
+ existingState.known_traps.length > 0 ||
98
+ existingState.open_handoffs.length > 0 ||
99
+ existingState.plan_items.length > 0;
100
+ if (!hasExistingData) {
101
+ saveState(emptyState(), cwd);
102
+ }
103
+ // Create config
104
+ const projectIdentity = buildProjectIdentity({
105
+ existing: existingIdentity,
106
+ projectName,
107
+ storageDir,
108
+ topology,
109
+ });
110
+ const config = defaultConfig(projectName, {
111
+ projectId: projectIdentity.project_id,
112
+ currentAgent: currentAgent.agent_name,
113
+ currentAgentId: currentAgent.agent_id,
114
+ projectMode,
115
+ projectStrategy,
116
+ storageDir,
117
+ topology,
118
+ ignoreStrategy,
119
+ });
120
+ if (options.compact) {
121
+ config.markdown = { max_items_per_section: 20, compact_mode: true };
122
+ }
123
+ if (detectedAi && isAgentIntegrationName(detectedAi.name)) {
124
+ upsertAgentIntegrationDeclaration(config, detectedAi.name, 'detected');
125
+ }
126
+ saveConfig(config, cwd, storageDir);
127
+ saveProjectIdentity(projectIdentity, cwd, storageDir);
128
+ // Write to the detected agent's native instruction file after config exists.
129
+ const detectedExport = detectedAi ? writeDetectedAgentExport(detectedAi.name, cwd) : undefined;
130
+ // Write deterministic session-trigger hooks for Cursor / Windsurf
131
+ const detectedHooks = detectedAi
132
+ ? (detectedAi.name === 'windsurf'
133
+ ? []
134
+ : writeDetectedAgentHooks(detectedAi.name, projectName, cwd)
135
+ .filter((hook) => hook.relativePath !== detectedExport?.relativePath))
136
+ : [];
137
+ const detectedAutoConfig = detectedAi ? writeDetectedAgentAutoConfig(detectedAi.name, cwd) : [];
138
+ // Register in global project registry
139
+ try {
140
+ const entry = scanProject(cwd);
141
+ if (entry)
142
+ upsertProject(entry);
143
+ }
144
+ catch {
145
+ // Non-fatal: global registry is optional
146
+ }
147
+ // Create project.md
148
+ const md = generateMarkdown(loadState(cwd));
149
+ writeFileAtomic(memoryPath('project.md', cwd, storageDir), md);
150
+ if (ignoreStrategy === 'project-gitignore') {
151
+ ensureProjectGitignore(cwd, storageDir);
152
+ }
153
+ // Create or update AGENTS.md and .github/copilot-instructions.md
154
+ const agentFiles = skipAgentBootstrap
155
+ ? {
156
+ agentsMdCreated: false,
157
+ agentsMdUpdated: false,
158
+ copilotInstructionsCreated: false,
159
+ copilotInstructionsUpdated: false,
160
+ }
161
+ : ensureAgentFiles(cwd, storageDir);
162
+ // Add agent instruction files to .gitignore (they are generated, not source)
163
+ if (!skipAgentBootstrap) {
164
+ const generatedWorkspacePaths = detectedAutoConfig
165
+ .map((item) => item.relativePath)
166
+ .filter((item) => item !== undefined)
167
+ .filter((item) => !item.startsWith('.codeium/'));
168
+ ensureGitignoreEntries(cwd, ['AGENTS.md', '.github/copilot-instructions.md', ...generatedWorkspacePaths]);
169
+ }
170
+ console.log(`✔ Initialized project memory in ${storageDir}/`);
171
+ console.log('✔ Created project.md, config.yaml, and split state directories');
172
+ console.log(`✔ Project ID: ${projectIdentity.project_id}`);
173
+ console.log(`✔ Current agent: ${currentAgent.agent_name} (${currentAgent.agent_id})`);
174
+ if (registeredAiAgent) {
175
+ console.log(`✔ AI agent detected: ${registeredAiAgent.agent_name} [${detectedAi.detection_source}] (${registeredAiAgent.agent_id})`);
176
+ }
177
+ if (detectedExport) {
178
+ console.log(`\u2714 Agent instructions written to ${detectedExport.relativePath} (${detectedExport.created ? 'created' : 'updated'})`);
179
+ }
180
+ for (const hook of detectedHooks) {
181
+ console.log(`\u2714 Session hook written to ${hook.relativePath} (${hook.created ? 'created' : 'updated'})`);
182
+ }
183
+ console.log(`\u2714 Topology: ${topology}`);
184
+ console.log(`✔ Storage dir: ${storageDir}`);
185
+ console.log(`✔ Project mode: ${projectMode}`);
186
+ if (projectMode === 'multi-project') {
187
+ console.log(`✔ Project strategy: ${projectStrategy}`);
188
+ }
189
+ if (ignoreStrategy === 'project-gitignore') {
190
+ console.log(`✔ Added ${storageDir}/ to .gitignore`);
191
+ }
192
+ if (agentFiles.agentsMdCreated) {
193
+ console.log('✔ Created AGENTS.md with brainclaw bootstrap section');
194
+ }
195
+ else if (agentFiles.agentsMdUpdated) {
196
+ console.log('✔ Updated AGENTS.md with brainclaw bootstrap section');
197
+ }
198
+ if (agentFiles.copilotInstructionsCreated) {
199
+ console.log('✔ Created .github/copilot-instructions.md with brainclaw bootstrap section');
200
+ }
201
+ else if (agentFiles.copilotInstructionsUpdated) {
202
+ console.log('✔ Updated .github/copilot-instructions.md with brainclaw bootstrap section');
203
+ }
204
+ for (const autoConfig of detectedAutoConfig) {
205
+ const message = describeAutoConfigWrite(autoConfig);
206
+ if (message) {
207
+ console.log(message);
208
+ }
209
+ }
210
+ if (!skipAgentBootstrap) {
211
+ console.log('✔ Added generated agent files to .gitignore');
212
+ }
213
+ if (analysis) {
214
+ console.log('');
215
+ console.log(`Recommended project mode: ${analysis.recommendedMode}`);
216
+ for (const reason of analysis.reasons) {
217
+ console.log(` - ${reason}`);
218
+ }
219
+ }
220
+ const wsl = detectWslEnvironment();
221
+ if (wsl) {
222
+ console.log('');
223
+ console.log(`⚠ WSL detected (${wsl.distro}). brainclaw is installed in this WSL environment only.`);
224
+ console.log(` To use brainclaw from a Windows terminal (PowerShell/cmd), run inside this project:`);
225
+ console.log(` npm link (in PowerShell, with Node.js for Windows)`);
226
+ }
227
+ // Initialize internal git repo for memory versioning
228
+ if (initMemoryRepo(cwd)) {
229
+ console.log('✔ Initialized memory git repo for versioning');
230
+ }
231
+ console.log('');
232
+ console.log(`Tip: run 'brainclaw context --json' to load the shared memory into your agent session.`);
233
+ }
234
+ function resolveStorageDir(storageDir) {
235
+ const candidate = (storageDir ?? MEMORY_DIR).trim();
236
+ if (candidate !== MEMORY_DIR) {
237
+ console.error(`Error: custom storage directories are no longer supported. Use "${MEMORY_DIR}".`);
238
+ process.exit(1);
239
+ }
240
+ return candidate;
241
+ }
242
+ function resolveContainingMemoryStore(cwd) {
243
+ let current = path.resolve(cwd);
244
+ while (true) {
245
+ if (path.basename(current) === MEMORY_DIR && looksLikeBrainclawStore(current)) {
246
+ return current;
247
+ }
248
+ const parent = path.dirname(current);
249
+ if (parent === current) {
250
+ return undefined;
251
+ }
252
+ current = parent;
253
+ }
254
+ }
255
+ function looksLikeBrainclawStore(storePath) {
256
+ return fs.existsSync(path.join(storePath, 'config.yaml'))
257
+ || fs.existsSync(path.join(storePath, 'project.identity.json'))
258
+ || fs.existsSync(path.join(storePath, '.git'));
259
+ }
260
+ function resolveTopology(topology) {
261
+ return topology ?? 'embedded';
262
+ }
263
+ function ensureProjectGitignore(cwd, storageDir) {
264
+ const gitignorePath = path.join(cwd, '.gitignore');
265
+ const ignoreLine = `${storageDir}/`;
266
+ const current = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
267
+ const lines = current.split(/\r?\n/).filter((line) => line.length > 0);
268
+ if (lines.includes(ignoreLine)) {
269
+ return;
270
+ }
271
+ const next = current.length === 0
272
+ ? `${ignoreLine}\n`
273
+ : `${current.replace(/\s*$/, '')}\n${ignoreLine}\n`;
274
+ fs.writeFileSync(gitignorePath, next, 'utf-8');
275
+ }
276
+ async function resolveProjectMode(options, analysis) {
277
+ if (options.projectMode) {
278
+ return options.projectMode;
279
+ }
280
+ if (options.yes || !process.stdin.isTTY || !process.stdout.isTTY) {
281
+ return 'auto';
282
+ }
283
+ const rl = readline.createInterface({
284
+ input: process.stdin,
285
+ output: process.stdout,
286
+ });
287
+ try {
288
+ console.log('How is this repository organized?');
289
+ console.log(' 1) single-project - Use one shared memory space for the whole repository.');
290
+ console.log(' 2) multi-project - Segment memory across multiple projects or domains in this repository.');
291
+ console.log(' 3) auto - Start simple now and allow project segmentation later.');
292
+ if (analysis) {
293
+ console.log(`Suggested mode: ${analysis.recommendedMode}`);
294
+ }
295
+ const answer = (await rl.question('Select project mode [auto]: ')).trim().toLowerCase();
296
+ return parseProjectMode(answer) ?? 'auto';
297
+ }
298
+ finally {
299
+ rl.close();
300
+ }
301
+ }
302
+ async function resolveProjectStrategy(options, projectMode) {
303
+ if (options.projectStrategy) {
304
+ return options.projectStrategy;
305
+ }
306
+ if (projectMode !== 'multi-project') {
307
+ return 'manual';
308
+ }
309
+ if (options.yes || !process.stdin.isTTY || !process.stdout.isTTY) {
310
+ return 'manual';
311
+ }
312
+ const rl = readline.createInterface({
313
+ input: process.stdin,
314
+ output: process.stdout,
315
+ });
316
+ try {
317
+ console.log('How should project boundaries be managed?');
318
+ console.log(' 1) manual - Projects are named explicitly and assigned later.');
319
+ console.log(' 2) folder - Use folder paths as the default way to infer project boundaries.');
320
+ const answer = (await rl.question('Select project strategy [manual]: ')).trim().toLowerCase();
321
+ return parseProjectStrategy(answer) ?? 'manual';
322
+ }
323
+ finally {
324
+ rl.close();
325
+ }
326
+ }
327
+ function parseProjectMode(value) {
328
+ switch (value) {
329
+ case '1':
330
+ case 'single-project':
331
+ case 'single':
332
+ return 'single-project';
333
+ case '2':
334
+ case 'multi-project':
335
+ case 'multi':
336
+ return 'multi-project';
337
+ case '3':
338
+ case 'auto':
339
+ return 'auto';
340
+ default:
341
+ return undefined;
342
+ }
343
+ }
344
+ function parseProjectStrategy(value) {
345
+ switch (value) {
346
+ case '1':
347
+ case 'manual':
348
+ return 'manual';
349
+ case '2':
350
+ case 'folder':
351
+ return 'folder';
352
+ default:
353
+ return undefined;
354
+ }
355
+ }
356
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,115 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { memoryExists } from '../core/io.js';
4
+ export function runInstallHooks(options = {}) {
5
+ if (!memoryExists()) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const gitRoot = findGitRoot(process.cwd());
10
+ if (!gitRoot) {
11
+ console.error('Error: no .git/ directory found. Is this a Git repository?');
12
+ process.exit(1);
13
+ }
14
+ const hooksDir = path.join(gitRoot, '.git', 'hooks');
15
+ if (!fs.existsSync(hooksDir)) {
16
+ fs.mkdirSync(hooksDir, { recursive: true });
17
+ }
18
+ const hookPath = path.join(hooksDir, 'pre-commit');
19
+ if (fs.existsSync(hookPath) && !options.force) {
20
+ console.error(`Error: pre-commit hook already exists at ${hookPath}`);
21
+ console.error('Use --force to overwrite.');
22
+ process.exit(1);
23
+ }
24
+ fs.writeFileSync(hookPath, generateHookScript(), { encoding: 'utf-8', mode: 0o755 });
25
+ console.log(`✔ pre-commit hook installed at ${hookPath}`);
26
+ console.log(' Checks: sensitive content in .brainclaw/ + active constraint violations.');
27
+ console.log(' To bypass (not recommended): git commit --no-verify');
28
+ const postMergePath = path.join(hooksDir, 'post-merge');
29
+ if (!fs.existsSync(postMergePath) || options.force) {
30
+ fs.writeFileSync(postMergePath, generatePostMergeScript(), { encoding: 'utf-8', mode: 0o755 });
31
+ console.log(`✔ post-merge hook installed at ${postMergePath}`);
32
+ console.log(' Auto-releases claims whose scope was touched by the merge.');
33
+ }
34
+ }
35
+ function generatePostMergeScript() {
36
+ return `#!/bin/sh
37
+ # brainclaw post-merge hook
38
+ # Auto-releases claims whose scope overlaps with files changed by the merge
39
+ # Generated by: brainclaw install-hooks
40
+
41
+ BCLAW_CMD=""
42
+ if command -v brainclaw >/dev/null 2>&1; then
43
+ BCLAW_CMD="brainclaw"
44
+ elif command -v bclaw >/dev/null 2>&1; then
45
+ BCLAW_CMD="bclaw"
46
+ else
47
+ BCLAW_CMD="npx --no brainclaw"
48
+ fi
49
+
50
+ $BCLAW_CMD release-claims --from-git-diff 2>/dev/null || true
51
+ `;
52
+ }
53
+ function findGitRoot(cwd) {
54
+ let dir = cwd;
55
+ while (true) {
56
+ if (fs.existsSync(path.join(dir, '.git')))
57
+ return dir;
58
+ const parent = path.dirname(dir);
59
+ if (parent === dir)
60
+ return undefined;
61
+ dir = parent;
62
+ }
63
+ }
64
+ function generateHookScript() {
65
+ return `#!/bin/sh
66
+ # brainclaw pre-commit hook
67
+ # 1. Blocks commits if sensitive content is detected in .brainclaw/
68
+ # 2. Blocks commits if staged files are covered by an active constraint
69
+ # Generated by: brainclaw install-hooks
70
+
71
+ if ! command -v node >/dev/null 2>&1; then
72
+ echo "brainclaw hook: node not found, skipping check."
73
+ exit 0
74
+ fi
75
+
76
+ BCLAW_CMD=""
77
+ if command -v brainclaw >/dev/null 2>&1; then
78
+ BCLAW_CMD="brainclaw"
79
+ elif command -v bclaw >/dev/null 2>&1; then
80
+ BCLAW_CMD="bclaw"
81
+ else
82
+ BCLAW_CMD="npx --no brainclaw"
83
+ fi
84
+
85
+ # Check 1: sensitive content in .brainclaw/
86
+ RESULT=$($BCLAW_CMD doctor --json 2>/dev/null) || {
87
+ echo "brainclaw hook: could not run doctor check, skipping."
88
+ exit 0
89
+ }
90
+
91
+ echo "$RESULT" | node -e "
92
+ const chunks = [];
93
+ process.stdin.on('data', d => chunks.push(d));
94
+ process.stdin.on('end', () => {
95
+ try {
96
+ const data = JSON.parse(chunks.join(''));
97
+ const issues = (data.checks || []).filter(c =>
98
+ (c.name === 'state_security' || c.name === 'candidate_security') &&
99
+ c.status !== 'ok'
100
+ );
101
+ if (issues.length > 0) {
102
+ process.stderr.write('\\nbrainclaw: sensitive content in .brainclaw/ — commit blocked.\\n');
103
+ issues.forEach(c => process.stderr.write(' ⚠ ' + c.message + '\\n'));
104
+ process.stderr.write(' Fix: review with \`brainclaw doctor\`\\n\\n');
105
+ process.exit(1);
106
+ }
107
+ } catch (_) { /* skip parse errors */ }
108
+ });
109
+ " || exit 1
110
+
111
+ # Check 2: active constraint violations on staged files
112
+ $BCLAW_CMD check-constraints --staged 2>&1 || exit 1
113
+ `;
114
+ }
115
+ //# sourceMappingURL=install-hooks.js.map
@@ -0,0 +1,56 @@
1
+ import { resolveAgentScope, resolveCurrentAgentName } from '../core/agent-registry.js';
2
+ import { loadConfig } from '../core/config.js';
3
+ import { createInstruction } from '../core/instructions.js';
4
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
5
+ import { generateMarkdown } from '../core/markdown.js';
6
+ import { loadState } from '../core/state.js';
7
+ import { scanText } from '../core/security.js';
8
+ import { validateCliInput } from '../core/input-validation.js';
9
+ import { resolveTargetStore } from '../core/store-resolution.js';
10
+ export function runInstruction(text, options = {}) {
11
+ const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
12
+ if (!memoryExists(cwd)) {
13
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
14
+ process.exit(1);
15
+ }
16
+ validateCliInput(text, options.tag);
17
+ const layer = options.layer ?? 'global';
18
+ const scope = resolveScope(layer, options, cwd);
19
+ const config = loadConfig(cwd);
20
+ const warnings = scanText(text, config);
21
+ for (const w of warnings) {
22
+ console.warn(`⚠ ${w.message}`);
23
+ if (w.level === 'block') {
24
+ console.error('Blocked: strict redaction is enabled. Entry not added.');
25
+ process.exit(1);
26
+ }
27
+ }
28
+ const entry = createInstruction(text, {
29
+ layer,
30
+ scope,
31
+ tags: options.tag,
32
+ author: options.author ?? resolveCurrentAgentName(cwd),
33
+ supersedes: options.supersedes,
34
+ }, cwd);
35
+ writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(loadState(cwd)));
36
+ console.log(`✔ Instruction added: [${entry.id}] <${entry.layer}${entry.scope ? `:${entry.scope}` : ''}> ${entry.text}`);
37
+ }
38
+ function resolveScope(layer, options, cwd) {
39
+ if (layer === 'global') {
40
+ return undefined;
41
+ }
42
+ if (layer === 'project') {
43
+ if (!options.project) {
44
+ console.error('Error: --project is required when --layer project is used.');
45
+ process.exit(1);
46
+ }
47
+ return options.project;
48
+ }
49
+ const agentScope = resolveAgentScope(options.agent, cwd);
50
+ if (!agentScope) {
51
+ console.error('Error: no agent scope available. Use --agent or configure a current agent with `brainclaw register-agent <name> --set-current`.');
52
+ process.exit(1);
53
+ }
54
+ return agentScope;
55
+ }
56
+ //# sourceMappingURL=instruction.js.map
@@ -0,0 +1,44 @@
1
+ import { listAgentIdentities, resolveCurrentAgentIdentity } from '../core/agent-registry.js';
2
+ import { memoryExists } from '../core/io.js';
3
+ import { buildReputationSnapshot, toPublicReputationSummary } from '../core/reputation.js';
4
+ export function runListAgents(options = {}) {
5
+ if (!memoryExists()) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const agents = listAgentIdentities();
10
+ const current = resolveCurrentAgentIdentity();
11
+ const reputation = options.withReputation ? buildReputationSnapshot() : undefined;
12
+ const reputationById = new Map((reputation?.agents ?? []).map((agent) => [agent.agent_id ?? agent.key, toPublicReputationSummary(agent)]));
13
+ if (options.json) {
14
+ console.log(JSON.stringify({
15
+ current_agent_id: current?.agent_id,
16
+ current_agent: current?.agent_name,
17
+ agents: options.withReputation
18
+ ? agents.map((agent) => ({
19
+ ...agent,
20
+ reputation: reputationById.get(agent.agent_id),
21
+ }))
22
+ : agents,
23
+ }, null, 2));
24
+ return;
25
+ }
26
+ if (agents.length === 0) {
27
+ console.log('No registered agents.');
28
+ return;
29
+ }
30
+ console.log(`${agents.length} registered agent(s):`);
31
+ for (const agent of agents) {
32
+ const currentLabel = current?.agent_id === agent.agent_id ? ' [current]' : '';
33
+ const reputationLabel = options.withReputation
34
+ ? (() => {
35
+ const summary = reputationById.get(agent.agent_id);
36
+ return summary ? ` trust=${summary.internal_trust} cq=${summary.contribution_quality} rv=${summary.review_reliability} ct=${summary.continuity_hygiene}` : '';
37
+ })()
38
+ : '';
39
+ const capabilitiesLabel = agent.capabilities.length > 0 ? ` caps=${agent.capabilities.join(',')}` : '';
40
+ const fingerprintLabel = agent.identity_key ? ` fp=${agent.identity_key.fingerprint.slice(0, 12)}` : '';
41
+ console.log(` - ${agent.agent_name} (${agent.agent_id}, kind=${agent.kind})${currentLabel}${reputationLabel}${capabilitiesLabel}${fingerprintLabel}`);
42
+ }
43
+ }
44
+ //# sourceMappingURL=list-agents.js.map
@@ -0,0 +1,45 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { listClaims } from '../core/claims.js';
3
+ export function runListClaims(options = {}) {
4
+ if (!memoryExists(options.cwd)) {
5
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
6
+ process.exit(1);
7
+ }
8
+ let claims = listClaims(options.cwd);
9
+ if (!options.all) {
10
+ claims = claims.filter(c => c.status === 'active');
11
+ }
12
+ if (options.project) {
13
+ claims = claims.filter(c => c.project === options.project);
14
+ }
15
+ if (options.plan) {
16
+ claims = claims.filter(c => c.plan_id === options.plan);
17
+ }
18
+ if (options.agent) {
19
+ claims = claims.filter(c => c.agent === options.agent);
20
+ }
21
+ if (options.json) {
22
+ console.log(JSON.stringify(claims, null, 2));
23
+ return;
24
+ }
25
+ if (claims.length === 0) {
26
+ console.log('No active claims.');
27
+ return;
28
+ }
29
+ const label = options.all ? 'claim(s)' : 'active claim(s)';
30
+ console.log(`${claims.length} ${label}:`);
31
+ console.log('');
32
+ for (const c of claims) {
33
+ const status = c.status !== 'active' ? ` (${c.status})` : '';
34
+ const extras = [];
35
+ if (c.session_id)
36
+ extras.push(`session ${c.session_id.slice(-8)}`);
37
+ if (c.plan_id)
38
+ extras.push(`plan ${c.plan_id}`);
39
+ if (c.project)
40
+ extras.push(`project ${c.project}`);
41
+ const suffix = extras.length ? ` [${extras.join(', ')}]` : '';
42
+ console.log(` [${c.id}] ${c.agent} → ${c.scope}: ${c.description}${suffix}${status}`);
43
+ }
44
+ }
45
+ //# sourceMappingURL=list-claims.js.map
@@ -0,0 +1,50 @@
1
+ import { resolveAgentScope } from '../core/agent-registry.js';
2
+ import { inferProjectFromTarget, loadInstructions, resolveInstructions } from '../core/instructions.js';
3
+ import { loadConfig } from '../core/config.js';
4
+ import { memoryExists } from '../core/io.js';
5
+ export function runListInstructions(options = {}) {
6
+ if (!memoryExists()) {
7
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
8
+ process.exit(1);
9
+ }
10
+ const config = loadConfig();
11
+ const inferredProject = options.project ?? inferProjectFromTarget(options.for, config);
12
+ const resolvedAgent = options.resolved ? resolveAgentScope(options.agent) : options.agent;
13
+ const source = options.resolved
14
+ ? resolveInstructions(loadInstructions(), { project: inferredProject, agent: resolvedAgent })
15
+ : loadInstructions();
16
+ let entries = source;
17
+ if (options.active) {
18
+ entries = entries.filter((entry) => entry.active);
19
+ }
20
+ if (options.layer) {
21
+ entries = entries.filter((entry) => entry.layer === options.layer);
22
+ }
23
+ if (inferredProject) {
24
+ entries = entries.filter((entry) => entry.layer !== 'project' || entry.scope === inferredProject);
25
+ }
26
+ if (options.agent) {
27
+ entries = entries.filter((entry) => entry.layer !== 'agent' || entry.scope === options.agent);
28
+ }
29
+ if (options.json) {
30
+ console.log(JSON.stringify(entries, null, 2));
31
+ return;
32
+ }
33
+ if (entries.length === 0) {
34
+ console.log('No instructions found.');
35
+ return;
36
+ }
37
+ console.log(`${entries.length} instruction(s):`);
38
+ console.log('');
39
+ for (const entry of entries) {
40
+ const scope = entry.scope ? `:${entry.scope}` : '';
41
+ const flags = [entry.layer];
42
+ if (!entry.active)
43
+ flags.push('inactive');
44
+ if (entry.supersedes)
45
+ flags.push(`supersedes ${entry.supersedes}`);
46
+ const tags = entry.tags.length ? ` [${entry.tags.join(', ')}]` : '';
47
+ console.log(` [${entry.id}] <${entry.layer}${scope}> ${entry.text} (${flags.join(' · ')})${tags}`);
48
+ }
49
+ }
50
+ //# sourceMappingURL=list-instructions.js.map