brainclaw 0.19.12 → 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 +42 -11
- package/dist/cli.js +55 -1
- package/dist/commands/claude-desktop-extension.js +18 -0
- package/dist/commands/context.js +3 -1
- package/dist/commands/doctor.js +12 -5
- package/dist/commands/export.js +44 -0
- package/dist/commands/init.js +22 -6
- package/dist/commands/list-surface-tasks.js +39 -0
- package/dist/commands/mcp.js +86 -5
- package/dist/commands/reconcile.js +138 -0
- package/dist/commands/setup.js +19 -0
- package/dist/commands/status.js +17 -12
- package/dist/commands/surface-task-resource.js +35 -0
- package/dist/commands/surface-task.js +57 -0
- package/dist/commands/uninstall.js +145 -0
- package/dist/commands/update-surface-task.js +30 -0
- package/dist/core/agent-capability.js +184 -0
- package/dist/core/agent-context.js +24 -6
- package/dist/core/agent-files.js +18 -18
- package/dist/core/ai-surface-inventory.js +321 -0
- package/dist/core/ai-surface-tasks.js +40 -0
- package/dist/core/bootstrap.js +177 -0
- package/dist/core/claude-desktop-extension.js +224 -0
- package/dist/core/context.js +47 -24
- package/dist/core/ids.js +1 -0
- package/dist/core/instruction-templates.js +308 -0
- package/dist/core/io.js +1 -0
- package/dist/core/machine-profile.js +7 -1
- package/dist/core/migration.js +3 -1
- package/dist/core/schema.js +34 -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/dist/core/workspace-projects.js +115 -0
- package/docs/architecture/project-refs.md +305 -0
- package/docs/cli.md +133 -1
- package/docs/integrations/agents.md +102 -150
- package/docs/integrations/overview.md +71 -45
- package/docs/quickstart.md +44 -111
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import readline from 'node:readline/promises';
|
|
2
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { buildMachineProfile, saveMachineProfile } from '../core/machine-profile.js';
|
|
5
|
+
import { buildAgentInventory, saveAgentInventory } from '../core/agent-inventory.js';
|
|
6
|
+
import { applyBootstrapImport, runBootstrapProfile } from '../core/bootstrap.js';
|
|
7
|
+
import { loadConfig } from '../core/config.js';
|
|
8
|
+
import { memoryExists } from '../core/io.js';
|
|
9
|
+
import { summarizeWorkspaceProjects } from '../core/workspace-projects.js';
|
|
10
|
+
export async function runReconcile(options = {}) {
|
|
11
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
12
|
+
if (!memoryExists(cwd)) {
|
|
13
|
+
console.error('Error: .brainclaw/ not found in the current directory. Run `brainclaw init` first.');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const rootConfig = loadConfig(cwd);
|
|
17
|
+
const workspaceSummary = summarizeWorkspaceProjects(cwd, rootConfig);
|
|
18
|
+
const storeTargets = [
|
|
19
|
+
cwd,
|
|
20
|
+
...workspaceSummary.uses_folder_resolution
|
|
21
|
+
? workspaceSummary.discovered_projects
|
|
22
|
+
.filter((project) => project.source !== 'config')
|
|
23
|
+
.map((project) => project.path)
|
|
24
|
+
: [],
|
|
25
|
+
];
|
|
26
|
+
const uniqueTargets = [...new Set(storeTargets.map((store) => path.resolve(store)))];
|
|
27
|
+
if (options.json && options.dryRun) {
|
|
28
|
+
console.log(JSON.stringify({
|
|
29
|
+
cwd,
|
|
30
|
+
mode: 'dry_run',
|
|
31
|
+
workspace_summary: workspaceSummary,
|
|
32
|
+
planned_actions: {
|
|
33
|
+
machine_profile_refresh: !options.skipMachineProfile,
|
|
34
|
+
agent_inventory_refresh: !options.skipAgentInventory,
|
|
35
|
+
bootstrap_refresh: uniqueTargets.map((store) => ({
|
|
36
|
+
cwd: store,
|
|
37
|
+
relative_path: path.relative(cwd, store) || '.',
|
|
38
|
+
apply_bootstrap: Boolean(options.applyBootstrap),
|
|
39
|
+
})),
|
|
40
|
+
},
|
|
41
|
+
}, null, 2));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (options.applyBootstrap) {
|
|
45
|
+
await confirmBootstrapApply(options.yes);
|
|
46
|
+
}
|
|
47
|
+
const machineProfilePath = !options.skipMachineProfile
|
|
48
|
+
? saveMachineProfile(buildMachineProfile())
|
|
49
|
+
: null;
|
|
50
|
+
const agentInventoryPath = !options.skipAgentInventory
|
|
51
|
+
? saveAgentInventory(buildAgentInventory())
|
|
52
|
+
: null;
|
|
53
|
+
const stores = [];
|
|
54
|
+
for (const store of uniqueTargets) {
|
|
55
|
+
const config = loadConfig(store);
|
|
56
|
+
if (options.applyBootstrap) {
|
|
57
|
+
const result = applyBootstrapImport({ cwd: store, refresh: true });
|
|
58
|
+
stores.push({
|
|
59
|
+
cwd: store,
|
|
60
|
+
relative_path: path.relative(cwd, store) || '.',
|
|
61
|
+
project_name: config.project_name,
|
|
62
|
+
project_mode: config.project_mode,
|
|
63
|
+
project_strategy: config.projects.strategy,
|
|
64
|
+
bootstrap_refreshed: true,
|
|
65
|
+
bootstrap_applied: true,
|
|
66
|
+
workspace_kind: result.proposal.workspace_kind,
|
|
67
|
+
onboarding_mode: result.proposal.onboarding_mode,
|
|
68
|
+
confidence: result.proposal.confidence,
|
|
69
|
+
suggestion_count: result.proposal.suggestion_count,
|
|
70
|
+
created_count: result.createdCount,
|
|
71
|
+
skipped_count: result.skippedCount,
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const result = runBootstrapProfile({ cwd: store, refresh: true });
|
|
76
|
+
stores.push({
|
|
77
|
+
cwd: store,
|
|
78
|
+
relative_path: path.relative(cwd, store) || '.',
|
|
79
|
+
project_name: config.project_name,
|
|
80
|
+
project_mode: config.project_mode,
|
|
81
|
+
project_strategy: config.projects.strategy,
|
|
82
|
+
bootstrap_refreshed: true,
|
|
83
|
+
bootstrap_applied: false,
|
|
84
|
+
workspace_kind: result.profile.workspace_kind,
|
|
85
|
+
onboarding_mode: result.profile.onboarding_mode,
|
|
86
|
+
confidence: result.profile.confidence,
|
|
87
|
+
suggestion_count: result.importPlan.suggestion_count,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (options.json) {
|
|
91
|
+
console.log(JSON.stringify({
|
|
92
|
+
cwd,
|
|
93
|
+
mode: options.applyBootstrap ? 'apply' : 'refresh',
|
|
94
|
+
workspace_summary: workspaceSummary,
|
|
95
|
+
machine_profile_refreshed: !options.skipMachineProfile,
|
|
96
|
+
machine_profile_path: machineProfilePath,
|
|
97
|
+
agent_inventory_refreshed: !options.skipAgentInventory,
|
|
98
|
+
agent_inventory_path: agentInventoryPath,
|
|
99
|
+
stores,
|
|
100
|
+
}, null, 2));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!options.skipMachineProfile) {
|
|
104
|
+
console.log(`✔ Machine profile refreshed: ${machineProfilePath}`);
|
|
105
|
+
}
|
|
106
|
+
if (!options.skipAgentInventory) {
|
|
107
|
+
console.log(`✔ Agent inventory refreshed: ${agentInventoryPath}`);
|
|
108
|
+
}
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log(`Reconciled ${stores.length} store(s):`);
|
|
111
|
+
for (const store of stores) {
|
|
112
|
+
const suffix = store.bootstrap_applied
|
|
113
|
+
? `${store.created_count ?? 0} created, ${store.skipped_count ?? 0} skipped`
|
|
114
|
+
: `${store.suggestion_count ?? 0} bootstrap suggestion(s) available`;
|
|
115
|
+
console.log(` - ${store.relative_path} (${store.project_name}) — ${store.onboarding_mode ?? 'unknown'}, confidence=${store.confidence ?? 'unknown'}, ${suffix}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function confirmBootstrapApply(yes) {
|
|
119
|
+
if (yes) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
123
|
+
console.error('Error: bootstrap apply across multiple stores requires --yes in non-interactive mode.');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const rl = readline.createInterface({ input, output });
|
|
127
|
+
try {
|
|
128
|
+
const answer = await rl.question('Apply bootstrap suggestions across all selected stores? [y/N] ');
|
|
129
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
130
|
+
console.error('Cancelled.');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
rl.close();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=reconcile.js.map
|
package/dist/commands/setup.js
CHANGED
|
@@ -4,6 +4,7 @@ import readline from 'node:readline/promises';
|
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
import { runInit } from './init.js';
|
|
6
6
|
import { detectAiAgent } from '../core/ai-agent-detection.js';
|
|
7
|
+
import { buildAiSurfaceInventory, renderAiSurfaceUsageHints } from '../core/ai-surface-inventory.js';
|
|
7
8
|
import { buildMachineProfile, saveMachineProfile, loadMachineProfile } from '../core/machine-profile.js';
|
|
8
9
|
import { buildAgentInventory, saveAgentInventory, loadAgentInventory } from '../core/agent-inventory.js';
|
|
9
10
|
import { ensureClaudeCodeUserSettings, ensureClaudeCodeUserCommand, ensureCursorMcpConfig, ensureWindsurfMcpConfig, ensureAntigravityMcpConfig, ensureContinueUserMcpConfig, ensureCodexMcpConfig, writeDetectedAgentAutoConfig, describeAutoConfigWrite, ensureGitignoreEntries, collectWorkspaceGitignoreEntries, } from '../core/agent-files.js';
|
|
@@ -388,6 +389,7 @@ export async function runSetup(options = {}) {
|
|
|
388
389
|
// Step 4: Agent detection & selection
|
|
389
390
|
const detectedAi = detectAiAgent(env);
|
|
390
391
|
const detectedName = detectedAi?.name;
|
|
392
|
+
const detectedSurfaces = buildAiSurfaceInventory();
|
|
391
393
|
console.log('');
|
|
392
394
|
if (detectedName) {
|
|
393
395
|
console.log(`Detected AI agent: ${detectedName}`);
|
|
@@ -395,6 +397,23 @@ export async function runSetup(options = {}) {
|
|
|
395
397
|
else {
|
|
396
398
|
console.log('No AI agent detected automatically.');
|
|
397
399
|
}
|
|
400
|
+
const visibleSurfaces = detectedSurfaces.filter((surface) => surface.status !== 'not_detected');
|
|
401
|
+
if (visibleSurfaces.length > 0) {
|
|
402
|
+
console.log('Other AI work surfaces on this machine:');
|
|
403
|
+
for (const surface of visibleSurfaces) {
|
|
404
|
+
console.log(` - ${surface.display_name} [${surface.surface_kind}, ${surface.status}]`);
|
|
405
|
+
}
|
|
406
|
+
const usageHints = renderAiSurfaceUsageHints(visibleSurfaces);
|
|
407
|
+
if (usageHints.length > 0) {
|
|
408
|
+
console.log('');
|
|
409
|
+
console.log('Suggested uses:');
|
|
410
|
+
for (const line of usageHints) {
|
|
411
|
+
console.log(` ${line}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
console.log('');
|
|
415
|
+
console.log('These surfaces are tracked separately from coding agents and will use tailored onboarding flows.');
|
|
416
|
+
}
|
|
398
417
|
console.log('Supported agents:');
|
|
399
418
|
ALL_KNOWN_AGENTS.forEach((a, i) => {
|
|
400
419
|
const tag = a === detectedName ? ' ← detected' : '';
|
package/dist/commands/status.js
CHANGED
|
@@ -9,7 +9,8 @@ import { loadState } from '../core/state.js';
|
|
|
9
9
|
import { isTrapActive, listOperationalTraps } from '../core/traps.js';
|
|
10
10
|
import { generateMarkdown } from '../core/markdown.js';
|
|
11
11
|
import { memoryExists } from '../core/io.js';
|
|
12
|
-
|
|
12
|
+
import { summarizeWorkspaceProjects } from '../core/workspace-projects.js';
|
|
13
|
+
function printHumanStatus(state, config, cwd) {
|
|
13
14
|
const activePlans = state.plan_items.filter((plan) => plan.status !== 'done' && plan.status !== 'dropped');
|
|
14
15
|
const activeInstructions = loadInstructions().filter((entry) => entry.active);
|
|
15
16
|
const activeClaims = listClaims().filter((claim) => claim.status === 'active');
|
|
@@ -21,6 +22,7 @@ function printHumanStatus(state, config) {
|
|
|
21
22
|
const activeSharedTraps = state.known_traps.filter((trap) => isTrapActive(trap));
|
|
22
23
|
const resolvedSharedTraps = state.known_traps.filter((trap) => trap.status === 'resolved');
|
|
23
24
|
const activeMachineTraps = machineTraps.filter((trap) => isTrapActive(trap));
|
|
25
|
+
const workspaceProjects = summarizeWorkspaceProjects(cwd, config);
|
|
24
26
|
const counts = {
|
|
25
27
|
instructions: activeInstructions.length,
|
|
26
28
|
claims: activeClaims.length,
|
|
@@ -50,7 +52,7 @@ function printHumanStatus(state, config) {
|
|
|
50
52
|
console.log(` Traps : ${activeSharedTraps.length} active shared, ${resolvedSharedTraps.length} resolved shared, ${activeMachineTraps.length} active machine-local (${currentHost})`);
|
|
51
53
|
console.log(` Handoffs : ${counts.handoffs}`);
|
|
52
54
|
if (config.project_mode === 'multi-project') {
|
|
53
|
-
console.log(` Projects : ${
|
|
55
|
+
console.log(` Projects : ${workspaceProjects.effective_project_count} effective (${workspaceProjects.strategy})`);
|
|
54
56
|
}
|
|
55
57
|
// Show recent items (last 3 per section)
|
|
56
58
|
const sections = [
|
|
@@ -84,24 +86,27 @@ function printHumanStatus(state, config) {
|
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
export function runStatus(options = {}) {
|
|
87
|
-
|
|
89
|
+
const cwd = process.cwd();
|
|
90
|
+
if (!memoryExists(cwd)) {
|
|
88
91
|
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
89
92
|
process.exit(1);
|
|
90
93
|
}
|
|
91
|
-
const config = loadConfig();
|
|
92
|
-
const state = loadState();
|
|
94
|
+
const config = loadConfig(cwd);
|
|
95
|
+
const state = loadState(cwd);
|
|
93
96
|
const currentHost = resolveCurrentHostId();
|
|
94
|
-
const sharedRuntimeNotes = listRuntimeNotes({ visibility: 'shared' });
|
|
95
|
-
const machineRuntimeNotes = listRuntimeNotes({ visibility: 'machine' });
|
|
96
|
-
const machineTraps = listOperationalTraps({ visibility: 'machine' });
|
|
97
|
+
const sharedRuntimeNotes = listRuntimeNotes({ visibility: 'shared' }, cwd);
|
|
98
|
+
const machineRuntimeNotes = listRuntimeNotes({ visibility: 'machine' }, cwd);
|
|
99
|
+
const machineTraps = listOperationalTraps({ visibility: 'machine' }, cwd);
|
|
97
100
|
const activeSharedTraps = state.known_traps.filter((trap) => isTrapActive(trap));
|
|
98
101
|
const resolvedSharedTraps = state.known_traps.filter((trap) => trap.status === 'resolved');
|
|
99
102
|
const activeMachineTraps = machineTraps.filter((trap) => isTrapActive(trap));
|
|
100
|
-
const registeredAgents = listAgentIdentities();
|
|
101
|
-
const reputation = buildReputationSnapshot();
|
|
103
|
+
const registeredAgents = listAgentIdentities(cwd);
|
|
104
|
+
const reputation = buildReputationSnapshot(cwd);
|
|
105
|
+
const workspaceProjects = summarizeWorkspaceProjects(cwd, config);
|
|
102
106
|
if (options.json) {
|
|
103
107
|
console.log(JSON.stringify({
|
|
104
108
|
config,
|
|
109
|
+
workspace_projects: workspaceProjects,
|
|
105
110
|
agents: {
|
|
106
111
|
current_name: config.current_agent,
|
|
107
112
|
current_id: config.current_agent_id,
|
|
@@ -125,9 +130,9 @@ export function runStatus(options = {}) {
|
|
|
125
130
|
return;
|
|
126
131
|
}
|
|
127
132
|
if (options.markdown) {
|
|
128
|
-
console.log(generateMarkdown(state));
|
|
133
|
+
console.log(generateMarkdown(state, cwd));
|
|
129
134
|
return;
|
|
130
135
|
}
|
|
131
|
-
printHumanStatus(state, config);
|
|
136
|
+
printHumanStatus(state, config, cwd);
|
|
132
137
|
}
|
|
133
138
|
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { runSurfaceTask } from './surface-task.js';
|
|
2
|
+
import { runListSurfaceTasks } from './list-surface-tasks.js';
|
|
3
|
+
import { runUpdateSurfaceTask } from './update-surface-task.js';
|
|
4
|
+
export function runSurfaceTaskResource(subcommand, args, options = {}) {
|
|
5
|
+
const normalized = subcommand.trim().toLowerCase();
|
|
6
|
+
if (normalized === 'create') {
|
|
7
|
+
const title = args.join(' ').trim();
|
|
8
|
+
if (!title) {
|
|
9
|
+
console.error('Error: surface-task create requires <title>.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
runSurfaceTask(title, options);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (normalized === 'list' || normalized === 'ls') {
|
|
16
|
+
runListSurfaceTasks(options);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (normalized === 'update') {
|
|
20
|
+
const id = args[0];
|
|
21
|
+
if (!id) {
|
|
22
|
+
console.error('Error: surface-task update requires <id>.');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
runUpdateSurfaceTask(id, options);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const legacyTitle = [subcommand, ...args].join(' ').trim();
|
|
29
|
+
if (!legacyTitle) {
|
|
30
|
+
console.error('Error: missing surface-task subcommand or title.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
runSurfaceTask(legacyTitle, options);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=surface-task-resource.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { buildOperationalIdentity } from '../core/identity.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
|
+
import { generateIdWithLabel, nowISO } from '../core/ids.js';
|
|
4
|
+
import { requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
|
|
5
|
+
import { saveAiSurfaceTask } from '../core/ai-surface-tasks.js';
|
|
6
|
+
export function runSurfaceTask(title, options = {}) {
|
|
7
|
+
if (!memoryExists(options.cwd)) {
|
|
8
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
if (!options.target) {
|
|
12
|
+
console.error('Error: surface-task create requires --target <surface>.');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
if (!options.instructions?.trim()) {
|
|
16
|
+
console.error('Error: surface-task create requires --instructions <text>.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const registeredAgent = requireRegisteredAgentIdentity({
|
|
20
|
+
agentName: options.agent,
|
|
21
|
+
agentId: options.agentId,
|
|
22
|
+
cwd: options.cwd,
|
|
23
|
+
allowCurrent: true,
|
|
24
|
+
allowEnv: true,
|
|
25
|
+
});
|
|
26
|
+
requireMinimumTrustLevel(registeredAgent, 'contributor');
|
|
27
|
+
const actor = buildOperationalIdentity(registeredAgent.agent_name, options.cwd, {
|
|
28
|
+
agentId: registeredAgent.agent_id,
|
|
29
|
+
});
|
|
30
|
+
const ids = generateIdWithLabel('ai_surface_tasks', options.cwd);
|
|
31
|
+
const timestamp = nowISO();
|
|
32
|
+
const task = {
|
|
33
|
+
id: ids.id,
|
|
34
|
+
short_label: ids.short_label,
|
|
35
|
+
title: title.trim(),
|
|
36
|
+
instructions: options.instructions.trim(),
|
|
37
|
+
target_surface: options.target.trim(),
|
|
38
|
+
kind: options.kind ?? 'custom',
|
|
39
|
+
created_at: timestamp,
|
|
40
|
+
updated_at: timestamp,
|
|
41
|
+
author: actor.agent,
|
|
42
|
+
author_id: actor.agent_id,
|
|
43
|
+
project_id: actor.project_id,
|
|
44
|
+
session_id: actor.session_id,
|
|
45
|
+
status: 'queued',
|
|
46
|
+
requested_outputs: options.output ?? [],
|
|
47
|
+
related_paths: options.path,
|
|
48
|
+
tags: options.tag ?? [],
|
|
49
|
+
};
|
|
50
|
+
saveAiSurfaceTask(task, options.cwd);
|
|
51
|
+
console.log(`✔ Surface task queued: [${task.id}] ${task.title}`);
|
|
52
|
+
console.log(` Target: ${task.target_surface} (${task.kind})`);
|
|
53
|
+
if (task.requested_outputs.length > 0) {
|
|
54
|
+
console.log(` Outputs: ${task.requested_outputs.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=surface-task.js.map
|
|
@@ -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
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { memoryExists } from '../core/io.js';
|
|
2
|
+
import { loadAiSurfaceTask, saveAiSurfaceTask } from '../core/ai-surface-tasks.js';
|
|
3
|
+
import { nowISO } from '../core/ids.js';
|
|
4
|
+
export function runUpdateSurfaceTask(id, options = {}) {
|
|
5
|
+
if (!memoryExists(options.cwd)) {
|
|
6
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
const task = loadAiSurfaceTask(id, options.cwd);
|
|
10
|
+
const timestamp = nowISO();
|
|
11
|
+
if (options.status) {
|
|
12
|
+
task.status = options.status;
|
|
13
|
+
if (options.status === 'in_progress' && !task.claimed_at) {
|
|
14
|
+
task.claimed_at = timestamp;
|
|
15
|
+
}
|
|
16
|
+
if ((options.status === 'completed' || options.status === 'failed' || options.status === 'cancelled') && !task.completed_at) {
|
|
17
|
+
task.completed_at = timestamp;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (options.result !== undefined) {
|
|
21
|
+
task.result_note = options.result;
|
|
22
|
+
}
|
|
23
|
+
if (options.output && options.output.length > 0) {
|
|
24
|
+
task.requested_outputs = options.output;
|
|
25
|
+
}
|
|
26
|
+
task.updated_at = timestamp;
|
|
27
|
+
saveAiSurfaceTask(task, options.cwd);
|
|
28
|
+
console.log(`✔ Surface task updated: [${task.id}] ${task.title}`);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=update-surface-task.js.map
|