brainclaw 0.25.3 → 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 +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-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 +6 -1
- package/package.json +7 -1
|
@@ -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(),
|
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",
|