brainclaw 0.19.14 → 0.20.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/README.md CHANGED
@@ -66,18 +66,20 @@ If you are documenting or integrating an agent workflow, treat MCP as the primar
66
66
 
67
67
  brainclaw is designed to sit alongside the coding agents teams are already using, not behind a separate hosted control plane.
68
68
 
69
- | Logo | Agent | Brainclaw fit | Best use today |
69
+ | Logo | Agent | Integration | What brainclaw configures |
70
70
  |---|---|---|---|
71
- | [![Claude Code](https://img.shields.io/badge/Claude_Code-111111?logo=anthropic&logoColor=white)](https://github.com/anthropics/claude-code) | **[Claude Code](https://github.com/anthropics/claude-code)** | Best fit | full agent workflow with MCP, instructions, commands, and session hooks |
72
- | [![Codex](https://img.shields.io/badge/Codex-111111?logo=openai&logoColor=white)](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** | Strong fit | agent-first repo coordination with explicit plans, claims, and handoffs |
73
- | [![Cursor](https://img.shields.io/badge/Cursor-1F2430?logo=cursor&logoColor=white)](https://cursor.com/en-US) | **[Cursor](https://cursor.com/en-US)** | Strong fit | repo-native agent workflows with rules plus MCP |
74
- | [![OpenCode](https://img.shields.io/badge/OpenCode-0F172A?logoColor=white)](https://github.com/opencode-ai/opencode) | **[OpenCode](https://github.com/opencode-ai/opencode)** | Strong fit | simple agent bootstrap with `AGENTS.md` plus workspace MCP |
75
- | [![Windsurf](https://img.shields.io/badge/Windsurf-0B1220?logo=codeium&logoColor=white)](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** | Good fit | guided agent workflows with instructions, hooks, and MCP |
76
- | [![Roo](https://img.shields.io/badge/Roo-7C3AED?logoColor=white)](https://github.com/RooCodeInc/Roo-Code) | **[Roo](https://github.com/RooCodeInc/Roo-Code)** | Good fit | workspace agent coordination with rules plus MCP |
77
- | [![Continue](https://img.shields.io/badge/Continue-2563EB?logoColor=white)](https://github.com/continuedev/continue) | **[Continue](https://github.com/continuedev/continue)** | Good fit | editor-based agent workflows with MCP context access |
78
- | [![Gemini CLI](https://img.shields.io/badge/Gemini_CLI-1A73E8?logo=googlegemini&logoColor=white)](https://github.com/google-gemini/gemini-cli) | **[Antigravity / Gemini CLI](https://github.com/google-gemini/gemini-cli)** | Promising fit | local agent workflows with `GEMINI.md` plus MCP |
79
- | [![Cline](https://img.shields.io/badge/Cline-0F766E?logoColor=white)](https://github.com/cline/cline) | **[Cline](https://github.com/cline/cline)** | Functional fit | lightweight agent use through rules plus MCP |
80
- | [![GitHub Copilot](https://img.shields.io/badge/GitHub_Copilot-181717?logo=githubcopilot&logoColor=white)](https://github.com/features/copilot) | **[GitHub Copilot](https://github.com/features/copilot)** | Supported fit | project awareness and shared local instructions with lighter workflow control |
71
+ | [![Claude Code](https://img.shields.io/badge/Claude_Code-111111?logo=anthropic&logoColor=white)](https://github.com/anthropics/claude-code) | **[Claude Code](https://github.com/anthropics/claude-code)** | Full | MCP + CLAUDE.md + hooks + permissions + /brainclaw command |
72
+ | [![Codex](https://img.shields.io/badge/Codex-111111?logo=openai&logoColor=white)](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** | Standard | MCP + AGENTS.md |
73
+ | [![Cursor](https://img.shields.io/badge/Cursor-1F2430?logo=cursor&logoColor=white)](https://cursor.com/en-US) | **[Cursor](https://cursor.com/en-US)** | Standard | MCP + .cursor/rules/ + alwaysApply MDC |
74
+ | [![OpenCode](https://img.shields.io/badge/OpenCode-0F172A?logoColor=white)](https://github.com/opencode-ai/opencode) | **[OpenCode](https://github.com/opencode-ai/opencode)** | Standard | MCP + AGENTS.md |
75
+ | [![Windsurf](https://img.shields.io/badge/Windsurf-0B1220?logo=codeium&logoColor=white)](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** | Standard | MCP + .windsurfrules session trigger |
76
+ | [![Roo](https://img.shields.io/badge/Roo-7C3AED?logoColor=white)](https://github.com/RooCodeInc/Roo-Code) | **[Roo](https://github.com/RooCodeInc/Roo-Code)** | Standard | MCP (auto-approve) + .roo/rules/ |
77
+ | [![Cline](https://img.shields.io/badge/Cline-0F766E?logoColor=white)](https://github.com/cline/cline) | **[Cline](https://github.com/cline/cline)** | Standard | MCP (auto-approve) + .clinerules/ |
78
+ | [![Continue](https://img.shields.io/badge/Continue-2563EB?logoColor=white)](https://github.com/continuedev/continue) | **[Continue](https://github.com/continuedev/continue)** | Standard | MCP + .continue/rules/ |
79
+ | [![Gemini CLI](https://img.shields.io/badge/Gemini_CLI-1A73E8?logo=googlegemini&logoColor=white)](https://github.com/google-gemini/gemini-cli) | **[Antigravity / Gemini CLI](https://github.com/google-gemini/gemini-cli)** | Standard | MCP + GEMINI.md |
80
+ | [![GitHub Copilot](https://img.shields.io/badge/GitHub_Copilot-181717?logo=githubcopilot&logoColor=white)](https://github.com/features/copilot) | **[GitHub Copilot](https://github.com/features/copilot)** | Limited | copilot-instructions.md + brainclaw-context skill |
81
+
82
+ **Full** = MCP + hooks + auto-approve (context injected every prompt). **Standard** = MCP + instruction file (agent must call tools explicitly). **Limited** = no MCP (static instruction file is the only source of context).
81
83
 
82
84
  brainclaw is most effective today when one agent works at a time in a given checkout and the next agent resumes from shared context, claims, and handoffs.
83
85
 
@@ -303,6 +305,19 @@ npm run test:coverage # with coverage report
303
305
 
304
306
  ## Changelog
305
307
 
308
+ ### v0.20.0
309
+
310
+ - **Onboarding rework** : nouveau parcours d'installation en 2-3 étapes au lieu de 4, avec choix explicites (type de projet, topologie mémoire) au lieu de termes techniques
311
+ - **Agent capability profiles** : chaque agent (10 supportés) a un profil de capacité (MCP, hooks, auto-approve, skills, rules) qui détermine le contenu de ses fichiers d'instructions
312
+ - **Templates adaptatifs** : les fichiers d'instructions (CLAUDE.md, AGENTS.md, etc.) sont générés selon 3 tiers — Full (léger, hooks font le reste), Standard (directif, top traps inclus), Limited (riche, tout en statique)
313
+ - **Séparation core/run** : les fichiers statiques ne contiennent plus que le protocole, les contraintes et instructions. Les traps, plans, decisions et claims sont exclusivement dans le contexte dynamique MCP
314
+ - **Bootstrap enrichi** : scan des workflows CI/CD, ADR, CONTRIBUTING, Docker, .env.example, branches actives et tags git
315
+ - **Quick setup MCP** : `bclaw_setup` détecte le repo courant et propose un init rapide au lieu de forcer le scan multi-repo
316
+ - **`brainclaw uninstall`** : nouvelle commande pour retirer brainclaw d'un projet (`--project`) ou d'une machine (`--machine`)
317
+ - **Traps machine-scoped** : nouveau champ `platform_scope` sur les traps — les traps machine ne polluent plus les fichiers d'instructions statiques
318
+ - **Init sans setup préalable** : `brainclaw init` crée automatiquement le user store si absent, plus besoin de `brainclaw setup` d'abord
319
+ - **Docs réécrites** : intégrations, quickstart et README avec niveaux Full/Standard/Limited et matrice agent factuelle
320
+
306
321
  ### v0.9.10
307
322
 
308
323
  - **OpenCode** : détection et auto-config MCP workspace via `opencode.json`; l'export réutilise `AGENTS.md`
package/dist/cli.js CHANGED
@@ -463,6 +463,17 @@ program
463
463
  .action((options) => {
464
464
  runVersion(options);
465
465
  });
466
+ // --- uninstall ---
467
+ import { runUninstall } from './commands/uninstall.js';
468
+ program
469
+ .command('uninstall')
470
+ .description('Remove brainclaw from a project and/or machine')
471
+ .option('--project', 'Remove brainclaw from the current project (.brainclaw/, agent files, configs)')
472
+ .option('--machine', 'Remove brainclaw global config (~/.brainclaw/)')
473
+ .option('-y, --yes', 'Skip confirmation prompts')
474
+ .action(async (options) => {
475
+ await runUninstall(options);
476
+ });
466
477
  // --- rebuild ---
467
478
  program
468
479
  .command('rebuild')
@@ -3,6 +3,7 @@ import { buildContext, renderContextMarkdown, renderContextPromptTemplate } from
3
3
  import { writeContextMarker } from '../core/freshness.js';
4
4
  import { nowISO } from '../core/ids.js';
5
5
  import { logger } from '../core/logger.js';
6
+ import { resolveContextStoreCwd } from '../core/store-resolution.js';
6
7
  export function runContext(options = {}) {
7
8
  const cwd = options.cwd ?? process.cwd();
8
9
  if (!memoryExists(cwd)) {
@@ -17,6 +18,7 @@ export function runContext(options = {}) {
17
18
  }
18
19
  return;
19
20
  }
21
+ const contextCwd = resolveContextStoreCwd(cwd, options.for);
20
22
  const result = buildContext({
21
23
  target: options.for,
22
24
  project: options.project,
@@ -43,7 +45,7 @@ export function runContext(options = {}) {
43
45
  else {
44
46
  console.log(renderContextMarkdown(result, options.explain));
45
47
  }
46
- writeLastContextMarker(result, options, cwd);
48
+ writeLastContextMarker(result, options, contextCwd);
47
49
  }
48
50
  function writeLastContextMarker(result, options, cwd) {
49
51
  try {
@@ -8,6 +8,9 @@ import { resolveInstructions, loadInstructions } from '../core/instructions.js';
8
8
  import { detectAiAgent } from '../core/ai-agent-detection.js';
9
9
  import { resolveExportTarget, resolveExportTargetByFormat, writeExportFile, buildHygieneSection, describeAutoConfigWrite, writeExportCompanionFiles, collectExportGitignoreEntries, ensureGitignoreEntries, } from '../core/agent-files.js';
10
10
  import { logger } from '../core/logger.js';
11
+ import { getAgentCapabilityProfile } from '../core/agent-capability.js';
12
+ import { renderBrainclawSection } from '../core/instruction-templates.js';
13
+ import { getInstalledBrainclawVersion } from '../core/brainclaw-version.js';
11
14
  export function runExport(options) {
12
15
  const cwd = options.cwd ?? process.cwd();
13
16
  if (!memoryExists(cwd)) {
@@ -125,7 +128,28 @@ function declareAgentIntegrationFromTarget(cwd, agentName, declarationSource) {
125
128
  saveConfig(config, cwd);
126
129
  }
127
130
  }
131
+ function formatToAgentName(format) {
132
+ const map = {
133
+ 'claude-md': 'claude-code',
134
+ 'cursor-rules': 'cursor',
135
+ 'copilot-instructions': 'github-copilot',
136
+ 'agents-md': 'codex',
137
+ 'gemini-md': 'antigravity',
138
+ 'windsurf': 'windsurf',
139
+ 'cline': 'cline',
140
+ 'roo': 'roo',
141
+ 'continue': 'continue',
142
+ };
143
+ return map[format];
144
+ }
128
145
  function generateExport(format, options, cwd) {
146
+ const agentName = formatToAgentName(format);
147
+ if (agentName) {
148
+ const adaptive = generateAdaptiveExport(agentName, options, cwd);
149
+ if (adaptive)
150
+ return adaptive;
151
+ }
152
+ // Fallback to legacy generators for unknown formats
129
153
  switch (format) {
130
154
  case 'copilot-instructions': return generateCopilotInstructions(options, cwd);
131
155
  case 'cursor-rules': return generateCursorRules(options, cwd);
@@ -140,6 +164,26 @@ function generateExport(format, options, cwd) {
140
164
  throw new Error(`Unknown export format: ${format}`);
141
165
  }
142
166
  }
167
+ /**
168
+ * Generate export content using adaptive templates when a capability profile
169
+ * exists for the agent, falling back to the legacy per-format generators.
170
+ */
171
+ function generateAdaptiveExport(agentName, options, cwd) {
172
+ const profile = getAgentCapabilityProfile(agentName);
173
+ if (!profile)
174
+ return undefined;
175
+ const state = loadState(cwd);
176
+ const instructions = getInstructionText(options, cwd);
177
+ const config = loadConfig(cwd);
178
+ const result = renderBrainclawSection({
179
+ profile,
180
+ state,
181
+ projectName: config.project_name,
182
+ brainclawVersion: getInstalledBrainclawVersion(),
183
+ resolvedInstructions: instructions,
184
+ });
185
+ return result.content;
186
+ }
143
187
  function getInstructionText(options, cwd) {
144
188
  try {
145
189
  const all = loadInstructions(cwd);
@@ -15,17 +15,18 @@ import { isAgentIntegrationName, upsertAgentIntegrationDeclaration } from '../co
15
15
  import { describeAutoConfigWrite, ensureAgentFiles, ensureGitignoreEntries, writeDetectedAgentAutoConfig } from '../core/agent-files.js';
16
16
  import { detectAiAgent, detectWslEnvironment } from '../core/ai-agent-detection.js';
17
17
  import { buildAiSurfaceInventory, renderAiSurfaceUsageHints } from '../core/ai-surface-inventory.js';
18
- import { hasCompletedSetup } from '../core/setup-state.js';
18
+ import { ensureUserStore, hasCompletedSetup } from '../core/setup-state.js';
19
19
  import { writeDetectedAgentExport } from './export.js';
20
20
  import { writeDetectedAgentHooks } from './hooks.js';
21
21
  export async function runInit(options = {}) {
22
22
  const cwd = options.cwd ?? process.cwd();
23
23
  const containingMemoryStore = resolveContainingMemoryStore(cwd);
24
- const skipSetupRequirement = options.skipSetupRequirement === true || process.env.BRAINCLAW_SKIP_SETUP_REQUIREMENT === '1';
25
- if (!skipSetupRequirement && !hasCompletedSetup()) {
26
- console.error('Error: global brainclaw setup has not been completed on this machine.');
27
- console.error('Run `brainclaw setup` first, then retry `brainclaw init` from your project root.');
28
- process.exit(1);
24
+ // Auto-create user store if absent (replaces the old "setup required" guard).
25
+ // The skipSetupRequirement flag and BRAINCLAW_SKIP_SETUP_REQUIREMENT env var
26
+ // are kept for backward compatibility but are now effectively no-ops —
27
+ // init always ensures the user store exists before proceeding.
28
+ if (!hasCompletedSetup()) {
29
+ ensureUserStore();
29
30
  }
30
31
  if (containingMemoryStore) {
31
32
  console.error(`Error: cannot run \`brainclaw init\` from inside an existing project memory store (${containingMemoryStore}).`);
@@ -30,6 +30,8 @@ import { buildEstimationReport } from './estimation-report.js';
30
30
  import { detectAiAgent } from '../core/ai-agent-detection.js';
31
31
  import { checkGitPresence, scanGitRepos, parseRoots, parseRepoSelection, parseAgentSelection, runGlobalInstall, initReposAndConfigureAgents, readSetupState, ALL_KNOWN_AGENTS, } from './setup.js';
32
32
  import { resolveTargetStore, resolveStoreChain } from '../core/store-resolution.js';
33
+ import { probeForQuickSetup, buildQuickSetupProbeResponse, buildOnboardingPreview } from '../core/setup-flow.js';
34
+ import { ensureUserStore } from '../core/setup-state.js';
33
35
  import { readUnseenEvents, buildNotificationSummary } from '../core/event-log.js';
34
36
  import { BootstrapInterviewAnswerSchema } from '../core/schema.js';
35
37
  export const SCHEMA_VERSION = '0.6.0';
@@ -246,14 +248,17 @@ export const MCP_READ_TOOLS = [
246
248
  const MCP_WRITE_TOOLS = [
247
249
  {
248
250
  name: 'bclaw_setup',
249
- description: 'Interactive onboarding wizard global agent install + multi-repo brainclaw init. Use the resume pattern: call without step to start, then pass step+choice to advance through each stage.',
251
+ description: 'Interactive onboarding wizard. Two modes: (1) Quick mode (default): probes the current repo and asks project type + topology, then inits. (2) Batch mode: scan root directories and init multiple repos. Call without step to start brainclaw auto-detects the best mode.',
250
252
  inputSchema: {
251
253
  type: 'object',
252
254
  properties: {
253
- step: { type: 'string', description: 'Current step to resume: "project_roots", "repo_selection", or "agent_selection". Omit to start from the beginning.' },
254
- choice: { type: 'string', description: 'User choice for the current step (e.g. path list, "all", "detected", or comma-separated numbers).' },
255
- roots: { type: 'string', description: 'Comma-separated root paths (required from step "repo_selection" onward to re-scan).' },
256
- repo_selection: { type: 'string', description: 'Repo selection choice from previous step (required for "agent_selection" step).' },
255
+ step: { type: 'string', description: 'Resume step: "quick_init" (quick mode), or "project_roots"/"repo_selection"/"agent_selection" (batch mode). Omit to start.' },
256
+ choice: { type: 'string', description: 'User choice for the current step.' },
257
+ project_type: { type: 'string', description: 'Quick mode: "standalone", "workspace", or "linked".' },
258
+ topology: { type: 'string', description: 'Quick mode: "embedded" (shared via git) or "sidecar" (local only).' },
259
+ roots: { type: 'string', description: 'Batch mode: comma-separated root paths.' },
260
+ repo_selection: { type: 'string', description: 'Batch mode: repo selection from previous step.' },
261
+ mode: { type: 'string', description: 'Force "quick" or "batch" mode. Default: auto-detect.' },
257
262
  },
258
263
  },
259
264
  },
@@ -1112,6 +1117,16 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
1112
1117
  const text = args.interview
1113
1118
  ? renderBootstrapInterview(result, audience)
1114
1119
  : renderBootstrapSummary(result);
1120
+ // Extract top-level suggested questions for conversational bootstrap (step 11)
1121
+ const suggestedQuestions = result.importPlan.interview?.questions?.map((q) => ({
1122
+ id: q.id,
1123
+ prompt: q.prompt,
1124
+ rationale: q.rationale,
1125
+ priority: q.priority,
1126
+ })) ?? [];
1127
+ // Separate auto-imports (high confidence) from proposals (need discussion)
1128
+ const autoImports = result.importPlan.suggestions.filter((s) => s.confidence === 'high');
1129
+ const proposals = result.importPlan.suggestions.filter((s) => s.confidence !== 'high');
1115
1130
  return {
1116
1131
  content: [{ type: 'text', text }],
1117
1132
  structuredContent: {
@@ -1127,6 +1142,9 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
1127
1142
  seed_count: result.profile.seed_count,
1128
1143
  seeds: result.seeds,
1129
1144
  import_plan: result.importPlan,
1145
+ auto_imports: autoImports,
1146
+ proposals,
1147
+ suggested_questions: suggestedQuestions,
1130
1148
  last_application: result.lastApplication,
1131
1149
  reused_profile: result.reusedProfile,
1132
1150
  },
@@ -1571,15 +1589,78 @@ export async function executeMcpToolCall(payload) {
1571
1589
  const choice = args.choice ?? '';
1572
1590
  const rootsArg = args.roots;
1573
1591
  const repoSelectionArg = args.repo_selection;
1592
+ const modeArg = args.mode;
1574
1593
  const env = process.env;
1575
1594
  if (!checkGitPresence()) {
1576
1595
  return { response: toolResponse({ content: [{ type: 'text', text: 'Git is not installed or not found in PATH. Install git from https://git-scm.com before running brainclaw setup.' }], structuredContent: { error: 'git_not_found' } }, true) };
1577
1596
  }
1597
+ // ─── Quick mode: probe current repo ──────────────────────────────
1578
1598
  if (!step) {
1599
+ // Auto-detect mode: if we're in a git repo, use quick mode unless batch is forced
1600
+ const forceBatch = modeArg === 'batch';
1601
+ if (!forceBatch) {
1602
+ const probe = probeForQuickSetup(cwd);
1603
+ if (probe.isGitRepo || probe.alreadyInitialized) {
1604
+ const response = buildQuickSetupProbeResponse(probe);
1605
+ return { response: toolResponse({ content: [{ type: 'text', text: response.text }], structuredContent: response.structured }) };
1606
+ }
1607
+ }
1608
+ // Fall through to batch mode
1579
1609
  const existingState = readSetupState(env);
1580
1610
  const alreadyRun = existingState ? `Setup was previously run on ${new Date(existingState.completed_at).toLocaleDateString()}. You can re-run it.` : undefined;
1581
1611
  return { response: toolResponse({ content: [{ type: 'text', text: [alreadyRun, "Where are the user's project directories? Please ask the user to provide one or more root paths where their git repositories are located (e.g. ~/Projects, C:\\Users\\user\\code)."].filter(Boolean).join('\n\n') }], structuredContent: { pending_question: 'project_roots', prompt: 'Please ask the user: "Where are your projects? Enter one or more root directories (comma-separated):"', ...(alreadyRun ? { already_run: alreadyRun } : {}) } }) };
1582
1612
  }
1613
+ // ─── Quick mode step: init with choices ──────────────────────────
1614
+ if (step === 'quick_init') {
1615
+ const projectType = args.project_type ?? 'standalone';
1616
+ const topology = args.topology ?? 'embedded';
1617
+ // Ensure user store exists
1618
+ ensureUserStore(env);
1619
+ // Map choices to init options
1620
+ const projectMode = projectType === 'workspace' ? 'multi-project' : 'auto';
1621
+ const topologyMode = topology === 'sidecar' ? 'sidecar' : 'embedded';
1622
+ // Run init
1623
+ try {
1624
+ const { runInit } = await import('./init.js');
1625
+ await runInit({
1626
+ yes: true,
1627
+ cwd,
1628
+ skipAgentBootstrap: false,
1629
+ projectMode,
1630
+ topology: topologyMode,
1631
+ });
1632
+ }
1633
+ catch (err) {
1634
+ return { response: toolResponse({ content: [{ type: 'text', text: `Init failed: ${err instanceof Error ? err.message : String(err)}` }], structuredContent: { error: 'init_failed', details: err instanceof Error ? err.message : String(err) } }, true) };
1635
+ }
1636
+ // Detect agent and report
1637
+ const detected = detectAiAgent(env);
1638
+ const summary = [
1639
+ `✔ Initialized ${cwd.split(/[\\/]/).pop() ?? cwd} (${projectType}, ${topology})`,
1640
+ ];
1641
+ if (detected) {
1642
+ summary.push(`✔ Agent detected: ${detected.name}`);
1643
+ }
1644
+ summary.push('✔ Reload your agent session to activate brainclaw MCP tools.');
1645
+ // Check if bootstrap is available and generate preview
1646
+ const probe = probeForQuickSetup(cwd);
1647
+ const bootstrapAvailable = probe.hasContent;
1648
+ const preview = buildOnboardingPreview(cwd);
1649
+ return {
1650
+ response: toolResponse({
1651
+ content: [{ type: 'text', text: summary.join('\n') + (bootstrapAvailable ? '\n\nThe repo has existing content. Run bclaw_bootstrap to extract initial project context.' : '') + '\n\n' + preview }],
1652
+ structuredContent: {
1653
+ setup_complete: true,
1654
+ project_type: projectType,
1655
+ topology,
1656
+ detected_agent: detected?.name ?? null,
1657
+ bootstrap_available: bootstrapAvailable,
1658
+ preview,
1659
+ summary,
1660
+ },
1661
+ }),
1662
+ };
1663
+ }
1583
1664
  if (step === 'project_roots') {
1584
1665
  const roots = parseRoots(choice, env);
1585
1666
  if (roots.length === 0) {
@@ -0,0 +1,145 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import readline from 'node:readline/promises';
4
+ import { MEMORY_DIR, memoryExists } from '../core/io.js';
5
+ import { BRAINCLAW_SECTION_START, BRAINCLAW_SECTION_END, AGENT_EXPORT_REGISTRY, } from '../core/agent-files.js';
6
+ import { resolveHomeDir } from '../core/setup-state.js';
7
+ /**
8
+ * Remove brainclaw from a project and/or machine.
9
+ *
10
+ * --project: removes .brainclaw/, agent instruction files, MCP configs,
11
+ * and brainclaw sections from shared instruction files.
12
+ * --machine: removes ~/.brainclaw/ and global agent configs.
13
+ */
14
+ export async function runUninstall(options) {
15
+ const cwd = options.cwd ?? process.cwd();
16
+ if (!options.project && !options.machine) {
17
+ console.error('Error: specify --project, --machine, or both.');
18
+ process.exit(1);
19
+ }
20
+ if (options.project) {
21
+ await uninstallProject(cwd, options.yes);
22
+ }
23
+ if (options.machine) {
24
+ await uninstallMachine(options.yes);
25
+ }
26
+ }
27
+ async function uninstallProject(cwd, skipConfirm) {
28
+ if (!memoryExists(cwd)) {
29
+ console.log('No .brainclaw/ found in this project. Nothing to uninstall.');
30
+ return;
31
+ }
32
+ if (!skipConfirm && process.stdin.isTTY) {
33
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
34
+ try {
35
+ const answer = (await rl.question('Remove brainclaw from this project? This deletes .brainclaw/ and all generated agent files. [y/N]: ')).trim().toLowerCase();
36
+ if (answer !== 'y' && answer !== 'yes') {
37
+ console.log('Aborted.');
38
+ return;
39
+ }
40
+ }
41
+ finally {
42
+ rl.close();
43
+ }
44
+ }
45
+ // Remove .brainclaw/ directory
46
+ const brainclawDir = path.join(cwd, MEMORY_DIR);
47
+ if (fs.existsSync(brainclawDir)) {
48
+ fs.rmSync(brainclawDir, { recursive: true, force: true });
49
+ console.log(`✔ Removed ${MEMORY_DIR}/`);
50
+ }
51
+ // Remove dedicated agent files (non-shared, brainclaw-owned)
52
+ const dedicatedFiles = AGENT_EXPORT_REGISTRY
53
+ .map((entry) => entry.relativePath)
54
+ .filter((p) => p.includes('/'));
55
+ for (const relativePath of dedicatedFiles) {
56
+ const fullPath = path.join(cwd, relativePath);
57
+ if (fs.existsSync(fullPath)) {
58
+ fs.unlinkSync(fullPath);
59
+ console.log(`✔ Removed ${relativePath}`);
60
+ }
61
+ }
62
+ // Remove brainclaw sections from shared files
63
+ const sharedFiles = [
64
+ 'CLAUDE.md',
65
+ '.windsurfrules',
66
+ 'AGENTS.md',
67
+ 'GEMINI.md',
68
+ '.github/copilot-instructions.md',
69
+ ];
70
+ for (const relativePath of sharedFiles) {
71
+ const fullPath = path.join(cwd, relativePath);
72
+ if (!fs.existsSync(fullPath))
73
+ continue;
74
+ const content = fs.readFileSync(fullPath, 'utf-8');
75
+ const startIdx = content.indexOf(BRAINCLAW_SECTION_START);
76
+ const endIdx = content.indexOf(BRAINCLAW_SECTION_END);
77
+ if (startIdx !== -1 && endIdx !== -1) {
78
+ const before = content.slice(0, startIdx).trimEnd();
79
+ const after = content.slice(endIdx + BRAINCLAW_SECTION_END.length).trimStart();
80
+ const cleaned = [before, after].filter(Boolean).join('\n\n');
81
+ if (cleaned.trim().length === 0) {
82
+ fs.unlinkSync(fullPath);
83
+ console.log(`✔ Removed ${relativePath} (was brainclaw-only)`);
84
+ }
85
+ else {
86
+ fs.writeFileSync(fullPath, cleaned + '\n', 'utf-8');
87
+ console.log(`✔ Removed brainclaw section from ${relativePath}`);
88
+ }
89
+ }
90
+ }
91
+ // Remove companion config files
92
+ const companionFiles = [
93
+ '.mcp.json',
94
+ '.claude/commands/brainclaw.md',
95
+ '.claude/settings.local.json',
96
+ '.claude/.bclaw-session',
97
+ '.cursor/rules/brainclaw-mcp-shim.mdc',
98
+ '.vscode/cline_mcp_settings.json',
99
+ '.roo/mcp.json',
100
+ '.continue/config.json',
101
+ 'opencode.json',
102
+ '.github/skills/brainclaw-context/SKILL.md',
103
+ ];
104
+ for (const relativePath of companionFiles) {
105
+ const fullPath = path.join(cwd, relativePath);
106
+ if (fs.existsSync(fullPath)) {
107
+ fs.unlinkSync(fullPath);
108
+ console.log(`✔ Removed ${relativePath}`);
109
+ }
110
+ }
111
+ console.log('✔ Project uninstall complete.');
112
+ }
113
+ async function uninstallMachine(skipConfirm) {
114
+ const home = resolveHomeDir();
115
+ if (!home) {
116
+ console.log('Cannot determine home directory. Nothing to uninstall.');
117
+ return;
118
+ }
119
+ const userStore = path.join(home, '.brainclaw');
120
+ if (!fs.existsSync(userStore)) {
121
+ console.log('No ~/.brainclaw/ found. Nothing to uninstall.');
122
+ return;
123
+ }
124
+ if (!skipConfirm && process.stdin.isTTY) {
125
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
126
+ try {
127
+ const answer = (await rl.question('Remove brainclaw global config (~/.brainclaw/)? [y/N]: ')).trim().toLowerCase();
128
+ if (answer !== 'y' && answer !== 'yes') {
129
+ console.log('Aborted.');
130
+ return;
131
+ }
132
+ }
133
+ finally {
134
+ rl.close();
135
+ }
136
+ }
137
+ fs.rmSync(userStore, { recursive: true, force: true });
138
+ console.log('✔ Removed ~/.brainclaw/');
139
+ // Note: global MCP configs in ~/.claude/settings.json, ~/.cursor/mcp.json etc.
140
+ // are NOT removed automatically — they may contain non-brainclaw entries.
141
+ console.log('Note: global agent MCP configs (e.g. ~/.claude/settings.json) were not modified.');
142
+ console.log('Remove brainclaw entries manually if needed.');
143
+ console.log('✔ Machine uninstall complete.');
144
+ }
145
+ //# sourceMappingURL=uninstall.js.map