brainclaw 0.19.14 → 0.21.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 +27 -11
- package/dist/cli.js +11 -0
- package/dist/commands/context.js +3 -1
- package/dist/commands/export.js +44 -0
- package/dist/commands/init.js +7 -6
- package/dist/commands/mcp.js +86 -5
- package/dist/commands/setup.js +1 -0
- package/dist/commands/uninstall.js +145 -0
- package/dist/core/agent-capability.js +196 -0
- package/dist/core/agent-context.js +24 -6
- package/dist/core/agent-integrations.js +3 -0
- package/dist/core/ai-agent-detection.js +34 -19
- package/dist/core/bootstrap.js +177 -0
- package/dist/core/context.js +47 -24
- package/dist/core/instruction-templates.js +308 -0
- package/dist/core/schema.js +10 -0
- package/dist/core/setup-flow.js +191 -0
- package/dist/core/setup-state.js +30 -1
- package/dist/core/store-resolution.js +58 -0
- package/docs/architecture/project-refs.md +305 -0
- package/docs/cli.md +2 -1
- package/docs/integrations/agents.md +102 -150
- package/docs/integrations/openclaw.md +98 -0
- package/docs/integrations/overview.md +73 -45
- package/docs/quickstart.md +43 -147
- package/package.json +1 -1
package/dist/core/context.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import { loadConfig } from './config.js';
|
|
2
3
|
import { resolveCrossProjectLinks, loadCrossProjectState } from './cross-project.js';
|
|
3
4
|
import { buildContextDiff } from './context-diff.js';
|
|
4
|
-
import { resolveStoreChain } from './store-resolution.js';
|
|
5
|
+
import { resolveContextStoreCwd, resolveStoreChain } from './store-resolution.js';
|
|
5
6
|
import { findAgentIdentityByName, resolveCurrentAgentIdentity } from './agent-registry.js';
|
|
6
7
|
import { hasReusableBootstrapProfile, runBootstrapProfile, selectDerivedSignals } from './bootstrap.js';
|
|
7
8
|
import { buildAgentToolingContext } from './agent-context.js';
|
|
@@ -18,26 +19,28 @@ import { isTrapActive, listOperationalTraps } from './traps.js';
|
|
|
18
19
|
import { buildEstimationReport } from '../commands/estimation-report.js';
|
|
19
20
|
export const CONTEXT_SCHEMA_VERSION = '1.2';
|
|
20
21
|
export function buildContext(options = {}) {
|
|
21
|
-
const
|
|
22
|
-
const
|
|
22
|
+
const requestedCwd = options.cwd ?? process.cwd();
|
|
23
|
+
const contextCwd = resolveContextStoreCwd(requestedCwd, options.target);
|
|
24
|
+
const state = loadState(contextCwd);
|
|
25
|
+
const config = loadConfig(contextCwd);
|
|
23
26
|
// Resolve parent stores for multi-store merge (walk-up from cwd)
|
|
24
|
-
const storeChain = resolveStoreChain(
|
|
27
|
+
const storeChain = resolveStoreChain(contextCwd);
|
|
25
28
|
const profile = options.profile ?? config.profile ?? 'dev';
|
|
26
29
|
const projectMode = config.project_mode ?? 'auto';
|
|
27
30
|
const projectStrategy = config.projects?.strategy ?? 'manual';
|
|
28
31
|
const currentHost = resolveCurrentHostId();
|
|
29
|
-
const memoryVersion = getVisibleMemoryVersion({ cwd:
|
|
30
|
-
const target = options.target
|
|
32
|
+
const memoryVersion = getVisibleMemoryVersion({ cwd: contextCwd, hostId: options.host, allHosts: options.allHosts });
|
|
33
|
+
const target = normalizeContextTarget(options.target, requestedCwd, contextCwd);
|
|
31
34
|
const project = options.project?.trim() || inferProjectFromTarget(target, config);
|
|
32
35
|
const agent = options.agent?.trim() || config.current_agent?.trim();
|
|
33
36
|
const currentAgentIdentity = agent
|
|
34
|
-
? (options.agent?.trim() ? findAgentIdentityByName(agent,
|
|
37
|
+
? (options.agent?.trim() ? findAgentIdentityByName(agent, contextCwd) : resolveCurrentAgentIdentity(contextCwd))
|
|
35
38
|
: undefined;
|
|
36
39
|
const profileMaxItems = { compact: 6, copilot: 5, quick: 3 };
|
|
37
40
|
const maxItems = options.maxItems ?? profileMaxItems[profile] ?? 8;
|
|
38
41
|
const maxChars = options.maxChars && options.maxChars > 0 ? options.maxChars : undefined;
|
|
39
42
|
// Instructions will be resolved after parent-store merge below (line ~460)
|
|
40
|
-
const rankingLookup = buildReputationRankingLookup(
|
|
43
|
+
const rankingLookup = buildReputationRankingLookup(contextCwd);
|
|
41
44
|
const profileSections = {
|
|
42
45
|
compact: ['plan', 'constraint'],
|
|
43
46
|
copilot: ['constraint', 'trap'],
|
|
@@ -123,7 +126,7 @@ export function buildContext(options = {}) {
|
|
|
123
126
|
},
|
|
124
127
|
});
|
|
125
128
|
}
|
|
126
|
-
for (const trap of listOperationalTraps({ hostId: options.host, includeAllHosts: options.allHosts },
|
|
129
|
+
for (const trap of listOperationalTraps({ hostId: options.host, includeAllHosts: options.allHosts }, contextCwd).filter((entry) => isTrapActive(entry))) {
|
|
127
130
|
items.push({
|
|
128
131
|
id: trap.id,
|
|
129
132
|
section: 'trap',
|
|
@@ -157,7 +160,7 @@ export function buildContext(options = {}) {
|
|
|
157
160
|
const runtimeNotes = listRuntimeNotes({
|
|
158
161
|
hostId: options.host,
|
|
159
162
|
includeAllHosts: options.allHosts,
|
|
160
|
-
},
|
|
163
|
+
}, contextCwd);
|
|
161
164
|
for (const note of runtimeNotes) {
|
|
162
165
|
if (project && note.project && note.project !== project) {
|
|
163
166
|
continue;
|
|
@@ -191,7 +194,7 @@ export function buildContext(options = {}) {
|
|
|
191
194
|
});
|
|
192
195
|
}
|
|
193
196
|
if (options.includePending) {
|
|
194
|
-
for (const p of listCandidates('pending',
|
|
197
|
+
for (const p of listCandidates('pending', contextCwd)) {
|
|
195
198
|
const meta = [`${p.type}`, `stars:${p.star_count ?? 0}`, `uses:${p.usage_count ?? 0}`];
|
|
196
199
|
if (p.author_id)
|
|
197
200
|
meta.push(`author_id:${p.author_id}`);
|
|
@@ -294,7 +297,7 @@ export function buildContext(options = {}) {
|
|
|
294
297
|
catch { /* non-fatal */ }
|
|
295
298
|
}
|
|
296
299
|
// Merge instructions from all stores in the chain
|
|
297
|
-
const allInstructions = [...loadInstructions(
|
|
300
|
+
const allInstructions = [...loadInstructions(contextCwd)];
|
|
298
301
|
for (const parentStore of storeChain.slice(1)) {
|
|
299
302
|
try {
|
|
300
303
|
const parentInstrs = loadInstructions(parentStore.cwd);
|
|
@@ -330,34 +333,34 @@ export function buildContext(options = {}) {
|
|
|
330
333
|
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
|
331
334
|
.slice(0, maxItems);
|
|
332
335
|
const selected = maxChars ? applyCharBudget(ranked, maxChars) : ranked;
|
|
333
|
-
const resumeSummary = buildCurrentAgentResumeSummary(
|
|
336
|
+
const resumeSummary = buildCurrentAgentResumeSummary(contextCwd);
|
|
334
337
|
const scopedActivity = buildScopedActivity({
|
|
335
338
|
target,
|
|
336
339
|
project,
|
|
337
340
|
state,
|
|
338
341
|
runtimeNotes,
|
|
339
|
-
pendingCandidates: listCandidates('pending',
|
|
342
|
+
pendingCandidates: listCandidates('pending', contextCwd),
|
|
340
343
|
});
|
|
341
344
|
const memoryDensity = classifyMemoryDensity(selected.length);
|
|
342
345
|
const bootstrapEnabled = options.bootstrap !== false;
|
|
343
|
-
let bootstrapAvailable = hasReusableBootstrapProfile(target,
|
|
346
|
+
let bootstrapAvailable = hasReusableBootstrapProfile(target, contextCwd);
|
|
344
347
|
let derivedSignals;
|
|
345
348
|
if (bootstrapEnabled && (options.refreshBootstrap || memoryDensity === 'low')) {
|
|
346
349
|
const bootstrap = runBootstrapProfile({
|
|
347
350
|
target,
|
|
348
351
|
refresh: options.refreshBootstrap,
|
|
349
|
-
cwd:
|
|
352
|
+
cwd: contextCwd,
|
|
350
353
|
});
|
|
351
354
|
bootstrapAvailable = bootstrap.profile.seed_count > 0;
|
|
352
355
|
if (memoryDensity === 'low') {
|
|
353
|
-
const signals = selectDerivedSignals(target, 5,
|
|
356
|
+
const signals = selectDerivedSignals(target, 5, contextCwd);
|
|
354
357
|
if (signals.length > 0) {
|
|
355
358
|
derivedSignals = signals;
|
|
356
359
|
}
|
|
357
360
|
}
|
|
358
361
|
}
|
|
359
362
|
else if (bootstrapEnabled && bootstrapAvailable && memoryDensity === 'low') {
|
|
360
|
-
const signals = selectDerivedSignals(target, 5,
|
|
363
|
+
const signals = selectDerivedSignals(target, 5, contextCwd);
|
|
361
364
|
if (signals.length > 0) {
|
|
362
365
|
derivedSignals = signals;
|
|
363
366
|
}
|
|
@@ -365,7 +368,7 @@ export function buildContext(options = {}) {
|
|
|
365
368
|
const executionSensitive = isExecutionSensitiveTarget(target);
|
|
366
369
|
const derivedUsesExecution = derivedSignals?.some((signal) => signal.source_kind === 'machine') ?? false;
|
|
367
370
|
const derivedUsesTooling = derivedSignals?.some((signal) => signal.source_kind === 'skill' || signal.source_kind === 'mcp') ?? false;
|
|
368
|
-
const rawAgentTooling = buildAgentToolingContext({ cwd:
|
|
371
|
+
const rawAgentTooling = buildAgentToolingContext({ cwd: contextCwd });
|
|
369
372
|
const actionableAgentRules = rawAgentTooling.agents_rules.length > 0;
|
|
370
373
|
const blockingTooling = rawAgentTooling.mcp_servers.some((server) => server.availability === 'missing_command');
|
|
371
374
|
const shouldExposeExecution = memoryDensity === 'low' || executionSensitive || derivedUsesExecution;
|
|
@@ -375,7 +378,7 @@ export function buildContext(options = {}) {
|
|
|
375
378
|
|| actionableAgentRules
|
|
376
379
|
|| blockingTooling;
|
|
377
380
|
const executionContext = shouldExposeExecution
|
|
378
|
-
? compactExecutionContext(buildExecutionContext({ cwd:
|
|
381
|
+
? compactExecutionContext(buildExecutionContext({ cwd: contextCwd }))
|
|
379
382
|
: undefined;
|
|
380
383
|
const agentTooling = shouldExposeAgentTooling
|
|
381
384
|
? summariseAgentTooling(rawAgentTooling)
|
|
@@ -385,7 +388,7 @@ export function buildContext(options = {}) {
|
|
|
385
388
|
if (currentAgentIdentity || agent) {
|
|
386
389
|
const agentName = agent;
|
|
387
390
|
const agentId = currentAgentIdentity?.agent_id;
|
|
388
|
-
const allClaims = [...listClaims(
|
|
391
|
+
const allClaims = [...listClaims(contextCwd), ...parentStoreClaims];
|
|
389
392
|
const activeClaims = allClaims.filter((c) => c.status === 'active' && (agentId ? c.agent_id === agentId : c.agent === agentName));
|
|
390
393
|
const claimPlanIds = new Set(activeClaims.map((c) => c.plan_id).filter(Boolean));
|
|
391
394
|
const inProgressPlans = state.plan_items.filter((p) => p.status === 'in_progress' &&
|
|
@@ -399,7 +402,7 @@ export function buildContext(options = {}) {
|
|
|
399
402
|
}
|
|
400
403
|
// Cross-project items (subscriber links — read-only, always injected, bypass scoring)
|
|
401
404
|
const crossProjectItems = [];
|
|
402
|
-
for (const link of resolveCrossProjectLinks(
|
|
405
|
+
for (const link of resolveCrossProjectLinks(contextCwd)) {
|
|
403
406
|
if (!link.available)
|
|
404
407
|
continue;
|
|
405
408
|
try {
|
|
@@ -448,7 +451,7 @@ export function buildContext(options = {}) {
|
|
|
448
451
|
context_diff: options.sinceSession
|
|
449
452
|
? buildContextDiff({
|
|
450
453
|
session: options.sinceSession,
|
|
451
|
-
cwd:
|
|
454
|
+
cwd: contextCwd,
|
|
452
455
|
includeItems: true,
|
|
453
456
|
})
|
|
454
457
|
: undefined,
|
|
@@ -460,7 +463,7 @@ export function buildContext(options = {}) {
|
|
|
460
463
|
: undefined,
|
|
461
464
|
estimation_calibration: (() => {
|
|
462
465
|
try {
|
|
463
|
-
const report = buildEstimationReport({ agent, cwd:
|
|
466
|
+
const report = buildEstimationReport({ agent, cwd: contextCwd });
|
|
464
467
|
return report.summary.with_both >= 3 ? report.summary.calibration_hint : undefined;
|
|
465
468
|
}
|
|
466
469
|
catch {
|
|
@@ -1053,6 +1056,26 @@ function tokenise(input) {
|
|
|
1053
1056
|
.map((x) => x.trim())
|
|
1054
1057
|
.filter(Boolean);
|
|
1055
1058
|
}
|
|
1059
|
+
function normalizeContextTarget(target, requestedCwd, contextCwd) {
|
|
1060
|
+
const trimmed = target?.trim() ?? '';
|
|
1061
|
+
if (!trimmed) {
|
|
1062
|
+
return '';
|
|
1063
|
+
}
|
|
1064
|
+
if (path.resolve(requestedCwd) === path.resolve(contextCwd)) {
|
|
1065
|
+
return trimmed;
|
|
1066
|
+
}
|
|
1067
|
+
if (!(path.isAbsolute(trimmed) || trimmed.includes('/') || trimmed.includes('\\') || trimmed.startsWith('.'))) {
|
|
1068
|
+
return trimmed;
|
|
1069
|
+
}
|
|
1070
|
+
const absoluteTarget = path.isAbsolute(trimmed)
|
|
1071
|
+
? path.resolve(trimmed)
|
|
1072
|
+
: path.resolve(requestedCwd, trimmed);
|
|
1073
|
+
const relativeToContext = path.relative(contextCwd, absoluteTarget);
|
|
1074
|
+
if (relativeToContext.startsWith('..') || path.isAbsolute(relativeToContext)) {
|
|
1075
|
+
return trimmed;
|
|
1076
|
+
}
|
|
1077
|
+
return relativeToContext.split(path.sep).join('/');
|
|
1078
|
+
}
|
|
1056
1079
|
function matchesPath(pattern, target) {
|
|
1057
1080
|
if (pattern === target)
|
|
1058
1081
|
return true;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive instruction file templates — generates brainclaw section content
|
|
3
|
+
* based on the agent's capability profile (tier A/B/C).
|
|
4
|
+
*
|
|
5
|
+
* Core (static) vs Run (dynamic) separation:
|
|
6
|
+
* Core: protocol, "why brainclaw", constraints, instructions, estimation rule
|
|
7
|
+
* Run: traps, plans, decisions, claims, handoffs, runtime notes
|
|
8
|
+
*
|
|
9
|
+
* Tier A (MCP + hooks): lightweight — hooks inject run content automatically
|
|
10
|
+
* Tier B (MCP, no hooks): directive — includes top traps, forces MCP calls
|
|
11
|
+
* Tier C (no MCP): rich — includes plans, traps, decisions (only source)
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Render the brainclaw section content for an instruction file,
|
|
15
|
+
* adapted to the agent's capability profile.
|
|
16
|
+
*/
|
|
17
|
+
export function renderBrainclawSection(input) {
|
|
18
|
+
const { profile } = input;
|
|
19
|
+
switch (profile.templateTier) {
|
|
20
|
+
case 'A': return renderTierA(input);
|
|
21
|
+
case 'B': return renderTierB(input);
|
|
22
|
+
case 'C': return renderTierC(input);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// ─── Tier A: Full integration (MCP + hooks) ─────────────────────────────────
|
|
26
|
+
function renderTierA(input) {
|
|
27
|
+
const sections = [];
|
|
28
|
+
const included = [];
|
|
29
|
+
sections.push(renderHeader(input));
|
|
30
|
+
included.push('header');
|
|
31
|
+
sections.push(renderWhySection(input.profile));
|
|
32
|
+
included.push('why');
|
|
33
|
+
sections.push(renderProtocolTierA());
|
|
34
|
+
included.push('protocol');
|
|
35
|
+
sections.push(renderEstimationRule());
|
|
36
|
+
included.push('estimation');
|
|
37
|
+
sections.push(renderVersionCheckRule());
|
|
38
|
+
included.push('version-check');
|
|
39
|
+
const constraints = renderConstraints(input.state);
|
|
40
|
+
if (constraints) {
|
|
41
|
+
sections.push(constraints);
|
|
42
|
+
included.push('constraints');
|
|
43
|
+
}
|
|
44
|
+
const instructions = renderInstructions(input.resolvedInstructions);
|
|
45
|
+
if (instructions) {
|
|
46
|
+
sections.push(instructions);
|
|
47
|
+
included.push('instructions');
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
content: sections.join('\n\n'),
|
|
51
|
+
tier: 'A',
|
|
52
|
+
sectionsIncluded: included,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// ─── Tier B: Standard integration (MCP, no hooks) ───────────────────────────
|
|
56
|
+
function renderTierB(input) {
|
|
57
|
+
const sections = [];
|
|
58
|
+
const included = [];
|
|
59
|
+
sections.push(renderHeader(input));
|
|
60
|
+
included.push('header');
|
|
61
|
+
sections.push(renderWhySection(input.profile));
|
|
62
|
+
included.push('why');
|
|
63
|
+
sections.push(renderProtocolTierB());
|
|
64
|
+
included.push('protocol');
|
|
65
|
+
sections.push(renderEstimationRule());
|
|
66
|
+
included.push('estimation');
|
|
67
|
+
sections.push(renderVersionCheckRule());
|
|
68
|
+
included.push('version-check');
|
|
69
|
+
const constraints = renderConstraints(input.state);
|
|
70
|
+
if (constraints) {
|
|
71
|
+
sections.push(constraints);
|
|
72
|
+
included.push('constraints');
|
|
73
|
+
}
|
|
74
|
+
const instructions = renderInstructions(input.resolvedInstructions);
|
|
75
|
+
if (instructions) {
|
|
76
|
+
sections.push(instructions);
|
|
77
|
+
included.push('instructions');
|
|
78
|
+
}
|
|
79
|
+
// Tier B includes top traps statically (agent has no hooks to get them)
|
|
80
|
+
const traps = renderTopTraps(input.state, input.maxTraps ?? 5);
|
|
81
|
+
if (traps) {
|
|
82
|
+
sections.push(traps);
|
|
83
|
+
included.push('traps');
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
content: sections.join('\n\n'),
|
|
87
|
+
tier: 'B',
|
|
88
|
+
sectionsIncluded: included,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// ─── Tier C: Limited integration (no MCP) ────────────────────────────────────
|
|
92
|
+
function renderTierC(input) {
|
|
93
|
+
const sections = [];
|
|
94
|
+
const included = [];
|
|
95
|
+
sections.push(renderHeader(input));
|
|
96
|
+
included.push('header');
|
|
97
|
+
sections.push(renderWhySection(input.profile));
|
|
98
|
+
included.push('why');
|
|
99
|
+
sections.push(renderProtocolTierC(input.profile));
|
|
100
|
+
included.push('protocol');
|
|
101
|
+
sections.push(renderEstimationRule());
|
|
102
|
+
included.push('estimation');
|
|
103
|
+
const constraints = renderConstraints(input.state);
|
|
104
|
+
if (constraints) {
|
|
105
|
+
sections.push(constraints);
|
|
106
|
+
included.push('constraints');
|
|
107
|
+
}
|
|
108
|
+
const instructions = renderInstructions(input.resolvedInstructions);
|
|
109
|
+
if (instructions) {
|
|
110
|
+
sections.push(instructions);
|
|
111
|
+
included.push('instructions');
|
|
112
|
+
}
|
|
113
|
+
// Tier C includes everything statically — it's the only source of context
|
|
114
|
+
const traps = renderTopTraps(input.state, input.maxTraps ?? 10);
|
|
115
|
+
if (traps) {
|
|
116
|
+
sections.push(traps);
|
|
117
|
+
included.push('traps');
|
|
118
|
+
}
|
|
119
|
+
const plans = renderActivePlans(input.state, input.maxPlans ?? 10);
|
|
120
|
+
if (plans) {
|
|
121
|
+
sections.push(plans);
|
|
122
|
+
included.push('plans');
|
|
123
|
+
}
|
|
124
|
+
const decisions = renderRecentDecisions(input.state);
|
|
125
|
+
if (decisions) {
|
|
126
|
+
sections.push(decisions);
|
|
127
|
+
included.push('decisions');
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
content: sections.join('\n\n'),
|
|
131
|
+
tier: 'C',
|
|
132
|
+
sectionsIncluded: included,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// ─── Shared section renderers ────────────────────────────────────────────────
|
|
136
|
+
function renderHeader(input) {
|
|
137
|
+
return [
|
|
138
|
+
`> Managed by brainclaw v${input.brainclawVersion} — do not edit manually.`,
|
|
139
|
+
`> Regenerate: brainclaw export --format ${formatForAgent(input.profile.name)} --write`,
|
|
140
|
+
].join('\n');
|
|
141
|
+
}
|
|
142
|
+
function renderWhySection(profile) {
|
|
143
|
+
const lines = [
|
|
144
|
+
'## brainclaw — why this matters',
|
|
145
|
+
'',
|
|
146
|
+
'This project uses brainclaw for shared memory and multi-agent coordination.',
|
|
147
|
+
];
|
|
148
|
+
if (profile.hasMcp) {
|
|
149
|
+
lines.push('Other agents and developers work in this repo. Without brainclaw context:', '- You may edit files another agent is actively working on', '- You will miss known traps and architectural decisions', '- You will duplicate or contradict planned work');
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
lines.push('Project context is maintained in brainclaw memory. This includes:', '- Active constraints that must be respected', '- Known traps to avoid repeating mistakes', '- Plans and decisions for ongoing work coordination');
|
|
153
|
+
}
|
|
154
|
+
return lines.join('\n');
|
|
155
|
+
}
|
|
156
|
+
function renderProtocolTierA() {
|
|
157
|
+
return [
|
|
158
|
+
'## brainclaw — session protocol',
|
|
159
|
+
'',
|
|
160
|
+
'Brainclaw context is injected automatically via hooks at each prompt.',
|
|
161
|
+
'',
|
|
162
|
+
'1. Read the injected board state before acting',
|
|
163
|
+
'2. Call `bclaw_get_context(target)` for deeper memory on a specific scope',
|
|
164
|
+
'3. Call `bclaw_claim(scope)` before editing if other agents are active',
|
|
165
|
+
'4. Call `bclaw_session_end(auto_release: true)` when done',
|
|
166
|
+
].join('\n');
|
|
167
|
+
}
|
|
168
|
+
function renderProtocolTierB() {
|
|
169
|
+
return [
|
|
170
|
+
'## brainclaw — session protocol (REQUIRED)',
|
|
171
|
+
'',
|
|
172
|
+
'You MUST follow this protocol before any work in this project.',
|
|
173
|
+
'',
|
|
174
|
+
'1. Call `bclaw_session_start` — identify yourself and see the active board',
|
|
175
|
+
'2. Call `bclaw_get_context(target: "<file or dir>")` — load relevant memory',
|
|
176
|
+
'3. Call `bclaw_get_execution_context` — check for brainclaw updates',
|
|
177
|
+
'4. Check that no other agent has claimed your target scope',
|
|
178
|
+
'5. Call `bclaw_claim(scope)` before editing',
|
|
179
|
+
'6. Call `bclaw_write_note(text)` to record observations',
|
|
180
|
+
'7. Call `bclaw_session_end(auto_release: true)` when done',
|
|
181
|
+
].join('\n');
|
|
182
|
+
}
|
|
183
|
+
function renderProtocolTierC(profile) {
|
|
184
|
+
const lines = [
|
|
185
|
+
'## brainclaw — project coordination',
|
|
186
|
+
'',
|
|
187
|
+
];
|
|
188
|
+
if (profile.hasSkills) {
|
|
189
|
+
lines.push('Use the brainclaw-context skill to refresh project memory.', 'Trigger: "refresh project memory", "load brainclaw context", "inspect active plans"', '');
|
|
190
|
+
}
|
|
191
|
+
lines.push('Before working:', '- Review the constraints and active plans below', '- Check the known traps section for pitfalls in your scope', '', 'The sections below are regenerated from brainclaw memory.', 'For the freshest state, use the brainclaw skill or ask the developer to run `brainclaw context`.');
|
|
192
|
+
return lines.join('\n');
|
|
193
|
+
}
|
|
194
|
+
function renderEstimationRule() {
|
|
195
|
+
return [
|
|
196
|
+
'## brainclaw — plans and estimation',
|
|
197
|
+
'',
|
|
198
|
+
'When creating a plan or step, always estimate duration in minutes.',
|
|
199
|
+
'When completing, report actual effort. This calibrates future estimates.',
|
|
200
|
+
].join('\n');
|
|
201
|
+
}
|
|
202
|
+
function renderVersionCheckRule() {
|
|
203
|
+
return [
|
|
204
|
+
'## brainclaw — version check',
|
|
205
|
+
'',
|
|
206
|
+
'Call `bclaw_get_execution_context` at session start to check for brainclaw updates.',
|
|
207
|
+
'If a newer version is available, inform the developer and suggest updating.',
|
|
208
|
+
'Brainclaw updates may include new features, bug fixes, and improved coordination.',
|
|
209
|
+
].join('\n');
|
|
210
|
+
}
|
|
211
|
+
function renderConstraints(state) {
|
|
212
|
+
const active = state.active_constraints.filter((c) => c.status === 'active');
|
|
213
|
+
if (active.length === 0)
|
|
214
|
+
return undefined;
|
|
215
|
+
return [
|
|
216
|
+
'## brainclaw — active constraints',
|
|
217
|
+
'',
|
|
218
|
+
...active.map((c) => `- ${c.text}`),
|
|
219
|
+
].join('\n');
|
|
220
|
+
}
|
|
221
|
+
function renderInstructions(instructions) {
|
|
222
|
+
if (instructions.length === 0)
|
|
223
|
+
return undefined;
|
|
224
|
+
return [
|
|
225
|
+
'## brainclaw — active instructions',
|
|
226
|
+
'',
|
|
227
|
+
...instructions.map((text) => `- ${text}`),
|
|
228
|
+
].join('\n');
|
|
229
|
+
}
|
|
230
|
+
function renderTopTraps(state, limit) {
|
|
231
|
+
const traps = state.known_traps
|
|
232
|
+
.filter((t) => t.visibility === 'shared' && (!t.status || t.status === 'active') && !t.platform_scope)
|
|
233
|
+
.sort((a, b) => severityOrder(b.severity) - severityOrder(a.severity))
|
|
234
|
+
.slice(0, limit);
|
|
235
|
+
if (traps.length === 0)
|
|
236
|
+
return undefined;
|
|
237
|
+
return [
|
|
238
|
+
'## brainclaw — known traps',
|
|
239
|
+
'',
|
|
240
|
+
...traps.map((t) => `- [${t.severity}] ${t.text}`),
|
|
241
|
+
].join('\n');
|
|
242
|
+
}
|
|
243
|
+
function renderActivePlans(state, limit) {
|
|
244
|
+
const active = state.plan_items
|
|
245
|
+
.filter((p) => p.status === 'in_progress' || p.status === 'todo')
|
|
246
|
+
.sort((a, b) => {
|
|
247
|
+
if (a.status === 'in_progress' && b.status !== 'in_progress')
|
|
248
|
+
return -1;
|
|
249
|
+
if (b.status === 'in_progress' && a.status !== 'in_progress')
|
|
250
|
+
return 1;
|
|
251
|
+
return priorityOrder(b.priority) - priorityOrder(a.priority);
|
|
252
|
+
})
|
|
253
|
+
.slice(0, limit);
|
|
254
|
+
if (active.length === 0)
|
|
255
|
+
return undefined;
|
|
256
|
+
return [
|
|
257
|
+
'## brainclaw — active plans',
|
|
258
|
+
'',
|
|
259
|
+
...active.map((p) => {
|
|
260
|
+
const assignee = p.assignee ? ` (@${p.assignee})` : '';
|
|
261
|
+
return `- [${p.status}] ${p.text}${assignee}`;
|
|
262
|
+
}),
|
|
263
|
+
].join('\n');
|
|
264
|
+
}
|
|
265
|
+
function renderRecentDecisions(state) {
|
|
266
|
+
const decisions = state.recent_decisions.slice(-5);
|
|
267
|
+
if (decisions.length === 0)
|
|
268
|
+
return undefined;
|
|
269
|
+
return [
|
|
270
|
+
'## brainclaw — recent decisions',
|
|
271
|
+
'',
|
|
272
|
+
...decisions.map((d) => `- ${d.text}`),
|
|
273
|
+
].join('\n');
|
|
274
|
+
}
|
|
275
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
276
|
+
function severityOrder(severity) {
|
|
277
|
+
switch (severity) {
|
|
278
|
+
case 'high': return 3;
|
|
279
|
+
case 'medium': return 2;
|
|
280
|
+
case 'low': return 1;
|
|
281
|
+
default: return 0;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function priorityOrder(priority) {
|
|
285
|
+
switch (priority) {
|
|
286
|
+
case 'critical': return 4;
|
|
287
|
+
case 'high': return 3;
|
|
288
|
+
case 'medium': return 2;
|
|
289
|
+
case 'low': return 1;
|
|
290
|
+
default: return 0;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function formatForAgent(agentName) {
|
|
294
|
+
switch (agentName) {
|
|
295
|
+
case 'claude-code': return 'claude-md';
|
|
296
|
+
case 'cursor': return 'cursor-rules';
|
|
297
|
+
case 'github-copilot': return 'copilot-instructions';
|
|
298
|
+
case 'opencode':
|
|
299
|
+
case 'codex': return 'agents-md';
|
|
300
|
+
case 'antigravity': return 'gemini-md';
|
|
301
|
+
case 'windsurf': return 'windsurf';
|
|
302
|
+
case 'cline': return 'cline';
|
|
303
|
+
case 'roo': return 'roo';
|
|
304
|
+
case 'continue': return 'continue';
|
|
305
|
+
default: return 'agents-md';
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=instruction-templates.js.map
|
package/dist/core/schema.js
CHANGED
|
@@ -96,6 +96,7 @@ export const TrapSchema = z.object({
|
|
|
96
96
|
visibility: MemoryVisibilitySchema.default('shared'),
|
|
97
97
|
host_id: z.string().optional(),
|
|
98
98
|
expires_at: z.string().optional(),
|
|
99
|
+
platform_scope: z.string().optional(),
|
|
99
100
|
});
|
|
100
101
|
export const HandoffSchema = z.object({
|
|
101
102
|
schema_version: z.number().int().positive().optional(),
|
|
@@ -484,6 +485,12 @@ export const MemorySeedSourceKindSchema = z.enum([
|
|
|
484
485
|
'machine',
|
|
485
486
|
'skill',
|
|
486
487
|
'mcp',
|
|
488
|
+
'ci_config',
|
|
489
|
+
'contributing',
|
|
490
|
+
'changelog',
|
|
491
|
+
'docker',
|
|
492
|
+
'env_example',
|
|
493
|
+
'adr',
|
|
487
494
|
]);
|
|
488
495
|
export const MemorySeedConfidenceSchema = z.enum(['low', 'medium', 'high']);
|
|
489
496
|
export const MemorySeedDocumentSchema = z.object({
|
|
@@ -617,6 +624,7 @@ export const AgentIntegrationNameSchema = z.enum([
|
|
|
617
624
|
'antigravity',
|
|
618
625
|
'continue',
|
|
619
626
|
'roo',
|
|
627
|
+
'openclaw',
|
|
620
628
|
]);
|
|
621
629
|
export const AgentIntegrationSurfaceKindSchema = z.enum(['instructions', 'mcp', 'skill', 'rule', 'hook']);
|
|
622
630
|
export const AgentIntegrationLocationSchema = z.enum(['workspace', 'machine']);
|
|
@@ -626,9 +634,11 @@ export const AgentIntegrationSurfaceSchema = z.object({
|
|
|
626
634
|
location: AgentIntegrationLocationSchema,
|
|
627
635
|
path: z.string().optional(),
|
|
628
636
|
});
|
|
637
|
+
export const AgentIntegrationLevelSchema = z.enum(['full', 'standard', 'limited', 'custom']);
|
|
629
638
|
export const AgentIntegrationDeclarationSchema = z.object({
|
|
630
639
|
agent_name: AgentIntegrationNameSchema,
|
|
631
640
|
declaration_source: AgentIntegrationDeclarationSourceSchema.default('manual'),
|
|
641
|
+
level: AgentIntegrationLevelSchema.optional(),
|
|
632
642
|
surfaces: z.array(AgentIntegrationSurfaceSchema).default([]),
|
|
633
643
|
notes: z.string().optional(),
|
|
634
644
|
});
|