brainclaw 1.5.4 → 1.5.5
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 +52 -28
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli.js +35 -5
- package/dist/commands/assignment-resource.js +182 -0
- package/dist/commands/init.js +158 -22
- package/dist/commands/mcp-read-handlers.js +8 -4
- package/dist/commands/mcp.js +7 -3
- package/dist/commands/setup.js +87 -48
- package/dist/commands/switch.js +4 -0
- package/dist/core/agentruns.js +10 -0
- package/dist/core/assignments.js +29 -10
- package/dist/core/context.js +1 -1
- package/dist/core/coordination.js +1 -1
- package/dist/core/entity-operations.js +38 -1
- package/dist/core/entity-registry.js +11 -10
- package/dist/core/schema.js +3 -0
- package/dist/core/store-resolution.js +26 -16
- package/dist/facts.js +3 -3
- package/dist/facts.json +2 -2
- package/docs/cli.md +115 -30
- package/docs/integrations/agents.md +14 -14
- package/docs/integrations/codex.md +15 -12
- package/docs/integrations/mcp.md +10 -4
- package/docs/integrations/overview.md +12 -3
- package/docs/playbooks/productivity/index.md +3 -3
- package/docs/quickstart-existing-project.md +48 -28
- package/docs/quickstart.md +42 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,28 +108,52 @@ If you want the least surprising setup today, use Linux first. If you are on Win
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
-
## Get Started
|
|
112
|
-
|
|
113
|
-
### 1.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
111
|
+
## Get Started
|
|
112
|
+
|
|
113
|
+
### 1. Let your coding agent lead
|
|
114
|
+
|
|
115
|
+
The smoothest first-run path is agent-first:
|
|
116
|
+
|
|
117
|
+
1. ask your coding agent to inspect the package and explain what brainclaw does
|
|
118
|
+
2. ask it to install brainclaw and initialize or join the project you're working on
|
|
119
|
+
3. use the CLI yourself when you need an explicit operator or fallback path
|
|
120
|
+
|
|
121
|
+
If you want to drive setup manually, use the steps below.
|
|
122
|
+
|
|
123
|
+
### 2. Install
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm install -g brainclaw
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 3. Bootstrap this machine
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
brainclaw setup-machine --yes
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This detects the installed agents on the current machine, writes the machine-level MCP and user config Brainclaw manages, and does **not** scan or initialize repositories.
|
|
136
|
+
|
|
137
|
+
### 4. Initialize or refresh the current project
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
cd your-project
|
|
141
|
+
brainclaw init
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`brainclaw init` is now safe to rerun. It creates `.brainclaw/` when the project is new, or refreshes the managed Brainclaw and agent integration files when the project already has memory.
|
|
145
|
+
|
|
146
|
+
If you are explicitly adding another agent to an existing Brainclaw project, use:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
brainclaw enable-agent <agent-name>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 5. Restart your agent
|
|
153
|
+
|
|
154
|
+
Restart your coding agent (or reload MCP servers) so it picks up the new configuration. After that, brainclaw tools are available.
|
|
155
|
+
|
|
156
|
+
### 6. Start working
|
|
133
157
|
|
|
134
158
|
Pick one of the canonical entry points depending on what you're doing:
|
|
135
159
|
|
|
@@ -161,7 +185,7 @@ For agents without MCP (e.g. Copilot reads `.github/copilot-instructions.md`), r
|
|
|
161
185
|
brainclaw export --detect --write
|
|
162
186
|
```
|
|
163
187
|
|
|
164
|
-
###
|
|
188
|
+
### 7. Verify it works
|
|
165
189
|
|
|
166
190
|
```bash
|
|
167
191
|
brainclaw status # see active sessions, claims, plans
|
|
@@ -172,11 +196,11 @@ brainclaw agent-board # see what each agent is doing
|
|
|
172
196
|
|
|
173
197
|
To configure brainclaw for all your repos and agents at once:
|
|
174
198
|
|
|
175
|
-
```bash
|
|
176
|
-
brainclaw setup --yes
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
This
|
|
199
|
+
```bash
|
|
200
|
+
brainclaw setup --yes
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This is the broader multi-repo wizard. It bootstraps the machine, scans your project roots, and initializes selected repositories in one pass.
|
|
180
204
|
|
|
181
205
|
### Existing projects
|
|
182
206
|
|
|
Binary file
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { runInit } from './commands/init.js';
|
|
5
|
-
import { runSetup } from './commands/setup.js';
|
|
5
|
+
import { runSetup, runSetupMachine } from './commands/setup.js';
|
|
6
6
|
import { runUpgrade } from './commands/upgrade.js';
|
|
7
7
|
import { patchAllMcpConfigs } from './core/agent-files.js';
|
|
8
8
|
import { runReconcile } from './commands/reconcile.js';
|
|
@@ -47,6 +47,7 @@ import { cleanupStaleCandidates } from './core/candidates.js';
|
|
|
47
47
|
import { runListClaims } from './commands/list-claims.js';
|
|
48
48
|
import { runReleaseClaim } from './commands/release-claim.js';
|
|
49
49
|
import { runClaimResource } from './commands/claim-resource.js';
|
|
50
|
+
import { runAssignmentResource } from './commands/assignment-resource.js';
|
|
50
51
|
import { runMemoryCommand } from './commands/memory.js';
|
|
51
52
|
import { runReleaseClaims } from './commands/release-claims.js';
|
|
52
53
|
import { runAgentBoard } from './commands/agent-board.js';
|
|
@@ -161,7 +162,7 @@ program
|
|
|
161
162
|
initLogLevel({ verbose: root.verbose, debug: root.debug });
|
|
162
163
|
// Skip effective cwd resolution for commands that create the store
|
|
163
164
|
const cmdName = actionCommand.name();
|
|
164
|
-
const skipResolution = cmdName === 'init' || cmdName === 'setup';
|
|
165
|
+
const skipResolution = cmdName === 'init' || cmdName === 'setup' || cmdName === 'setup-machine';
|
|
165
166
|
// pln#359 phase 1c — `--project <name>` resolves a linked project to an
|
|
166
167
|
// absolute path via resolveProjectCwd, then feeds the same chdir flow
|
|
167
168
|
// as --cwd. Mutually exclusive with --cwd to avoid ambiguity.
|
|
@@ -201,9 +202,9 @@ program
|
|
|
201
202
|
// --- init ---
|
|
202
203
|
program
|
|
203
204
|
.command('init')
|
|
204
|
-
.description('Initialize project memory in .brainclaw/ storage directory')
|
|
205
|
+
.description('Initialize or refresh project memory in .brainclaw/ storage directory')
|
|
205
206
|
.option('-y, --yes', 'Skip interactive wizard and use defaults')
|
|
206
|
-
.option('--force', '
|
|
207
|
+
.option('--force', 'Rebuild managed Brainclaw config and generated files from defaults')
|
|
207
208
|
.option('--compact', 'Enable compact markdown mode')
|
|
208
209
|
.option('--topology <mode>', 'Topology mode: embedded, sidecar, local-only')
|
|
209
210
|
.option('--project-mode <mode>', 'Project mode: single-project, multi-project, auto')
|
|
@@ -217,7 +218,7 @@ program
|
|
|
217
218
|
// --- setup ---
|
|
218
219
|
program
|
|
219
220
|
.command('setup')
|
|
220
|
-
.description('Interactive onboarding wizard —
|
|
221
|
+
.description('Interactive onboarding wizard — machine bootstrap plus multi-repo init')
|
|
221
222
|
.option('--roots <paths>', 'Comma-separated root directories to scan (skips interactive prompt)')
|
|
222
223
|
.option('--agents <agents>', 'Agents to configure: all, detected, or comma-separated names')
|
|
223
224
|
.option('--repos <mode>', 'Repo selection: all, current, or comma-separated numbers')
|
|
@@ -225,6 +226,15 @@ program
|
|
|
225
226
|
.action(async (options) => {
|
|
226
227
|
await runSetup(options);
|
|
227
228
|
});
|
|
229
|
+
// --- setup-machine ---
|
|
230
|
+
program
|
|
231
|
+
.command('setup-machine')
|
|
232
|
+
.description('Machine-only onboarding — detect/configure agents and MCP without scanning or initializing repositories')
|
|
233
|
+
.option('--agents <agents>', 'Agents to configure: all, detected, or comma-separated names')
|
|
234
|
+
.option('-y, --yes', 'Accept all defaults non-interactively')
|
|
235
|
+
.action(async (options) => {
|
|
236
|
+
await runSetupMachine(options);
|
|
237
|
+
});
|
|
228
238
|
// --- memory-log ---
|
|
229
239
|
program
|
|
230
240
|
.command('memory-log')
|
|
@@ -1039,6 +1049,26 @@ program
|
|
|
1039
1049
|
.action((subcommand, args, options) => {
|
|
1040
1050
|
runClaimResource(subcommand, args, { ...options, planStatus: options.planStatus, localOnly: options.localOnly });
|
|
1041
1051
|
});
|
|
1052
|
+
// --- assignment ---
|
|
1053
|
+
program
|
|
1054
|
+
.command('assignment <subcommand> [args...]')
|
|
1055
|
+
.description('Manage work assignments (list, show, update, cancel)')
|
|
1056
|
+
.option('--json', 'Output as JSON for list/show')
|
|
1057
|
+
.option('--all', 'Include terminal assignments in list')
|
|
1058
|
+
.option('--status <status>', 'Status filter for list or target status for update')
|
|
1059
|
+
.option('--agent <agent>', 'Filter by agent name')
|
|
1060
|
+
.option('--claim <id>', 'Filter by linked claim ID')
|
|
1061
|
+
.option('--plan <id>', 'Filter by linked plan ID')
|
|
1062
|
+
.option('--sequence <id>', 'Filter by linked sequence ID')
|
|
1063
|
+
.option('--reason <text>', 'Optional status reason for update/cancel')
|
|
1064
|
+
.action((subcommand, args, options) => {
|
|
1065
|
+
runAssignmentResource(subcommand, args, {
|
|
1066
|
+
...options,
|
|
1067
|
+
claim: options.claim,
|
|
1068
|
+
plan: options.plan,
|
|
1069
|
+
sequence: options.sequence,
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1042
1072
|
// --- list-claims ---
|
|
1043
1073
|
program
|
|
1044
1074
|
.command('list-claims')
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { memoryExists } from '../core/io.js';
|
|
2
|
+
import { listAssignments, loadAssignment, transitionAssignment } from '../core/assignments.js';
|
|
3
|
+
import { resolveCurrentAgentName } from '../core/agent-registry.js';
|
|
4
|
+
import { AssignmentStatusSchema } from '../core/schema.js';
|
|
5
|
+
const TERMINAL_STATUSES = new Set(['completed', 'cancelled', 'expired', 'rerouted']);
|
|
6
|
+
const KNOWN_SUBCOMMANDS = new Set(['list', 'ls', 'show', 'get', 'update', 'cancel']);
|
|
7
|
+
export function runAssignmentResource(subcommand, args, options = {}) {
|
|
8
|
+
const normalized = subcommand.trim().toLowerCase();
|
|
9
|
+
if (normalized === 'list' || normalized === 'ls') {
|
|
10
|
+
runListAssignmentsCommand(options);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (normalized === 'show' || normalized === 'get') {
|
|
14
|
+
const id = args[0];
|
|
15
|
+
if (!id) {
|
|
16
|
+
console.error(`Error: assignment ${normalized} requires <id>.`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
runShowAssignmentCommand(id, options);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (normalized === 'update') {
|
|
23
|
+
const id = args[0];
|
|
24
|
+
if (!id) {
|
|
25
|
+
console.error('Error: assignment update requires <id>.');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const status = options.status;
|
|
29
|
+
if (!status) {
|
|
30
|
+
console.error('Error: assignment update requires --status <status>.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
runTransitionAssignmentCommand(id, status, options);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (normalized === 'cancel') {
|
|
37
|
+
const id = args[0];
|
|
38
|
+
if (!id) {
|
|
39
|
+
console.error('Error: assignment cancel requires <id>.');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
runTransitionAssignmentCommand(id, 'cancelled', options);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (normalized.startsWith('asgn_') || KNOWN_SUBCOMMANDS.has(normalized)) {
|
|
46
|
+
console.error(`Error: unknown assignment subcommand "${subcommand}".`);
|
|
47
|
+
console.error(' Available: list, show, get, update, cancel');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
console.error('Error: missing assignment subcommand.');
|
|
51
|
+
console.error(' Available: list, show, get, update, cancel');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
function runListAssignmentsCommand(options) {
|
|
55
|
+
ensureInitialized(options.cwd);
|
|
56
|
+
const requestedStatus = parseAssignmentStatus(options.status);
|
|
57
|
+
let assignments = listAssignments(options.cwd, {
|
|
58
|
+
status: requestedStatus,
|
|
59
|
+
agent: options.agent,
|
|
60
|
+
claim_id: options.claim,
|
|
61
|
+
plan_id: options.plan,
|
|
62
|
+
sequence_id: options.sequence,
|
|
63
|
+
});
|
|
64
|
+
if (!options.all && !requestedStatus) {
|
|
65
|
+
assignments = assignments.filter((assignment) => !TERMINAL_STATUSES.has(assignment.status));
|
|
66
|
+
}
|
|
67
|
+
if (options.json) {
|
|
68
|
+
console.log(JSON.stringify(assignments, null, 2));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (assignments.length === 0) {
|
|
72
|
+
console.log(options.all ? 'No assignments.' : 'No active assignments.');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(`${assignments.length} ${options.all ? 'assignment(s)' : 'active assignment(s)'}:`);
|
|
76
|
+
console.log('');
|
|
77
|
+
for (const assignment of assignments) {
|
|
78
|
+
const extras = [];
|
|
79
|
+
if (assignment.plan_id)
|
|
80
|
+
extras.push(`plan ${assignment.plan_id}`);
|
|
81
|
+
if (assignment.claim_id)
|
|
82
|
+
extras.push(`claim ${assignment.claim_id}`);
|
|
83
|
+
if (assignment.sequence_id)
|
|
84
|
+
extras.push(`sequence ${assignment.sequence_id}`);
|
|
85
|
+
if (assignment.worktree_path)
|
|
86
|
+
extras.push(`worktree ${assignment.worktree_path}`);
|
|
87
|
+
const suffix = extras.length > 0 ? ` [${extras.join(', ')}]` : '';
|
|
88
|
+
console.log(` [${assignment.id}] ${assignment.agent} (${assignment.status}) -> ${assignment.scope}: ${assignment.description}${suffix}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function runShowAssignmentCommand(id, options) {
|
|
92
|
+
ensureInitialized(options.cwd);
|
|
93
|
+
const assignment = requireAssignment(id, options.cwd);
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(assignment, null, 2));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
console.log(`Assignment: ${assignment.id}`);
|
|
99
|
+
console.log(` Agent: ${assignment.agent}`);
|
|
100
|
+
console.log(` Status: ${assignment.status}`);
|
|
101
|
+
console.log(` Scope: ${assignment.scope}`);
|
|
102
|
+
console.log(` Description: ${assignment.description}`);
|
|
103
|
+
console.log(` Claim: ${assignment.claim_id}`);
|
|
104
|
+
if (assignment.message_id)
|
|
105
|
+
console.log(` Message: ${assignment.message_id}`);
|
|
106
|
+
if (assignment.plan_id)
|
|
107
|
+
console.log(` Plan: ${assignment.plan_id}`);
|
|
108
|
+
if (assignment.sequence_id)
|
|
109
|
+
console.log(` Sequence: ${assignment.sequence_id}`);
|
|
110
|
+
if (assignment.session_id)
|
|
111
|
+
console.log(` Session: ${assignment.session_id}`);
|
|
112
|
+
if (assignment.status_reason)
|
|
113
|
+
console.log(` Reason: ${assignment.status_reason}`);
|
|
114
|
+
if (assignment.worktree_path)
|
|
115
|
+
console.log(` Worktree: ${assignment.worktree_path}`);
|
|
116
|
+
console.log(` Created: ${assignment.created_at}`);
|
|
117
|
+
if (assignment.updated_at)
|
|
118
|
+
console.log(` Updated: ${assignment.updated_at}`);
|
|
119
|
+
if (assignment.offered_at)
|
|
120
|
+
console.log(` Offered: ${assignment.offered_at}`);
|
|
121
|
+
if (assignment.accepted_at)
|
|
122
|
+
console.log(` Accepted: ${assignment.accepted_at}`);
|
|
123
|
+
if (assignment.started_at)
|
|
124
|
+
console.log(` Started: ${assignment.started_at}`);
|
|
125
|
+
if (assignment.completed_at)
|
|
126
|
+
console.log(` Completed: ${assignment.completed_at}`);
|
|
127
|
+
if (assignment.cancelled_at)
|
|
128
|
+
console.log(` Cancelled: ${assignment.cancelled_at}`);
|
|
129
|
+
if (assignment.failed_at)
|
|
130
|
+
console.log(` Failed: ${assignment.failed_at}`);
|
|
131
|
+
if (assignment.blocked_at)
|
|
132
|
+
console.log(` Blocked: ${assignment.blocked_at}`);
|
|
133
|
+
if (assignment.timed_out_at)
|
|
134
|
+
console.log(` Timed out: ${assignment.timed_out_at}`);
|
|
135
|
+
if (assignment.expired_at)
|
|
136
|
+
console.log(` Expired: ${assignment.expired_at}`);
|
|
137
|
+
if (assignment.rerouted_at)
|
|
138
|
+
console.log(` Rerouted: ${assignment.rerouted_at}`);
|
|
139
|
+
}
|
|
140
|
+
function runTransitionAssignmentCommand(id, statusInput, options) {
|
|
141
|
+
ensureInitialized(options.cwd);
|
|
142
|
+
const nextStatus = parseAssignmentStatus(statusInput, 'status');
|
|
143
|
+
const actor = resolveCurrentAgentName(options.cwd);
|
|
144
|
+
try {
|
|
145
|
+
const result = transitionAssignment(id, nextStatus, {
|
|
146
|
+
actor,
|
|
147
|
+
status_reason: options.reason,
|
|
148
|
+
}, options.cwd);
|
|
149
|
+
const verb = nextStatus === 'cancelled' ? 'cancelled' : 'updated';
|
|
150
|
+
console.log(`✔ Assignment ${verb}: [${result.assignment.id}] ${result.previous_status} -> ${result.assignment.status}`);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.error(`Error: ${error.message}`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function ensureInitialized(cwd) {
|
|
158
|
+
if (!memoryExists(cwd)) {
|
|
159
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function requireAssignment(id, cwd) {
|
|
164
|
+
const assignment = loadAssignment(id, cwd);
|
|
165
|
+
if (!assignment) {
|
|
166
|
+
console.error(`Error: assignment not found: ${id}`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
return assignment;
|
|
170
|
+
}
|
|
171
|
+
function parseAssignmentStatus(value, label = 'filter') {
|
|
172
|
+
if (value === undefined) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
const parsed = AssignmentStatusSchema.safeParse(value);
|
|
176
|
+
if (!parsed.success) {
|
|
177
|
+
console.error(`Error: invalid ${label} '${value}'. Expected one of: ${AssignmentStatusSchema.options.join(', ')}`);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
return parsed.data;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=assignment-resource.js.map
|
package/dist/commands/init.js
CHANGED
|
@@ -4,7 +4,7 @@ import readline from 'node:readline/promises';
|
|
|
4
4
|
import { registerAgentIdentity, resolveDefaultAgentName, resolveExistingCurrentAgent } from '../core/agent-registry.js';
|
|
5
5
|
import { MEMORY_DIR, memoryExists, ensureMemoryDir, memoryPath, writeFileAtomic } from '../core/io.js';
|
|
6
6
|
import { emptyState, loadState, saveState } from '../core/state.js';
|
|
7
|
-
import { defaultConfig, saveConfig } from '../core/config.js';
|
|
7
|
+
import { defaultConfig, loadConfig, saveConfig } from '../core/config.js';
|
|
8
8
|
import { generateMarkdown } from '../core/markdown.js';
|
|
9
9
|
import { initMemoryRepo } from '../core/memory-git.js';
|
|
10
10
|
import { buildProjectIdentity, resolveExistingProjectIdentity, saveProjectIdentity } from '../core/project-registry.js';
|
|
@@ -18,6 +18,7 @@ import { buildAiSurfaceInventory, renderAiSurfaceUsageHints } from '../core/ai-s
|
|
|
18
18
|
import { ensureUserStore, hasCompletedSetup } from '../core/setup-state.js';
|
|
19
19
|
import { writeDetectedAgentExport } from './export.js';
|
|
20
20
|
import { writeDetectedAgentHooks } from './hooks.js';
|
|
21
|
+
import { ConfigSchema } from '../core/schema.js';
|
|
21
22
|
export async function runInit(options = {}) {
|
|
22
23
|
const cwd = options.cwd ?? process.cwd();
|
|
23
24
|
const containingMemoryStore = resolveContainingMemoryStore(cwd);
|
|
@@ -58,22 +59,20 @@ export async function runInit(options = {}) {
|
|
|
58
59
|
const existingIdentity = resolveExistingProjectIdentity(cwd);
|
|
59
60
|
const existingCurrentAgent = resolveExistingCurrentAgent(cwd);
|
|
60
61
|
const storageDir = resolveStorageDir(options.storageDir);
|
|
61
|
-
const
|
|
62
|
-
const
|
|
62
|
+
const projectMemoryExists = memoryExists(cwd);
|
|
63
|
+
const existingConfig = projectMemoryExists ? loadExistingConfig(cwd, storageDir) : undefined;
|
|
64
|
+
const topology = resolveTopology(options.topology, existingConfig?.topology);
|
|
65
|
+
const ignoreStrategy = resolveIgnoreStrategy(topology, existingConfig?.ignore_strategy);
|
|
63
66
|
const skipAgentBootstrap = options.skipAgentBootstrap === true || process.env.BRAINCLAW_SKIP_AGENT_BOOTSTRAP === '1';
|
|
64
67
|
const testMode = process.env.BRAINCLAW_TEST_MODE === '1';
|
|
65
68
|
const skipAiSurfaceScan = testMode || options.noAiScan === true || options.aiScan === false;
|
|
66
|
-
if (memoryExists(cwd) && !options.force) {
|
|
67
|
-
console.error('Error: project memory already exists. Use --force to overwrite.');
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
69
|
// Derive project name from directory
|
|
71
70
|
const projectName = path.basename(cwd);
|
|
72
71
|
const shouldAnalyzeRepo = options.analyzeRepo !== false
|
|
73
72
|
&& process.env.BRAINCLAW_SKIP_REPO_ANALYSIS !== '1';
|
|
74
73
|
const analysis = shouldAnalyzeRepo ? analyzeRepository(cwd) : undefined;
|
|
75
|
-
const projectMode = await resolveProjectMode(options, analysis);
|
|
76
|
-
const projectStrategy = await resolveProjectStrategy(options, projectMode);
|
|
74
|
+
const projectMode = await resolveProjectMode(options, analysis, existingConfig?.project_mode);
|
|
75
|
+
const projectStrategy = await resolveProjectStrategy(options, projectMode, existingConfig?.projects?.strategy);
|
|
77
76
|
ensureMemoryDir(cwd, storageDir);
|
|
78
77
|
const currentAgent = registerAgentIdentity({
|
|
79
78
|
agentName: existingCurrentAgent?.agent_name ?? resolveDefaultAgentName(),
|
|
@@ -112,19 +111,21 @@ export async function runInit(options = {}) {
|
|
|
112
111
|
storageDir,
|
|
113
112
|
topology,
|
|
114
113
|
});
|
|
115
|
-
const config =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
const config = buildInitConfig({
|
|
115
|
+
projectName,
|
|
116
|
+
projectIdentity,
|
|
117
|
+
currentAgent: {
|
|
118
|
+
name: currentAgent.agent_name,
|
|
119
|
+
id: currentAgent.agent_id,
|
|
120
|
+
},
|
|
119
121
|
projectMode,
|
|
120
122
|
projectStrategy,
|
|
121
123
|
storageDir,
|
|
122
124
|
topology,
|
|
123
125
|
ignoreStrategy,
|
|
126
|
+
existingConfig: options.force ? undefined : existingConfig,
|
|
127
|
+
compact: options.compact === true,
|
|
124
128
|
});
|
|
125
|
-
if (options.compact) {
|
|
126
|
-
config.markdown = { max_items_per_section: 20, compact_mode: true };
|
|
127
|
-
}
|
|
128
129
|
if (detectedAi && isAgentIntegrationName(detectedAi.name)) {
|
|
129
130
|
upsertAgentIntegrationDeclaration(config, detectedAi.name, 'detected');
|
|
130
131
|
}
|
|
@@ -173,8 +174,19 @@ export async function runInit(options = {}) {
|
|
|
173
174
|
.filter((item) => !item.startsWith('.codeium/'));
|
|
174
175
|
ensureGitignoreEntries(cwd, ['AGENTS.md', '.github/copilot-instructions.md', ...generatedWorkspacePaths, ...BRAINCLAW_EXCLUSIVE_DIRECTORIES]);
|
|
175
176
|
}
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
if (projectMemoryExists) {
|
|
178
|
+
console.log(`✔ Refreshed existing project memory in ${storageDir}/`);
|
|
179
|
+
if (options.force) {
|
|
180
|
+
console.log('✔ Existing memory preserved; rebuilt managed configuration and agent integration files from defaults');
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.log('✔ Existing memory preserved; refreshed managed configuration and agent integration files');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.log(`✔ Initialized project memory in ${storageDir}/`);
|
|
188
|
+
console.log('✔ Created project.md, config.yaml, and split state directories');
|
|
189
|
+
}
|
|
178
190
|
console.log(`✔ Project ID: ${projectIdentity.project_id}`);
|
|
179
191
|
console.log(`✔ Current agent: ${currentAgent.agent_name} (${currentAgent.agent_id})`);
|
|
180
192
|
if (registeredAiAgent) {
|
|
@@ -275,6 +287,12 @@ export async function runInit(options = {}) {
|
|
|
275
287
|
}
|
|
276
288
|
}
|
|
277
289
|
console.log('');
|
|
290
|
+
if (projectMemoryExists) {
|
|
291
|
+
console.log(`Tip: run 'brainclaw enable-agent <agent-name>' when you want to explicitly add another agent to this existing project.`);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
console.log(`Tip: run 'brainclaw init' again later to refresh the detected agent's integration files on this project.`);
|
|
295
|
+
}
|
|
278
296
|
console.log(`Tip: run 'brainclaw context --json' to load the shared memory into your agent session.`);
|
|
279
297
|
}
|
|
280
298
|
function installPostMergeHookIfMissing(cwd) {
|
|
@@ -342,8 +360,19 @@ function looksLikeBrainclawStore(storePath) {
|
|
|
342
360
|
|| fs.existsSync(path.join(storePath, 'project.identity.json'))
|
|
343
361
|
|| fs.existsSync(path.join(storePath, '.git'));
|
|
344
362
|
}
|
|
345
|
-
function resolveTopology(topology) {
|
|
346
|
-
return topology ?? 'embedded';
|
|
363
|
+
function resolveTopology(topology, existingTopology) {
|
|
364
|
+
return topology ?? existingTopology ?? 'embedded';
|
|
365
|
+
}
|
|
366
|
+
function resolveIgnoreStrategy(topology, existingIgnoreStrategy) {
|
|
367
|
+
return existingIgnoreStrategy ?? (topology === 'embedded' ? 'none' : 'project-gitignore');
|
|
368
|
+
}
|
|
369
|
+
function loadExistingConfig(cwd, storageDir) {
|
|
370
|
+
try {
|
|
371
|
+
return loadConfig(cwd, storageDir);
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
347
376
|
}
|
|
348
377
|
function ensureProjectGitignore(cwd, storageDir) {
|
|
349
378
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
@@ -358,10 +387,13 @@ function ensureProjectGitignore(cwd, storageDir) {
|
|
|
358
387
|
: `${current.replace(/\s*$/, '')}\n${ignoreLine}\n`;
|
|
359
388
|
fs.writeFileSync(gitignorePath, next, 'utf-8');
|
|
360
389
|
}
|
|
361
|
-
async function resolveProjectMode(options, analysis) {
|
|
390
|
+
async function resolveProjectMode(options, analysis, existingProjectMode) {
|
|
362
391
|
if (options.projectMode) {
|
|
363
392
|
return options.projectMode;
|
|
364
393
|
}
|
|
394
|
+
if (existingProjectMode) {
|
|
395
|
+
return existingProjectMode;
|
|
396
|
+
}
|
|
365
397
|
if (options.yes || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
366
398
|
return 'auto';
|
|
367
399
|
}
|
|
@@ -384,13 +416,16 @@ async function resolveProjectMode(options, analysis) {
|
|
|
384
416
|
rl.close();
|
|
385
417
|
}
|
|
386
418
|
}
|
|
387
|
-
async function resolveProjectStrategy(options, projectMode) {
|
|
419
|
+
async function resolveProjectStrategy(options, projectMode, existingProjectStrategy) {
|
|
388
420
|
if (options.projectStrategy) {
|
|
389
421
|
return options.projectStrategy;
|
|
390
422
|
}
|
|
391
423
|
if (projectMode !== 'multi-project') {
|
|
392
424
|
return 'manual';
|
|
393
425
|
}
|
|
426
|
+
if (existingProjectStrategy) {
|
|
427
|
+
return existingProjectStrategy;
|
|
428
|
+
}
|
|
394
429
|
if (options.yes || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
395
430
|
return 'manual';
|
|
396
431
|
}
|
|
@@ -438,4 +473,105 @@ function parseProjectStrategy(value) {
|
|
|
438
473
|
return undefined;
|
|
439
474
|
}
|
|
440
475
|
}
|
|
476
|
+
function buildInitConfig(input) {
|
|
477
|
+
const fallbackConfig = defaultConfig(input.projectName, {
|
|
478
|
+
projectId: input.projectIdentity.project_id,
|
|
479
|
+
currentAgent: input.currentAgent.name,
|
|
480
|
+
currentAgentId: input.currentAgent.id,
|
|
481
|
+
projectMode: input.projectMode,
|
|
482
|
+
projectStrategy: input.projectStrategy,
|
|
483
|
+
storageDir: input.storageDir,
|
|
484
|
+
topology: input.topology,
|
|
485
|
+
ignoreStrategy: input.ignoreStrategy,
|
|
486
|
+
});
|
|
487
|
+
const config = input.existingConfig
|
|
488
|
+
? mergeConfigWithDefaults(input.existingConfig, fallbackConfig)
|
|
489
|
+
: fallbackConfig;
|
|
490
|
+
const projects = config.projects ?? fallbackConfig.projects;
|
|
491
|
+
config.project_name = input.projectName;
|
|
492
|
+
config.project_id = input.projectIdentity.project_id;
|
|
493
|
+
config.current_agent = input.currentAgent.name;
|
|
494
|
+
config.current_agent_id = input.currentAgent.id;
|
|
495
|
+
config.storage_dir = input.storageDir;
|
|
496
|
+
config.topology = input.topology;
|
|
497
|
+
config.ignore_strategy = input.ignoreStrategy;
|
|
498
|
+
config.project_mode = input.projectMode;
|
|
499
|
+
config.projects = {
|
|
500
|
+
...projects,
|
|
501
|
+
strategy: input.projectStrategy,
|
|
502
|
+
known: projects.known ?? fallbackConfig.projects.known,
|
|
503
|
+
};
|
|
504
|
+
if (input.compact) {
|
|
505
|
+
const markdown = config.markdown ?? fallbackConfig.markdown ?? {
|
|
506
|
+
max_items_per_section: 20,
|
|
507
|
+
compact_mode: false,
|
|
508
|
+
};
|
|
509
|
+
config.markdown = {
|
|
510
|
+
...markdown,
|
|
511
|
+
compact_mode: true,
|
|
512
|
+
max_items_per_section: Math.min(markdown.max_items_per_section, 20),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
return config;
|
|
516
|
+
}
|
|
517
|
+
function mergeConfigWithDefaults(existingConfig, fallbackConfig) {
|
|
518
|
+
return ConfigSchema.parse({
|
|
519
|
+
...fallbackConfig,
|
|
520
|
+
...existingConfig,
|
|
521
|
+
projects: {
|
|
522
|
+
...fallbackConfig.projects,
|
|
523
|
+
...(existingConfig.projects ?? {}),
|
|
524
|
+
known: existingConfig.projects?.known ?? fallbackConfig.projects.known,
|
|
525
|
+
},
|
|
526
|
+
redaction: {
|
|
527
|
+
...fallbackConfig.redaction,
|
|
528
|
+
...(existingConfig.redaction ?? {}),
|
|
529
|
+
patterns: existingConfig.redaction?.patterns ?? fallbackConfig.redaction.patterns,
|
|
530
|
+
},
|
|
531
|
+
security: existingConfig.security
|
|
532
|
+
? {
|
|
533
|
+
...fallbackConfig.security,
|
|
534
|
+
...existingConfig.security,
|
|
535
|
+
}
|
|
536
|
+
: fallbackConfig.security,
|
|
537
|
+
markdown: existingConfig.markdown
|
|
538
|
+
? {
|
|
539
|
+
...fallbackConfig.markdown,
|
|
540
|
+
...existingConfig.markdown,
|
|
541
|
+
}
|
|
542
|
+
: fallbackConfig.markdown,
|
|
543
|
+
reflective_memory: existingConfig.reflective_memory
|
|
544
|
+
? {
|
|
545
|
+
...fallbackConfig.reflective_memory,
|
|
546
|
+
...existingConfig.reflective_memory,
|
|
547
|
+
}
|
|
548
|
+
: fallbackConfig.reflective_memory,
|
|
549
|
+
governance: fallbackConfig.governance
|
|
550
|
+
? {
|
|
551
|
+
...fallbackConfig.governance,
|
|
552
|
+
...existingConfig.governance,
|
|
553
|
+
curators: existingConfig.governance?.curators ?? fallbackConfig.governance.curators,
|
|
554
|
+
}
|
|
555
|
+
: existingConfig.governance,
|
|
556
|
+
reputation: existingConfig.reputation
|
|
557
|
+
? {
|
|
558
|
+
...fallbackConfig.reputation,
|
|
559
|
+
...existingConfig.reputation,
|
|
560
|
+
}
|
|
561
|
+
: fallbackConfig.reputation,
|
|
562
|
+
agent_integrations: {
|
|
563
|
+
...fallbackConfig.agent_integrations,
|
|
564
|
+
...(existingConfig.agent_integrations ?? {}),
|
|
565
|
+
declarations: existingConfig.agent_integrations?.declarations ?? fallbackConfig.agent_integrations.declarations,
|
|
566
|
+
},
|
|
567
|
+
claims: existingConfig.claims
|
|
568
|
+
? {
|
|
569
|
+
...fallbackConfig.claims,
|
|
570
|
+
...existingConfig.claims,
|
|
571
|
+
}
|
|
572
|
+
: fallbackConfig.claims,
|
|
573
|
+
sensitive_paths: existingConfig.sensitive_paths ?? fallbackConfig.sensitive_paths,
|
|
574
|
+
cross_project_links: existingConfig.cross_project_links ?? fallbackConfig.cross_project_links,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
441
577
|
//# sourceMappingURL=init.js.map
|
|
@@ -68,20 +68,24 @@ function getReviewAssignee(tags) {
|
|
|
68
68
|
return undefined;
|
|
69
69
|
}
|
|
70
70
|
export function handleMcpReadToolCall(name, args = {}, context = {}) {
|
|
71
|
-
|
|
71
|
+
const baseCwd = context.cwd ?? process.cwd();
|
|
72
|
+
let cwd = name === 'bclaw_switch'
|
|
73
|
+
? baseCwd
|
|
74
|
+
: resolveEffectiveCwd({ baseCwd });
|
|
72
75
|
// If a project param is provided, resolve it to an actual cwd override.
|
|
73
76
|
// resolveProjectCwd unifies cross_project_links (siblings/peers) AND
|
|
74
77
|
// workspace store-chain children. Throws on unknown project — surfaces
|
|
75
78
|
// visibly as a tool error rather than silently falling back to the
|
|
76
79
|
// current project, which would mislead the caller.
|
|
77
80
|
const projectArg = args.project;
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
const targetProjectArg = name === 'bclaw_switch' ? undefined : projectArg;
|
|
82
|
+
if (targetProjectArg) {
|
|
83
|
+
cwd = resolveProjectCwd(targetProjectArg, cwd);
|
|
80
84
|
}
|
|
81
85
|
if (name === 'bclaw_get_context') {
|
|
82
86
|
const result = buildContext({
|
|
83
87
|
target: args.path,
|
|
84
|
-
project:
|
|
88
|
+
project: targetProjectArg,
|
|
85
89
|
agent: args.agent,
|
|
86
90
|
host: args.host,
|
|
87
91
|
allHosts: args.allHosts,
|