brainclaw 0.19.6 → 0.19.10
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 +225 -126
- package/dist/cli.js +8 -3
- package/dist/commands/accept.js +102 -104
- package/dist/commands/add-step.js +3 -5
- package/dist/commands/bootstrap.js +72 -3
- package/dist/commands/capability.js +3 -5
- package/dist/commands/claim.js +14 -12
- package/dist/commands/complete-step.js +3 -5
- package/dist/commands/constraint.js +3 -5
- package/dist/commands/decision.js +3 -6
- package/dist/commands/delete-plan.js +3 -5
- package/dist/commands/handoff.js +3 -5
- package/dist/commands/init.js +20 -0
- package/dist/commands/instruction.js +16 -9
- package/dist/commands/mcp.js +18 -22
- package/dist/commands/memory.js +4 -7
- package/dist/commands/plan.js +3 -5
- package/dist/commands/prune.js +27 -25
- package/dist/commands/reflect.js +3 -5
- package/dist/commands/release-claim.js +23 -20
- package/dist/commands/release-claims.js +22 -21
- package/dist/commands/rollback.js +2 -2
- package/dist/commands/tool.js +3 -5
- package/dist/commands/trap.js +3 -5
- package/dist/commands/update-handoff.js +3 -5
- package/dist/commands/update-plan.js +3 -5
- package/dist/commands/upgrade.js +2 -2
- package/dist/core/audit.js +25 -25
- package/dist/core/bootstrap.js +587 -4
- package/dist/core/candidates.js +10 -6
- package/dist/core/claims.js +10 -8
- package/dist/core/coordination.js +2 -2
- package/dist/core/instructions.js +4 -2
- package/dist/core/io.js +8 -0
- package/dist/core/lock.js +18 -2
- package/dist/core/migration.js +6 -2
- package/dist/core/runtime.js +18 -14
- package/dist/core/schema.js +69 -0
- package/dist/core/state.js +21 -4
- package/docs/cli.md +21 -1
- package/docs/integrations/agents.md +42 -29
- package/docs/integrations/claude-code.md +4 -4
- package/docs/integrations/codex.md +5 -5
- package/docs/integrations/copilot.md +3 -2
- package/docs/integrations/cursor.md +4 -4
- package/docs/integrations/mcp.md +53 -24
- package/docs/integrations/overview.md +53 -40
- package/docs/mcp-schema-changelog.md +3 -0
- package/docs/product/positioning.md +17 -18
- package/docs/quickstart.md +87 -55
- package/package.json +1 -2
package/dist/commands/mcp.js
CHANGED
|
@@ -8,9 +8,8 @@ import { buildCoordinationSnapshot } from '../core/coordination.js';
|
|
|
8
8
|
import { buildContext, renderContextMarkdown, renderContextPromptTemplate } from '../core/context.js';
|
|
9
9
|
import { buildExecutionContext, renderExecutionContextSummary } from '../core/execution-context.js';
|
|
10
10
|
import { loadConfig } from '../core/config.js';
|
|
11
|
-
import { loadState,
|
|
12
|
-
import { memoryExists
|
|
13
|
-
import { generateMarkdown } from '../core/markdown.js';
|
|
11
|
+
import { loadState, persistState } from '../core/state.js';
|
|
12
|
+
import { memoryExists } from '../core/io.js';
|
|
14
13
|
import { generateCandidateIdWithLabel, listArchivedCandidates, listCandidates, saveCandidate } from '../core/candidates.js';
|
|
15
14
|
import { generateClaimId, listClaims, loadClaim, saveClaim } from '../core/claims.js';
|
|
16
15
|
import { createRuntimeNote } from './runtime-note.js';
|
|
@@ -62,7 +61,7 @@ export const MCP_READ_TOOLS = [
|
|
|
62
61
|
},
|
|
63
62
|
{
|
|
64
63
|
name: 'bclaw_bootstrap',
|
|
65
|
-
description: 'Derive brownfield bootstrap signals from repository docs, manifests, and git history.',
|
|
64
|
+
description: 'Derive brownfield bootstrap signals, adaptive interview prompts for CLI or IDE chat agents, and an import proposal from repository docs, manifests, native agent files, and git history.',
|
|
66
65
|
inputSchema: {
|
|
67
66
|
type: 'object',
|
|
68
67
|
properties: {
|
|
@@ -1053,8 +1052,14 @@ export function handleMcpReadToolCall(name, args = {}, context = {}) {
|
|
|
1053
1052
|
target: result.profile.target,
|
|
1054
1053
|
repo_fingerprint: result.profile.repo_fingerprint,
|
|
1055
1054
|
sources_scanned: result.profile.sources_scanned,
|
|
1055
|
+
workspace_kind: result.profile.workspace_kind,
|
|
1056
|
+
confidence: result.profile.confidence,
|
|
1057
|
+
native_instruction_files: result.profile.native_instruction_files,
|
|
1058
|
+
gaps: result.profile.gaps,
|
|
1056
1059
|
seed_count: result.profile.seed_count,
|
|
1057
1060
|
seeds: result.seeds,
|
|
1061
|
+
import_plan: result.importPlan,
|
|
1062
|
+
last_application: result.lastApplication,
|
|
1058
1063
|
reused_profile: result.reusedProfile,
|
|
1059
1064
|
},
|
|
1060
1065
|
};
|
|
@@ -1808,8 +1813,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
1808
1813
|
if (releasePlanStatus === 'done' && !releasePlan.completed_at)
|
|
1809
1814
|
releasePlan.completed_at = ts;
|
|
1810
1815
|
releasePlan.updated_at = ts;
|
|
1811
|
-
|
|
1812
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(releaseState, cwd));
|
|
1816
|
+
persistState(releaseState, cwd);
|
|
1813
1817
|
releasePlanUpdated = true;
|
|
1814
1818
|
}
|
|
1815
1819
|
}
|
|
@@ -1957,8 +1961,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
1957
1961
|
estimated_effort: estimatedEffort,
|
|
1958
1962
|
};
|
|
1959
1963
|
state.plan_items.push(entry);
|
|
1960
|
-
|
|
1961
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
|
|
1964
|
+
persistState(state, cwd);
|
|
1962
1965
|
appendAuditEntry({ actor: resolved.identity.agent_name, actor_id: resolved.identity.agent_id, action: 'create', item_id: id, item_type: 'plan' }, cwd);
|
|
1963
1966
|
return {
|
|
1964
1967
|
response: toolResponse({
|
|
@@ -1996,8 +1999,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
1996
1999
|
if (args.actualEffort)
|
|
1997
2000
|
plan.actual_effort = args.actualEffort;
|
|
1998
2001
|
plan.updated_at = timestamp;
|
|
1999
|
-
|
|
2000
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
|
|
2002
|
+
persistState(state, cwd);
|
|
2001
2003
|
appendAuditEntry({ actor: resolved.identity.agent_name, actor_id: resolved.identity.agent_id, action: 'update', item_id: plan.id, item_type: 'plan' }, cwd);
|
|
2002
2004
|
return {
|
|
2003
2005
|
response: toolResponse({
|
|
@@ -2032,8 +2034,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
2032
2034
|
};
|
|
2033
2035
|
plan.steps = [...(plan.steps ?? []), step];
|
|
2034
2036
|
plan.updated_at = nowISO();
|
|
2035
|
-
|
|
2036
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
|
|
2037
|
+
persistState(state, cwd);
|
|
2037
2038
|
const total = plan.steps.length;
|
|
2038
2039
|
const done = plan.steps.filter((s) => s.status === 'done').length;
|
|
2039
2040
|
return {
|
|
@@ -2066,8 +2067,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
2066
2067
|
step.status = 'done';
|
|
2067
2068
|
step.updated_at = nowISO();
|
|
2068
2069
|
plan.updated_at = nowISO();
|
|
2069
|
-
|
|
2070
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
|
|
2070
|
+
persistState(state, cwd);
|
|
2071
2071
|
const total = plan.steps.length;
|
|
2072
2072
|
const done = plan.steps.filter((s) => s.status === 'done').length;
|
|
2073
2073
|
return {
|
|
@@ -2124,8 +2124,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
2124
2124
|
else if (itemType === 'trap') {
|
|
2125
2125
|
state.known_traps = state.known_traps.filter((t) => t.id !== itemId && t.short_label !== itemId);
|
|
2126
2126
|
}
|
|
2127
|
-
|
|
2128
|
-
writeFileAtomic(memoryPath('project.md', foundStore.cwd), generateMarkdown(state, foundStore.cwd));
|
|
2127
|
+
persistState(state, foundStore.cwd);
|
|
2129
2128
|
appendAuditEntry({ actor: resolved.identity.agent_name, actor_id: resolved.identity.agent_id, action: 'delete', item_id: itemId, item_type: itemType }, foundStore.cwd);
|
|
2130
2129
|
return {
|
|
2131
2130
|
response: toolResponse({
|
|
@@ -2211,8 +2210,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
2211
2210
|
else if (itemType === 'trap') {
|
|
2212
2211
|
sourceState.known_traps = sourceState.known_traps.filter((t) => t.id !== itemId);
|
|
2213
2212
|
}
|
|
2214
|
-
|
|
2215
|
-
writeFileAtomic(memoryPath('project.md', sourceStore.cwd), generateMarkdown(sourceState, sourceStore.cwd));
|
|
2213
|
+
persistState(sourceState, sourceStore.cwd);
|
|
2216
2214
|
// Add to target store
|
|
2217
2215
|
const targetState = loadState(targetCwd);
|
|
2218
2216
|
if (itemType === 'constraint') {
|
|
@@ -2224,8 +2222,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
2224
2222
|
else if (itemType === 'trap') {
|
|
2225
2223
|
targetState.known_traps.push(item);
|
|
2226
2224
|
}
|
|
2227
|
-
|
|
2228
|
-
writeFileAtomic(memoryPath('project.md', targetCwd), generateMarkdown(targetState, targetCwd));
|
|
2225
|
+
persistState(targetState, targetCwd);
|
|
2229
2226
|
}
|
|
2230
2227
|
else {
|
|
2231
2228
|
// Just update in place
|
|
@@ -2245,8 +2242,7 @@ export async function executeMcpToolCall(payload) {
|
|
|
2245
2242
|
if (idx >= 0)
|
|
2246
2243
|
state.known_traps[idx] = item;
|
|
2247
2244
|
}
|
|
2248
|
-
|
|
2249
|
-
writeFileAtomic(memoryPath('project.md', sourceStore.cwd), generateMarkdown(state, sourceStore.cwd));
|
|
2245
|
+
persistState(state, sourceStore.cwd);
|
|
2250
2246
|
}
|
|
2251
2247
|
appendAuditEntry({ actor: resolved.identity.agent_name, actor_id: resolved.identity.agent_id, action: 'update', item_id: itemId, item_type: itemType }, sourceStore.cwd);
|
|
2252
2248
|
return {
|
package/dist/commands/memory.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from '../core/config.js';
|
|
2
|
-
import { memoryExists
|
|
3
|
-
import {
|
|
4
|
-
import { loadState, saveState } from '../core/state.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
|
+
import { loadState, persistState } from '../core/state.js';
|
|
5
4
|
import { scanText } from '../core/security.js';
|
|
6
5
|
import { validateCliInput } from '../core/input-validation.js';
|
|
7
6
|
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
@@ -222,8 +221,7 @@ function runMemoryUpdate(id, options) {
|
|
|
222
221
|
break;
|
|
223
222
|
}
|
|
224
223
|
}
|
|
225
|
-
|
|
226
|
-
writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
|
|
224
|
+
persistState(state, options.cwd);
|
|
227
225
|
console.log(`✔ Memory item updated: [${resolved.item.id}] ${resolved.item.text}`);
|
|
228
226
|
}
|
|
229
227
|
function runMemoryDelete(id, cwd) {
|
|
@@ -250,8 +248,7 @@ function runMemoryDelete(id, cwd) {
|
|
|
250
248
|
console.error(`Error: Memory item '${id}' not found.`);
|
|
251
249
|
process.exit(1);
|
|
252
250
|
}
|
|
253
|
-
|
|
254
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
|
|
251
|
+
persistState(state, cwd);
|
|
255
252
|
console.log(`✔ Memory item deleted: [${deleted.id}] ${deleted.text}`);
|
|
256
253
|
}
|
|
257
254
|
function collectMemoryItems(state) {
|
package/dist/commands/plan.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { loadState,
|
|
1
|
+
import { loadState, persistState } from '../core/state.js';
|
|
2
2
|
import { resolveCurrentAgentName } from '../core/agent-registry.js';
|
|
3
3
|
import { loadConfig } from '../core/config.js';
|
|
4
|
-
import { generateMarkdown } from '../core/markdown.js';
|
|
5
4
|
import { generateIdWithLabel, nowISO } from '../core/ids.js';
|
|
6
5
|
import { scanText } from '../core/security.js';
|
|
7
|
-
import { memoryExists
|
|
6
|
+
import { memoryExists } from '../core/io.js';
|
|
8
7
|
import { validateCliInput } from '../core/input-validation.js';
|
|
9
8
|
import { runListPlans } from './list-plans.js';
|
|
10
9
|
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
@@ -68,8 +67,7 @@ export function runPlan(text, options = {}) {
|
|
|
68
67
|
estimated_effort: estimatedEffort,
|
|
69
68
|
};
|
|
70
69
|
state.plan_items.push(entry);
|
|
71
|
-
|
|
72
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
|
|
70
|
+
persistState(state, cwd);
|
|
73
71
|
const storeLabel = options.store && options.store !== 'local' ? ` [store:${options.store}]` : '';
|
|
74
72
|
console.log(`✔ Plan item added: [${id}] ${text}${storeLabel}`);
|
|
75
73
|
}
|
package/dist/commands/prune.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadState, saveState } from '../core/state.js';
|
|
2
|
-
import { memoryExists } from '../core/io.js';
|
|
2
|
+
import { memoryExists, memoryPath, withStoreLock, writeFileAtomic } from '../core/io.js';
|
|
3
|
+
import { generateMarkdown } from '../core/markdown.js';
|
|
3
4
|
import { deleteRuntimeNote, listRuntimeNotes } from '../core/runtime.js';
|
|
4
5
|
import { expireStaleActiveClaims } from '../core/claims.js';
|
|
5
6
|
export function runPrune(options = {}) {
|
|
@@ -8,36 +9,37 @@ export function runPrune(options = {}) {
|
|
|
8
9
|
console.error('Project memory not initialized. Run `brainclaw init` first.');
|
|
9
10
|
process.exit(1);
|
|
10
11
|
}
|
|
11
|
-
const state = loadState(cwd);
|
|
12
12
|
const now = new Date().toISOString();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
for (const c of state.active_constraints) {
|
|
16
|
-
if (c.status === 'active' && c.expires_at && c.expires_at < now) {
|
|
17
|
-
c.status = 'expired';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
// Filter out constraints that have been expired for a while to keep memory clean
|
|
21
|
-
// For the MVP, we just remove them if they are expired
|
|
22
|
-
state.active_constraints = state.active_constraints.filter(c => c.status !== 'expired');
|
|
23
|
-
const prunedCount = originalLength - state.active_constraints.length;
|
|
24
|
-
saveState(state, cwd);
|
|
25
|
-
const expiredClaimsCount = expireStaleActiveClaims(cwd);
|
|
13
|
+
let prunedCount = 0;
|
|
14
|
+
let expiredClaimsCount = 0;
|
|
26
15
|
let expiredNotesCount = 0;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
for (const
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
16
|
+
withStoreLock(cwd, () => {
|
|
17
|
+
const state = loadState(cwd);
|
|
18
|
+
const originalLength = state.active_constraints.length;
|
|
19
|
+
for (const c of state.active_constraints) {
|
|
20
|
+
if (c.status === 'active' && c.expires_at && c.expires_at < now) {
|
|
21
|
+
c.status = 'expired';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
state.active_constraints = state.active_constraints.filter(c => c.status !== 'expired');
|
|
25
|
+
prunedCount = originalLength - state.active_constraints.length;
|
|
26
|
+
saveState(state, cwd);
|
|
27
|
+
expiredClaimsCount = expireStaleActiveClaims(cwd);
|
|
28
|
+
if (options.expired) {
|
|
29
|
+
const notes = listRuntimeNotes(undefined, cwd);
|
|
30
|
+
for (const note of notes) {
|
|
31
|
+
if (note.expires_at && note.expires_at < now) {
|
|
32
|
+
try {
|
|
33
|
+
if (deleteRuntimeNote(note, cwd)) {
|
|
34
|
+
expiredNotesCount++;
|
|
35
|
+
}
|
|
35
36
|
}
|
|
37
|
+
catch { /* ignore */ }
|
|
36
38
|
}
|
|
37
|
-
catch { /* ignore */ }
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
+
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(loadState(cwd), cwd));
|
|
42
|
+
});
|
|
41
43
|
if (options.expired) {
|
|
42
44
|
console.log(`✔ Pruned ${prunedCount} expired constraints, ${expiredNotesCount} expired runtime notes, ${expiredClaimsCount} expired claims.`);
|
|
43
45
|
}
|
package/dist/commands/reflect.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { memoryExists
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
3
3
|
import { loadConfig } from '../core/config.js';
|
|
4
4
|
import { buildOperationalIdentity } from '../core/identity.js';
|
|
5
|
-
import { loadState,
|
|
6
|
-
import { generateMarkdown } from '../core/markdown.js';
|
|
5
|
+
import { loadState, persistState } from '../core/state.js';
|
|
7
6
|
import { scanText } from '../core/security.js';
|
|
8
7
|
import { nowISO, generateIdWithLabel } from '../core/ids.js';
|
|
9
8
|
import { saveCandidate, generateCandidateIdWithLabel, listCandidates } from '../core/candidates.js';
|
|
@@ -265,8 +264,7 @@ function promoteCandidateToState(candidate, cwd) {
|
|
|
265
264
|
break;
|
|
266
265
|
}
|
|
267
266
|
}
|
|
268
|
-
|
|
269
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state));
|
|
267
|
+
persistState(state, cwd);
|
|
270
268
|
return promotedItemId;
|
|
271
269
|
}
|
|
272
270
|
export function mapEventTypeToCandidateType(eventType) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memoryExists } from '../core/io.js';
|
|
2
|
-
import { memoryPath, writeFileAtomic } from '../core/io.js';
|
|
2
|
+
import { memoryPath, withStoreLock, writeFileAtomic } from '../core/io.js';
|
|
3
3
|
import { loadClaim, listClaims, releaseClaim } from '../core/claims.js';
|
|
4
4
|
import { generateMarkdown } from '../core/markdown.js';
|
|
5
5
|
import { loadState, saveState } from '../core/state.js';
|
|
@@ -9,27 +9,30 @@ export function runReleaseClaim(id, options = {}) {
|
|
|
9
9
|
process.exit(1);
|
|
10
10
|
}
|
|
11
11
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
|
|
12
|
+
let claim = loadClaim(id, options.cwd);
|
|
13
|
+
withStoreLock(options.cwd, () => {
|
|
14
|
+
const existing = loadClaim(id, options.cwd);
|
|
15
|
+
claim = releaseClaim(id, options.cwd);
|
|
16
|
+
let state = loadState(options.cwd);
|
|
17
|
+
if (existing.plan_id) {
|
|
18
|
+
const plan = state.plan_items.find((item) => item.id === existing.plan_id);
|
|
19
|
+
if (plan) {
|
|
20
|
+
const otherActiveClaims = listClaims(options.cwd).filter((item) => item.status === 'active' && item.plan_id === existing.plan_id);
|
|
21
|
+
if (options.planStatus) {
|
|
22
|
+
plan.status = options.planStatus;
|
|
23
|
+
}
|
|
24
|
+
else if (otherActiveClaims.length === 0 && plan.status === 'in_progress') {
|
|
25
|
+
plan.status = 'todo';
|
|
26
|
+
}
|
|
27
|
+
if (otherActiveClaims.length === 0 && plan.assignee === existing.agent) {
|
|
28
|
+
plan.assignee = undefined;
|
|
29
|
+
}
|
|
30
|
+
plan.updated_at = new Date().toISOString();
|
|
31
|
+
saveState(state, options.cwd);
|
|
21
32
|
}
|
|
22
|
-
else if (otherActiveClaims.length === 0 && plan.status === 'in_progress') {
|
|
23
|
-
plan.status = 'todo';
|
|
24
|
-
}
|
|
25
|
-
if (otherActiveClaims.length === 0 && plan.assignee === existing.agent) {
|
|
26
|
-
plan.assignee = undefined;
|
|
27
|
-
}
|
|
28
|
-
plan.updated_at = new Date().toISOString();
|
|
29
|
-
saveState(state, options.cwd);
|
|
30
33
|
}
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
|
|
35
|
+
});
|
|
33
36
|
console.log(`✔ Claim [${id}] released (was: ${claim.agent} → ${claim.scope})`);
|
|
34
37
|
}
|
|
35
38
|
catch (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
|
|
2
|
+
import { memoryExists, memoryPath, withStoreLock, writeFileAtomic } from '../core/io.js';
|
|
3
3
|
import { listClaims, releaseClaim } from '../core/claims.js';
|
|
4
4
|
import { generateMarkdown } from '../core/markdown.js';
|
|
5
5
|
import { loadState, saveState } from '../core/state.js';
|
|
@@ -35,31 +35,32 @@ export function runReleaseClaims(options = {}) {
|
|
|
35
35
|
const toRelease = claims.filter((c) => changedFiles.some((f) => scopeMatchesFile(c.scope, f)));
|
|
36
36
|
if (toRelease.length === 0)
|
|
37
37
|
process.exit(0);
|
|
38
|
-
let state = loadState(options.cwd);
|
|
39
38
|
let released = 0;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
plan.status
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
withStoreLock(options.cwd, () => {
|
|
40
|
+
let state = loadState(options.cwd);
|
|
41
|
+
for (const claim of toRelease) {
|
|
42
|
+
try {
|
|
43
|
+
releaseClaim(claim.id, options.cwd);
|
|
44
|
+
released++;
|
|
45
|
+
if (claim.plan_id) {
|
|
46
|
+
state = loadState(options.cwd);
|
|
47
|
+
const plan = state.plan_items.find((p) => p.id === claim.plan_id);
|
|
48
|
+
if (plan) {
|
|
49
|
+
const remaining = listClaims(options.cwd).filter((c) => c.status === 'active' && c.plan_id === claim.plan_id);
|
|
50
|
+
if (remaining.length === 0 && plan.status === 'in_progress') {
|
|
51
|
+
plan.status = 'todo';
|
|
52
|
+
plan.updated_at = new Date().toISOString();
|
|
53
|
+
saveState(state, options.cwd);
|
|
54
|
+
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
57
|
+
console.log(`✔ Auto-released claim [${claim.id}]: ${claim.scope}`);
|
|
56
58
|
}
|
|
57
|
-
|
|
59
|
+
catch { /* skip individual failures */ }
|
|
58
60
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
|
|
61
|
+
state = loadState(options.cwd);
|
|
62
|
+
writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
|
|
63
|
+
});
|
|
63
64
|
if (released > 0) {
|
|
64
65
|
console.log(`brainclaw: ${released} claim(s) auto-released after merge.`);
|
|
65
66
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { memoryExists } from '../core/io.js';
|
|
2
2
|
import { readAuditLog } from '../core/audit.js';
|
|
3
|
-
import { loadState,
|
|
3
|
+
import { loadState, persistState } from '../core/state.js';
|
|
4
4
|
import { loadCandidate, saveCandidate } from '../core/candidates.js';
|
|
5
5
|
import { appendAuditEntry } from '../core/audit.js';
|
|
6
6
|
import { buildOperationalIdentity } from '../core/identity.js';
|
|
@@ -128,7 +128,7 @@ function applyRollback(entry, actorName, options) {
|
|
|
128
128
|
state.known_traps.push(before);
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
persistState(state);
|
|
132
132
|
}
|
|
133
133
|
else {
|
|
134
134
|
return { ok: false, message: `Rollback not supported for item_type '${item_type}'.` };
|
package/dist/commands/tool.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { loadState,
|
|
1
|
+
import { loadState, persistState } from '../core/state.js';
|
|
2
2
|
import { resolveCurrentAgentName } from '../core/agent-registry.js';
|
|
3
|
-
import { memoryExists
|
|
4
|
-
import { generateMarkdown } from '../core/markdown.js';
|
|
3
|
+
import { memoryExists } from '../core/io.js';
|
|
5
4
|
import { generateIdWithLabel, nowISO } from '../core/ids.js';
|
|
6
5
|
import { loadConfig } from '../core/config.js';
|
|
7
6
|
import { scanText } from '../core/security.js';
|
|
@@ -89,8 +88,7 @@ function runToolAdd(name, description, options, cwd) {
|
|
|
89
88
|
// For now, store as decision to avoid schema migration
|
|
90
89
|
// Will migrate to separate tool storage in v0.16
|
|
91
90
|
state.recent_decisions.push(entry);
|
|
92
|
-
|
|
93
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state));
|
|
91
|
+
persistState(state, cwd);
|
|
94
92
|
console.log(`✔ Tool added: [${id}] ${name}`);
|
|
95
93
|
console.log(' (Stored in decisions for now; will move to dedicated registry in v0.16)');
|
|
96
94
|
}
|
package/dist/commands/trap.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { loadState,
|
|
1
|
+
import { loadState, persistState } from '../core/state.js';
|
|
2
2
|
import { resolveCurrentAgentName } from '../core/agent-registry.js';
|
|
3
3
|
import { resolveCurrentHostId } from '../core/host.js';
|
|
4
4
|
import { loadConfig } from '../core/config.js';
|
|
5
|
-
import { generateMarkdown } from '../core/markdown.js';
|
|
6
5
|
import { nowISO } from '../core/ids.js';
|
|
7
6
|
import { scanText } from '../core/security.js';
|
|
8
|
-
import { memoryExists
|
|
7
|
+
import { memoryExists } from '../core/io.js';
|
|
9
8
|
import { generateTrapIdWithLabel, saveOperationalTrap } from '../core/traps.js';
|
|
10
9
|
import { validateCliInput, validateCliTtl } from '../core/input-validation.js';
|
|
11
10
|
import { resolveTargetStore } from '../core/store-resolution.js';
|
|
@@ -49,8 +48,7 @@ export function runTrap(text, options = {}) {
|
|
|
49
48
|
};
|
|
50
49
|
if (visibility === 'shared') {
|
|
51
50
|
state.known_traps.push(entry);
|
|
52
|
-
|
|
53
|
-
writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
|
|
51
|
+
persistState(state, cwd);
|
|
54
52
|
}
|
|
55
53
|
else {
|
|
56
54
|
saveOperationalTrap(entry, cwd);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { loadState,
|
|
2
|
-
import { memoryExists
|
|
3
|
-
import { generateMarkdown } from '../core/markdown.js';
|
|
1
|
+
import { loadState, persistState } from '../core/state.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
4
3
|
export function runUpdateHandoff(id, options = {}) {
|
|
5
4
|
if (!memoryExists()) {
|
|
6
5
|
console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
|
|
@@ -16,8 +15,7 @@ export function runUpdateHandoff(id, options = {}) {
|
|
|
16
15
|
handoff.status = options.status;
|
|
17
16
|
if (options.to !== undefined)
|
|
18
17
|
handoff.to = options.to;
|
|
19
|
-
|
|
20
|
-
writeFileAtomic(memoryPath('project.md'), generateMarkdown(state));
|
|
18
|
+
persistState(state);
|
|
21
19
|
console.log(`✔ Handoff updated: [${handoff.id}] ${handoff.from} → ${handoff.to} (${handoff.status})`);
|
|
22
20
|
}
|
|
23
21
|
//# sourceMappingURL=update-handoff.js.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { loadState,
|
|
2
|
-
import { memoryExists
|
|
3
|
-
import { generateMarkdown } from '../core/markdown.js';
|
|
1
|
+
import { loadState, persistState } from '../core/state.js';
|
|
2
|
+
import { memoryExists } from '../core/io.js';
|
|
4
3
|
import { nowISO } from '../core/ids.js';
|
|
5
4
|
export function runUpdatePlan(id, options = {}) {
|
|
6
5
|
if (!memoryExists(options.cwd)) {
|
|
@@ -30,8 +29,7 @@ export function runUpdatePlan(id, options = {}) {
|
|
|
30
29
|
if (options.actualEffort)
|
|
31
30
|
plan.actual_effort = options.actualEffort;
|
|
32
31
|
plan.updated_at = timestamp;
|
|
33
|
-
|
|
34
|
-
writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
|
|
32
|
+
persistState(state, options.cwd);
|
|
35
33
|
console.log(`✔ Plan item updated: [${plan.id}] ${plan.text}`);
|
|
36
34
|
}
|
|
37
35
|
//# sourceMappingURL=update-plan.js.map
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { ensureMemoryDir, memoryDir, memoryExists } from '../core/io.js';
|
|
4
|
-
import { loadState,
|
|
4
|
+
import { loadState, persistState } from '../core/state.js';
|
|
5
5
|
import { scanMigrationStatus } from '../core/migration.js';
|
|
6
6
|
import { commitMemoryChange, initMemoryRepo } from '../core/memory-git.js';
|
|
7
7
|
import { BRAINCLAW_SECTION_END, BRAINCLAW_SECTION_START, buildBrainclawSection, buildClaudeCodeCommandText, ensureClaudeCodeCommand, hasBrainclawSection, } from '../core/agent-files.js';
|
|
@@ -114,7 +114,7 @@ export function runUpgrade(options = {}) {
|
|
|
114
114
|
// Execute schema migrations by re-saving state (loadState auto-migrates via Zod parse)
|
|
115
115
|
if (outdated.length > 0) {
|
|
116
116
|
const state = loadState(cwd);
|
|
117
|
-
|
|
117
|
+
persistState(state, cwd);
|
|
118
118
|
}
|
|
119
119
|
const refreshedAgentFiles = refreshManagedWorkspaceAgentFiles(cwd);
|
|
120
120
|
// Clean up empty legacy directories (recursively removes empty subdirs first)
|
package/dist/core/audit.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { memoryDir } from './io.js';
|
|
3
|
+
import { memoryDir, withStoreLock } from './io.js';
|
|
4
4
|
import { nowISO } from './ids.js';
|
|
5
5
|
import { logger } from './logger.js';
|
|
6
6
|
import { appendEvent } from './event-log.js';
|
|
@@ -23,31 +23,31 @@ function auditLogPath(cwd) {
|
|
|
23
23
|
}
|
|
24
24
|
export function appendAuditEntry(entry, cwd) {
|
|
25
25
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
item_type: entry.item_type,
|
|
32
|
-
before: entry.before,
|
|
33
|
-
after: entry.after,
|
|
34
|
-
actor_id: entry.actor_id,
|
|
35
|
-
reason: entry.reason,
|
|
36
|
-
};
|
|
37
|
-
// Remove undefined fields for compactness
|
|
38
|
-
const line = JSON.stringify(Object.fromEntries(Object.entries(full).filter(([, v]) => v !== undefined)));
|
|
39
|
-
fs.appendFileSync(auditLogPath(cwd), line + '\n', 'utf-8');
|
|
40
|
-
// Mirror to structured event log
|
|
41
|
-
const eventAction = AUDIT_TO_EVENT_ACTION[entry.action];
|
|
42
|
-
if (eventAction) {
|
|
43
|
-
appendEvent({
|
|
44
|
-
action: eventAction,
|
|
45
|
-
item_type: entry.item_type ?? 'state',
|
|
26
|
+
withStoreLock(cwd, () => {
|
|
27
|
+
const full = {
|
|
28
|
+
timestamp: nowISO(),
|
|
29
|
+
actor: entry.actor,
|
|
30
|
+
action: entry.action,
|
|
46
31
|
item_id: entry.item_id,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
item_type: entry.item_type,
|
|
33
|
+
before: entry.before,
|
|
34
|
+
after: entry.after,
|
|
35
|
+
actor_id: entry.actor_id,
|
|
36
|
+
reason: entry.reason,
|
|
37
|
+
};
|
|
38
|
+
const line = JSON.stringify(Object.fromEntries(Object.entries(full).filter(([, v]) => v !== undefined)));
|
|
39
|
+
fs.appendFileSync(auditLogPath(cwd), line + '\n', 'utf-8');
|
|
40
|
+
const eventAction = AUDIT_TO_EVENT_ACTION[entry.action];
|
|
41
|
+
if (eventAction) {
|
|
42
|
+
appendEvent({
|
|
43
|
+
action: eventAction,
|
|
44
|
+
item_type: entry.item_type ?? 'state',
|
|
45
|
+
item_id: entry.item_id,
|
|
46
|
+
agent: entry.actor,
|
|
47
|
+
agent_id: entry.actor_id,
|
|
48
|
+
}, cwd);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
53
53
|
logger.debug('Failed to write audit log entry:', err);
|