brainclaw 1.9.0 → 1.9.1

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 (91) hide show
  1. package/README.md +585 -499
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/commands/harvest.js +1 -1
  4. package/dist/commands/hooks.js +73 -73
  5. package/dist/commands/init.js +1 -1
  6. package/dist/commands/install-hooks.js +78 -78
  7. package/dist/commands/mcp-read-handlers.js +57 -14
  8. package/dist/commands/mcp.js +79 -13
  9. package/dist/commands/switch.js +26 -5
  10. package/dist/commands/version.js +1 -1
  11. package/dist/core/agent-capability.js +19 -4
  12. package/dist/core/agent-files.js +119 -119
  13. package/dist/core/codev-prompts.js +38 -38
  14. package/dist/core/default-profiles/doctor.yaml +11 -11
  15. package/dist/core/default-profiles/janitor.yaml +11 -11
  16. package/dist/core/default-profiles/onboarder.yaml +11 -11
  17. package/dist/core/default-profiles/reviewer.yaml +13 -13
  18. package/dist/core/dispatcher.js +1 -1
  19. package/dist/core/entity-operations.js +29 -3
  20. package/dist/core/execution.js +1 -1
  21. package/dist/core/loops/verbs.js +0 -1
  22. package/dist/core/messaging.js +2 -2
  23. package/dist/core/protocol-skills.js +164 -164
  24. package/dist/core/runtime-signals.js +1 -1
  25. package/dist/core/search.js +19 -2
  26. package/dist/core/security-guard.js +207 -207
  27. package/dist/core/spawn-check.js +16 -2
  28. package/dist/core/staleness.js +1 -1
  29. package/dist/core/store-resolution.js +26 -7
  30. package/dist/core/worktree.js +18 -18
  31. package/dist/facts.js +3 -3
  32. package/dist/facts.json +2 -2
  33. package/docs/PROTOCOL.md +1 -1
  34. package/docs/adapters/openclaw.md +43 -43
  35. package/docs/architecture/project-refs.md +328 -328
  36. package/docs/cli.md +2093 -2093
  37. package/docs/concepts/coordination.md +52 -52
  38. package/docs/concepts/coordinator-runbook.md +129 -129
  39. package/docs/concepts/dispatch-lifecycle.md +245 -245
  40. package/docs/concepts/event-log-store.md +928 -928
  41. package/docs/concepts/ideation-loop.md +317 -317
  42. package/docs/concepts/loop-engine.md +520 -511
  43. package/docs/concepts/mcp-governance.md +268 -268
  44. package/docs/concepts/memory.md +84 -84
  45. package/docs/concepts/multi-agent-workflows.md +167 -167
  46. package/docs/concepts/observer-protocol.md +361 -361
  47. package/docs/concepts/plans-and-claims.md +217 -217
  48. package/docs/concepts/project-md-convention.md +35 -35
  49. package/docs/concepts/runtime-notes.md +38 -38
  50. package/docs/concepts/troubleshooting.md +254 -254
  51. package/docs/concepts/workspace-bootstrapping.md +142 -142
  52. package/docs/context-format-changelog.md +35 -35
  53. package/docs/context-format.md +48 -48
  54. package/docs/index.md +65 -65
  55. package/docs/integrations/agents.md +158 -158
  56. package/docs/integrations/claude-code.md +23 -23
  57. package/docs/integrations/cline.md +77 -77
  58. package/docs/integrations/continue.md +55 -55
  59. package/docs/integrations/copilot.md +68 -68
  60. package/docs/integrations/cursor.md +23 -23
  61. package/docs/integrations/kilocode.md +72 -72
  62. package/docs/integrations/mcp.md +377 -377
  63. package/docs/integrations/mistral-vibe.md +122 -122
  64. package/docs/integrations/openclaw.md +92 -92
  65. package/docs/integrations/opencode.md +84 -84
  66. package/docs/integrations/overview.md +115 -115
  67. package/docs/integrations/roo.md +71 -71
  68. package/docs/integrations/windsurf.md +77 -77
  69. package/docs/mcp-schema-changelog.md +360 -356
  70. package/docs/playbooks/integration/index.md +121 -121
  71. package/docs/playbooks/orchestration.md +37 -0
  72. package/docs/playbooks/productivity/index.md +99 -99
  73. package/docs/playbooks/team/index.md +117 -117
  74. package/docs/product/agent-first-model.md +184 -184
  75. package/docs/product/entity-model-audit.md +462 -462
  76. package/docs/product/positioning.md +86 -86
  77. package/docs/quickstart-existing-project.md +107 -107
  78. package/docs/quickstart.md +183 -183
  79. package/docs/release-maintenance.md +79 -79
  80. package/docs/reputation.md +52 -52
  81. package/docs/review.md +45 -45
  82. package/docs/security.md +212 -212
  83. package/docs/server-operations.md +118 -118
  84. package/docs/storage.md +106 -106
  85. package/package.json +80 -65
  86. package/docs/concepts/event-log-store-critique-A.md +0 -333
  87. package/docs/concepts/event-log-store-critique-B.md +0 -353
  88. package/docs/concepts/event-log-store-phase0-measurements.md +0 -58
  89. package/docs/concepts/event-log-store-proposal-A.md +0 -365
  90. package/docs/concepts/event-log-store-proposal-B.md +0 -404
  91. package/docs/concepts/identity-model-proposal.md +0 -371
@@ -39,7 +39,7 @@ import { createCapability, createTool as createRegistryTool } from '../core/regi
39
39
  import { detectAiAgent } from '../core/ai-agent-detection.js';
