brainclaw 0.19.2

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.
Files changed (128) hide show
  1. package/LICENSE +74 -0
  2. package/README.md +226 -0
  3. package/dist/cli.js +1037 -0
  4. package/dist/commands/accept.js +149 -0
  5. package/dist/commands/adapter-openclaw-import.js +75 -0
  6. package/dist/commands/add-step.js +35 -0
  7. package/dist/commands/agent-board.js +106 -0
  8. package/dist/commands/audit.js +35 -0
  9. package/dist/commands/bootstrap.js +34 -0
  10. package/dist/commands/capability.js +104 -0
  11. package/dist/commands/changes.js +112 -0
  12. package/dist/commands/check-constraints.js +63 -0
  13. package/dist/commands/claim-resource.js +54 -0
  14. package/dist/commands/claim.js +92 -0
  15. package/dist/commands/complete-step.js +34 -0
  16. package/dist/commands/constraint.js +44 -0
  17. package/dist/commands/context-diff.js +32 -0
  18. package/dist/commands/context.js +63 -0
  19. package/dist/commands/decision.js +45 -0
  20. package/dist/commands/delete-plan.js +20 -0
  21. package/dist/commands/diff.js +99 -0
  22. package/dist/commands/doctor.js +1275 -0
  23. package/dist/commands/enable-agent.js +63 -0
  24. package/dist/commands/env.js +46 -0
  25. package/dist/commands/estimation-report.js +167 -0
  26. package/dist/commands/explore.js +47 -0
  27. package/dist/commands/export.js +381 -0
  28. package/dist/commands/handoff.js +63 -0
  29. package/dist/commands/history.js +22 -0
  30. package/dist/commands/hooks.js +123 -0
  31. package/dist/commands/init.js +356 -0
  32. package/dist/commands/install-hooks.js +115 -0
  33. package/dist/commands/instruction.js +56 -0
  34. package/dist/commands/list-agents.js +44 -0
  35. package/dist/commands/list-claims.js +45 -0
  36. package/dist/commands/list-instructions.js +50 -0
  37. package/dist/commands/list-plans.js +48 -0
  38. package/dist/commands/mcp-worker.js +12 -0
  39. package/dist/commands/mcp.js +2272 -0
  40. package/dist/commands/memory.js +283 -0
  41. package/dist/commands/metrics.js +175 -0
  42. package/dist/commands/plan-resource.js +62 -0
  43. package/dist/commands/plan.js +76 -0
  44. package/dist/commands/prune-candidates.js +36 -0
  45. package/dist/commands/prune.js +48 -0
  46. package/dist/commands/pull.js +25 -0
  47. package/dist/commands/push.js +28 -0
  48. package/dist/commands/rebuild.js +14 -0
  49. package/dist/commands/reflect-runtime-note.js +74 -0
  50. package/dist/commands/reflect.js +286 -0
  51. package/dist/commands/register-agent.js +29 -0
  52. package/dist/commands/reject.js +52 -0
  53. package/dist/commands/release-claim.js +41 -0
  54. package/dist/commands/release-claims.js +67 -0
  55. package/dist/commands/review.js +242 -0
  56. package/dist/commands/rollback.js +156 -0
  57. package/dist/commands/runtime-note.js +144 -0
  58. package/dist/commands/runtime-status.js +49 -0
  59. package/dist/commands/search.js +36 -0
  60. package/dist/commands/session-end.js +187 -0
  61. package/dist/commands/session-start.js +147 -0
  62. package/dist/commands/set-trust.js +92 -0
  63. package/dist/commands/setup.js +446 -0
  64. package/dist/commands/show-candidate.js +31 -0
  65. package/dist/commands/star-candidate.js +28 -0
  66. package/dist/commands/status.js +133 -0
  67. package/dist/commands/sync.js +159 -0
  68. package/dist/commands/tool.js +126 -0
  69. package/dist/commands/trap.js +74 -0
  70. package/dist/commands/update-handoff.js +23 -0
  71. package/dist/commands/update-plan.js +37 -0
  72. package/dist/commands/upgrade.js +382 -0
  73. package/dist/commands/use-candidate.js +35 -0
  74. package/dist/commands/version.js +96 -0
  75. package/dist/commands/watch.js +215 -0
  76. package/dist/commands/whoami.js +104 -0
  77. package/dist/core/agent-context.js +340 -0
  78. package/dist/core/agent-files.js +874 -0
  79. package/dist/core/agent-integrations.js +135 -0
  80. package/dist/core/agent-inventory.js +401 -0
  81. package/dist/core/agent-registry.js +420 -0
  82. package/dist/core/ai-agent-detection.js +140 -0
  83. package/dist/core/audit.js +85 -0
  84. package/dist/core/bootstrap.js +658 -0
  85. package/dist/core/brainclaw-version.js +433 -0
  86. package/dist/core/candidates.js +137 -0
  87. package/dist/core/circuit-breaker.js +118 -0
  88. package/dist/core/claims.js +72 -0
  89. package/dist/core/config.js +86 -0
  90. package/dist/core/context-diff.js +122 -0
  91. package/dist/core/context.js +1212 -0
  92. package/dist/core/contradictions.js +270 -0
  93. package/dist/core/coordination.js +86 -0
  94. package/dist/core/cross-project.js +99 -0
  95. package/dist/core/duplicates.js +72 -0
  96. package/dist/core/event-log.js +152 -0
  97. package/dist/core/events.js +56 -0
  98. package/dist/core/execution-context.js +204 -0
  99. package/dist/core/freshness.js +87 -0
  100. package/dist/core/global-registry.js +182 -0
  101. package/dist/core/host.js +10 -0
  102. package/dist/core/identity.js +151 -0
  103. package/dist/core/ids.js +56 -0
  104. package/dist/core/input-validation.js +81 -0
  105. package/dist/core/instructions.js +117 -0
  106. package/dist/core/io.js +191 -0
  107. package/dist/core/json-store.js +63 -0
  108. package/dist/core/lifecycle.js +45 -0
  109. package/dist/core/lock.js +129 -0
  110. package/dist/core/logger.js +49 -0
  111. package/dist/core/machine-profile.js +332 -0
  112. package/dist/core/markdown.js +120 -0
  113. package/dist/core/memory-git.js +133 -0
  114. package/dist/core/migration.js +247 -0
  115. package/dist/core/project-registry.js +64 -0
  116. package/dist/core/reflection-safety.js +21 -0
  117. package/dist/core/repo-analysis.js +133 -0
  118. package/dist/core/reputation.js +409 -0
  119. package/dist/core/runtime.js +134 -0
  120. package/dist/core/schema.js +580 -0
  121. package/dist/core/search.js +115 -0
  122. package/dist/core/security.js +66 -0
  123. package/dist/core/setup-state.js +50 -0
  124. package/dist/core/state.js +83 -0
  125. package/dist/core/store-resolution.js +119 -0
  126. package/dist/core/sync-remote.js +83 -0
  127. package/dist/core/traps.js +86 -0
  128. package/package.json +60 -0
