brainclaw 0.24.0 → 0.27.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/dist/cli.js +33 -2
- package/dist/commands/capability.js +21 -32
- package/dist/commands/check-events.js +36 -0
- package/dist/commands/claim.js +2 -1
- package/dist/commands/context-diff.js +62 -4
- package/dist/commands/discover.js +21 -0
- package/dist/commands/doctor.js +67 -11
- package/dist/commands/explore.js +7 -10
- package/dist/commands/export.js +40 -2
- package/dist/commands/hooks.js +33 -17
- package/dist/commands/init.js +41 -0
- package/dist/commands/mcp.js +417 -75
- package/dist/commands/migrate.js +75 -0
- package/dist/commands/runtime-note.js +1 -0
- package/dist/commands/tool.js +29 -39
- package/dist/core/agent-files.js +13 -0
- package/dist/core/context.js +102 -8
- package/dist/core/coordination.js +25 -0
- package/dist/core/io.js +2 -0
- package/dist/core/migration.js +3 -1
- package/dist/core/project-discovery.js +236 -0
- package/dist/core/registries.js +120 -0
- package/dist/core/schema.js +14 -1
- package/docs/cli.md +7 -5
- package/docs/concepts/plans-and-claims.md +10 -0
- package/docs/integrations/agents.md +2 -1
- package/docs/integrations/mcp.md +1 -1
- package/package.json +7 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project discovery — unified workspace inventory that composes existing
|
|
3
|
+
* scan functions into a single structured profile.
|
|
4
|
+
*
|
|
5
|
+
* Boundary: discovery describes what exists in the workspace RIGHT NOW.
|
|
6
|
+
* It is NOT canonical memory (decisions, traps, plans). It is NOT
|
|
7
|
+
* machine profile (shells, SSH keys, WSL distros). It is the project-level
|
|
8
|
+
* answer to "what MCP servers, skills, hooks, instruction files, and
|
|
9
|
+
* agent integrations are available in this workspace?"
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { buildAgentToolingContext } from './agent-context.js';
|
|
14
|
+
import { assessAgentIntegrationReadiness } from './agent-integrations.js';
|
|
15
|
+
import { loadConfig } from './config.js';
|
|
16
|
+
import { memoryDir } from './io.js';
|
|
17
|
+
import { nowISO } from './ids.js';
|
|
18
|
+
// --- Native instruction file discovery (extracted from bootstrap.ts) ---
|
|
19
|
+
const NATIVE_INSTRUCTION_FILES = [
|
|
20
|
+
'AGENTS.md',
|
|
21
|
+
'CLAUDE.md',
|
|
22
|
+
'GEMINI.md',
|
|
23
|
+
'.windsurfrules',
|
|
24
|
+
'.github/copilot-instructions.md',
|
|
25
|
+
];
|
|
26
|
+
const NATIVE_INSTRUCTION_DIRS = [
|
|
27
|
+
'.cursor/rules',
|
|
28
|
+
'.roo/rules',
|
|
29
|
+
'.continue/rules',
|
|
30
|
+
'.clinerules',
|
|
31
|
+
];
|
|
32
|
+
// MCP config files that agents use
|
|
33
|
+
const MCP_CONFIG_FILES = [
|
|
34
|
+
'.mcp.json',
|
|
35
|
+
'opencode.json',
|
|
36
|
+
'.cursor/mcp.json',
|
|
37
|
+
'.roo/mcp.json',
|
|
38
|
+
'.continue/config.json',
|
|
39
|
+
];
|
|
40
|
+
// Hook config files
|
|
41
|
+
const HOOK_CONFIG_FILES = [
|
|
42
|
+
'.claude/settings.local.json',
|
|
43
|
+
'.cursor/rules/brainclaw-session.mdc',
|
|
44
|
+
];
|
|
45
|
+
export function buildProjectDiscovery(options = {}) {
|
|
46
|
+
const cwd = options.cwd ?? process.cwd();
|
|
47
|
+
const env = options.env ?? process.env;
|
|
48
|
+
// 1. Agent tooling (AGENTS.md, skills, MCP servers)
|
|
49
|
+
const agentTooling = buildAgentToolingContext({ cwd, env });
|
|
50
|
+
// 2. Native instruction files
|
|
51
|
+
const instructionFiles = discoverFiles(cwd, NATIVE_INSTRUCTION_FILES, NATIVE_INSTRUCTION_DIRS);
|
|
52
|
+
// 3. MCP config files
|
|
53
|
+
const mcpConfigs = discoverStaticFiles(cwd, MCP_CONFIG_FILES);
|
|
54
|
+
// 4. Hook config files
|
|
55
|
+
const hookConfigs = discoverStaticFiles(cwd, HOOK_CONFIG_FILES);
|
|
56
|
+
// 5. Integration readiness
|
|
57
|
+
let integrations = [];
|
|
58
|
+
try {
|
|
59
|
+
const config = loadConfig(cwd);
|
|
60
|
+
integrations = assessAgentIntegrationReadiness(config, cwd, env);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// config may not exist yet
|
|
64
|
+
}
|
|
65
|
+
const foundInstructions = instructionFiles.filter(f => f.exists);
|
|
66
|
+
const foundMcpConfigs = mcpConfigs.filter(f => f.exists);
|
|
67
|
+
const foundHookConfigs = hookConfigs.filter(f => f.exists);
|
|
68
|
+
return {
|
|
69
|
+
discovered_at: nowISO(),
|
|
70
|
+
workspace_root: cwd,
|
|
71
|
+
agent_tooling: agentTooling,
|
|
72
|
+
instruction_files: foundInstructions,
|
|
73
|
+
mcp_configs: foundMcpConfigs,
|
|
74
|
+
hook_configs: foundHookConfigs,
|
|
75
|
+
integrations,
|
|
76
|
+
summary: {
|
|
77
|
+
total_instruction_files: foundInstructions.length,
|
|
78
|
+
total_mcp_servers: agentTooling.mcp_servers.length,
|
|
79
|
+
total_skills: agentTooling.skills.length,
|
|
80
|
+
total_mcp_configs: foundMcpConfigs.length,
|
|
81
|
+
total_hook_configs: foundHookConfigs.length,
|
|
82
|
+
integrations_ready: integrations.filter(i => i.ready).length,
|
|
83
|
+
integrations_total: integrations.length,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// --- Persistence ---
|
|
88
|
+
const DISCOVERY_PROFILE_FILE = 'discovery-profile.json';
|
|
89
|
+
export function saveDiscoveryProfile(profile, cwd) {
|
|
90
|
+
const dir = path.join(memoryDir(cwd), 'discovery');
|
|
91
|
+
if (!fs.existsSync(dir)) {
|
|
92
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
const filePath = path.join(dir, DISCOVERY_PROFILE_FILE);
|
|
95
|
+
fs.writeFileSync(filePath, JSON.stringify(profile, null, 2), 'utf-8');
|
|
96
|
+
}
|
|
97
|
+
export function loadDiscoveryProfile(cwd) {
|
|
98
|
+
const filePath = path.join(memoryDir(cwd), 'discovery', DISCOVERY_PROFILE_FILE);
|
|
99
|
+
if (!fs.existsSync(filePath))
|
|
100
|
+
return undefined;
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// --- Rendering ---
|
|
109
|
+
export function renderDiscoverySummary(profile) {
|
|
110
|
+
const lines = [];
|
|
111
|
+
const s = profile.summary;
|
|
112
|
+
lines.push('# Project Discovery');
|
|
113
|
+
lines.push(`Discovered at: ${profile.discovered_at}`);
|
|
114
|
+
lines.push('');
|
|
115
|
+
// Instruction files
|
|
116
|
+
if (profile.instruction_files.length > 0) {
|
|
117
|
+
lines.push(`## Instruction Files (${s.total_instruction_files})`);
|
|
118
|
+
for (const f of profile.instruction_files) {
|
|
119
|
+
const managed = f.managed_by_brainclaw ? ' (managed)' : '';
|
|
120
|
+
lines.push(` ${f.path}${managed}`);
|
|
121
|
+
}
|
|
122
|
+
lines.push('');
|
|
123
|
+
}
|
|
124
|
+
// MCP configs
|
|
125
|
+
if (profile.mcp_configs.length > 0) {
|
|
126
|
+
lines.push(`## MCP Configs (${s.total_mcp_configs})`);
|
|
127
|
+
for (const f of profile.mcp_configs) {
|
|
128
|
+
lines.push(` ${f.path}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push('');
|
|
131
|
+
}
|
|
132
|
+
// MCP servers (from agent tooling)
|
|
133
|
+
if (s.total_mcp_servers > 0) {
|
|
134
|
+
lines.push(`## MCP Servers (${s.total_mcp_servers})`);
|
|
135
|
+
for (const server of profile.agent_tooling.mcp_servers) {
|
|
136
|
+
lines.push(` ${server.name} (${server.transport}, ${server.availability})`);
|
|
137
|
+
}
|
|
138
|
+
lines.push('');
|
|
139
|
+
}
|
|
140
|
+
// Skills
|
|
141
|
+
if (s.total_skills > 0) {
|
|
142
|
+
lines.push(`## Skills (${s.total_skills})`);
|
|
143
|
+
for (const skill of profile.agent_tooling.skills.slice(0, 10)) {
|
|
144
|
+
lines.push(` ${skill.name}${skill.description ? `: ${skill.description}` : ''}`);
|
|
145
|
+
}
|
|
146
|
+
if (s.total_skills > 10) {
|
|
147
|
+
lines.push(` ... and ${s.total_skills - 10} more`);
|
|
148
|
+
}
|
|
149
|
+
lines.push('');
|
|
150
|
+
}
|
|
151
|
+
// Hook configs
|
|
152
|
+
if (profile.hook_configs.length > 0) {
|
|
153
|
+
lines.push(`## Hook Configs (${s.total_hook_configs})`);
|
|
154
|
+
for (const f of profile.hook_configs) {
|
|
155
|
+
lines.push(` ${f.path}`);
|
|
156
|
+
}
|
|
157
|
+
lines.push('');
|
|
158
|
+
}
|
|
159
|
+
// Integrations
|
|
160
|
+
if (profile.integrations.length > 0) {
|
|
161
|
+
lines.push(`## Agent Integrations (${s.integrations_ready}/${s.integrations_total} ready)`);
|
|
162
|
+
for (const integ of profile.integrations) {
|
|
163
|
+
const status = integ.ready ? '✔' : '✗';
|
|
164
|
+
const missing = integ.missing_surfaces.length > 0
|
|
165
|
+
? ` — missing: ${integ.missing_surfaces.map(s => s.kind).join(', ')}`
|
|
166
|
+
: '';
|
|
167
|
+
lines.push(` ${status} ${integ.agent_name}${missing}`);
|
|
168
|
+
}
|
|
169
|
+
lines.push('');
|
|
170
|
+
}
|
|
171
|
+
if (s.total_instruction_files + s.total_mcp_servers + s.total_skills === 0) {
|
|
172
|
+
lines.push('No integration surfaces detected.');
|
|
173
|
+
}
|
|
174
|
+
return lines.join('\n');
|
|
175
|
+
}
|
|
176
|
+
// --- Helpers ---
|
|
177
|
+
function discoverStaticFiles(cwd, files) {
|
|
178
|
+
const results = [];
|
|
179
|
+
for (const relativePath of files) {
|
|
180
|
+
const fullPath = path.join(cwd, relativePath);
|
|
181
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
182
|
+
const stat = fs.statSync(fullPath);
|
|
183
|
+
results.push({
|
|
184
|
+
path: relativePath,
|
|
185
|
+
exists: true,
|
|
186
|
+
size: stat.size,
|
|
187
|
+
managed_by_brainclaw: isManagedByBrainclaw(fullPath),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return results;
|
|
192
|
+
}
|
|
193
|
+
function discoverFiles(cwd, files, dirs) {
|
|
194
|
+
const results = [];
|
|
195
|
+
for (const relativePath of files) {
|
|
196
|
+
const fullPath = path.join(cwd, relativePath);
|
|
197
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
198
|
+
results.push({
|
|
199
|
+
path: relativePath,
|
|
200
|
+
exists: true,
|
|
201
|
+
size: fs.statSync(fullPath).size,
|
|
202
|
+
managed_by_brainclaw: isManagedByBrainclaw(fullPath),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (const relativeDir of dirs) {
|
|
207
|
+
const dir = path.join(cwd, relativeDir);
|
|
208
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
|
|
209
|
+
continue;
|
|
210
|
+
for (const entry of fs.readdirSync(dir).sort()) {
|
|
211
|
+
if (!/\.(md|mdc)$/i.test(entry))
|
|
212
|
+
continue;
|
|
213
|
+
const relativePath = path.posix.join(relativeDir.replace(/\\/g, '/'), entry);
|
|
214
|
+
const fullPath = path.join(cwd, relativePath);
|
|
215
|
+
results.push({
|
|
216
|
+
path: relativePath,
|
|
217
|
+
exists: true,
|
|
218
|
+
size: fs.statSync(fullPath).size,
|
|
219
|
+
managed_by_brainclaw: isManagedByBrainclaw(fullPath),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return results;
|
|
224
|
+
}
|
|
225
|
+
function isManagedByBrainclaw(filePath) {
|
|
226
|
+
try {
|
|
227
|
+
const content = fs.readFileSync(filePath, 'utf-8').slice(0, 200);
|
|
228
|
+
return content.includes('brainclaw') && (content.includes('Managed by brainclaw') ||
|
|
229
|
+
content.includes('BRAINCLAW_SECTION') ||
|
|
230
|
+
content.includes('brainclaw export'));
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=project-discovery.js.map
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedicated registries for project capabilities and tools.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the legacy hack of storing capabilities/tools as decisions
|
|
5
|
+
* with 'capability'/'tool' tags. Items are now persisted as individual
|
|
6
|
+
* JSON files under discovery/capabilities/ and discovery/tools/.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { resolveEntityDir } from './io.js';
|
|
11
|
+
import { loadVersionedJsonFile, saveVersionedJsonFile } from './migration.js';
|
|
12
|
+
import { ProjectCapabilitySchema, ProjectToolSchema } from './schema.js';
|
|
13
|
+
import { generateIdWithLabel, nowISO } from './ids.js';
|
|
14
|
+
import { appendEvent } from './event-log.js';
|
|
15
|
+
// --- Capabilities ---
|
|
16
|
+
export function listCapabilities(cwd) {
|
|
17
|
+
const dir = resolveEntityDir('capabilities', cwd, 'read');
|
|
18
|
+
if (!dir || !fs.existsSync(dir))
|
|
19
|
+
return [];
|
|
20
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
21
|
+
const items = [];
|
|
22
|
+
for (const file of files) {
|
|
23
|
+
try {
|
|
24
|
+
const result = loadVersionedJsonFile('capability', path.join(dir, file));
|
|
25
|
+
items.push(ProjectCapabilitySchema.parse(result.document));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// skip invalid
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return items;
|
|
32
|
+
}
|
|
33
|
+
export function saveCapability(cap, cwd) {
|
|
34
|
+
const dir = resolveEntityDir('capabilities', cwd, 'write');
|
|
35
|
+
if (!fs.existsSync(dir))
|
|
36
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
37
|
+
saveVersionedJsonFile('capability', path.join(dir, `${cap.id}.json`), cap);
|
|
38
|
+
}
|
|
39
|
+
export function deleteCapability(id, cwd) {
|
|
40
|
+
const dir = resolveEntityDir('capabilities', cwd, 'read');
|
|
41
|
+
if (!dir)
|
|
42
|
+
return false;
|
|
43
|
+
const fp = path.join(dir, `${id}.json`);
|
|
44
|
+
if (!fs.existsSync(fp))
|
|
45
|
+
return false;
|
|
46
|
+
fs.unlinkSync(fp);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
export function createCapability(opts, cwd) {
|
|
50
|
+
const idObj = generateIdWithLabel('cap');
|
|
51
|
+
const cap = {
|
|
52
|
+
id: idObj.id,
|
|
53
|
+
name: opts.name,
|
|
54
|
+
description: opts.description,
|
|
55
|
+
category: opts.category ?? 'general',
|
|
56
|
+
tags: opts.tags ?? [],
|
|
57
|
+
status: 'stable',
|
|
58
|
+
created_at: nowISO(),
|
|
59
|
+
author: opts.author,
|
|
60
|
+
author_id: opts.authorId,
|
|
61
|
+
model: opts.model,
|
|
62
|
+
};
|
|
63
|
+
saveCapability(cap, cwd);
|
|
64
|
+
appendEvent({ action: 'create', item_type: 'decision', item_id: cap.id, agent: opts.author, agent_id: opts.authorId, summary: `capability: ${opts.name}` }, cwd);
|
|
65
|
+
return cap;
|
|
66
|
+
}
|
|
67
|
+
// --- Tools ---
|
|
68
|
+
export function listTools(cwd) {
|
|
69
|
+
const dir = resolveEntityDir('tools', cwd, 'read');
|
|
70
|
+
if (!dir || !fs.existsSync(dir))
|
|
71
|
+
return [];
|
|
72
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
|
|
73
|
+
const items = [];
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
try {
|
|
76
|
+
const result = loadVersionedJsonFile('tool', path.join(dir, file));
|
|
77
|
+
items.push(ProjectToolSchema.parse(result.document));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// skip invalid
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return items;
|
|
84
|
+
}
|
|
85
|
+
export function saveTool(tool, cwd) {
|
|
86
|
+
const dir = resolveEntityDir('tools', cwd, 'write');
|
|
87
|
+
if (!fs.existsSync(dir))
|
|
88
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
89
|
+
saveVersionedJsonFile('tool', path.join(dir, `${tool.id}.json`), tool);
|
|
90
|
+
}
|
|
91
|
+
export function deleteTool(id, cwd) {
|
|
92
|
+
const dir = resolveEntityDir('tools', cwd, 'read');
|
|
93
|
+
if (!dir)
|
|
94
|
+
return false;
|
|
95
|
+
const fp = path.join(dir, `${id}.json`);
|
|
96
|
+
if (!fs.existsSync(fp))
|
|
97
|
+
return false;
|
|
98
|
+
fs.unlinkSync(fp);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
export function createTool(opts, cwd) {
|
|
102
|
+
const idObj = generateIdWithLabel('tol');
|
|
103
|
+
const tool = {
|
|
104
|
+
id: idObj.id,
|
|
105
|
+
name: opts.name,
|
|
106
|
+
description: opts.description,
|
|
107
|
+
type: (opts.type ?? 'utility'),
|
|
108
|
+
implementation: opts.implementation ?? '',
|
|
109
|
+
tags: opts.tags ?? [],
|
|
110
|
+
status: 'stable',
|
|
111
|
+
created_at: nowISO(),
|
|
112
|
+
author: opts.author,
|
|
113
|
+
author_id: opts.authorId,
|
|
114
|
+
model: opts.model,
|
|
115
|
+
};
|
|
116
|
+
saveTool(tool, cwd);
|
|
117
|
+
appendEvent({ action: 'create', item_type: 'decision', item_id: tool.id, agent: opts.author, agent_id: opts.authorId, summary: `tool: ${opts.name}` }, cwd);
|
|
118
|
+
return tool;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=registries.js.map
|
package/dist/core/schema.js
CHANGED
|
@@ -42,6 +42,7 @@ export const PrioritySchema = z.enum(['low', 'medium', 'high']);
|
|
|
42
42
|
export const MemoryVisibilitySchema = z.enum(['shared', 'machine', 'private']);
|
|
43
43
|
export const HandoffStatusSchema = z.enum(['open', 'accepted', 'closed']);
|
|
44
44
|
export const DecisionOutcomeSchema = z.enum(['approved', 'rejected', 'deferred', 'pending']);
|
|
45
|
+
export const MemoryScopeSchema = z.enum(['project', 'machine', 'user']).default('project');
|
|
45
46
|
export const ConstraintSchema = z.object({
|
|
46
47
|
schema_version: z.number().int().positive().optional(),
|
|
47
48
|
id: z.string(),
|
|
@@ -56,6 +57,7 @@ export const ConstraintSchema = z.object({
|
|
|
56
57
|
session_id: z.string().optional(),
|
|
57
58
|
status: ConstraintStatusSchema,
|
|
58
59
|
category: ConstraintCategorySchema.optional(),
|
|
60
|
+
scope: MemoryScopeSchema.optional(),
|
|
59
61
|
tags: z.array(z.string()),
|
|
60
62
|
related_paths: z.array(z.string()).optional(),
|
|
61
63
|
expires_at: z.string().optional(),
|
|
@@ -73,6 +75,7 @@ export const DecisionSchema = z.object({
|
|
|
73
75
|
host_id: z.string().optional(),
|
|
74
76
|
session_id: z.string().optional(),
|
|
75
77
|
outcome: DecisionOutcomeSchema.optional(),
|
|
78
|
+
scope: MemoryScopeSchema.optional(),
|
|
76
79
|
related_paths: z.array(z.string()).optional(),
|
|
77
80
|
plan_id: z.string().optional(),
|
|
78
81
|
tags: z.array(z.string()),
|
|
@@ -90,6 +93,7 @@ export const TrapSchema = z.object({
|
|
|
90
93
|
session_id: z.string().optional(),
|
|
91
94
|
status: TrapStatusSchema.default('active'),
|
|
92
95
|
severity: SeveritySchema,
|
|
96
|
+
scope: MemoryScopeSchema.optional(),
|
|
93
97
|
tags: z.array(z.string()),
|
|
94
98
|
related_paths: z.array(z.string()).optional(),
|
|
95
99
|
plan_id: z.string().optional(),
|
|
@@ -122,10 +126,11 @@ export const HandoffSchema = z.object({
|
|
|
122
126
|
}).optional(),
|
|
123
127
|
});
|
|
124
128
|
export const PlanStatusSchema = z.enum(['todo', 'in_progress', 'blocked', 'done', 'dropped']);
|
|
129
|
+
export const PlanStepStatusSchema = z.enum(['todo', 'in_progress', 'testing', 'done', 'blocked']);
|
|
125
130
|
export const PlanStepSchema = z.object({
|
|
126
131
|
id: z.string(),
|
|
127
132
|
text: z.string(),
|
|
128
|
-
status:
|
|
133
|
+
status: PlanStepStatusSchema.default('todo'),
|
|
129
134
|
assignee: z.string().optional(),
|
|
130
135
|
created_at: z.string(),
|
|
131
136
|
updated_at: z.string(),
|
|
@@ -164,6 +169,7 @@ export const InstructionEntrySchema = z.object({
|
|
|
164
169
|
created_at: z.string(),
|
|
165
170
|
updated_at: z.string(),
|
|
166
171
|
author: z.string(),
|
|
172
|
+
model: z.string().optional(),
|
|
167
173
|
tags: z.array(z.string()).default([]),
|
|
168
174
|
active: z.boolean().default(true),
|
|
169
175
|
supersedes: z.string().optional(),
|
|
@@ -184,6 +190,7 @@ export const ProjectCapabilitySchema = z.object({
|
|
|
184
190
|
created_at: z.string(),
|
|
185
191
|
author: z.string(),
|
|
186
192
|
author_id: z.string().optional(),
|
|
193
|
+
model: z.string().optional(),
|
|
187
194
|
});
|
|
188
195
|
export const ToolTypeSchema = z.enum(['workflow', 'validator', 'generator', 'utility', 'explorer']);
|
|
189
196
|
export const ProjectToolSchema = z.object({
|
|
@@ -204,6 +211,7 @@ export const ProjectToolSchema = z.object({
|
|
|
204
211
|
created_at: z.string(),
|
|
205
212
|
author: z.string(),
|
|
206
213
|
author_id: z.string().optional(),
|
|
214
|
+
model: z.string().optional(),
|
|
207
215
|
});
|
|
208
216
|
// --- State schema ---
|
|
209
217
|
export const StateSchema = z.object({
|
|
@@ -258,6 +266,7 @@ export const CandidateSchema = z.object({
|
|
|
258
266
|
created_at: z.string(),
|
|
259
267
|
author: z.string(),
|
|
260
268
|
author_id: z.string().optional(),
|
|
269
|
+
model: z.string().optional(),
|
|
261
270
|
project_id: z.string().optional(),
|
|
262
271
|
host_id: z.string().optional(),
|
|
263
272
|
session_id: z.string().optional(),
|
|
@@ -326,6 +335,7 @@ export const ClaimSchema = z.object({
|
|
|
326
335
|
status: ClaimStatusSchema,
|
|
327
336
|
released_at: z.string().optional(),
|
|
328
337
|
expires_at: z.string().optional(),
|
|
338
|
+
model: z.string().optional(),
|
|
329
339
|
});
|
|
330
340
|
// --- Runtime notes schemas ---
|
|
331
341
|
export const RuntimeNoteSchema = z.object({
|
|
@@ -344,6 +354,7 @@ export const RuntimeNoteSchema = z.object({
|
|
|
344
354
|
host_id: z.string().optional(),
|
|
345
355
|
expires_at: z.string().optional(),
|
|
346
356
|
note_type: z.enum(['observation', 'session_start', 'session_end']).default('observation'),
|
|
357
|
+
model: z.string().optional(),
|
|
347
358
|
});
|
|
348
359
|
// --- AI surface task request schemas ---
|
|
349
360
|
export const AiSurfaceTaskStatusSchema = z.enum(['queued', 'in_progress', 'completed', 'cancelled', 'failed']);
|
|
@@ -369,6 +380,7 @@ export const AiSurfaceTaskRequestSchema = z.object({
|
|
|
369
380
|
claimed_at: z.string().optional(),
|
|
370
381
|
completed_at: z.string().optional(),
|
|
371
382
|
result_note: z.string().optional(),
|
|
383
|
+
model: z.string().optional(),
|
|
372
384
|
});
|
|
373
385
|
// --- Runtime event schemas ---
|
|
374
386
|
export const RuntimeEventTypeSchema = z.enum([
|
|
@@ -398,6 +410,7 @@ export const RuntimeEventSchema = z.object({
|
|
|
398
410
|
to: z.string().optional(),
|
|
399
411
|
related_paths: z.array(z.string()).optional(),
|
|
400
412
|
metadata: z.record(z.unknown()).optional(),
|
|
413
|
+
model: z.string().optional(),
|
|
401
414
|
});
|
|
402
415
|
// --- Profile schema ---
|
|
403
416
|
export const ProfileSchema = z.enum(['dev', 'openclaw', 'ops', 'research']);
|
package/docs/cli.md
CHANGED
|
@@ -86,7 +86,7 @@ brainclaw setup --roots ~/Projects,~/work --agents all # all agents, multiple r
|
|
|
86
86
|
|
|
87
87
|
### `brainclaw init`
|
|
88
88
|
|
|
89
|
-
Initialize workspace state for the current project root. Detects the AI agent environment
|
|
89
|
+
Initialize workspace state for the current project root. Detects the AI agent environment, writes to its native instruction file, and installs a git post-merge hook for automatic claim release. `brainclaw setup` must have been run first on this machine. Do not run `init` from inside `.brainclaw/`; that directory is Brainclaw's own memory store, not a project root.
|
|
90
90
|
|
|
91
91
|
| Option | Description |
|
|
92
92
|
|---|---|
|
|
@@ -993,13 +993,13 @@ brainclaw context --since-session --max-items 20
|
|
|
993
993
|
|
|
994
994
|
### `brainclaw context-diff`
|
|
995
995
|
|
|
996
|
-
|
|
996
|
+
Hybrid context view for subsequent prompts: always includes **critical anchors** (active claims, resolved instructions, top 5 traps) plus the memory delta since a reference point. Used by the Claude Code UserPromptSubmit hook after the first prompt.
|
|
997
997
|
|
|
998
998
|
| Option | Description |
|
|
999
999
|
|---|---|
|
|
1000
1000
|
| `--since <date>` | Start date for the diff |
|
|
1001
1001
|
| `--session <id>` | Compare against a specific session start |
|
|
1002
|
-
| `--json` | Output as JSON |
|
|
1002
|
+
| `--json` | Output as JSON (includes anchors + changed items) |
|
|
1003
1003
|
|
|
1004
1004
|
```bash
|
|
1005
1005
|
brainclaw context-diff --session sess_42
|
|
@@ -1127,6 +1127,7 @@ Export memory as a native agent instruction file.
|
|
|
1127
1127
|
|---|---|
|
|
1128
1128
|
| `--format <format>` | Target format: `copilot-instructions`, `cursor-rules`, `agents-md`, `claude-md`, `gemini-md`, `windsurf`, `cline`, `roo`, or `continue` |
|
|
1129
1129
|
| `--detect` | Auto-detect the running agent and write to its native file |
|
|
1130
|
+
| `--all` | Write all known agent instruction files at once (deduplicates by format) |
|
|
1130
1131
|
| `--write` | Write output to the native file path (instead of stdout); generated workspace files are treated as local and added to `.gitignore` |
|
|
1131
1132
|
| `--shared` | Keep the main exported instruction file versionable when used with `--write`; companion MCP/settings files stay local |
|
|
1132
1133
|
| `--output <path>` | Write to a custom output path |
|
|
@@ -1146,6 +1147,7 @@ brainclaw export --format roo --write # .roo/rules/brainclaw.
|
|
|
1146
1147
|
brainclaw export --format continue --write # .continue/rules/brainclaw.md
|
|
1147
1148
|
brainclaw export --format claude-md --write --shared # publish CLAUDE.md intentionally
|
|
1148
1149
|
brainclaw export --format claude-md # stdout
|
|
1150
|
+
brainclaw export --all # all 9 agent files at once
|
|
1149
1151
|
```
|
|
1150
1152
|
|
|
1151
1153
|
`brainclaw export --write` is local-first by default: the generated workspace file and any companion MCP/settings files are added to `.gitignore`. Use `--shared` only when you intentionally want the main instruction file to be committed. `brainclaw export --detect` also writes companion MCP config where relevant, including `opencode.json` for OpenCode and `.gemini/antigravity/mcp_config.json` for Antigravity/Gemini when the local environment is available.
|
|
@@ -1174,7 +1176,7 @@ See [adapters/openclaw.md](adapters/openclaw.md).
|
|
|
1174
1176
|
|
|
1175
1177
|
### `brainclaw install-hooks`
|
|
1176
1178
|
|
|
1177
|
-
Install Git hooks
|
|
1179
|
+
Install Git hooks: pre-commit (constraint checking, sensitive content detection) and post-merge (auto-release of claims whose scope overlaps merged files).
|
|
1178
1180
|
|
|
1179
1181
|
| Option | Description |
|
|
1180
1182
|
|---|---|
|
|
@@ -1391,7 +1393,7 @@ brainclaw mcp
|
|
|
1391
1393
|
| `bclaw_get_agent_board` | Live plan + claim board with active sessions |
|
|
1392
1394
|
| `bclaw_search` | Full-text BM25 search across all memory items |
|
|
1393
1395
|
| `bclaw_estimation_report` | Estimation accuracy report for completed plans |
|
|
1394
|
-
| `bclaw_list_plans` | List plan items with
|
|
1396
|
+
| `bclaw_list_plans` | List plan items with filters, pagination (`limit`/`offset`), `compact` mode, and direct `id` lookup |
|
|
1395
1397
|
| `bclaw_list_claims` | List claims with the same filters as `brainclaw claim list` |
|
|
1396
1398
|
| `bclaw_list_agents` | List registered agents, optionally with bounded reputation summaries |
|
|
1397
1399
|
| `bclaw_list_instructions` | List raw or resolved shared instructions |
|
|
@@ -106,6 +106,16 @@ brainclaw claim release <id>
|
|
|
106
106
|
|
|
107
107
|
`claim list` shows who holds each claim and whether it is still active. If a claim has a `session_id`, the last 8 characters are shown so you can correlate with the agent session that created it.
|
|
108
108
|
|
|
109
|
+
### Automatic claim release
|
|
110
|
+
|
|
111
|
+
Claims are automatically released after a `git merge` if the post-merge hook is installed (default since `brainclaw init` v0.25.3+). The hook matches merged file paths against active claim scopes and releases overlapping claims.
|
|
112
|
+
|
|
113
|
+
You can also install or reinstall the hook manually:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
brainclaw install-hooks
|
|
117
|
+
```
|
|
118
|
+
|
|
109
119
|
## Why claims matter
|
|
110
120
|
|
|
111
121
|
Without claims, multiple agents can easily touch the same area at once and generate conflicting changes.
|
|
@@ -83,7 +83,8 @@ brainclaw export --detect --write # auto-detect and write all formats
|
|
|
83
83
|
When brainclaw memory changes (new constraints, resolved traps, updated plans), regenerate instruction files:
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
|
-
brainclaw export --
|
|
86
|
+
brainclaw export --all # all 9 agent formats at once
|
|
87
|
+
brainclaw export --detect --write # only the detected agent
|
|
87
88
|
```
|
|
88
89
|
|
|
89
90
|
For agents without MCP (Copilot), this is especially important — the instruction file is their only source of project context.
|
package/docs/integrations/mcp.md
CHANGED
|
@@ -42,7 +42,7 @@ This keeps session continuity inside Brainclaw instead of pushing the agent back
|
|
|
42
42
|
| `bclaw_write_note` | Record a runtime note, supports `autoReflect: true` |
|
|
43
43
|
| `bclaw_read_handoff` | Read active handoffs |
|
|
44
44
|
| `bclaw_get_agent_board` | Coordination snapshot |
|
|
45
|
-
| `bclaw_list_plans` | Structured plan listing with
|
|
45
|
+
| `bclaw_list_plans` | Structured plan listing with filters, pagination (`limit`/`offset`), `compact` mode, and `id` lookup |
|
|
46
46
|
| `bclaw_list_claims` | Structured claim listing with CLI-equivalent filters |
|
|
47
47
|
| `bclaw_list_agents` | Registered agent inventory, optionally with bounded reputation |
|
|
48
48
|
| `bclaw_list_instructions` | Raw or resolved instruction listing |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Shared project memory for humans and coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
"README.md",
|
|
14
14
|
"LICENSE"
|
|
15
15
|
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.0.0"
|
|
21
|
+
},
|
|
16
22
|
"scripts": {
|
|
17
23
|
"build": "tsc",
|
|
18
24
|
"dev": "tsc && node dist/cli.js",
|