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 +26 -11
- package/dist/cli.js +11 -0
- package/dist/commands/context.js +3 -1
- package/dist/commands/export.js +44 -0
- package/dist/commands/init.js +7 -6
- package/dist/commands/mcp.js +86 -5
- package/dist/commands/uninstall.js +145 -0
- package/dist/core/agent-capability.js +184 -0
- package/dist/core/agent-context.js +24 -6
- package/dist/core/bootstrap.js +177 -0
- package/dist/core/context.js +47 -24
- package/dist/core/instruction-templates.js +308 -0
- package/dist/core/schema.js +9 -0
- package/dist/core/setup-flow.js +191 -0
- package/dist/core/setup-state.js +30 -1
- package/dist/core/store-resolution.js +58 -0
- package/docs/architecture/project-refs.md +305 -0
- package/docs/cli.md +2 -1
- package/docs/integrations/agents.md +102 -150
- package/docs/integrations/overview.md +71 -45
- package/docs/quickstart.md +43 -147
- package/package.json +1 -1
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 |
|
|
69
|
+
| Logo | Agent | Integration | What brainclaw configures |
|
|
70
70
|
|---|---|---|---|
|
|
71
|
-
| [](https://github.com/anthropics/claude-code) | **[Claude Code](https://github.com/anthropics/claude-code)** |
|
|
72
|
-
| [](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** |
|
|
73
|
-
| [](https://cursor.com/en-US) | **[Cursor](https://cursor.com/en-US)** |
|
|
74
|
-
| [](https://github.com/opencode-ai/opencode) | **[OpenCode](https://github.com/opencode-ai/opencode)** |
|
|
75
|
-
| [](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** |
|
|
76
|
-
| [](https://github.com/RooCodeInc/Roo-Code) | **[Roo](https://github.com/RooCodeInc/Roo-Code)** |
|
|
77
|
-
| [](https://github.com/features/copilot) | **[GitHub Copilot](https://github.com/features/copilot)** |
|
|
71
|
+
| [](https://github.com/anthropics/claude-code) | **[Claude Code](https://github.com/anthropics/claude-code)** | Full | MCP + CLAUDE.md + hooks + permissions + /brainclaw command |
|
|
72
|
+
| [](https://openai.com/codex/) | **[Codex](https://openai.com/codex/)** | Standard | MCP + AGENTS.md |
|
|
73
|
+
| [](https://cursor.com/en-US) | **[Cursor](https://cursor.com/en-US)** | Standard | MCP + .cursor/rules/ + alwaysApply MDC |
|
|
74
|
+
| [](https://github.com/opencode-ai/opencode) | **[OpenCode](https://github.com/opencode-ai/opencode)** | Standard | MCP + AGENTS.md |
|
|
75
|
+
| [](https://windsurf.com/) | **[Windsurf](https://windsurf.com/)** | Standard | MCP + .windsurfrules session trigger |
|
|
76
|
+
| [](https://github.com/RooCodeInc/Roo-Code) | **[Roo](https://github.com/RooCodeInc/Roo-Code)** | Standard | MCP (auto-approve) + .roo/rules/ |
|
|
77
|
+
| [](https://github.com/cline/cline) | **[Cline](https://github.com/cline/cline)** | Standard | MCP (auto-approve) + .clinerules/ |
|
|
78
|
+
| [](https://github.com/continuedev/continue) | **[Continue](https://github.com/continuedev/continue)** | Standard | MCP + .continue/rules/ |
|
|
79
|
+
| [](https://github.com/google-gemini/gemini-cli) | **[Antigravity / Gemini CLI](https://github.com/google-gemini/gemini-cli)** | Standard | MCP + GEMINI.md |
|
|
80
|
+
| [](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')
|
package/dist/commands/context.js
CHANGED
|
@@ -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,
|
|
48
|
+
writeLastContextMarker(result, options, contextCwd);
|
|
47
49
|
}
|
|
48
50
|
function writeLastContextMarker(result, options, cwd) {
|
|
49
51
|
try {
|
package/dist/commands/export.js
CHANGED
|
@@ -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);
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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}).`);
|
package/dist/commands/mcp.js
CHANGED
|
@@ -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
|
|
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: '
|
|
254
|
-
choice: { type: 'string', description: 'User choice for the current step
|
|
255
|
-
|
|
256
|
-
|
|
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
|