@@ -0,0 +1,283 @@
1
+ import { loadConfig } from '../core/config.js';
2
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
3
+ import { generateMarkdown } from '../core/markdown.js';
4
+ import { loadState, saveState } from '../core/state.js';
5
+ import { scanText } from '../core/security.js';
6
+ import { validateCliInput } from '../core/input-validation.js';
7
+ import { resolveTargetStore } from '../core/store-resolution.js';
8
+ import { runConstraint } from './constraint.js';
9
+ import { runDecision } from './decision.js';
10
+ import { runHandoff } from './handoff.js';
11
+ import { runTrap } from './trap.js';
12
+ export function runMemoryCommand(subcommand, args, options = {}) {
13
+ const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
14
+ const normalized = subcommand.trim().toLowerCase();
15
+ if (normalized === 'create') {
16
+ const kind = args[0];
17
+ const text = args.slice(1).join(' ').trim();
18
+ if (!kind || !isMemoryKind(kind)) {
19
+ console.error('Error: memory create requires <type> where type is decision, constraint, trap, or handoff');
20
+ process.exit(1);
21
+ }
22
+ if (!text) {
23
+ console.error('Error: memory create requires <text>');
24
+ process.exit(1);
25
+ }
26
+ runMemoryCreate(kind, text, { ...options, cwd });
27
+ return;
28
+ }
29
+ if (normalized === 'list') {
30
+ runMemoryList({ ...options, cwd });
31
+ return;
32
+ }
33
+ if (normalized === 'update') {
34
+ const id = args[0];
35
+ if (!id) {
36
+ console.error('Error: memory update requires <id>');
37
+ process.exit(1);
38
+ }
39
+ runMemoryUpdate(id, { ...options, cwd });
40
+ return;
41
+ }
42
+ if (normalized === 'delete') {
43
+ const id = args[0];
44
+ if (!id) {
45
+ console.error('Error: memory delete requires <id>');
46
+ process.exit(1);
47
+ }
48
+ runMemoryDelete(id, cwd);
49
+ return;
50
+ }
51
+ console.error(`Error: unknown memory subcommand '${subcommand}'. Use create, list, update, or delete.`);
52
+ process.exit(1);
53
+ }
54
+ function runMemoryCreate(kind, text, options) {
55
+ switch (kind) {
56
+ case 'decision':
57
+ runDecision(text, options);
58
+ return;
59
+ case 'constraint':
60
+ runConstraint(text, options);
61
+ return;
62
+ case 'trap':
63
+ runTrap(text, {
64
+ status: options.status,
65
+ severity: options.severity,
66
+ tag: options.tag,
67
+ path: options.path,
68
+ author: options.author,
69
+ visibility: options.visibility,
70
+ host: options.host,
71
+ ttl: options.ttl,
72
+ cwd: options.cwd,
73
+ store: options.store,
74
+ plan: options.plan,
75
+ });
76
+ return;
77
+ case 'handoff': {
78
+ if (!options.from || !options.to) {
79
+ console.error('Error: memory create handoff requires --from <from> and --to <to>');
80
+ process.exit(1);
81
+ }
82
+ runHandoff(text, {
83
+ from: options.from,
84
+ to: options.to,
85
+ tag: options.tag,
86
+ path: options.path,
87
+ project: options.project,
88
+ plan: options.plan,
89
+ author: options.author,
90
+ captureDiff: options.captureDiff,
91
+ cwd: options.cwd,
92
+ store: options.store,
93
+ });
94
+ }
95
+ }
96
+ }
97
+ function runMemoryList(options) {
98
+ if (!memoryExists(options.cwd)) {
99
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
100
+ process.exit(1);
101
+ }
102
+ const state = loadState(options.cwd);
103
+ let items = collectMemoryItems(state);
104
+ if (options.type) {
105
+ items = items.filter((entry) => entry.kind === options.type);
106
+ }
107
+ if (options.status) {
108
+ items = items.filter(({ kind, item }) => {
109
+ if (kind === 'constraint' || kind === 'handoff' || kind === 'trap') {
110
+ return 'status' in item && item.status === options.status;
111
+ }
112
+ return false;
113
+ });
114
+ }
115
+ items.sort((a, b) => a.item.created_at.localeCompare(b.item.created_at));
116
+ if (options.json) {
117
+ console.log(JSON.stringify({
118
+ total: items.length,
119
+ items: items.map(({ kind, item }) => ({ type: kind, ...item })),
120
+ }, null, 2));
121
+ return;
122
+ }
123
+ if (items.length === 0) {
124
+ console.log('No memory items found.');
125
+ return;
126
+ }
127
+ console.log(`${items.length} memory item(s):`);
128
+ console.log('');
129
+ for (const { kind, item } of items) {
130
+ const tags = item.tags.length ? ` [${item.tags.join(', ')}]` : '';
131
+ const meta = renderMemoryMeta(kind, item);
132
+ console.log(` [${item.id}] <${kind}> ${item.text}${meta}${tags}`);
133
+ }
134
+ }
135
+ function runMemoryUpdate(id, options) {
136
+ if (!memoryExists(options.cwd)) {
137
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
138
+ process.exit(1);
139
+ }
140
+ const state = loadState(options.cwd);
141
+ const resolved = resolveMemoryItem(state, id);
142
+ if (!resolved) {
143
+ console.error(`Error: Memory item '${id}' not found.`);
144
+ process.exit(1);
145
+ }
146
+ if (options.text !== undefined || options.tag !== undefined) {
147
+ validateCliInput(options.text ?? resolved.item.text, options.tag ?? resolved.item.tags);
148
+ const config = loadConfig(options.cwd);
149
+ const warnings = scanText(options.text ?? resolved.item.text, config);
150
+ for (const warning of warnings) {
151
+ console.warn(`⚠ ${warning.message}`);
152
+ if (warning.level === 'block') {
153
+ console.error('Blocked: strict redaction is enabled. Entry not updated.');
154
+ process.exit(1);
155
+ }
156
+ }
157
+ }
158
+ if (options.text !== undefined) {
159
+ resolved.item.text = options.text;
160
+ }
161
+ if (options.tag !== undefined) {
162
+ resolved.item.tags = options.tag;
163
+ }
164
+ if (options.path !== undefined) {
165
+ resolved.item.related_paths = options.path;
166
+ }
167
+ switch (resolved.kind) {
168
+ case 'decision':
169
+ {
170
+ const item = resolved.item;
171
+ if (options.outcome !== undefined) {
172
+ item.outcome = options.outcome;
173
+ }
174
+ if (options.plan !== undefined) {
175
+ item.plan_id = options.plan;
176
+ }
177
+ break;
178
+ }
179
+ case 'constraint':
180
+ {
181
+ const item = resolved.item;
182
+ if (options.status !== undefined) {
183
+ item.status = options.status;
184
+ }
185
+ if (options.category !== undefined) {
186
+ item.category = options.category;
187
+ }
188
+ break;
189
+ }
190
+ case 'trap':
191
+ {
192
+ const item = resolved.item;
193
+ if (options.status !== undefined) {
194
+ item.status = options.status;
195
+ }
196
+ if (options.severity !== undefined) {
197
+ item.severity = options.severity;
198
+ }
199
+ if (options.plan !== undefined) {
200
+ item.plan_id = options.plan;
201
+ }
202
+ break;
203
+ }
204
+ case 'handoff':
205
+ {
206
+ const item = resolved.item;
207
+ if (options.status !== undefined) {
208
+ item.status = options.status;
209
+ }
210
+ if (options.from !== undefined) {
211
+ item.from = options.from;
212
+ }
213
+ if (options.to !== undefined) {
214
+ item.to = options.to;
215
+ }
216
+ if (options.project !== undefined) {
217
+ item.project = options.project;
218
+ }
219
+ if (options.plan !== undefined) {
220
+ item.plan_id = options.plan;
221
+ }
222
+ break;
223
+ }
224
+ }
225
+ saveState(state, options.cwd);
226
+ writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
227
+ console.log(`✔ Memory item updated: [${resolved.item.id}] ${resolved.item.text}`);
228
+ }
229
+ function runMemoryDelete(id, cwd) {
230
+ if (!memoryExists(cwd)) {
231
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
232
+ process.exit(1);
233
+ }
234
+ const state = loadState(cwd);
235
+ const arrays = [
236
+ state.recent_decisions,
237
+ state.active_constraints,
238
+ state.known_traps,
239
+ state.open_handoffs,
240
+ ];
241
+ let deleted;
242
+ for (const items of arrays) {
243
+ const index = items.findIndex((item) => item.id === id || item.short_label === id);
244
+ if (index >= 0) {
245
+ [deleted] = items.splice(index, 1);
246
+ break;
247
+ }
248
+ }
249
+ if (!deleted) {
250
+ console.error(`Error: Memory item '${id}' not found.`);
251
+ process.exit(1);
252
+ }
253
+ saveState(state, cwd);
254
+ writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
255
+ console.log(`✔ Memory item deleted: [${deleted.id}] ${deleted.text}`);
256
+ }
257
+ function collectMemoryItems(state) {
258
+ return [
259
+ ...state.recent_decisions.map((item) => ({ kind: 'decision', item })),
260
+ ...state.active_constraints.map((item) => ({ kind: 'constraint', item })),
261
+ ...state.known_traps.map((item) => ({ kind: 'trap', item })),
262
+ ...state.open_handoffs.map((item) => ({ kind: 'handoff', item })),
263
+ ];
264
+ }
265
+ function resolveMemoryItem(state, id) {
266
+ return collectMemoryItems(state).find(({ item }) => item.id === id || item.short_label === id);
267
+ }
268
+ function renderMemoryMeta(kind, item) {
269
+ switch (kind) {
270
+ case 'decision':
271
+ return item.outcome ? ` (${item.outcome})` : '';
272
+ case 'constraint':
273
+ return ` (${item.status}${item.category ? ` · ${item.category}` : ''})`;
274
+ case 'trap':
275
+ return ` (${item.severity} · ${item.status})`;
276
+ case 'handoff':
277
+ return ` (${item.from} -> ${item.to} · ${item.status})`;
278
+ }
279
+ }
280
+ function isMemoryKind(value) {
281
+ return value === 'decision' || value === 'constraint' || value === 'trap' || value === 'handoff';
282
+ }
283
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1,175 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { loadState } from '../core/state.js';
3
+ import { listCandidates } from '../core/candidates.js';
4
+ import { listRuntimeNotes } from '../core/runtime.js';
5
+ import { listClaims } from '../core/claims.js';
6
+ import { readAuditLog } from '../core/audit.js';
7
+ import { loadConfig } from '../core/config.js';
8
+ export function runMetrics(options = {}) {
9
+ if (!memoryExists()) {
10
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
11
+ process.exit(1);
12
+ }
13
+ const config = loadConfig();
14
+ const slaHours = config.governance?.review_sla_hours ?? 24;
15
+ const now = new Date();
16
+ const report = buildMetricsReport(slaHours, options.since);
17
+ if (options.json) {
18
+ console.log(JSON.stringify(report, null, 2));
19
+ return;
20
+ }
21
+ console.log(`Memory Health Dashboard — ${report.snapshot_at}`);
22
+ console.log('');
23
+ console.log('═ Memory');
24
+ console.log(` Constraints active: ${report.memory.constraints_active}`);
25
+ console.log(` Decisions: ${report.memory.decisions_total}`);
26
+ console.log(` Traps: ${report.memory.traps_total}`);
27
+ console.log(` Plans: ${report.memory.plans_total} (${report.memory.plans_in_progress} in-progress, ${report.memory.plans_done} done)`);
28
+ console.log(` Handoffs open: ${report.memory.handoffs_open}`);
29
+ console.log('');
30
+ console.log('═ Reflective Memory');
31
+ console.log(` Pending candidates: ${report.reflective.candidates_pending}`);
32
+ console.log(` Accepted: ${report.reflective.candidates_accepted}`);
33
+ console.log(` Rejected: ${report.reflective.candidates_rejected}`);
34
+ if (report.reflective.oldest_pending_hours > 0) {
35
+ console.log(` Oldest pending: ${report.reflective.oldest_pending_hours}h`);
36
+ }
37
+ console.log('');
38
+ console.log('═ Runtime');
39
+ console.log(` Notes: ${report.runtime.notes_total} (${report.runtime.notes_shared} shared)`);
40
+ console.log(` Active claims: ${report.runtime.claims_active}`);
41
+ console.log('');
42
+ if (report.audit.total_events > 0) {
43
+ console.log('═ Audit Log');
44
+ console.log(` Events: ${report.audit.total_events}`);
45
+ const byAction = Object.entries(report.audit.events_by_action)
46
+ .sort((a, b) => b[1] - a[1])
47
+ .slice(0, 5);
48
+ for (const [action, count] of byAction) {
49
+ console.log(` ${action}: ${count}`);
50
+ }
51
+ const topActors = report.audit.top_actors.slice(0, 3);
52
+ if (topActors.length > 0) {
53
+ console.log(` Top actors: ${topActors.map(a => `${a.actor}(${a.count})`).join(', ')}`);
54
+ }
55
+ console.log('');
56
+ }
57
+ console.log('═ Health');
58
+ console.log(` Score: ${report.health.score}/100`);
59
+ if (report.health.issues.length === 0) {
60
+ console.log(` Status: ✔ No issues detected`);
61
+ }
62
+ else {
63
+ for (const issue of report.health.issues) {
64
+ console.log(` ⚠ ${issue}`);
65
+ }
66
+ }
67
+ }
68
+ function buildMetricsReport(slaHours, since) {
69
+ const now = new Date();
70
+ const snapshotAt = now.toISOString();
71
+ let state;
72
+ try {
73
+ state = loadState();
74
+ }
75
+ catch {
76
+ state = null;
77
+ }
78
+ let candidates = [];
79
+ try {
80
+ candidates = listCandidates();
81
+ }
82
+ catch { /* empty */ }
83
+ let notes = [];
84
+ try {
85
+ notes = listRuntimeNotes();
86
+ }
87
+ catch { /* empty */ }
88
+ let claims = [];
89
+ try {
90
+ claims = listClaims();
91
+ }
92
+ catch { /* empty */ }
93
+ let auditEntries = [];
94
+ try {
95
+ auditEntries = readAuditLog(since ? { since } : {});
96
+ }
97
+ catch { /* empty */ }
98
+ // Memory metrics
99
+ const memory = {
100
+ constraints_active: state?.active_constraints.filter(c => c.status === 'active').length ?? 0,
101
+ decisions_total: state?.recent_decisions.length ?? 0,
102
+ traps_total: state?.known_traps.length ?? 0,
103
+ plans_total: state?.plan_items.length ?? 0,
104
+ plans_in_progress: state?.plan_items.filter(p => p.status === 'in_progress').length ?? 0,
105
+ plans_done: state?.plan_items.filter(p => p.status === 'done').length ?? 0,
106
+ handoffs_open: state?.open_handoffs.filter(h => h.status === 'open').length ?? 0,
107
+ };
108
+ // Reflective memory metrics
109
+ const pending = candidates.filter(c => c.status === 'pending');
110
+ const accepted = candidates.filter(c => c.status === 'accepted');
111
+ const rejected = candidates.filter(c => c.status === 'rejected');
112
+ let oldestPendingHours = 0;
113
+ if (pending.length > 0) {
114
+ const oldestMs = Math.min(...pending.map(c => Date.parse(c.created_at)));
115
+ oldestPendingHours = Math.floor((now.getTime() - oldestMs) / (1000 * 60 * 60));
116
+ }
117
+ // Runtime metrics
118
+ const runtime = {
119
+ notes_total: notes.length,
120
+ notes_shared: notes.filter(n => n.visibility === 'shared').length,
121
+ claims_active: claims.filter(c => c.status === 'active').length,
122
+ };
123
+ // Audit metrics
124
+ const eventsByAction = {};
125
+ const actorCounts = {};
126
+ for (const entry of auditEntries) {
127
+ eventsByAction[entry.action] = (eventsByAction[entry.action] ?? 0) + 1;
128
+ actorCounts[entry.actor] = (actorCounts[entry.actor] ?? 0) + 1;
129
+ }
130
+ const topActors = Object.entries(actorCounts)
131
+ .map(([actor, count]) => ({ actor, count }))
132
+ .sort((a, b) => b.count - a.count);
133
+ // Health score
134
+ const issues = [];
135
+ let score = 100;
136
+ if (memory.handoffs_open > 5) {
137
+ score -= 5;
138
+ issues.push(`${memory.handoffs_open} open handoffs`);
139
+ }
140
+ if (pending.length > 20) {
141
+ score -= 10;
142
+ issues.push(`${pending.length} pending candidates backlog`);
143
+ }
144
+ if (oldestPendingHours > slaHours * 2) {
145
+ score -= 10;
146
+ issues.push(`oldest pending candidate: ${oldestPendingHours}h (SLA: ${slaHours}h)`);
147
+ }
148
+ if (runtime.claims_active > 10) {
149
+ score -= 5;
150
+ issues.push(`${runtime.claims_active} active claims — possible stale locks`);
151
+ }
152
+ if (memory.constraints_active === 0 && memory.decisions_total === 0) {
153
+ score -= 5;
154
+ issues.push('no constraints or decisions recorded yet');
155
+ }
156
+ score = Math.max(0, score);
157
+ return {
158
+ snapshot_at: snapshotAt,
159
+ memory,
160
+ reflective: {
161
+ candidates_pending: pending.length,
162
+ candidates_accepted: accepted.length,
163
+ candidates_rejected: rejected.length,
164
+ oldest_pending_hours: oldestPendingHours,
165
+ },
166
+ runtime,
167
+ audit: {
168
+ total_events: auditEntries.length,
169
+ events_by_action: eventsByAction,
170
+ top_actors: topActors,
171
+ },
172
+ health: { score, issues },
173
+ };
174
+ }
175
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1,62 @@
1
+ import { runPlan } from './plan.js';
2
+ import { runListPlans } from './list-plans.js';
3
+ import { runUpdatePlan } from './update-plan.js';
4
+ import { runDeletePlan } from './delete-plan.js';
5
+ export function runPlanResource(subcommand, args, options = {}) {
6
+ const normalized = subcommand.trim().toLowerCase();
7
+ if (normalized === 'create') {
8
+ const text = args.join(' ').trim();
9
+ if (!text) {
10
+ console.error('Error: plan create requires <text>');
11
+ process.exit(1);
12
+ }
13
+ runPlan(text, options);
14
+ return;
15
+ }
16
+ if (normalized === 'list' || normalized === 'ls') {
17
+ runListPlans({
18
+ json: options.json,
19
+ status: options.status,
20
+ type: options.type,
21
+ assignee: options.assignee,
22
+ project: options.project,
23
+ all: options.all,
24
+ });
25
+ return;
26
+ }
27
+ if (normalized === 'update') {
28
+ const id = args[0];
29
+ if (!id) {
30
+ console.error('Error: plan update requires <id>.');
31
+ console.error(' Usage: brainclaw plan update <id> --status <status>');
32
+ process.exit(1);
33
+ }
34
+ runUpdatePlan(id, {
35
+ status: options.status,
36
+ assignee: options.assignee,
37
+ project: options.project,
38
+ priority: options.priority,
39
+ actualEffort: options.actualEffort,
40
+ cwd: options.cwd,
41
+ });
42
+ return;
43
+ }
44
+ if (normalized === 'delete') {
45
+ const id = args[0];
46
+ if (!id) {
47
+ console.error('Error: plan delete requires <id>.');
48
+ console.error(' Usage: brainclaw plan delete <id>');
49
+ process.exit(1);
50
+ }
51
+ runDeletePlan(id, { cwd: options.cwd });
52
+ return;
53
+ }
54
+ // Compatibility path: `brainclaw plan "text"` still creates a plan.
55
+ const legacyText = [subcommand, ...args].join(' ').trim();
56
+ if (!legacyText) {
57
+ console.error('Error: missing plan subcommand or description.');
58
+ process.exit(1);
59
+ }
60
+ runPlan(legacyText, options);
61
+ }
62
+ //# sourceMappingURL=plan-resource.js.map
@@ -0,0 +1,76 @@
1
+ import { loadState, saveState } from '../core/state.js';
2
+ import { resolveCurrentAgentName } from '../core/agent-registry.js';
3
+ import { loadConfig } from '../core/config.js';
4
+ import { generateMarkdown } from '../core/markdown.js';
5
+ import { generateIdWithLabel, nowISO } from '../core/ids.js';
6
+ import { scanText } from '../core/security.js';
7
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
8
+ import { validateCliInput } from '../core/input-validation.js';
9
+ import { runListPlans } from './list-plans.js';
10
+ import { resolveTargetStore } from '../core/store-resolution.js';
11
+ // Known plan subcommands that should not be accepted as plan text
12
+ const PLAN_SUBCOMMAND_ALIASES = new Set(['list', 'ls']);
13
+ const PLAN_SUBCOMMAND_ERRORS = new Set(['update']);
14
+ export function runPlan(text, options = {}) {
15
+ const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
16
+ if (!memoryExists(cwd)) {
17
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
18
+ process.exit(1);
19
+ }
20
+ const normalized = text.trim().toLowerCase();
21
+ if (PLAN_SUBCOMMAND_ALIASES.has(normalized)) {
22
+ runListPlans({});
23
+ return;
24
+ }
25
+ if (PLAN_SUBCOMMAND_ERRORS.has(normalized)) {
26
+ console.error(`Error: '${text}' looks like a subcommand, not a plan description.`);
27
+ console.error(' To update a plan, use: brainclaw plan update <id> --status <status>');
28
+ process.exit(1);
29
+ }
30
+ validateCliInput(text, options.tag);
31
+ const config = loadConfig(cwd);
32
+ const warnings = scanText(text, config);
33
+ for (const w of warnings) {
34
+ console.warn(`⚠ ${w.message}`);
35
+ if (w.level === 'block') {
36
+ console.error('Blocked: strict redaction is enabled. Entry not added.');
37
+ process.exit(1);
38
+ }
39
+ }
40
+ // Validate and normalise --estimate: must be a positive integer (minutes)
41
+ let estimatedEffort;
42
+ if (options.estimate !== undefined) {
43
+ const n = typeof options.estimate === 'number' ? options.estimate : parseInt(String(options.estimate), 10);
44
+ if (!Number.isInteger(n) || n <= 0) {
45
+ console.error('Error: --estimate must be a positive integer representing minutes (e.g. --estimate 30)');
46
+ process.exit(1);
47
+ }
48
+ estimatedEffort = n;
49
+ }
50
+ const state = loadState(cwd);
51
+ const { id, short_label } = generateIdWithLabel('plan_items');
52
+ const timestamp = nowISO();
53
+ const entry = {
54
+ id,
55
+ short_label,
56
+ text,
57
+ type: options.type,
58
+ created_at: timestamp,
59
+ updated_at: timestamp,
60
+ author: options.author ?? resolveCurrentAgentName(cwd),
61
+ status: 'todo',
62
+ priority: options.priority ?? 'medium',
63
+ assignee: options.assignee,
64
+ project: options.project,
65
+ tags: options.tag ?? [],
66
+ related_paths: options.path,
67
+ depends_on: options.dependsOn ?? [],
68
+ estimated_effort: estimatedEffort,
69
+ };
70
+ state.plan_items.push(entry);
71
+ saveState(state, cwd);
72
+ writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
73
+ const storeLabel = options.store && options.store !== 'local' ? ` [store:${options.store}]` : '';
74
+ console.log(`✔ Plan item added: [${id}] ${text}${storeLabel}`);
75
+ }
76
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1,36 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { loadConfig } from '../core/config.js';
3
+ import { listArchivedCandidates, deleteArchivedCandidate } from '../core/candidates.js';
4
+ export function runPruneCandidates(options = {}) {
5
+ if (!memoryExists()) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const config = loadConfig();
10
+ const maxDays = options.days ?? config.reflective_memory?.prune_rejected_after_days ?? 30;
11
+ const cutoff = new Date(Date.now() - maxDays * 24 * 60 * 60 * 1000).toISOString();
12
+ const rejected = listArchivedCandidates('rejected');
13
+ const toPrune = rejected.filter(c => {
14
+ const date = c.resolved_at ?? c.created_at;
15
+ return date < cutoff;
16
+ });
17
+ if (toPrune.length === 0) {
18
+ console.log(`No rejected candidates older than ${maxDays} days to prune.`);
19
+ return;
20
+ }
21
+ if (options.dryRun) {
22
+ console.log(`Would prune ${toPrune.length} rejected candidate(s):`);
23
+ for (const c of toPrune) {
24
+ console.log(` [${c.id}] ${c.text}`);
25
+ }
26
+ return;
27
+ }
28
+ let pruned = 0;
29
+ for (const c of toPrune) {
30
+ if (deleteArchivedCandidate(c.id, 'rejected')) {
31
+ pruned++;
32
+ }
33
+ }
34
+ console.log(`✔ Pruned ${pruned} rejected candidate(s) older than ${maxDays} days.`);
35
+ }
36
+ //# sourceMappingURL=prune-candidates.js.map
@@ -0,0 +1,48 @@
1
+ import { loadState, saveState } from '../core/state.js';
2
+ import { memoryExists } from '../core/io.js';
3
+ import { deleteRuntimeNote, listRuntimeNotes } from '../core/runtime.js';
4
+ import { expireStaleActiveClaims } from '../core/claims.js';
5
+ export function runPrune(options = {}) {
6
+ const cwd = process.cwd();
7
+ if (!memoryExists(cwd)) {
8
+ console.error('Project memory not initialized. Run `brainclaw init` first.');
9
+ process.exit(1);
10
+ }
11
+ const state = loadState(cwd);
12
+ const now = new Date().toISOString();
13
+ const originalLength = state.active_constraints.length;
14
+ // Transition expired constraints to "expired"
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);
26
+ let expiredNotesCount = 0;
27
+ if (options.expired) {
28
+ // Prune expired runtime notes
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
+ }
36
+ }
37
+ catch { /* ignore */ }
38
+ }
39
+ }
40
+ }
41
+ if (options.expired) {
42
+ console.log(`✔ Pruned ${prunedCount} expired constraints, ${expiredNotesCount} expired runtime notes, ${expiredClaimsCount} expired claims.`);
43
+ }
44
+ else {
45
+ console.log(`✔ Pruned ${prunedCount} expired constraints, ${expiredClaimsCount} expired claims.`);
46
+ }
47
+ }
48
+ //# sourceMappingURL=prune.js.map