brainclaw 0.24.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -81,6 +81,9 @@ import { cleanOrphanFiles, memoryDir } from './core/io.js';
81
81
  import { initLogLevel, logger } from './core/logger.js';
82
82
  import { resolveEffectiveCwd } from './core/store-resolution.js';
83
83
  import { runSwitch } from './commands/switch.js';
84
+ import { runCheckEvents } from './commands/check-events.js';
85
+ import { runDiscover } from './commands/discover.js';
86
+ import { runMigrate } from './commands/migrate.js';
84
87
  const program = new Command();
85
88
  function collect(value, previous) {
86
89
  return [...previous, value];
@@ -963,6 +966,7 @@ program
963
966
  .description('Export memory as instructions for IDE/AI tools')
964
967
  .option('--format <format>', 'Format: copilot-instructions, cursor-rules, agents-md, claude-md, gemini-md, windsurf, cline, roo, continue')
965
968
  .option('--detect', 'Auto-detect agent environment and write to its native file')
969
+ .option('--all', 'Write all known agent instruction files at once (claude-md, agents-md, copilot-instructions, cursor-rules, etc.)')
966
970
  .option('--write', 'Write to canonical file path instead of stdout (when --format is given); local files are gitignored by default')
967
971
  .option('--shared', 'Keep the main exported instruction file versionable instead of auto-ignoring it (companions remain local)')
968
972
  .option('--output <file>', 'Write to a specific file path instead of stdout')
@@ -993,8 +997,8 @@ program
993
997
  // --- hooks ---
994
998
  program
995
999
  .command('hooks')
996
- .description('Write deterministic session-trigger hooks for Cursor (.cursor/rules/brainclaw-session.mdc) and Windsurf (.windsurfrules)')
997
- .option('--target <target>', 'Which hooks to write: cursor, windsurf, all (default: all)')
1000
+ .description('Write deterministic session-trigger hooks for Cursor, Windsurf, and Claude Code (PostToolUse event check)')
1001
+ .option('--target <target>', 'Which hooks to write: cursor, windsurf, claude-code, all (default: all)')
998
1002
  .action((options) => {
999
1003
  runHooks(options);
1000
1004
  });
@@ -1008,6 +1012,15 @@ program
1008
1012
  .action((options) => {
1009
1013
  runWatch({ ...options, autoClaim: options.autoClaim });
1010
1014
  });
1015
+ // --- check-events ---
1016
+ program
1017
+ .command('check-events')
1018
+ .description('Show unseen events from the event bus (events.jsonl) for the current agent')
1019
+ .option('--agent <name>', 'Agent name for cursor lookup (default: auto-detected)')
1020
+ .option('--json', 'Output as JSON')
1021
+ .action((options) => {
1022
+ runCheckEvents(options);
1023
+ });
1011
1024
  // --- metrics ---
1012
1025
  program
1013
1026
  .command('metrics')
@@ -1111,6 +1124,24 @@ program
1111
1124
  .action((options) => {
1112
1125
  runExplore({ query: options.query });
1113
1126
  });
1127
+ // --- discover ---
1128
+ program
1129
+ .command('discover')
1130
+ .description('Scan workspace for MCP configs, instruction files, skills, hooks, and agent integrations')
1131
+ .option('--json', 'Output as JSON')
1132
+ .option('--no-save', 'Do not persist discovery profile to .brainclaw/discovery/')
1133
+ .action((options) => {
1134
+ runDiscover({ json: options.json, save: options.save });
1135
+ });
1136
+ // --- migrate ---
1137
+ program
1138
+ .command('migrate')
1139
+ .description('Migrate memory items between stores (e.g. promote machine-scoped items to user store)')
1140
+ .option('--promote-machine-items', 'Move items with scope:machine from project store to user store (~/.brainclaw/)')
1141
+ .option('--dry-run', 'Show what would be moved without actually moving')
1142
+ .action((options) => {
1143
+ runMigrate({ promoteMachineItems: options.promoteMachineItems, dryRun: options.dryRun });
1144
+ });
1114
1145
  program
1115
1146
  .command('switch [project]')
1116
1147
  .description('Set the active project for subsequent commands')
@@ -1,11 +1,10 @@
1
- import { loadState, persistState } from '../core/state.js';
2
1
  import { resolveCurrentAgentName } from '../core/agent-registry.js';
3
2
  import { memoryExists } from '../core/io.js';
4
- import { generateIdWithLabel, nowISO } from '../core/ids.js';
5
3
  import { loadConfig } from '../core/config.js';
6
4
  import { scanText } from '../core/security.js';
7
5
  import { validateCliInput } from '../core/input-validation.js';
8
6
  import { resolveTargetStore } from '../core/store-resolution.js';
7
+ import { listCapabilities, createCapability } from '../core/registries.js';
9
8
  export function runCapability(subcommand, args, options = {}) {
10
9
  const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
11
10
  if (!memoryExists(cwd)) {
@@ -38,10 +37,7 @@ export function runCapability(subcommand, args, options = {}) {
38
37
  }
39
38
  }
40
39
  function runCapabilityList(cwd) {
41
- const state = loadState(cwd);
42
- const capabilities = state.active_constraints
43
- .filter((c) => c.tags.includes('capability'))
44
- .map((c) => ({ id: c.id, name: c.text.split('\n')[0], tags: c.tags }));
40
+ const capabilities = listCapabilities(cwd);
45
41
  if (capabilities.length === 0) {
46
42
  console.log('No capabilities registered yet.');
47
43
  return;
@@ -49,8 +45,8 @@ function runCapabilityList(cwd) {
49
45
  console.log(`\n${capabilities.length} capability(ies):\n`);
50
46
  capabilities.forEach((cap) => {
51
47
  console.log(` [${cap.id}] ${cap.name}`);
52
- if (cap.tags.length > 1) {
53
- console.log(` tags: ${cap.tags.filter((t) => t !== 'capability').join(', ')}`);
48
+ if (cap.tags.length > 0) {
49
+ console.log(` tags: ${cap.tags.join(', ')}`);
54
50
  }
55
51
  });
56
52
  console.log('');
@@ -66,36 +62,29 @@ function runCapabilityAdd(name, description, options, cwd) {
66
62
  process.exit(1);
67
63
  }
68
64
  }
69
- const state = loadState(cwd);
70
- const { id, short_label } = generateIdWithLabel('recent_decisions');
71
- const entry = {
72
- id,
73
- short_label,
74
- text: name,
75
- created_at: nowISO(),
65
+ const cap = createCapability({
66
+ name,
67
+ description,
68
+ tags: options.tag,
76
69
  author: options.author ?? resolveCurrentAgentName(cwd),
77
- tags: ['capability', ...(options.tag ?? [])],
78
- };
79
- // For now, store as decision to avoid schema migration
80
- // Will migrate to separate capability storage in v0.16
81
- state.recent_decisions.push(entry);
82
- persistState(state, cwd);
83
- console.log(`✔ Capability added: [${id}] ${name}`);
84
- console.log(' (Stored in decisions for now; will move to dedicated registry in v0.16)');
70
+ }, cwd);
71
+ console.log(`✔ Capability added: [${cap.id}] ${name}`);
85
72
  }
86
73
  function runCapabilityDescribe(capId, cwd) {
87
- const state = loadState(cwd);
88
- const decision = state.recent_decisions.find((d) => d.id === capId || d.short_label === capId);
89
- if (!decision) {
74
+ const capabilities = listCapabilities(cwd);
75
+ const cap = capabilities.find((c) => c.id === capId || c.id.startsWith(capId));
76
+ if (!cap) {
90
77
  console.error(`Error: capability '${capId}' not found`);
91
78
  process.exit(1);
92
79
  }
93
- console.log(`\nCapability: ${decision.text}`);
94
- console.log(`ID: ${decision.id}`);
95
- console.log(`Author: ${decision.author}`);
96
- console.log(`Created: ${decision.created_at}`);
97
- if (decision.tags.length > 1) {
98
- console.log(`Tags: ${decision.tags.filter((t) => t !== 'capability').join(', ')}`);
80
+ console.log(`\nCapability: ${cap.name}`);
81
+ console.log(`Description: ${cap.description}`);
82
+ console.log(`ID: ${cap.id}`);
83
+ console.log(`Category: ${cap.category}`);
84
+ console.log(`Author: ${cap.author}`);
85
+ console.log(`Created: ${cap.created_at}`);
86
+ if (cap.tags.length > 0) {
87
+ console.log(`Tags: ${cap.tags.join(', ')}`);
99
88
  }
100
89
  console.log('');
101
90
  }
@@ -0,0 +1,36 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { readUnseenEvents, buildNotificationSummary } from '../core/event-log.js';
3
+ import { resolveCurrentAgentName } from '../core/agent-registry.js';
4
+ export function runCheckEvents(options = {}) {
5
+ if (!memoryExists()) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const agent = options.agent ?? resolveCurrentAgentName();
10
+ const events = readUnseenEvents(agent);
11
+ if (events.length === 0) {
12
+ if (options.json) {
13
+ console.log(JSON.stringify({ agent, unseen: 0 }));
14
+ }
15
+ else {
16
+ console.log('No unseen events.');
17
+ }
18
+ return;
19
+ }
20
+ const summary = buildNotificationSummary(events) ?? {};
21
+ if (options.json) {
22
+ console.log(JSON.stringify({ agent, unseen: events.length, summary, events }));
23
+ return;
24
+ }
25
+ console.log(`${events.length} unseen event(s) since last read:\n`);
26
+ for (const [key, count] of Object.entries(summary)) {
27
+ console.log(` ${key}: ${count}`);
28
+ }
29
+ console.log('');
30
+ for (const evt of events) {
31
+ const id = evt.item_id ? ` [${evt.item_id.slice(0, 12)}]` : '';
32
+ const sum = evt.summary ? ` — ${evt.summary}` : '';
33
+ console.log(` ${evt.ts} ${evt.agent} ${evt.action}:${evt.item_type}${id}${sum}`);
34
+ }
35
+ }
36
+ //# sourceMappingURL=check-events.js.map
@@ -5,7 +5,7 @@ import { saveClaim, generateClaimId, listClaims } from '../core/claims.js';
5
5
  import { rebuildProjectMd } from '../core/markdown.js';
6
6
  import { loadState, saveState } from '../core/state.js';
7
7
  import { nowISO } from '../core/ids.js';
8
- import { requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
8
+ import { requireMinimumTrustLevel, requireRegisteredAgentIdentity, resolveCurrentModel } from '../core/agent-registry.js';
9
9
  import { validateCliTtl } from '../core/input-validation.js';
10
10
  import { resolveTargetStore } from '../core/store-resolution.js';
11
11
  function parseTtl(ttl) {
@@ -72,6 +72,7 @@ export function runClaim(description, options) {
72
72
  plan_id: options.plan,
73
73
  status: 'active',
74
74
  expires_at: options.ttl ? parseTtl(options.ttl) : undefined,
75
+ model: resolveCurrentModel(options.cwd),
75
76
  };
76
77
  mutate({ cwd: options.cwd }, () => {
77
78
  if (plan) {
@@ -1,5 +1,14 @@
1
1
  import { buildContextDiff, resolveContextDiffSince } from '../core/context-diff.js';
2
+ import { listClaims, isClaimExpired } from '../core/claims.js';
3
+ import { loadInstructions, resolveInstructions } from '../core/instructions.js';
2
4
  import { memoryExists } from '../core/io.js';
5
+ import { loadState } from '../core/state.js';
6
+ import { isTrapActive } from '../core/traps.js';
7
+ /**
8
+ * Hybrid context-diff: always includes critical anchors (active claims,
9
+ * top traps, instructions) so the agent stays grounded, plus the memory
10
+ * delta since last context read.
11
+ */
3
12
  export function runContextDiff(options = {}) {
4
13
  if (!memoryExists(options.cwd)) {
5
14
  console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
@@ -20,13 +29,62 @@ export function runContextDiff(options = {}) {
20
29
  process.exit(1);
21
30
  }
22
31
  if (options.json) {
23
- console.log(JSON.stringify(diff, null, 2));
32
+ const anchors = buildCriticalAnchors(options.cwd);
33
+ console.log(JSON.stringify({ ...diff, anchors }, null, 2));
24
34
  return;
25
35
  }
36
+ const lines = [];
37
+ // --- Critical anchors (always present) ---
38
+ const anchors = buildCriticalAnchors(options.cwd);
39
+ if (anchors.claims.length > 0) {
40
+ lines.push('Active claims:');
41
+ for (const c of anchors.claims) {
42
+ lines.push(`- [${c.id}] ${c.agent} → ${c.scope}: ${c.description}`);
43
+ }
44
+ lines.push('');
45
+ }
46
+ if (anchors.instructions.length > 0) {
47
+ lines.push('Instructions:');
48
+ for (const ins of anchors.instructions) {
49
+ const scope = ins.scope ? `:${ins.scope}` : '';
50
+ lines.push(`- [${ins.id}] <${ins.layer}${scope}> ${ins.text}`);
51
+ }
52
+ lines.push('');
53
+ }
54
+ if (anchors.traps.length > 0) {
55
+ lines.push('Active traps:');
56
+ for (const t of anchors.traps) {
57
+ lines.push(`- [${t.id}] (${t.severity}) ${t.text}`);
58
+ }
59
+ lines.push('');
60
+ }
61
+ // --- Memory delta ---
26
62
  if (diff.counts.total === 0) {
27
- console.log(`${diff.summary} since ${diff.since}.`);
28
- return;
63
+ lines.push(`Memory: no changes since ${diff.since?.slice(0, 16).replace('T', ' ')}.`);
29
64
  }
30
- console.log(`${diff.summary} since ${diff.since}.`);
65
+ else {
66
+ lines.push(`Memory delta (${diff.summary}):`);
67
+ for (const item of diff.changed_items ?? []) {
68
+ lines.push(`- [${item.section}] [${item.id}] ${item.text}`);
69
+ }
70
+ }
71
+ console.log(lines.join('\n'));
72
+ }
73
+ function buildCriticalAnchors(cwd) {
74
+ const activeClaims = listClaims(cwd)
75
+ .filter((c) => c.status === 'active' && !isClaimExpired(c))
76
+ .map((c) => ({ id: c.id, agent: c.agent, scope: c.scope, description: c.description }));
77
+ const instructions = resolveInstructions(loadInstructions(cwd))
78
+ .map((ins) => ({ id: ins.id, layer: ins.layer, scope: ins.scope, text: ins.text }));
79
+ const state = loadState(cwd);
80
+ const traps = state.known_traps
81
+ .filter((t) => isTrapActive(t))
82
+ .sort((a, b) => {
83
+ const severityOrder = { high: 0, medium: 1, low: 2 };
84
+ return (severityOrder[a.severity] ?? 1) - (severityOrder[b.severity] ?? 1);
85
+ })
86
+ .slice(0, 5)
87
+ .map((t) => ({ id: t.id, severity: t.severity, text: t.text }));
88
+ return { claims: activeClaims, instructions, traps };
31
89
  }
32
90
  //# sourceMappingURL=context-diff.js.map
@@ -0,0 +1,21 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { buildProjectDiscovery, saveDiscoveryProfile, renderDiscoverySummary, } from '../core/project-discovery.js';
3
+ export function runDiscover(options = {}) {
4
+ const cwd = options.cwd ?? process.cwd();
5
+ if (!memoryExists(cwd)) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const profile = buildProjectDiscovery({ cwd });
10
+ // Save by default (non-destructive refresh)
11
+ if (options.save !== false) {
12
+ saveDiscoveryProfile(profile, cwd);
13
+ }
14
+ if (options.json) {
15
+ console.log(JSON.stringify(profile, null, 2));
16
+ }
17
+ else {
18
+ console.log(renderDiscoverySummary(profile));
19
+ }
20
+ }
21
+ //# sourceMappingURL=discover.js.map
@@ -1,4 +1,5 @@
1
1
  import { listAgentIdentities, resolveCurrentAgentIdentity } from '../core/agent-registry.js';
2
+ import { listCapabilities as listRegistryCapabilities, listTools as listRegistryTools } from '../core/registries.js';
2
3
  import { buildReputationSummary } from '../core/reputation.js';
3
4
  import { buildCircuitBreakerSnapshot } from '../core/circuit-breaker.js';
4
5
  import { loadState } from '../core/state.js';
@@ -1195,28 +1196,26 @@ export function runDoctor(options = {}) {
1195
1196
  console.log('✔ Cross-level duplicates: no duplicates across store levels');
1196
1197
  }
1197
1198
  }
1198
- // Metadata consistency checks
1199
- const capabilities = state.recent_decisions.filter((d) => d.tags.includes('capability'));
1200
- const tools = state.recent_decisions.filter((d) => d.tags.includes('tool'));
1199
+ // Metadata consistency checks (capabilities/tools from dedicated registries)
1200
+ const capabilities = listRegistryCapabilities(options.cwd);
1201
+ const tools = listRegistryTools(options.cwd);
1201
1202
  const metadataIssues = [];
1202
1203
  // Check capabilities completeness
1203
1204
  capabilities.forEach((cap) => {
1204
- const category = cap.tags.find((t) => t !== 'capability');
1205
- if (!category) {
1205
+ if (!cap.category) {
1206
1206
  metadataIssues.push(`Capability [${cap.id}] missing category`);
1207
1207
  }
1208
- if (!cap.text || cap.text.trim().length === 0) {
1209
- metadataIssues.push(`Capability [${cap.id}] has empty description`);
1208
+ if (!cap.name || cap.name.trim().length === 0) {
1209
+ metadataIssues.push(`Capability [${cap.id}] has empty name`);
1210
1210
  }
1211
1211
  });
1212
1212
  // Check tools completeness
1213
1213
  tools.forEach((tool) => {
1214
- const type = tool.tags.find((t) => t !== 'tool');
1215
- if (!type) {
1214
+ if (!tool.type) {
1216
1215
  metadataIssues.push(`Tool [${tool.id}] missing type`);
1217
1216
  }
1218
- if (!tool.text || tool.text.trim().length === 0) {
1219
- metadataIssues.push(`Tool [${tool.id}] has empty description`);
1217
+ if (!tool.name || tool.name.trim().length === 0) {
1218
+ metadataIssues.push(`Tool [${tool.id}] has empty name`);
1220
1219
  }
1221
1220
  });
1222
1221
  if (metadataIssues.length > 0) {
@@ -1245,6 +1244,63 @@ export function runDoctor(options = {}) {
1245
1244
  }
1246
1245
  }
1247
1246
  catch { /* non-fatal */ }
1247
+ // Check for machine-scoped items in project store (should be in user store)
1248
+ try {
1249
+ const machineInProject = [
1250
+ ...state.active_constraints.filter((c) => c.scope === 'machine'),
1251
+ ...state.recent_decisions.filter((d) => d.scope === 'machine'),
1252
+ ...state.known_traps.filter((t) => t.scope === 'machine'),
1253
+ ];
1254
+ if (machineInProject.length > 0) {
1255
+ const ids = machineInProject.map((i) => i.id).slice(0, 5);
1256
+ checks.push({
1257
+ name: 'machine_scope_placement',
1258
+ status: 'warn',
1259
+ message: `${machineInProject.length} machine-scoped item(s) in project store — consider promoting to user store with 'brainclaw migrate --promote-machine-items'`,
1260
+ details: ids,
1261
+ });
1262
+ if (!options.json) {
1263
+ console.warn(`⚠ ${machineInProject.length} machine-scoped item(s) in project store: ${ids.join(', ')}`);
1264
+ }
1265
+ }
1266
+ else {
1267
+ checks.push({ name: 'machine_scope_placement', status: 'ok', message: 'No machine-scoped items misplaced in project store' });
1268
+ if (!options.json) {
1269
+ console.log('✔ Machine-scope placement: no misplaced items');
1270
+ }
1271
+ }
1272
+ }
1273
+ catch { /* non-fatal */ }
1274
+ // Workflow hygiene check (Phase 9)
1275
+ try {
1276
+ const activeClaims = listClaims(options.cwd).filter((c) => c.status === 'active');
1277
+ const inProgressPlans = state.plan_items.filter((p) => p.status === 'in_progress');
1278
+ const workflowIssues = [];
1279
+ if (activeClaims.length > 3) {
1280
+ workflowIssues.push(`${activeClaims.length} active claims — consider releasing finished ones`);
1281
+ }
1282
+ const unclaimedInProgress = inProgressPlans.filter((p) => !activeClaims.some((c) => c.plan_id === p.id));
1283
+ if (unclaimedInProgress.length > 0) {
1284
+ workflowIssues.push(`${unclaimedInProgress.length} in-progress plan(s) without a claim`);
1285
+ }
1286
+ if (workflowIssues.length > 0) {
1287
+ checks.push({
1288
+ name: 'workflow_hygiene',
1289
+ status: 'warn',
1290
+ message: `Workflow hygiene: ${workflowIssues.join('; ')}`,
1291
+ details: workflowIssues,
1292
+ });
1293
+ if (!options.json) {
1294
+ console.warn(`⚠ Workflow hygiene: ${workflowIssues.join('; ')}`);
1295
+ }
1296
+ }
1297
+ else {
1298
+ checks.push({ name: 'workflow_hygiene', status: 'ok', message: 'Workflow hygiene OK' });
1299
+ if (!options.json)
1300
+ console.log('✔ Workflow hygiene: OK');
1301
+ }
1302
+ }
1303
+ catch { /* non-fatal */ }
1248
1304
  }
1249
1305
  catch { /* non-fatal */ }
1250
1306
  if (options.json) {
@@ -1,14 +1,13 @@
1
- import { loadState } from '../core/state.js';
2
1
  import { memoryExists } from '../core/io.js';
2
+ import { listCapabilities, listTools } from '../core/registries.js';
3
3
  export function runExplore(options = {}) {
4
4
  const cwd = options.cwd ?? process.cwd();
5
5
  if (!memoryExists(cwd)) {
6
6
  console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
7
  process.exit(1);
8
8
  }
9
- const state = loadState(cwd);
10
- const capabilities = state.recent_decisions.filter((d) => d.tags.includes('capability'));
11
- const tools = state.recent_decisions.filter((d) => d.tags.includes('tool'));
9
+ const capabilities = listCapabilities(cwd);
10
+ const tools = listTools(cwd);
12
11
  console.log('\n╔════════════════════════════════════════════════════════╗');
13
12
  console.log('║ Project Capabilities & Tools ║');
14
13
  console.log('╚════════════════════════════════════════════════════════╝\n');
@@ -18,9 +17,8 @@ export function runExplore(options = {}) {
18
17
  }
19
18
  else {
20
19
  capabilities.forEach((cap) => {
21
- const category = cap.tags.find((t) => t !== 'capability') || 'general';
22
- console.log(` [${cap.id}] ${cap.text.split('\n')[0]}`);
23
- console.log(` Category: ${category}\n`);
20
+ console.log(` [${cap.id}] ${cap.name}`);
21
+ console.log(` Category: ${cap.category}\n`);
24
22
  });
25
23
  }
26
24
  console.log(`🔧 Tools (${tools.length}):\n`);
@@ -29,9 +27,8 @@ export function runExplore(options = {}) {
29
27
  }
30
28
  else {
31
29
  tools.forEach((tool) => {
32
- const type = tool.tags.find((t) => t !== 'tool') || 'utility';
33
- console.log(` [${tool.id}] ${tool.text.split('\n')[0]}`);
34
- console.log(` Type: ${type}\n`);
30
+ console.log(` [${tool.id}] ${tool.name}`);
31
+ console.log(` Type: ${tool.type}\n`);
35
32
  });
36
33
  }
37
34
  if (capabilities.length === 0 && tools.length === 0) {
@@ -6,7 +6,7 @@ import { loadConfig, saveConfig } from '../core/config.js';
6
6
  import { isAgentIntegrationName, upsertAgentIntegrationDeclaration } from '../core/agent-integrations.js';
7
7
  import { resolveInstructions, loadInstructions } from '../core/instructions.js';
8
8
  import { detectAiAgent } from '../core/ai-agent-detection.js';
9
- import { resolveExportTarget, resolveExportTargetByFormat, writeExportFile, buildHygieneSection, describeAutoConfigWrite, writeExportCompanionFiles, collectExportGitignoreEntries, ensureGitignoreEntries, } from '../core/agent-files.js';
9
+ import { AGENT_EXPORT_REGISTRY, resolveExportTarget, resolveExportTargetByFormat, writeExportFile, buildHygieneSection, describeAutoConfigWrite, writeExportCompanionFiles, collectExportGitignoreEntries, ensureGitignoreEntries, } from '../core/agent-files.js';
10
10
  import { logger } from '../core/logger.js';
11
11
  import { getAgentCapabilityProfile } from '../core/agent-capability.js';
12
12
  import { renderBrainclawSection } from '../core/instruction-templates.js';
@@ -17,6 +17,10 @@ export function runExport(options) {
17
17
  console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
18
18
  process.exit(1);
19
19
  }
20
+ if (options.all) {
21
+ runExportAll(cwd, options);
22
+ return;
23
+ }
20
24
  if (options.detect) {
21
25
  if (options.shared) {
22
26
  console.error('Error: --shared cannot be used with --detect. Use `brainclaw export --format <format> --write --shared` to publish a specific instruction file.');
@@ -26,7 +30,7 @@ export function runExport(options) {
26
30
  return;
27
31
  }
28
32
  if (!options.format) {
29
- console.error('Error: --format or --detect is required.');
33
+ console.error('Error: --format, --detect, or --all is required.');
30
34
  process.exit(1);
31
35
  }
32
36
  const content = generateExport(options.format, options, cwd);
@@ -90,6 +94,40 @@ function runExportDetect(cwd, options) {
90
94
  }
91
95
  }
92
96
  }
97
+ function runExportAll(cwd, options) {
98
+ // Deduplicate by format (e.g. codex and opencode both use agents-md)
99
+ const seen = new Set();
100
+ const targets = AGENT_EXPORT_REGISTRY.filter((t) => {
101
+ if (seen.has(t.format))
102
+ return false;
103
+ seen.add(t.format);
104
+ return true;
105
+ });
106
+ let written = 0;
107
+ const allGitignoreEntries = [];
108
+ for (const target of targets) {
109
+ try {
110
+ const content = generateExport(target.format, options, cwd);
111
+ const result = writeExportFile(content, target.relativePath, cwd);
112
+ const autoConfigs = writeExportCompanionFiles(target.format, cwd);
113
+ const gitignoreEntries = collectExportGitignoreEntries(cwd, target.relativePath, autoConfigs);
114
+ allGitignoreEntries.push(...gitignoreEntries);
115
+ declareAgentIntegrationFromTarget(cwd, target.agentName, 'manual');
116
+ console.log(`✔ ${target.relativePath} (${result.created ? 'created' : 'updated'})`);
117
+ written++;
118
+ }
119
+ catch (err) {
120
+ logger.debug(`Failed to export ${target.format}:`, err);
121
+ console.warn(`⚠ Skipped ${target.format}: ${err instanceof Error ? err.message : String(err)}`);
122
+ }
123
+ }
124
+ // Consolidate gitignore entries
125
+ if (allGitignoreEntries.length > 0) {
126
+ ensureGitignoreEntries(cwd, [...new Set(allGitignoreEntries)]);
127
+ console.log('✔ Updated .gitignore');
128
+ }
129
+ console.log(`✔ Exported ${written} agent file(s)`);
130
+ }
93
131
  export function writeAgentExportForAgent(agentName, cwd) {
94
132
  const rendered = renderAgentExportForAgent(agentName, cwd);
95
133
  if (!rendered) {
@@ -2,7 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { memoryExists } from '../core/io.js';
4
4
  import { loadConfig } from '../core/config.js';
5
- import { BRAINCLAW_SECTION_START, BRAINCLAW_SECTION_END, upsertBrainclawSection } from '../core/agent-files.js';
5
+ import { BRAINCLAW_SECTION_START, BRAINCLAW_SECTION_END, upsertBrainclawSection, ensureClaudeCodeSettings } from '../core/agent-files.js';
6
6
  /**
7
7
  * Generate the Cursor MDC hook file content.
8
8
  * Uses MDC frontmatter with `alwaysApply: true` so Cursor injects it
@@ -25,14 +25,14 @@ brainclaw context
25
25
  This loads the shared project memory: active constraints, recent decisions, known traps,
26
26
  open plan items, active claims, and the last handoff.
27
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
- \`\`\`
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
36
  `;
37
37
  }
38
38
  /**
@@ -55,14 +55,14 @@ brainclaw context
55
55
  This gives you: active constraints, recent decisions, known traps, open plans, active claims,
56
56
  and the last handoff note. Do not skip this step.
57
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
- \`\`\`
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
66
  `;
67
67
  }
68
68
  export function writeHook(content, relativePath, cwd) {
@@ -100,6 +100,14 @@ export function runHooks(options = {}) {
100
100
  const content = generateWindsurfHook(config.project_name);
101
101
  results.push(writeHook(content, '.windsurfrules', cwd));
102
102
  }
103
+ if (target === 'claude-code' || target === 'all') {
104
+ const autoResult = ensureClaudeCodeSettings(cwd);
105
+ results.push({
106
+ target: 'claude-code',
107
+ relativePath: autoResult.relativePath ?? '.claude/settings.local.json',
108
+ created: autoResult.created,
109
+ });
110
+ }
103
111
  for (const r of results) {
104
112
  console.log(`✔ Hook written to ${r.relativePath} (${r.created ? 'created' : 'updated'})`);
105
113
  }
@@ -118,6 +126,14 @@ export function writeDetectedAgentHooks(agentName, projectName, cwd) {
118
126
  const content = generateWindsurfHook(projectName);
119
127
  results.push(writeHook(content, '.windsurfrules', cwd));
120
128
  }
129
+ if (agentName === 'claude-code') {
130
+ const autoResult = ensureClaudeCodeSettings(cwd);
131
+ results.push({
132
+ target: 'claude-code',
133
+ relativePath: autoResult.relativePath ?? '.claude/settings.local.json',
134
+ created: autoResult.created,
135
+ });
136
+ }
121
137
  return results;
122
138
  }
123
139
  //# sourceMappingURL=hooks.js.map