brainclaw 0.25.3 → 0.28.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 +14 -1
- package/dist/cli.js +32 -2
- package/dist/commands/capability.js +21 -32
- package/dist/commands/check-events.js +36 -0
- package/dist/commands/discover.js +21 -0
- package/dist/commands/doctor.js +67 -11
- package/dist/commands/explore.js +7 -10
- package/dist/commands/hooks.js +33 -17
- package/dist/commands/mcp.js +380 -70
- package/dist/commands/migrate.js +75 -0
- package/dist/commands/tool.js +29 -39
- package/dist/core/agent-capability.js +55 -110
- package/dist/core/agent-files.js +13 -0
- package/dist/core/agent-integrations.js +17 -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 +11 -2
- package/docs/integrations/agents.md +10 -3
- package/package.json +7 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { loadState, persistState } from '../core/state.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
|
+
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
4
|
+
import { appendAuditEntry } from '../core/audit.js';
|
|
5
|
+
import { resolveCurrentAgentName } from '../core/agent-registry.js';
|
|
6
|
+
export function runMigrate(options = {}) {
|
|
7
|
+
const cwd = options.cwd ?? process.cwd();
|
|
8
|
+
if (!memoryExists(cwd)) {
|
|
9
|
+
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
if (options.promoteMachineItems) {
|
|
13
|
+
promoteMachineItems(cwd, options.dryRun ?? false);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
console.log('Usage: brainclaw migrate --promote-machine-items [--dry-run]');
|
|
17
|
+
console.log('');
|
|
18
|
+
console.log('Moves items tagged scope:machine from project store to user store (~/.brainclaw/).');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function promoteMachineItems(cwd, dryRun) {
|
|
22
|
+
const state = loadState(cwd);
|
|
23
|
+
const agent = resolveCurrentAgentName(cwd);
|
|
24
|
+
const machineConstraints = state.active_constraints.filter((c) => c.scope === 'machine');
|
|
25
|
+
const machineDecisions = state.recent_decisions.filter((d) => d.scope === 'machine');
|
|
26
|
+
const machineTraps = state.known_traps.filter((t) => t.scope === 'machine');
|
|
27
|
+
const total = machineConstraints.length + machineDecisions.length + machineTraps.length;
|
|
28
|
+
if (total === 0) {
|
|
29
|
+
console.log('No machine-scoped items found in project store.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.log(`Found ${total} machine-scoped item(s) in project store:\n`);
|
|
33
|
+
for (const c of machineConstraints)
|
|
34
|
+
console.log(` [constraint] ${c.id} — ${c.text.slice(0, 80)}`);
|
|
35
|
+
for (const d of machineDecisions)
|
|
36
|
+
console.log(` [decision] ${d.id} — ${d.text.slice(0, 80)}`);
|
|
37
|
+
for (const t of machineTraps)
|
|
38
|
+
console.log(` [trap] ${t.id} — ${t.text.slice(0, 80)}`);
|
|
39
|
+
if (dryRun) {
|
|
40
|
+
console.log(`\n(dry-run) Would move ${total} item(s) to user store.`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Resolve user store
|
|
44
|
+
let userCwd;
|
|
45
|
+
try {
|
|
46
|
+
userCwd = resolveTargetStore(cwd, 'user');
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
console.error('Error: cannot resolve user store. Run `brainclaw setup` first.');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const userState = loadState(userCwd);
|
|
53
|
+
// Move constraints
|
|
54
|
+
for (const c of machineConstraints) {
|
|
55
|
+
userState.active_constraints.push(c);
|
|
56
|
+
state.active_constraints = state.active_constraints.filter((x) => x.id !== c.id);
|
|
57
|
+
appendAuditEntry({ actor: agent, action: 'update', item_id: c.id, item_type: 'constraint', reason: 'promote to user store (machine scope)' }, cwd);
|
|
58
|
+
}
|
|
59
|
+
// Move decisions
|
|
60
|
+
for (const d of machineDecisions) {
|
|
61
|
+
userState.recent_decisions.push(d);
|
|
62
|
+
state.recent_decisions = state.recent_decisions.filter((x) => x.id !== d.id);
|
|
63
|
+
appendAuditEntry({ actor: agent, action: 'update', item_id: d.id, item_type: 'decision', reason: 'promote to user store (machine scope)' }, cwd);
|
|
64
|
+
}
|
|
65
|
+
// Move traps
|
|
66
|
+
for (const t of machineTraps) {
|
|
67
|
+
userState.known_traps.push(t);
|
|
68
|
+
state.known_traps = state.known_traps.filter((x) => x.id !== t.id);
|
|
69
|
+
appendAuditEntry({ actor: agent, action: 'update', item_id: t.id, item_type: 'trap', reason: 'promote to user store (machine scope)' }, cwd);
|
|
70
|
+
}
|
|
71
|
+
persistState(userState, userCwd);
|
|
72
|
+
persistState(state, cwd);
|
|
73
|
+
console.log(`\n✔ Promoted ${total} item(s) to user store (${userCwd})`);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=migrate.js.map
|
package/dist/commands/tool.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { loadState, persistState } from '../core/state.js';
|
|
2
1
|
import { resolveCurrentAgentName } from '../core/agent-registry.js';
|
|
3
2
|
import { memoryExists } from '../core/io.js';
|
|
4
|
-
import { generateIdWithLabel, nowISO } from '../core/ids.js';
|
|
5
3
|
import { loadConfig } from '../core/config.js';
|
|
6
4
|
import { scanText } from '../core/security.js';
|
|
7
5
|
import { validateCliInput } from '../core/input-validation.js';
|
|
8
6
|
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
7
|
+
import { listTools, createTool } from '../core/registries.js';
|
|
9
8
|
export function runTool(subcommand, args, options = {}) {
|
|
10
9
|
const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
|
|
11
10
|
if (!memoryExists(cwd)) {
|
|
@@ -46,10 +45,7 @@ export function runTool(subcommand, args, options = {}) {
|
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
47
|
function runToolList(cwd) {
|
|
49
|
-
const
|
|
50
|
-
const tools = state.recent_decisions
|
|
51
|
-
.filter((d) => d.tags.includes('tool'))
|
|
52
|
-
.map((d) => ({ id: d.id, name: d.text.split('\n')[0], type: d.tags.find((t) => t !== 'tool') }));
|
|
48
|
+
const tools = listTools(cwd);
|
|
53
49
|
if (tools.length === 0) {
|
|
54
50
|
console.log('No tools registered yet.');
|
|
55
51
|
return;
|
|
@@ -57,9 +53,7 @@ function runToolList(cwd) {
|
|
|
57
53
|
console.log(`\n${tools.length} tool(s):\n`);
|
|
58
54
|
tools.forEach((tool) => {
|
|
59
55
|
console.log(` [${tool.id}] ${tool.name}`);
|
|
60
|
-
|
|
61
|
-
console.log(` type: ${tool.type}`);
|
|
62
|
-
}
|
|
56
|
+
console.log(` type: ${tool.type}`);
|
|
63
57
|
});
|
|
64
58
|
console.log('');
|
|
65
59
|
}
|
|
@@ -74,50 +68,46 @@ function runToolAdd(name, description, options, cwd) {
|
|
|
74
68
|
process.exit(1);
|
|
75
69
|
}
|
|
76
70
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
short_label,
|
|
83
|
-
text: name,
|
|
84
|
-
created_at: nowISO(),
|
|
71
|
+
const tool = createTool({
|
|
72
|
+
name,
|
|
73
|
+
description,
|
|
74
|
+
type: options.type,
|
|
75
|
+
tags: options.tag,
|
|
85
76
|
author: options.author ?? resolveCurrentAgentName(cwd),
|
|
86
|
-
|
|
87
|
-
};
|
|
88
|
-
// For now, store as decision to avoid schema migration
|
|
89
|
-
// Will migrate to separate tool storage in v0.16
|
|
90
|
-
state.recent_decisions.push(entry);
|
|
91
|
-
persistState(state, cwd);
|
|
92
|
-
console.log(`✔ Tool added: [${id}] ${name}`);
|
|
93
|
-
console.log(' (Stored in decisions for now; will move to dedicated registry in v0.16)');
|
|
77
|
+
}, cwd);
|
|
78
|
+
console.log(`✔ Tool added: [${tool.id}] ${name} (${tool.type})`);
|
|
94
79
|
}
|
|
95
80
|
function runToolDescribe(toolId, cwd) {
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
if (!
|
|
81
|
+
const tools = listTools(cwd);
|
|
82
|
+
const tool = tools.find((t) => t.id === toolId || t.id.startsWith(toolId));
|
|
83
|
+
if (!tool) {
|
|
99
84
|
console.error(`Error: tool '${toolId}' not found`);
|
|
100
85
|
process.exit(1);
|
|
101
86
|
}
|
|
102
|
-
console.log(`\nTool: ${
|
|
103
|
-
console.log(`
|
|
104
|
-
console.log(`
|
|
105
|
-
console.log(`
|
|
106
|
-
console.log(`
|
|
87
|
+
console.log(`\nTool: ${tool.name}`);
|
|
88
|
+
console.log(`Description: ${tool.description}`);
|
|
89
|
+
console.log(`ID: ${tool.id}`);
|
|
90
|
+
console.log(`Type: ${tool.type}`);
|
|
91
|
+
console.log(`Author: ${tool.author}`);
|
|
92
|
+
console.log(`Created: ${tool.created_at}`);
|
|
93
|
+
if (tool.tags.length > 0) {
|
|
94
|
+
console.log(`Tags: ${tool.tags.join(', ')}`);
|
|
95
|
+
}
|
|
107
96
|
console.log('');
|
|
108
97
|
}
|
|
109
98
|
function runToolSearch(query, cwd) {
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
const results = tools.filter((tool) => tool.
|
|
113
|
-
tool.
|
|
99
|
+
const tools = listTools(cwd);
|
|
100
|
+
const queryLower = query.toLowerCase();
|
|
101
|
+
const results = tools.filter((tool) => tool.name.toLowerCase().includes(queryLower) ||
|
|
102
|
+
tool.description.toLowerCase().includes(queryLower) ||
|
|
103
|
+
tool.tags.some((tag) => tag.toLowerCase().includes(queryLower)));
|
|
114
104
|
if (results.length === 0) {
|
|
115
105
|
console.log(`No tools found matching '${query}'`);
|
|
116
106
|
return;
|
|
117
107
|
}
|
|
118
108
|
console.log(`\n${results.length} tool(s) matching '${query}':\n`);
|
|
119
|
-
results.forEach((
|
|
120
|
-
console.log(` [${
|
|
109
|
+
results.forEach((tool) => {
|
|
110
|
+
console.log(` [${tool.id}] ${tool.name} (${tool.type})`);
|
|
121
111
|
});
|
|
122
112
|
console.log('');
|
|
123
113
|
}
|
|
@@ -9,137 +9,82 @@
|
|
|
9
9
|
* C (limited) — no MCP → rich static content (plans, traps, decisions)
|
|
10
10
|
*/
|
|
11
11
|
const PROFILES = {
|
|
12
|
+
// --- Code agents (interactive, IDE-driven) ---
|
|
12
13
|
'claude-code': {
|
|
13
|
-
name: 'claude-code',
|
|
14
|
-
hasMcp: true,
|
|
15
|
-
|
|
16
|
-
hasAutoApprove: true,
|
|
17
|
-
hasSkills: true,
|
|
18
|
-
hasRules: true,
|
|
19
|
-
instructionFile: 'CLAUDE.md',
|
|
20
|
-
sharedInstructionFile: true,
|
|
21
|
-
mcpConfigScope: 'both',
|
|
22
|
-
templateTier: 'A',
|
|
14
|
+
name: 'claude-code', category: 'code-agent', workflowModel: 'interactive',
|
|
15
|
+
hasMcp: true, hasHooks: true, hasAutoApprove: true, hasSkills: true, hasRules: true,
|
|
16
|
+
instructionFile: 'CLAUDE.md', sharedInstructionFile: true, mcpConfigScope: 'both', templateTier: 'A',
|
|
23
17
|
},
|
|
24
18
|
cursor: {
|
|
25
|
-
name: 'cursor',
|
|
26
|
-
hasMcp: true,
|
|
27
|
-
|
|
28
|
-
hasAutoApprove: false,
|
|
29
|
-
hasSkills: false,
|
|
30
|
-
hasRules: true,
|
|
31
|
-
instructionFile: '.cursor/rules/brainclaw.md',
|
|
32
|
-
sharedInstructionFile: false,
|
|
33
|
-
mcpConfigScope: 'machine',
|
|
34
|
-
templateTier: 'B',
|
|
19
|
+
name: 'cursor', category: 'code-agent', workflowModel: 'interactive',
|
|
20
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: false, hasSkills: false, hasRules: true,
|
|
21
|
+
instructionFile: '.cursor/rules/brainclaw.md', sharedInstructionFile: false, mcpConfigScope: 'machine', templateTier: 'B',
|
|
35
22
|
},
|
|
36
23
|
windsurf: {
|
|
37
|
-
name: 'windsurf',
|
|
38
|
-
hasMcp: true,
|
|
39
|
-
|
|
40
|
-
hasAutoApprove: false,
|
|
41
|
-
hasSkills: false,
|
|
42
|
-
hasRules: true,
|
|
43
|
-
instructionFile: '.windsurfrules',
|
|
44
|
-
sharedInstructionFile: true,
|
|
45
|
-
mcpConfigScope: 'machine',
|
|
46
|
-
templateTier: 'B',
|
|
24
|
+
name: 'windsurf', category: 'code-agent', workflowModel: 'interactive',
|
|
25
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: false, hasSkills: false, hasRules: true,
|
|
26
|
+
instructionFile: '.windsurfrules', sharedInstructionFile: true, mcpConfigScope: 'machine', templateTier: 'B',
|
|
47
27
|
},
|
|
48
28
|
cline: {
|
|
49
|
-
name: 'cline',
|
|
50
|
-
hasMcp: true,
|
|
51
|
-
|
|
52
|
-
hasAutoApprove: true,
|
|
53
|
-
hasSkills: false,
|
|
54
|
-
hasRules: true,
|
|
55
|
-
instructionFile: '.clinerules/brainclaw.md',
|
|
56
|
-
sharedInstructionFile: false,
|
|
57
|
-
mcpConfigScope: 'project',
|
|
58
|
-
templateTier: 'B',
|
|
29
|
+
name: 'cline', category: 'code-agent', workflowModel: 'interactive',
|
|
30
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: true, hasSkills: false, hasRules: true,
|
|
31
|
+
instructionFile: '.clinerules/brainclaw.md', sharedInstructionFile: false, mcpConfigScope: 'project', templateTier: 'B',
|
|
59
32
|
},
|
|
60
33
|
roo: {
|
|
61
|
-
name: 'roo',
|
|
62
|
-
hasMcp: true,
|
|
63
|
-
|
|
64
|
-
hasAutoApprove: true,
|
|
65
|
-
hasSkills: false,
|
|
66
|
-
hasRules: true,
|
|
67
|
-
instructionFile: '.roo/rules/brainclaw.md',
|
|
68
|
-
sharedInstructionFile: false,
|
|
69
|
-
mcpConfigScope: 'project',
|
|
70
|
-
templateTier: 'B',
|
|
34
|
+
name: 'roo', category: 'code-agent', workflowModel: 'interactive',
|
|
35
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: true, hasSkills: false, hasRules: true,
|
|
36
|
+
instructionFile: '.roo/rules/brainclaw.md', sharedInstructionFile: false, mcpConfigScope: 'project', templateTier: 'B',
|
|
71
37
|
},
|
|
72
38
|
continue: {
|
|
73
|
-
name: 'continue',
|
|
74
|
-
hasMcp: true,
|
|
75
|
-
|
|
76
|
-
hasAutoApprove: false,
|
|
77
|
-
hasSkills: false,
|
|
78
|
-
hasRules: true,
|
|
79
|
-
instructionFile: '.continue/rules/brainclaw.md',
|
|
80
|
-
sharedInstructionFile: false,
|
|
81
|
-
mcpConfigScope: 'both',
|
|
82
|
-
templateTier: 'B',
|
|
39
|
+
name: 'continue', category: 'code-agent', workflowModel: 'interactive',
|
|
40
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: false, hasSkills: false, hasRules: true,
|
|
41
|
+
instructionFile: '.continue/rules/brainclaw.md', sharedInstructionFile: false, mcpConfigScope: 'both', templateTier: 'B',
|
|
83
42
|
},
|
|
84
43
|
opencode: {
|
|
85
|
-
name: 'opencode',
|
|
86
|
-
hasMcp: true,
|
|
87
|
-
|
|
88
|
-
hasAutoApprove: false,
|
|
89
|
-
hasSkills: false,
|
|
90
|
-
hasRules: true,
|
|
91
|
-
instructionFile: 'AGENTS.md',
|
|
92
|
-
sharedInstructionFile: true,
|
|
93
|
-
mcpConfigScope: 'project',
|
|
94
|
-
templateTier: 'B',
|
|
44
|
+
name: 'opencode', category: 'code-agent', workflowModel: 'interactive',
|
|
45
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: false, hasSkills: false, hasRules: true,
|
|
46
|
+
instructionFile: 'AGENTS.md', sharedInstructionFile: true, mcpConfigScope: 'project', templateTier: 'B',
|
|
95
47
|
},
|
|
96
48
|
codex: {
|
|
97
|
-
name: 'codex',
|
|
98
|
-
hasMcp: true,
|
|
99
|
-
|
|
100
|
-
hasAutoApprove: false,
|
|
101
|
-
hasSkills: false,
|
|
102
|
-
hasRules: true,
|
|
103
|
-
instructionFile: 'AGENTS.md',
|
|
104
|
-
sharedInstructionFile: true,
|
|
105
|
-
mcpConfigScope: 'machine',
|
|
106
|
-
templateTier: 'B',
|
|
49
|
+
name: 'codex', category: 'code-agent', workflowModel: 'task-based',
|
|
50
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: false, hasSkills: false, hasRules: true,
|
|
51
|
+
instructionFile: 'AGENTS.md', sharedInstructionFile: true, mcpConfigScope: 'machine', templateTier: 'B',
|
|
107
52
|
},
|
|
108
53
|
antigravity: {
|
|
109
|
-
name: 'antigravity',
|
|
110
|
-
hasMcp: true,
|
|
111
|
-
|
|
112
|
-
hasAutoApprove: false,
|
|
113
|
-
hasSkills: false,
|
|
114
|
-
hasRules: true,
|
|
115
|
-
instructionFile: 'GEMINI.md',
|
|
116
|
-
sharedInstructionFile: true,
|
|
117
|
-
mcpConfigScope: 'machine',
|
|
118
|
-
templateTier: 'B',
|
|
54
|
+
name: 'antigravity', category: 'code-agent', workflowModel: 'interactive',
|
|
55
|
+
hasMcp: true, hasHooks: false, hasAutoApprove: false, hasSkills: false, hasRules: true,
|
|
56
|
+
instructionFile: 'GEMINI.md', sharedInstructionFile: true, mcpConfigScope: 'machine', templateTier: 'B',
|
|
119
57
|
},
|
|
120
58
|
'github-copilot': {
|
|
121
|
-
name: 'github-copilot',
|
|
122
|
-
hasMcp: false,
|
|
123
|
-
|
|
124
|
-
hasAutoApprove: false,
|
|
125
|
-
hasSkills: true,
|
|
126
|
-
hasRules: true,
|
|
127
|
-
instructionFile: '.github/copilot-instructions.md',
|
|
128
|
-
sharedInstructionFile: true,
|
|
129
|
-
mcpConfigScope: 'none',
|
|
130
|
-
templateTier: 'C',
|
|
59
|
+
name: 'github-copilot', category: 'code-agent', workflowModel: 'interactive',
|
|
60
|
+
hasMcp: false, hasHooks: false, hasAutoApprove: false, hasSkills: true, hasRules: true,
|
|
61
|
+
instructionFile: '.github/copilot-instructions.md', sharedInstructionFile: true, mcpConfigScope: 'none', templateTier: 'C',
|
|
131
62
|
},
|
|
63
|
+
// --- Autonomous agents (headless, task-based or scheduled) ---
|
|
132
64
|
openclaw: {
|
|
133
|
-
name: 'openclaw',
|
|
134
|
-
hasMcp: false,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
sharedInstructionFile: false,
|
|
141
|
-
|
|
142
|
-
|
|
65
|
+
name: 'openclaw', category: 'autonomous-agent', workflowModel: 'task-based',
|
|
66
|
+
hasMcp: false, hasHooks: false, hasAutoApprove: false, hasSkills: true, hasRules: false,
|
|
67
|
+
instructionFile: 'skills/openclaw/SKILL.md', sharedInstructionFile: false, mcpConfigScope: 'none', templateTier: 'C',
|
|
68
|
+
},
|
|
69
|
+
nanoclaw: {
|
|
70
|
+
name: 'nanoclaw', category: 'autonomous-agent', workflowModel: 'task-based',
|
|
71
|
+
hasMcp: false, hasHooks: false, hasAutoApprove: false, hasSkills: true, hasRules: false,
|
|
72
|
+
instructionFile: 'skills/nanoclaw/SKILL.md', sharedInstructionFile: false, mcpConfigScope: 'none', templateTier: 'C',
|
|
73
|
+
},
|
|
74
|
+
nemoclaw: {
|
|
75
|
+
name: 'nemoclaw', category: 'autonomous-agent', workflowModel: 'task-based',
|
|
76
|
+
hasMcp: false, hasHooks: false, hasAutoApprove: false, hasSkills: true, hasRules: false,
|
|
77
|
+
instructionFile: 'skills/nemoclaw/SKILL.md', sharedInstructionFile: false, mcpConfigScope: 'none', templateTier: 'C',
|
|
78
|
+
},
|
|
79
|
+
picoclaw: {
|
|
80
|
+
name: 'picoclaw', category: 'autonomous-agent', workflowModel: 'scheduled',
|
|
81
|
+
hasMcp: false, hasHooks: false, hasAutoApprove: false, hasSkills: true, hasRules: false,
|
|
82
|
+
instructionFile: 'skills/picoclaw/SKILL.md', sharedInstructionFile: false, mcpConfigScope: 'none', templateTier: 'C',
|
|
83
|
+
},
|
|
84
|
+
zeroclaw: {
|
|
85
|
+
name: 'zeroclaw', category: 'autonomous-agent', workflowModel: 'task-based',
|
|
86
|
+
hasMcp: false, hasHooks: false, hasAutoApprove: false, hasSkills: true, hasRules: false,
|
|
87
|
+
instructionFile: 'skills/zeroclaw/SKILL.md', sharedInstructionFile: false, mcpConfigScope: 'none', templateTier: 'C',
|
|
143
88
|
},
|
|
144
89
|
};
|
|
145
90
|
/**
|
package/dist/core/agent-files.js
CHANGED
|
@@ -441,6 +441,12 @@ function buildCommandHookEntry(command) {
|
|
|
441
441
|
hooks: [{ type: 'command', command }],
|
|
442
442
|
};
|
|
443
443
|
}
|
|
444
|
+
function buildMatchedCommandHookEntry(matcher, command) {
|
|
445
|
+
return {
|
|
446
|
+
matcher,
|
|
447
|
+
hooks: [{ type: 'command', command }],
|
|
448
|
+
};
|
|
449
|
+
}
|
|
444
450
|
function containsCommandHook(entries, command) {
|
|
445
451
|
return entries.some((entry) => isJsonObject(entry) &&
|
|
446
452
|
Array.isArray(entry.hooks) &&
|
|
@@ -582,6 +588,13 @@ export function ensureClaudeCodeSettings(cwd) {
|
|
|
582
588
|
stopHooks.push(buildCommandHookEntry(stopCommand));
|
|
583
589
|
}
|
|
584
590
|
hooks.Stop = stopHooks;
|
|
591
|
+
// PostToolUse — check for unseen events after any brainclaw MCP tool call
|
|
592
|
+
const checkEventsCommand = 'npx brainclaw check-events 2>/dev/null';
|
|
593
|
+
const postToolHooks = Array.isArray(hooks.PostToolUse) ? [...hooks.PostToolUse] : [];
|
|
594
|
+
if (!containsCommandHook(postToolHooks, checkEventsCommand)) {
|
|
595
|
+
postToolHooks.push(buildMatchedCommandHookEntry('mcp__brainclaw__', checkEventsCommand));
|
|
596
|
+
}
|
|
597
|
+
hooks.PostToolUse = postToolHooks;
|
|
585
598
|
const { created, updated } = writeJsonFileIfChanged(filePath, {
|
|
586
599
|
...existing,
|
|
587
600
|
permissions,
|
|
@@ -12,6 +12,11 @@ const SUPPORTED_AGENT_INTEGRATION_NAMES = new Set([
|
|
|
12
12
|
'antigravity',
|
|
13
13
|
'continue',
|
|
14
14
|
'roo',
|
|
15
|
+
'openclaw',
|
|
16
|
+
'nanoclaw',
|
|
17
|
+
'nemoclaw',
|
|
18
|
+
'picoclaw',
|
|
19
|
+
'zeroclaw',
|
|
15
20
|
]);
|
|
16
21
|
const DEFAULT_SURFACES = {
|
|
17
22
|
'github-copilot': [
|
|
@@ -60,6 +65,18 @@ const DEFAULT_SURFACES = {
|
|
|
60
65
|
'openclaw': [
|
|
61
66
|
{ kind: 'skill', location: 'machine', path: '.openclaw/workspace/skills/brainclaw/SKILL.md' },
|
|
62
67
|
],
|
|
68
|
+
'nanoclaw': [
|
|
69
|
+
{ kind: 'skill', location: 'workspace', path: 'skills/nanoclaw/SKILL.md' },
|
|
70
|
+
],
|
|
71
|
+
'nemoclaw': [
|
|
72
|
+
{ kind: 'skill', location: 'workspace', path: 'skills/nemoclaw/SKILL.md' },
|
|
73
|
+
],
|
|
74
|
+
'picoclaw': [
|
|
75
|
+
{ kind: 'skill', location: 'workspace', path: 'skills/picoclaw/SKILL.md' },
|
|
76
|
+
],
|
|
77
|
+
'zeroclaw': [
|
|
78
|
+
{ kind: 'skill', location: 'workspace', path: 'skills/zeroclaw/SKILL.md' },
|
|
79
|
+
],
|
|
63
80
|
};
|
|
64
81
|
function mergeSurfaces(current, next) {
|
|
65
82
|
const merged = [...current];
|
package/dist/core/context.js
CHANGED
|
@@ -13,7 +13,7 @@ import { inferProjectFromTarget, loadInstructions, resolveInstructions } from '.
|
|
|
13
13
|
import { buildCurrentAgentResumeSummary, buildReputationRankingLookup } from './reputation.js';
|
|
14
14
|
import { loadState } from './state.js';
|
|
15
15
|
import { listCandidates } from './candidates.js';
|
|
16
|
-
import { listClaims } from './claims.js';
|
|
16
|
+
import { listClaims, isClaimExpired } from './claims.js';
|
|
17
17
|
import { listRuntimeNotes } from './runtime.js';
|
|
18
18
|
import { isTrapActive, listOperationalTraps } from './traps.js';
|
|
19
19
|
import { buildEstimationReport } from '../commands/estimation-report.js';
|
|
@@ -67,6 +67,7 @@ export function buildContext(options = {}) {
|
|
|
67
67
|
score: 0,
|
|
68
68
|
reasons: [],
|
|
69
69
|
extra: meta.join(', '),
|
|
70
|
+
provenance: { actor: plan.author },
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
73
|
for (const c of state.active_constraints) {
|
|
@@ -316,10 +317,36 @@ export function buildContext(options = {}) {
|
|
|
316
317
|
items.splice(0, items.length, ...items.filter((i) => allowed.includes(i.section)));
|
|
317
318
|
}
|
|
318
319
|
const queryTerms = tokenise(target);
|
|
320
|
+
// Agent-layer scoring: boost items related to the current agent's claims
|
|
321
|
+
const agentName = agent;
|
|
322
|
+
const agentId = currentAgentIdentity?.agent_id;
|
|
323
|
+
const allClaims = [...listClaims(contextCwd), ...parentStoreClaims];
|
|
324
|
+
const myClaims = allClaims.filter((c) => c.status === 'active' && (agentId ? c.agent_id === agentId : c.agent === agentName));
|
|
325
|
+
const myClaimScopes = myClaims.map((c) => c.scope);
|
|
326
|
+
const otherActiveClaims = allClaims.filter((c) => c.status === 'active' && !(agentId ? c.agent_id === agentId : c.agent === agentName));
|
|
319
327
|
for (const item of items) {
|
|
320
328
|
const relevance = computeRelevance(item, queryTerms, profile, target);
|
|
321
329
|
item.score = relevance.score;
|
|
322
330
|
item.reasons = relevance.reasons;
|
|
331
|
+
// Layer 1: boost items in my claimed scope (+6)
|
|
332
|
+
if (item.score >= 0 && myClaimScopes.length > 0 && item.related_paths) {
|
|
333
|
+
const overlaps = item.related_paths.some((p) => myClaimScopes.some((scope) => p.includes(scope) || scope.includes(p)));
|
|
334
|
+
if (overlaps) {
|
|
335
|
+
item.score += 6;
|
|
336
|
+
item.reasons = uniqueReasons([...item.reasons, 'agent-layer: my claimed scope']);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Layer 1: boost plans assigned to me (+5)
|
|
340
|
+
if (item.score >= 0 && item.section === 'plan' && item.extra?.includes(`assignee:${agentName}`)) {
|
|
341
|
+
item.score += 5;
|
|
342
|
+
item.reasons = uniqueReasons([...item.reasons, 'agent-layer: my assigned plan']);
|
|
343
|
+
}
|
|
344
|
+
// Layer 2: boost items authored by me (+2)
|
|
345
|
+
if (item.score >= 0 && item.provenance?.actor === agentName) {
|
|
346
|
+
item.score += 2;
|
|
347
|
+
item.reasons = uniqueReasons([...item.reasons, 'agent-layer: my authored item']);
|
|
348
|
+
}
|
|
349
|
+
// Reputation signal
|
|
323
350
|
if (item.score >= 0 && item.provenance) {
|
|
324
351
|
const trustBonus = rankingLookup.getRankingBonus(item.provenance.actor_id, item.provenance.actor);
|
|
325
352
|
if (trustBonus > 0) {
|
|
@@ -327,6 +354,14 @@ export function buildContext(options = {}) {
|
|
|
327
354
|
item.reasons = uniqueReasons([...item.reasons, `reputation signal:+${trustBonus.toFixed(2)}`]);
|
|
328
355
|
}
|
|
329
356
|
}
|
|
357
|
+
// Layer 3: boost machine-scoped items for broader visibility (+1)
|
|
358
|
+
if (item.score >= 0) {
|
|
359
|
+
const itemScope = item.scope;
|
|
360
|
+
if (itemScope === 'machine') {
|
|
361
|
+
item.score += 1;
|
|
362
|
+
item.reasons = uniqueReasons([...item.reasons, 'machine-scope signal']);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
330
365
|
}
|
|
331
366
|
const ranked = items
|
|
332
367
|
.filter(item => item.score >= 0)
|
|
@@ -384,18 +419,15 @@ export function buildContext(options = {}) {
|
|
|
384
419
|
? summariseAgentTooling(rawAgentTooling)
|
|
385
420
|
: undefined;
|
|
386
421
|
// Build open_work: active claims and in_progress plans owned by the current agent
|
|
422
|
+
// Reuses myClaims computed in agent-layer scoring above
|
|
387
423
|
let openWork;
|
|
388
424
|
if (currentAgentIdentity || agent) {
|
|
389
|
-
const
|
|
390
|
-
const agentId = currentAgentIdentity?.agent_id;
|
|
391
|
-
const allClaims = [...listClaims(contextCwd), ...parentStoreClaims];
|
|
392
|
-
const activeClaims = allClaims.filter((c) => c.status === 'active' && (agentId ? c.agent_id === agentId : c.agent === agentName));
|
|
393
|
-
const claimPlanIds = new Set(activeClaims.map((c) => c.plan_id).filter(Boolean));
|
|
425
|
+
const claimPlanIds = new Set(myClaims.map((c) => c.plan_id).filter(Boolean));
|
|
394
426
|
const inProgressPlans = state.plan_items.filter((p) => p.status === 'in_progress' &&
|
|
395
427
|
(p.assignee === agentName || claimPlanIds.has(p.id)));
|
|
396
|
-
if (
|
|
428
|
+
if (myClaims.length > 0 || inProgressPlans.length > 0) {
|
|
397
429
|
openWork = {
|
|
398
|
-
active_claims:
|
|
430
|
+
active_claims: myClaims.map(({ id, scope, description, created_at, plan_id, expires_at }) => ({ id, scope, description, created_at, plan_id, expires_at })),
|
|
399
431
|
in_progress_plans: inProgressPlans.map(({ id, text, assignee }) => ({ id, text, assignee })),
|
|
400
432
|
};
|
|
401
433
|
}
|
|
@@ -471,6 +503,8 @@ export function buildContext(options = {}) {
|
|
|
471
503
|
}
|
|
472
504
|
})(),
|
|
473
505
|
cross_project_items: crossProjectItems.length > 0 ? crossProjectItems : undefined,
|
|
506
|
+
claim_conflicts: detectClaimConflicts(myClaims, otherActiveClaims),
|
|
507
|
+
workflow_hints: buildWorkflowHints(myClaims, openWork, state.plan_items),
|
|
474
508
|
selected,
|
|
475
509
|
};
|
|
476
510
|
if (options.digest) {
|
|
@@ -1232,4 +1266,64 @@ function applyCharBudget(items, maxChars) {
|
|
|
1232
1266
|
}
|
|
1233
1267
|
return selected;
|
|
1234
1268
|
}
|
|
1269
|
+
// --- Claim conflict detection ---
|
|
1270
|
+
function detectClaimConflicts(myClaims, otherClaims) {
|
|
1271
|
+
if (myClaims.length === 0 || otherClaims.length === 0)
|
|
1272
|
+
return undefined;
|
|
1273
|
+
const conflicts = [];
|
|
1274
|
+
for (const mine of myClaims) {
|
|
1275
|
+
for (const other of otherClaims) {
|
|
1276
|
+
if (isClaimExpired(other))
|
|
1277
|
+
continue;
|
|
1278
|
+
const overlap = scopesOverlap(mine.scope, other.scope);
|
|
1279
|
+
if (overlap) {
|
|
1280
|
+
conflicts.push({
|
|
1281
|
+
my_claim_id: mine.id,
|
|
1282
|
+
my_scope: mine.scope,
|
|
1283
|
+
other_claim_id: other.id,
|
|
1284
|
+
other_agent: other.agent,
|
|
1285
|
+
other_scope: other.scope,
|
|
1286
|
+
overlap_reason: overlap,
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
return conflicts.length > 0 ? conflicts : undefined;
|
|
1292
|
+
}
|
|
1293
|
+
function scopesOverlap(a, b) {
|
|
1294
|
+
const aParts = a.replace(/\\/g, '/').split(/\s+/);
|
|
1295
|
+
const bParts = b.replace(/\\/g, '/').split(/\s+/);
|
|
1296
|
+
for (const ap of aParts) {
|
|
1297
|
+
for (const bp of bParts) {
|
|
1298
|
+
if (ap === bp)
|
|
1299
|
+
return `exact match: ${ap}`;
|
|
1300
|
+
if (ap.startsWith(bp + '/') || bp.startsWith(ap + '/'))
|
|
1301
|
+
return `path overlap: ${ap} ↔ ${bp}`;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
// --- Workflow hints ---
|
|
1307
|
+
function buildWorkflowHints(myClaims, openWork, plans) {
|
|
1308
|
+
const hints = [];
|
|
1309
|
+
// No claims — suggest claiming before editing
|
|
1310
|
+
if (myClaims.length === 0) {
|
|
1311
|
+
const todoPlans = plans.filter((p) => p.status === 'todo' && p.priority === 'high');
|
|
1312
|
+
if (todoPlans.length > 0) {
|
|
1313
|
+
hints.push(`${todoPlans.length} high-priority plan(s) available — consider claiming one with bclaw_claim`);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
// Multiple unclosed claims — suggest releasing finished ones
|
|
1317
|
+
if (myClaims.length > 2) {
|
|
1318
|
+
hints.push(`You have ${myClaims.length} active claims — consider releasing finished ones with bclaw_release_claim`);
|
|
1319
|
+
}
|
|
1320
|
+
// In-progress plans without claims
|
|
1321
|
+
if (openWork) {
|
|
1322
|
+
const unclaimedInProgress = openWork.in_progress_plans.filter((p) => !openWork.active_claims.some((c) => c.plan_id === p.id));
|
|
1323
|
+
if (unclaimedInProgress.length > 0) {
|
|
1324
|
+
hints.push(`${unclaimedInProgress.length} in-progress plan(s) without a claim — consider claiming the scope you're editing`);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
return hints.length > 0 ? hints : undefined;
|
|
1328
|
+
}
|
|
1235
1329
|
//# sourceMappingURL=context.js.map
|
|
@@ -81,6 +81,31 @@ export function buildCoordinationSnapshot(options = {}) {
|
|
|
81
81
|
resolved_instructions: instructions,
|
|
82
82
|
reputation_summary: reputationSummary,
|
|
83
83
|
agent_reputation: agentReputation,
|
|
84
|
+
other_agents: buildOtherAgentsSummary(filteredClaims, filteredNotes, agent),
|
|
84
85
|
};
|
|
85
86
|
}
|
|
87
|
+
function buildOtherAgentsSummary(claims, notes, currentAgent) {
|
|
88
|
+
const agentMap = new Map();
|
|
89
|
+
for (const claim of claims) {
|
|
90
|
+
if (claim.agent === currentAgent)
|
|
91
|
+
continue;
|
|
92
|
+
const existing = agentMap.get(claim.agent) ?? { name: claim.agent, claim_count: 0, scopes: [] };
|
|
93
|
+
existing.claim_count++;
|
|
94
|
+
existing.scopes.push(claim.scope);
|
|
95
|
+
if (!existing.last_active || claim.created_at > existing.last_active) {
|
|
96
|
+
existing.last_active = claim.created_at;
|
|
97
|
+
}
|
|
98
|
+
agentMap.set(claim.agent, existing);
|
|
99
|
+
}
|
|
100
|
+
for (const note of notes) {
|
|
101
|
+
if (note.agent === currentAgent)
|
|
102
|
+
continue;
|
|
103
|
+
const existing = agentMap.get(note.agent);
|
|
104
|
+
if (existing && (!existing.last_active || note.created_at > existing.last_active)) {
|
|
105
|
+
existing.last_active = note.created_at;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const result = [...agentMap.values()];
|
|
109
|
+
return result.length > 0 ? result : undefined;
|
|
110
|
+
}
|
|
86
111
|
//# sourceMappingURL=coordination.js.map
|
package/dist/core/io.js
CHANGED
|
@@ -35,6 +35,8 @@ const ENTITY_DIR_MAP = {
|
|
|
35
35
|
// discovery/ — Project entity: what's available
|
|
36
36
|
'bootstrap': 'discovery/bootstrap',
|
|
37
37
|
'bootstrap/seeds': 'discovery/bootstrap/seeds',
|
|
38
|
+
'capabilities': 'discovery/capabilities',
|
|
39
|
+
'tools': 'discovery/tools',
|
|
38
40
|
// agents/ — stays at top level (already entity-aligned)
|
|
39
41
|
'agents': 'agents',
|
|
40
42
|
};
|