40
40
  import { checkGitPresence, scanGitRepos, parseRoots, parseRepoSelection, parseAgentSelection, getDetectedSetupAgentNames, getInstalledAgentNames, runGlobalInstall, initReposAndConfigureAgents, readSetupState, ALL_KNOWN_AGENTS, } from './setup.js';
41
41
  import { buildAgentInventory } from '../core/agent-inventory.js';
42
- import { resolveEffectiveCwd, resolveProjectRef, resolveTargetStore } from '../core/store-resolution.js';
42
+ import { resolveEffectiveCwd, resolveEffectiveCwdInfo, resolveProjectRef, resolveTargetStore } from '../core/store-resolution.js';
43
43
  import { assessBootstrapNeed, probeForQuickSetup, buildQuickSetupProbeResponse, buildOnboardingPreview, resolveEmptyMemoryRecommendation } from '../core/setup-flow.js';
44
44
  import { ensureUserStore, resolveHomeDir } from '../core/setup-state.js';
45
45
  import { createPlan, addStep as addStepOp, completeStep as completeStepOp, updateStep as updateStepOp, deleteStep as deleteStepOp, deletePlan as deletePlanOp } from '../core/operations/plan.js';
@@ -167,6 +167,8 @@ export const MCP_READ_TOOLS = [
167
167
  type: { type: 'string', description: 'Filter by section: decisions, constraints, traps, handoffs, candidates, plans, sequences.' },
168
168
  section: { type: 'string', description: 'Filter by section (state, candidates, runtime).' },
169
169
  since: { type: 'string', description: 'Filter items created after this ISO date.' },
170
+ project: { type: 'string', description: 'Optional project name/path to search. Defaults to the active project.' },
171
+ includeLegacy: { type: 'boolean', description: 'Include records with provenance.kind="legacy" (default false). Response reports excluded_legacy when false.' },
170
172
  limit: { type: 'number', description: 'Maximum number of results to return (default 10).' },
171
173
  offset: { type: 'number', description: 'Number of results to skip (for pagination).' },
172
174
  budget_tokens: { type: 'number', description: 'Optional token budget for the result page (~4 chars/token). The page is size-bounded; has_more/next_offset advertise the rest.' },
@@ -1782,6 +1784,32 @@ function explicitSessionIdFromEnv() {
1782
1784
  || process.env.CLAUDE_SESSION_ID?.trim()
1783
1785
  || process.env.COPILOT_SESSION_ID?.trim();
1784
1786
  }
1787
+ function projectInfoForCwd(cwd) {
1788
+ try {
1789
+ const config = loadConfig(cwd);
1790
+ return { path: cwd, name: config.project_name };
1791
+ }
1792
+ catch {
1793
+ return { path: cwd };
1794
+ }
1795
+ }
1796
+ function scopeMetadataForTarget(args, targetCwd, effectiveScope) {
1797
+ const hasExplicitProject = typeof args.project === 'string' && args.project.trim().length > 0;
1798
+ return {
1799
+ resolved_project: projectInfoForCwd(targetCwd),
1800
+ active_source: hasExplicitProject ? 'explicit' : effectiveScope.active_source,
1801
+ };
1802
+ }
1803
+ function renderProvenanceFilterNote(result) {
1804
+ const parts = [];
1805
+ if ((result.excluded_legacy ?? 0) > 0) {
1806
+ parts.push(`${result.excluded_legacy} legacy item(s) excluded (pass filter.includeLegacy=true to include them)`);
1807
+ }
1808
+ if ((result.excluded_low_confidence_auto_reflect ?? 0) > 0) {
1809
+ parts.push(`${result.excluded_low_confidence_auto_reflect} low-confidence auto_reflect item(s) excluded (lower filter.minAutoReflectConfidence to include them)`);
1810
+ }
1811
+ return parts.length > 0 ? parts.join('; ') : undefined;
1812
+ }
1785
1813
  export function parseMcpLine(line) {
1786
1814
  let parsed;
1787
1815
  try {
@@ -2557,6 +2585,11 @@ import { handleMcpReadToolCall } from './mcp-read-handlers.js';
2557
2585
  export { handleMcpReadToolCall };
2558
2586
  async function _executeMcpToolCallInner(payload) {
2559
2587
  const { name, args, cwd, connectionSessionId } = payload;
2588
+ const scopeInfo = payload.effectiveScope ?? {
2589
+ cwd,
2590
+ active_source: 'cwd',
2591
+ resolved_project: projectInfoForCwd(cwd),
2592
+ };
2560
2593
  try {
2561
2594
  if (isLegacyMcpToolFacadeDisabled(name)) {
2562
2595
  return { response: createLegacyMcpToolDisabledResponse() };
@@ -2568,7 +2601,7 @@ async function _executeMcpToolCallInner(payload) {
2568
2601
  }
2569
2602
  if (MCP_READ_TOOLS.some((tool) => tool.name === name) || LEGACY_READ_TOOL_HANDLERS.has(name)) {
2570
2603
  return {
2571
- response: appendLegacyMcpToolWarning(toolResponse(handleMcpReadToolCall(name, args, { cwd })), name),
2604
+ response: appendLegacyMcpToolWarning(toolResponse(handleMcpReadToolCall(name, args, { cwd, connectionSessionId, effectiveScope: scopeInfo })), name),
2572
2605
  };
2573
2606
  }
2574
2607
  // Resolve model once for all write operations
@@ -6214,6 +6247,7 @@ async function _executeMcpToolCallInner(payload) {
6214
6247
  try {
6215
6248
  const entity = String(args.entity ?? '');
6216
6249
  const targetCwd = resolveProjectCwd(args.project, cwd);
6250
+ const targetScope = scopeMetadataForTarget(args, targetCwd, scopeInfo);
6217
6251
  // pln#460 follow-up — some MCP clients (notably Claude Code with a
6218
6252
  // tool schema that declares `filter: { type: 'object' }` without a
6219
6253
  // sub-property schema) stringify the filter object before shipping
@@ -6269,10 +6303,10 @@ async function _executeMcpToolCallInner(payload) {
6269
6303
  const bounded = boundListResult(result, offset, charBudget);
6270
6304
  const warnings = collectLoadValidationWarnings(entity, targetCwd);
6271
6305
  const nextActions = [
6272
- { tool: 'bclaw_get', args: { entity, id: '<id from items>' }, when: 'to read one item in full' },
6306
+ { tool: 'bclaw_get', args: { entity, id: '<id from items>', ...(args.project ? { project: args.project } : {}), ...(args.budget_tokens ? { budget_tokens: args.budget_tokens } : {}) }, when: 'to read one item in full' },
6273
6307
  ];
6274
6308
  if (bounded.has_more) {
6275
- nextActions.push({ tool: 'bclaw_find', args: { entity, filter: { ...filter, offset: bounded.next_offset } }, when: 'to fetch the next page' });
6309
+ nextActions.push({ tool: 'bclaw_find', args: { entity, filter: { ...filter, offset: bounded.next_offset }, ...(args.project ? { project: args.project } : {}), ...(args.budget_tokens ? { budget_tokens: args.budget_tokens } : {}) }, when: 'to fetch the next page' });
6276
6310
  }
6277
6311
  // structuredContent is the canonical MCP return channel that clients
6278
6312
  // (VS Code extension, Codex, etc.) read for machine-parseable data.
@@ -6281,10 +6315,20 @@ async function _executeMcpToolCallInner(payload) {
6281
6315
  // `result.items` arrived as undefined on the client — the root cause
6282
6316
  // of the VS Code Backlog section rendering empty.
6283
6317
  const moreNote = bounded.has_more ? ` (returned ${bounded.returned}; ${result.total - bounded.returned} more — offset ${bounded.next_offset})` : '';
6318
+ const provenanceFilterNote = renderProvenanceFilterNote(result);
6319
+ const legacyNote = provenanceFilterNote
6320
+ ? `; ${provenanceFilterNote}`
6321
+ : '';
6284
6322
  return {
6285
6323
  response: toolResponse({
6286
- content: [{ type: 'text', text: `✔ ${result.total} ${entity} item(s)${moreNote}` }],
6287
- structuredContent: { ...bounded, warnings, next_actions: nextActions },
6324
+ content: [{ type: 'text', text: `✔ ${result.total} ${entity} item(s)${moreNote}${legacyNote}` }],
6325
+ structuredContent: {
6326
+ ...bounded,
6327
+ warnings,
6328
+ resolved_project: targetScope.resolved_project,
6329
+ active_source: targetScope.active_source,
6330
+ next_actions: nextActions,
6331
+ },
6288
6332
  }),
6289
6333
  };
6290
6334
  }
@@ -6361,6 +6405,7 @@ async function _executeMcpToolCallInner(payload) {
6361
6405
  }
6362
6406
  const rawData = (args.data ?? {});
6363
6407
  const targetCwd = resolveProjectCwd(args.project, cwd);
6408
+ const targetScope = scopeMetadataForTarget(args, targetCwd, scopeInfo);
6364
6409
  // Auto-fill identity fields. Without this, a caller who omits author/agent
6365
6410
  // creates a schema-invalid record that is silently dropped on read and
6366
6411
  // GC'd from disk on the next mutation.
@@ -6392,7 +6437,11 @@ async function _executeMcpToolCallInner(payload) {
6392
6437
  return {
6393
6438
  response: toolResponse({
6394
6439
  content: [{ type: 'text', text: `✔ created ${entity} ${result.id}` }],
6395
- structuredContent: { ...result },
6440
+ structuredContent: {
6441
+ ...result,
6442
+ resolved_project: targetScope.resolved_project,
6443
+ active_source: targetScope.active_source,
6444
+ },
6396
6445
  }),
6397
6446
  };
6398
6447
  }
@@ -6406,13 +6455,18 @@ async function _executeMcpToolCallInner(payload) {
6406
6455
  const id = String(args.id ?? '');
6407
6456
  const patch = (args.patch ?? {});
6408
6457
  const targetCwd = resolveProjectCwd(args.project, cwd);
6458
+ const targetScope = scopeMetadataForTarget(args, targetCwd, scopeInfo);
6409
6459
  const { agent_name, agent_id } = resolveCanonicalAuthor(args, cwd, connectionSessionId);
6410
6460
  const result = updateEntity(entity, id, patch, targetCwd);
6411
6461
  appendAuditEntry({ actor: agent_name, ...(agent_id ? { actor_id: agent_id } : {}), action: 'update', item_id: id, item_type: entity }, targetCwd);
6412
6462
  return {
6413
6463
  response: toolResponse({
6414
6464
  content: [{ type: 'text', text: `✔ updated ${entity} ${id}` }],
6415
- structuredContent: { ...result },
6465
+ structuredContent: {
6466
+ ...result,
6467
+ resolved_project: targetScope.resolved_project,
6468
+ active_source: targetScope.active_source,
6469
+ },
6416
6470
  }),
6417
6471
  };
6418
6472
  }
@@ -6426,13 +6480,18 @@ async function _executeMcpToolCallInner(payload) {
6426
6480
  const id = String(args.id ?? '');
6427
6481
  const purge = args.purge === true;
6428
6482
  const targetCwd = resolveProjectCwd(args.project, cwd);
6483
+ const targetScope = scopeMetadataForTarget(args, targetCwd, scopeInfo);
6429
6484
  const { agent_name, agent_id } = resolveCanonicalAuthor(args, cwd, connectionSessionId);
6430
6485
  const result = removeEntity(entity, id, targetCwd, purge);
6431
6486
  appendAuditEntry({ actor: agent_name, ...(agent_id ? { actor_id: agent_id } : {}), action: 'delete', item_id: id, item_type: entity, reason: purge ? 'purged' : 'archived' }, targetCwd);
6432
6487
  return {
6433
6488
  response: toolResponse({
6434
6489
  content: [{ type: 'text', text: `✔ removed ${entity} ${id}` }],
6435
- structuredContent: { ...result },
6490
+ structuredContent: {
6491
+ ...result,
6492
+ resolved_project: targetScope.resolved_project,
6493
+ active_source: targetScope.active_source,
6494
+ },
6436
6495
  }),
6437
6496
  };
6438
6497
  }
@@ -6454,13 +6513,18 @@ async function _executeMcpToolCallInner(payload) {
6454
6513
  const to = String(args.to ?? '');
6455
6514
  const reason = args.reason;
6456
6515
  const targetCwd = resolveProjectCwd(args.project, cwd);
6516
+ const targetScope = scopeMetadataForTarget(args, targetCwd, scopeInfo);
6457
6517
  const { agent_name, agent_id } = resolveCanonicalAuthor(args, cwd, connectionSessionId);
6458
6518
  const result = transitionEntity(entity, id, to, targetCwd, reason);
6459
6519
  appendAuditEntry({ actor: agent_name, ...(agent_id ? { actor_id: agent_id } : {}), action: 'update', item_id: id, item_type: entity, reason: `transition ${result.from} → ${to}${reason ? ` (${reason})` : ''}` }, targetCwd);
6460
6520
  return {
6461
6521
  response: toolResponse({
6462
6522
  content: [{ type: 'text', text: `✔ ${entity} ${id}: ${result.from} → ${to}` }],
6463
- structuredContent: { ...result },
6523
+ structuredContent: {
6524
+ ...result,
6525
+ resolved_project: targetScope.resolved_project,
6526
+ active_source: targetScope.active_source,
6527
+ },
6464
6528
  }),
6465
6529
  };
6466
6530
  }
@@ -6504,9 +6568,10 @@ async function _executeMcpToolCallInner(payload) {
6504
6568
  */
6505
6569
  export async function executeMcpToolCall(payload) {
6506
6570
  const baseCwd = payload.cwd;
6507
- const cwd = payload.name === 'bclaw_switch'
6508
- ? baseCwd
6509
- : resolveEffectiveCwd({ baseCwd });
6571
+ const effective = payload.name === 'bclaw_switch'
6572
+ ? { cwd: baseCwd, active_source: 'cwd', resolved_project: undefined }
6573
+ : resolveEffectiveCwdInfo({ baseCwd, sessionId: payload.connectionSessionId });
6574
+ const cwd = effective.cwd;
6510
6575
  const envClaimId = process.env.BRAINCLAW_CLAIM_ID?.trim() || undefined;
6511
6576
  // ── Auto-session ────────────────────────────────────────────────────────────
6512
6577
  let autoSessionId;
@@ -6564,6 +6629,7 @@ export async function executeMcpToolCall(payload) {
6564
6629
  ...payload,
6565
6630
  cwd,
6566
6631
  connectionSessionId: effectiveConnectionSessionId,
6632
+ effectiveScope: effective,
6567
6633
  });
6568
6634
  // Apply legacy deprecation warning uniformly (Phase 3 slice 3g). Read tools
6569
6635
  // already get it at line 2560; write tools historically did not. This
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { loadActiveProject, saveActiveProject, clearActiveProject } from '../core/active-project.js';
3
- import { buildOperationalIdentity, loadCurrentSession, saveCurrentSession } from '../core/identity.js';
3
+ import { buildOperationalIdentity, loadCurrentSession, loadSessionById, saveCurrentSession } from '../core/identity.js';
4
4
  import { memoryExists } from '../core/io.js';
5
5
  import { resolveProjectRef } from '../core/store-resolution.js';
6
6
  import { resolveCrossProjectLinks, resolveProjectCwd } from '../core/cross-project.js';
@@ -44,10 +44,28 @@ export function switchProject(projectRef, options = {}) {
44
44
  catch { /* name is optional */ }
45
45
  const now = new Date().toISOString();
46
46
  const sessionOnly = options.sessionOnly ?? true;
47
- let session = loadCurrentSession(cwd);
47
+ let session = options.sessionId ? loadSessionById(options.sessionId, cwd) : loadCurrentSession(cwd);
48
48
  if (!session && sessionOnly) {
49
- buildOperationalIdentity(undefined, cwd, { persistImplicitSession: true });
50
- session = loadCurrentSession(cwd);
49
+ if (options.sessionId) {
50
+ const identity = buildOperationalIdentity(undefined, cwd, {
51
+ sessionId: options.sessionId,
52
+ persistImplicitSession: false,
53
+ });
54
+ saveCurrentSession({
55
+ session_id: options.sessionId,
56
+ started_at: now,
57
+ last_seen_at: now,
58
+ agent: identity.agent,
59
+ agent_id: identity.agent_id,
60
+ host_id: identity.host_id,
61
+ user: process.env.USER || process.env.USERNAME || undefined,
62
+ pid: process.pid,
63
+ }, cwd);
64
+ }
65
+ else {
66
+ buildOperationalIdentity(undefined, cwd, { persistImplicitSession: true });
67
+ }
68
+ session = options.sessionId ? loadSessionById(options.sessionId, cwd) : loadCurrentSession(cwd);
51
69
  }
52
70
  if (session && sessionOnly) {
53
71
  saveCurrentSession({
@@ -78,11 +96,14 @@ export function switchProject(projectRef, options = {}) {
78
96
  * List available projects in the workspace.
79
97
  */
80
98
  export function listAvailableProjects(cwd) {
99
+ return listAvailableProjectsForSession(cwd);
100
+ }
101
+ export function listAvailableProjectsForSession(cwd, sessionId) {
81
102
  const wsRoot = findOutermostWorkspaceRoot(cwd ?? process.cwd());
82
103
  if (!wsRoot) {
83
104
  throw new Error('No brainclaw workspace found.');
84
105
  }
85
- const sessionActive = loadCurrentSession(cwd)?.active_project;
106
+ const sessionActive = (sessionId ? loadSessionById(sessionId, cwd) : loadCurrentSession(cwd))?.active_project;
86
107
  const globalActive = loadActiveProject(wsRoot);
87
108
  const active = sessionActive ?? globalActive;
88
109
  const activeSource = sessionActive ? 'session' : globalActive ? 'global' : 'none';
@@ -8,7 +8,7 @@ import { generateAgentReleaseNotes } from './release-notes.js';
8
8
  export function runVersion(options = {}) {
9
9
  const cwd = options.cwd ?? process.cwd();
10
10
  const initialized = memoryExists(cwd);
11
- let config = initialized ? loadConfig(cwd) : undefined;
11
+ const config = initialized ? loadConfig(cwd) : undefined;
12
12
  let publishedLocalRelease;
13
13
  if (options.publishLocal) {
14
14
  if (!initialized || !config) {
@@ -23,9 +23,21 @@ const AGENT_ALIASES = {
23
23
  'vibe': 'mistral-vibe',
24
24
  'hermes-agent': 'hermes',
25
25
  };
26
- /** Resolve an alias to its canonical agent name, or return the input unchanged. */
26
+ /**
27
+ * Resolve an alias to its canonical agent name (case-insensitive).
28
+ *
29
+ * Agent names are case-insensitive: every canonical profile key and every alias
30
+ * is lowercase, so we trim + lowercase the input before resolving. This is the
31
+ * single normalization point — registry, messaging, spawn-check and the
32
+ * coordinate/dispatch pre-flight all route through here, so a target like
33
+ * "Codex" or "Gemini" resolves identically to "codex" / "gemini". Without it the
34
+ * dispatch pre-flight collapsed an unresolved name into "no CLI spawn template
35
+ * (IDE-only?)" and silently dropped the reviewer (github-copilot/Gemini hit this
36
+ * passing targetAgents=["Codex"]).
37
+ */
27
38
  export function resolveAgentAlias(name) {
28
- return AGENT_ALIASES[name] ?? name;
39
+ const key = name.trim().toLowerCase();
40
+ return AGENT_ALIASES[key] ?? key;
29
41
  }
30
42
  const PROFILES = {
31
43
  // --- Code agents (interactive, IDE-driven) ---
@@ -314,7 +326,10 @@ const _customProfiles = new Map();
314
326
  * Custom profiles take precedence over DEFAULT_CAPABILITY_PROFILES on lookup.
315
327
  */
316
328
  export function registerCapabilityProfile(profile) {
317
- _customProfiles.set(profile.name, profile);
329
+ // Key by the normalized (lowercased) name so lookups through the
330
+ // case-insensitive resolveAgentAlias match regardless of the casing the
331
+ // registrant used.
332
+ _customProfiles.set(profile.name.trim().toLowerCase(), profile);
318
333
  }
319
334
  /**
320
335
  * Get the capability profile for an agent by name.
@@ -667,7 +682,7 @@ export function resolveBriefMode(agentName) {
667
682
  * pln#528 — capability matrix DERIVED from the spawn template, so it stays in
668
683
  * sync with how each agent is actually invoked (no per-profile duplication).
669
684
  *
670
- * The motivating reality (debrief LeaseUp): codex is spawned with
685
+ * The motivating reality (a cross-project field debrief): codex is spawned with
671
686
  * `--sandbox workspace-write`, which (a) does NOT wire the brainclaw MCP server
672
687
  * and (b) puts `.git` outside the sandbox root — so a sandboxed worker can
673
688
  * neither call `bclaw_*` nor `git commit`, regardless of the profile's nominal
@@ -13,46 +13,46 @@ export { resetMcpCommandCache };
13
13
  export const BRAINCLAW_SECTION_START = '<!-- brainclaw:start -->';
14
14
  export const BRAINCLAW_SECTION_END = '<!-- brainclaw:end -->';
15
15
  export function buildBrainclawSection(storageDir) {
16
- return `${BRAINCLAW_SECTION_START}
17
- ## Brainclaw — shared project memory
18
-
19
- This project uses brainclaw for shared coordination between humans and agents.
20
-
21
- ### Session start (required)
22
-
23
- 1. Run \`brainclaw context\` to load shared state (constraints, decisions, traps, plans, handoffs)
24
- 2. Check **Your open work** for active claims and in-progress plans assigned to you
25
- 3. Respect active claims from other agents — check \`brainclaw claim list\` before editing a claimed scope
26
-
27
- ### Before finishing (required)
28
-
29
- 1. Release claims you opened: \`brainclaw claim release <id>\` — or \`brainclaw session-end --auto-release\`
30
- 2. Update completed plan items: \`brainclaw plan update <id> --status done\`
31
-
32
- ### Recording work
33
-
34
- \`\`\`bash
35
- brainclaw memory create decision "<text>" # record a decision
36
- brainclaw memory create constraint "<text>" # record an active constraint
37
- brainclaw memory create trap "<text>" # record a known trap
38
- brainclaw claim create "<text>" --scope <path> # claim a scope before editing
39
- brainclaw plan create "<text>" # add a shared work item
40
- \`\`\`
41
-
42
- Memory is stored in \`${storageDir}/\`. Run \`brainclaw doctor\` to verify health.
16
+ return `${BRAINCLAW_SECTION_START}
17
+ ## Brainclaw — shared project memory
18
+
19
+ This project uses brainclaw for shared coordination between humans and agents.
20
+
21
+ ### Session start (required)
22
+
23
+ 1. Run \`brainclaw context\` to load shared state (constraints, decisions, traps, plans, handoffs)
24
+ 2. Check **Your open work** for active claims and in-progress plans assigned to you
25
+ 3. Respect active claims from other agents — check \`brainclaw claim list\` before editing a claimed scope
26
+
27
+ ### Before finishing (required)
28
+
29
+ 1. Release claims you opened: \`brainclaw claim release <id>\` — or \`brainclaw session-end --auto-release\`
30
+ 2. Update completed plan items: \`brainclaw plan update <id> --status done\`
31
+
32
+ ### Recording work
33
+
34
+ \`\`\`bash
35
+ brainclaw memory create decision "<text>" # record a decision
36
+ brainclaw memory create constraint "<text>" # record an active constraint
37
+ brainclaw memory create trap "<text>" # record a known trap
38
+ brainclaw claim create "<text>" --scope <path> # claim a scope before editing
39
+ brainclaw plan create "<text>" # add a shared work item
40
+ \`\`\`
41
+
42
+ Memory is stored in \`${storageDir}/\`. Run \`brainclaw doctor\` to verify health.
43
43
  ${BRAINCLAW_SECTION_END}`;
44
44
  }
45
45
  export function buildHygieneSection() {
46
- return `## Brainclaw — required coordination
47
-
48
- Before starting work:
49
- 1. Run \`brainclaw context\` (or \`brainclaw context --json\`) to load shared memory
50
- 2. Run \`brainclaw claim list\` — do not edit a file claimed by another agent
51
- 3. Create a plan for significant work: \`brainclaw plan create "<description>"\`
52
- 4. Claim files you will modify: \`brainclaw claim create "<description>" --scope <path>\`
53
-
54
- Before finishing:
55
- 1. Run \`brainclaw session-end --auto-release\` — releases claims and updates plans
46
+ return `## Brainclaw — required coordination
47
+
48
+ Before starting work:
49
+ 1. Run \`brainclaw context\` (or \`brainclaw context --json\`) to load shared memory
50
+ 2. Run \`brainclaw claim list\` — do not edit a file claimed by another agent
51
+ 3. Create a plan for significant work: \`brainclaw plan create "<description>"\`
52
+ 4. Claim files you will modify: \`brainclaw claim create "<description>" --scope <path>\`
53
+
54
+ Before finishing:
55
+ 1. Run \`brainclaw session-end --auto-release\` — releases claims and updates plans
56
56
  2. Or manually: \`brainclaw claim release <id>\` and \`brainclaw plan update <id> --status done\``;
57
57
  }
58
58
  export function hasBrainclawSection(content) {
@@ -727,13 +727,13 @@ export function describeAutoConfigWrite(result) {
727
727
  return `✔ ${verb} ${result.label} at ${displayPath}`;
728
728
  }
729
729
  export function buildClaudeCodeCommandText() {
730
- return `Load brainclaw project memory and prepare for coordinated work.
731
-
732
- Steps:
733
- 1. Run \`brainclaw context --json\` — load constraints, decisions, traps, plans, handoffs
734
- 2. Run \`brainclaw claim list\` — check what files other agents have claimed
735
- 3. Before editing any file, run \`brainclaw claim create "<description>" --scope <path>\`
736
- 4. Before finishing, run \`brainclaw session-end --auto-release\`
730
+ return `Load brainclaw project memory and prepare for coordinated work.
731
+
732
+ Steps:
733
+ 1. Run \`brainclaw context --json\` — load constraints, decisions, traps, plans, handoffs
734
+ 2. Run \`brainclaw claim list\` — check what files other agents have claimed
735
+ 3. Before editing any file, run \`brainclaw claim create "<description>" --scope <path>\`
736
+ 4. Before finishing, run \`brainclaw session-end --auto-release\`
737
737
  `;
738
738
  }
739
739
  export function ensureClineMcpConfig(cwd) {
@@ -799,34 +799,34 @@ export function ensureWindsurfMcpConfig(homeDir) {
799
799
  */
800
800
  export function ensureWindsurfModernRules(cwd) {
801
801
  const filePath = path.join(cwd, WINDSURF_MODERN_RULES_RELATIVE_PATH);
802
- const content = `# Brainclaw coordination rules
803
-
804
- Brainclaw is the shared coordination layer. Use its MCP facades first — the CLI is only a fallback when MCP is unavailable.
805
-
806
- ## Session start
807
-
808
- Call \`bclaw_work(intent)\`. It loads memory (constraints, decisions, traps, plans, handoffs), resolves the claim, and starts a session in a single call.
809
-
810
- - \`bclaw_work(intent: "resume")\` — continue existing work (auto-surfaces the context diff).
811
- - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` — start new work and claim the scope.
812
- - \`bclaw_work(intent: "consult")\` — read-only context without claiming.
813
-
814
- ## During work
815
-
816
- - Mark plan steps done: \`bclaw_complete_step(planId, stepId)\`.
817
- - Read the inbox: \`bclaw_read_inbox\`.
818
- - Record notes, decisions, traps: \`bclaw_write_note\`, \`bclaw_create(entity, data)\`.
819
-
820
- ## To coordinate with other agents
821
-
822
- \`bclaw_coordinate(intent)\` — \`assign\`, \`consult\`, \`review\`, or \`reroute\`.
823
-
824
- ## Before finishing
825
-
826
- - Release your claims: \`bclaw_release_claim(id)\`.
827
- - Close the session: \`bclaw_session_end\` (auto-releases remaining claims).
828
-
829
- CLI fallback only when MCP is unavailable: \`brainclaw context\` / \`brainclaw session-end --auto-release\`.
802
+ const content = `# Brainclaw coordination rules
803
+
804
+ Brainclaw is the shared coordination layer. Use its MCP facades first — the CLI is only a fallback when MCP is unavailable.
805
+
806
+ ## Session start
807
+
808
+ Call \`bclaw_work(intent)\`. It loads memory (constraints, decisions, traps, plans, handoffs), resolves the claim, and starts a session in a single call.
809
+
810
+ - \`bclaw_work(intent: "resume")\` — continue existing work (auto-surfaces the context diff).
811
+ - \`bclaw_work(intent: "execute", scope: "<path>", task: "<text>")\` — start new work and claim the scope.
812
+ - \`bclaw_work(intent: "consult")\` — read-only context without claiming.
813
+
814
+ ## During work
815
+
816
+ - Mark plan steps done: \`bclaw_complete_step(planId, stepId)\`.
817
+ - Read the inbox: \`bclaw_read_inbox\`.
818
+ - Record notes, decisions, traps: \`bclaw_write_note\`, \`bclaw_create(entity, data)\`.
819
+
820
+ ## To coordinate with other agents
821
+
822
+ \`bclaw_coordinate(intent)\` — \`assign\`, \`consult\`, \`review\`, or \`reroute\`.
823
+
824
+ ## Before finishing
825
+
826
+ - Release your claims: \`bclaw_release_claim(id)\`.
827
+ - Close the session: \`bclaw_session_end\` (auto-releases remaining claims).
828
+
829
+ CLI fallback only when MCP is unavailable: \`brainclaw context\` / \`brainclaw session-end --auto-release\`.
830
830
  `;
831
831
  const { created, updated } = writeTextFileIfChanged(filePath, content);
832
832
  return {
@@ -840,22 +840,22 @@ CLI fallback only when MCP is unavailable: \`brainclaw context\` / \`brainclaw s
840
840
  }
841
841
  export function ensureCopilotSkill(cwd) {
842
842
  const filePath = path.join(cwd, '.github', 'skills', 'brainclaw-context', 'SKILL.md');
843
- const content = `---
844
- name: brainclaw-context
845
- description: "Use this skill when you need the latest Brainclaw context, active plans, constraints, traps, or handoffs before coding. Trigger phrases: refresh project memory, load brainclaw context, inspect active plans, inspect constraints."
846
- ---
847
-
848
- # Brainclaw Context
849
-
850
- Fetch live project memory before significant edits. Prefer the Brainclaw MCP facade; use the CLI only as a fallback when MCP is unavailable.
851
-
852
- ## Steps
853
-
854
- 1. Call \`bclaw_work(intent: "resume")\` to continue existing work, or \`bclaw_work(intent: "consult")\` for read-only context. The response contains active plans, constraints, decisions, traps, and handoffs.
855
- 2. Prefer Brainclaw state over stale assumptions from older instructions or prior sessions.
856
- 3. Coordinate with other agents via \`bclaw_coordinate(intent)\` (\`assign\`, \`consult\`, \`review\`).
857
-
858
- CLI fallback: \`brainclaw context --json\` if the MCP server is not reachable.
843
+ const content = `---
844
+ name: brainclaw-context
845
+ description: "Use this skill when you need the latest Brainclaw context, active plans, constraints, traps, or handoffs before coding. Trigger phrases: refresh project memory, load brainclaw context, inspect active plans, inspect constraints."
846
+ ---
847
+
848
+ # Brainclaw Context
849
+
850
+ Fetch live project memory before significant edits. Prefer the Brainclaw MCP facade; use the CLI only as a fallback when MCP is unavailable.
851
+
852
+ ## Steps
853
+
854
+ 1. Call \`bclaw_work(intent: "resume")\` to continue existing work, or \`bclaw_work(intent: "consult")\` for read-only context. The response contains active plans, constraints, decisions, traps, and handoffs.
855
+ 2. Prefer Brainclaw state over stale assumptions from older instructions or prior sessions.
856
+ 3. Coordinate with other agents via \`bclaw_coordinate(intent)\` (\`assign\`, \`consult\`, \`review\`).
857
+
858
+ CLI fallback: \`brainclaw context --json\` if the MCP server is not reachable.
859
859
  `;
860
860
  const { created, updated } = writeTextFileIfChanged(filePath, content);
861
861
  return {
@@ -877,24 +877,24 @@ CLI fallback: \`brainclaw context --json\` if the MCP server is not reachable.
877
877
  */
878
878
  export function ensureUniversalBrainclawSkill(cwd) {
879
879
  const filePath = path.join(cwd, UNIVERSAL_SKILL_RELATIVE_PATH);
880
- const content = `---
881
- name: brainclaw
882
- description: 'Load and act on Brainclaw project memory, active claims, plans, traps, and handoffs before code changes. Trigger: refresh brainclaw context, check active claims, load coordination state.'
883
- allowed-tools: 'Read Bash(npx brainclaw:*)'
884
- ---
885
-
886
- # Brainclaw
887
-
888
- Load the shared coordination state before any significant code change. Prefer the Brainclaw MCP facade; the CLI is a fallback when MCP is not reachable.
889
-
890
- ## Steps
891
-
892
- 1. Call \`bclaw_work(intent)\` — \`resume\` to continue existing work, \`execute\` to claim a new scope, or \`consult\` for read-only context. The response gives you memory, active claims, plans, traps, and handoffs.
893
- 2. Respect active claims from other agents reported in the response; do not edit a claimed scope unless you own the claim.
894
- 3. Use \`bclaw_coordinate(intent)\` to assign, consult, or review other agents when needed.
895
- 4. When done, call \`bclaw_session_end\` (auto-releases your remaining claims).
896
-
897
- CLI fallback only: \`brainclaw context --json\` / \`brainclaw claim create\` / \`brainclaw session-end --auto-release\` if the MCP server is unavailable.
880
+ const content = `---
881
+ name: brainclaw
882
+ description: 'Load and act on Brainclaw project memory, active claims, plans, traps, and handoffs before code changes. Trigger: refresh brainclaw context, check active claims, load coordination state.'
883
+ allowed-tools: 'Read Bash(npx brainclaw:*)'
884
+ ---
885
+
886
+ # Brainclaw
887
+
888
+ Load the shared coordination state before any significant code change. Prefer the Brainclaw MCP facade; the CLI is a fallback when MCP is not reachable.
889
+
890
+ ## Steps
891
+
892
+ 1. Call \`bclaw_work(intent)\` — \`resume\` to continue existing work, \`execute\` to claim a new scope, or \`consult\` for read-only context. The response gives you memory, active claims, plans, traps, and handoffs.
893
+ 2. Respect active claims from other agents reported in the response; do not edit a claimed scope unless you own the claim.
894
+ 3. Use \`bclaw_coordinate(intent)\` to assign, consult, or review other agents when needed.
895
+ 4. When done, call \`bclaw_session_end\` (auto-releases your remaining claims).
896
+
897
+ CLI fallback only: \`brainclaw context --json\` / \`brainclaw claim create\` / \`brainclaw session-end --auto-release\` if the MCP server is unavailable.
898
898
  `;
899
899
  const { created, updated } = writeTextFileIfChanged(filePath, content);
900
900
  return {
@@ -1005,19 +1005,19 @@ export function ensureVscodeExtensionRecommendation(cwd) {
1005
1005
  }
1006
1006
  export function ensureCursorMdc(cwd) {
1007
1007
  const filePath = path.join(cwd, '.cursor', 'rules', 'brainclaw-mcp-shim.mdc');
1008
- const content = `---
1009
- description: Use this rule when work depends on live Brainclaw memory or active project rules.
1010
- globs: "**/*"
1011
- alwaysApply: true
1012
- ---
1013
-
1014
- Brainclaw is the shared coordination layer. Call the MCP facade first; the CLI is only a fallback when MCP is not reachable.
1015
-
1016
- Before significant edits or when asked about project rules, call \`bclaw_work(intent: "consult")\` (or \`"resume"\` if continuing a task) via the Brainclaw MCP server. The response carries active claims, in-progress plans, constraints, decisions, traps, and handoffs.
1017
-
1018
- If the response lists active claims or in-progress plans, follow them before editing. Use \`bclaw_coordinate(intent)\` to dispatch, consult, or review other agents.
1019
-
1020
- CLI fallback only when MCP is unavailable: \`brainclaw context --json\`.
1008
+ const content = `---
1009
+ description: Use this rule when work depends on live Brainclaw memory or active project rules.
1010
+ globs: "**/*"
1011
+ alwaysApply: true
1012
+ ---
1013
+
1014
+ Brainclaw is the shared coordination layer. Call the MCP facade first; the CLI is only a fallback when MCP is not reachable.
1015
+
1016
+ Before significant edits or when asked about project rules, call \`bclaw_work(intent: "consult")\` (or \`"resume"\` if continuing a task) via the Brainclaw MCP server. The response carries active claims, in-progress plans, constraints, decisions, traps, and handoffs.
1017
+
1018
+ If the response lists active claims or in-progress plans, follow them before editing. Use \`bclaw_coordinate(intent)\` to dispatch, consult, or review other agents.
1019
+
1020
+ CLI fallback only when MCP is unavailable: \`brainclaw context --json\`.
1021
1021
  `;
1022
1022
  const { created, updated } = writeTextFileIfChanged(filePath, content);
1023
1023
  return {