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,25 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { pullRemoteMemory } from '../core/sync-remote.js';
3
+ export function runPull(options = {}) {
4
+ if (!memoryExists()) {
5
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
6
+ process.exit(1);
7
+ }
8
+ const result = pullRemoteMemory({ remote: options.remote });
9
+ if (options.json) {
10
+ console.log(JSON.stringify(result, null, 2));
11
+ if (!result.success)
12
+ process.exit(1);
13
+ return;
14
+ }
15
+ if (result.success) {
16
+ console.log(`✔ ${result.message}`);
17
+ if (result.details)
18
+ console.log(result.details);
19
+ }
20
+ else {
21
+ console.error(`✗ ${result.message}`);
22
+ process.exit(1);
23
+ }
24
+ }
25
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1,28 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { pushRemoteMemory } from '../core/sync-remote.js';
3
+ export function runPush(options = {}) {
4
+ if (!memoryExists()) {
5
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
6
+ process.exit(1);
7
+ }
8
+ const result = pushRemoteMemory({
9
+ remote: options.remote,
10
+ message: options.message,
11
+ });
12
+ if (options.json) {
13
+ console.log(JSON.stringify(result, null, 2));
14
+ if (!result.success)
15
+ process.exit(1);
16
+ return;
17
+ }
18
+ if (result.success) {
19
+ console.log(`✔ ${result.message}`);
20
+ if (result.details)
21
+ console.log(result.details);
22
+ }
23
+ else {
24
+ console.error(`✗ ${result.message}`);
25
+ process.exit(1);
26
+ }
27
+ }
28
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1,14 @@
1
+ import { loadState } from '../core/state.js';
2
+ import { generateMarkdown } from '../core/markdown.js';
3
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
4
+ export function runRebuild() {
5
+ if (!memoryExists()) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const state = loadState();
10
+ const md = generateMarkdown(state);
11
+ writeFileAtomic(memoryPath('project.md'), md);
12
+ console.log('✔ project.md rebuilt from canonical memory state');
13
+ }
14
+ //# sourceMappingURL=rebuild.js.map
@@ -0,0 +1,74 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { findRuntimeNoteById } from '../core/runtime.js';
3
+ import { createCandidateFromInput } from './reflect.js';
4
+ export function runReflectRuntimeNote(id, text, options) {
5
+ if (!memoryExists()) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const note = findRuntimeNoteById(id, {
10
+ hostId: options.host,
11
+ includeAllHosts: options.allHosts,
12
+ });
13
+ if (!note) {
14
+ console.error(`Error: runtime note '${id}' not found in visible runtime memory.`);
15
+ process.exit(1);
16
+ }
17
+ const suggestions = suggestCandidateTypes(note.text, note.tags);
18
+ if (!options.type || options.suggest) {
19
+ if (options.json) {
20
+ console.log(JSON.stringify({ runtime_note: note, suggestions }, null, 2));
21
+ }
22
+ else {
23
+ console.log(`Promotion suggestions for runtime note [${note.id}] (${note.visibility}${note.host_id ? `:${note.host_id}` : ''}):`);
24
+ for (const suggestion of suggestions) {
25
+ console.log(` - ${suggestion.type} (${suggestion.score}) ${suggestion.reason}`);
26
+ }
27
+ if (!options.type) {
28
+ return;
29
+ }
30
+ console.log('');
31
+ }
32
+ }
33
+ if (!options.type) {
34
+ return;
35
+ }
36
+ const candidateText = text?.trim() || note.text;
37
+ const mergedTags = uniqueTags([...(note.tags ?? []), ...(options.tag ?? [])]);
38
+ const reflectOptions = {
39
+ ...options,
40
+ tag: mergedTags,
41
+ author: options.author ?? note.agent,
42
+ authorId: note.agent_id,
43
+ projectId: note.project_id,
44
+ hostId: note.host_id,
45
+ sessionId: note.session_id,
46
+ source: options.source ?? `runtime-note:${note.agent}:${note.id}`,
47
+ };
48
+ createCandidateFromInput(candidateText, options.type, reflectOptions);
49
+ }
50
+ function uniqueTags(tags) {
51
+ return [...new Set(tags.filter((tag) => tag.trim().length > 0))];
52
+ }
53
+ export function suggestCandidateTypes(text, tags) {
54
+ const haystack = `${text.toLowerCase()} ${tags.join(' ').toLowerCase()}`;
55
+ const suggestions = [];
56
+ let trapScore = 1;
57
+ if (/(error|fail|flaky|broken|missing|workaround|retry|blocked|not on path|requires)/.test(haystack)) {
58
+ trapScore += 3;
59
+ }
60
+ suggestions.push({ type: 'trap', score: trapScore, reason: 'Operational friction, workaround, or failure mode' });
61
+ let constraintScore = 1;
62
+ if (/(must|required|cannot|blocked|frozen|until|only)/.test(haystack)) {
63
+ constraintScore += 3;
64
+ }
65
+ suggestions.push({ type: 'constraint', score: constraintScore, reason: 'Active limitation or operating boundary' });
66
+ let decisionScore = 1;
67
+ if (/(use |prefer|standard|go through|convention|policy)/.test(haystack)) {
68
+ decisionScore += 3;
69
+ }
70
+ suggestions.push({ type: 'decision', score: decisionScore, reason: 'Reusable convention, policy, or chosen approach' });
71
+ suggestions.push({ type: 'handoff', score: 1, reason: 'Use only if the note should become an explicit transfer of work' });
72
+ return suggestions.sort((a, b) => b.score - a.score);
73
+ }
74
+ //# sourceMappingURL=reflect-runtime-note.js.map
@@ -0,0 +1,286 @@
1
+ import fs from 'node:fs';
2
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
3
+ import { loadConfig } from '../core/config.js';
4
+ import { buildOperationalIdentity } from '../core/identity.js';
5
+ import { loadState, saveState } from '../core/state.js';
6
+ import { generateMarkdown } from '../core/markdown.js';
7
+ import { scanText } from '../core/security.js';
8
+ import { nowISO, generateIdWithLabel } from '../core/ids.js';
9
+ import { saveCandidate, generateCandidateIdWithLabel, listCandidates } from '../core/candidates.js';
10
+ import { detectDuplicates } from '../core/duplicates.js';
11
+ import { RuntimeEventSchema } from '../core/schema.js';
12
+ import { listRuntimeEventsBySession } from '../core/events.js';
13
+ import { agentCanWriteDirect, requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
14
+ import { appendAuditEntry } from '../core/audit.js';
15
+ import { generateTrapIdWithLabel } from '../core/traps.js';
16
+ import { evaluateReflectionSafety } from '../core/reflection-safety.js';
17
+ export function runReflect(text, options) {
18
+ if (!memoryExists(options.cwd)) {
19
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
20
+ process.exit(1);
21
+ }
22
+ if (options.batch) {
23
+ runReflectBatchFromFile(options.batch, options);
24
+ return;
25
+ }
26
+ if (options.session) {
27
+ runReflectBatchFromSession(options.session, options);
28
+ return;
29
+ }
30
+ if (!text || !options.type) {
31
+ console.error('Error: single reflect mode requires <text> and --type.');
32
+ process.exit(1);
33
+ }
34
+ createCandidateFromInput(text, options.type, options);
35
+ }
36
+ function runReflectBatchFromFile(filepath, baseOptions) {
37
+ if (!fs.existsSync(filepath)) {
38
+ console.error(`Error: batch file not found: ${filepath}`);
39
+ process.exit(1);
40
+ }
41
+ let parsed;
42
+ try {
43
+ parsed = JSON.parse(fs.readFileSync(filepath, 'utf-8'));
44
+ }
45
+ catch (e) {
46
+ const msg = e instanceof Error ? e.message : String(e);
47
+ console.error(`Error: invalid JSON batch file: ${msg}`);
48
+ process.exit(1);
49
+ }
50
+ const rawEvents = Array.isArray(parsed)
51
+ ? parsed
52
+ : parsed && typeof parsed === 'object' && Array.isArray(parsed.events)
53
+ ? parsed.events
54
+ : [parsed];
55
+ let created = 0;
56
+ for (const rawEvent of rawEvents) {
57
+ try {
58
+ const event = RuntimeEventSchema.parse(rawEvent);
59
+ const candidateType = event.candidate_type ?? mapEventTypeToCandidateType(event.event_type);
60
+ createCandidateFromInput(event.text, candidateType, {
61
+ ...baseOptions,
62
+ type: candidateType,
63
+ tag: event.tags.length ? event.tags : baseOptions.tag,
64
+ authorId: baseOptions.authorId ?? event.agent_id,
65
+ projectId: baseOptions.projectId ?? event.project_id,
66
+ hostId: baseOptions.hostId ?? event.host_id,
67
+ sessionId: baseOptions.sessionId ?? event.session_id,
68
+ source: baseOptions.source ?? event.agent,
69
+ severity: baseOptions.severity ?? event.severity,
70
+ from: baseOptions.from ?? event.from,
71
+ to: baseOptions.to ?? event.to,
72
+ path: baseOptions.path ?? event.related_paths?.[0],
73
+ }, false, true, true);
74
+ created++;
75
+ }
76
+ catch {
77
+ // skip malformed event records
78
+ }
79
+ }
80
+ console.log(`✔ Created ${created} candidate(s) from batch file`);
81
+ }
82
+ function runReflectBatchFromSession(session, baseOptions) {
83
+ const events = listRuntimeEventsBySession(session);
84
+ if (events.length === 0) {
85
+ console.error(`Error: no runtime events found for session '${session}'.`);
86
+ process.exit(1);
87
+ }
88
+ let created = 0;
89
+ for (const event of events) {
90
+ const candidateType = event.candidate_type ?? mapEventTypeToCandidateType(event.event_type);
91
+ createCandidateFromInput(event.text, candidateType, {
92
+ ...baseOptions,
93
+ type: candidateType,
94
+ tag: event.tags.length ? event.tags : baseOptions.tag,
95
+ authorId: baseOptions.authorId ?? event.agent_id,
96
+ projectId: baseOptions.projectId ?? event.project_id,
97
+ hostId: baseOptions.hostId ?? event.host_id,
98
+ sessionId: baseOptions.sessionId ?? event.session_id,
99
+ source: baseOptions.source ?? event.agent,
100
+ severity: baseOptions.severity ?? event.severity,
101
+ from: baseOptions.from ?? event.from,
102
+ to: baseOptions.to ?? event.to,
103
+ path: baseOptions.path ?? event.related_paths?.[0],
104
+ }, false, true, true);
105
+ created++;
106
+ }
107
+ console.log(`✔ Created ${created} candidate(s) from session '${session}'`);
108
+ }
109
+ export function createCandidateFromInput(text, type, options, printSuccess = true, forceStrict = false, automation = false) {
110
+ const config = loadConfig(options.cwd);
111
+ const explicitAuthor = options.author?.trim();
112
+ const explicitAuthorId = options.authorId?.trim();
113
+ const registeredAuthor = requireRegisteredAgentIdentity({
114
+ agentName: explicitAuthor,
115
+ agentId: explicitAuthorId,
116
+ cwd: options.cwd,
117
+ allowCurrent: true,
118
+ allowEnv: true,
119
+ });
120
+ requireMinimumTrustLevel(registeredAuthor, 'contributor');
121
+ const actorIdentity = buildOperationalIdentity(registeredAuthor.agent_name, options.cwd, {
122
+ agentId: registeredAuthor.agent_id,
123
+ sessionId: options.sessionId,
124
+ });
125
+ // Security scan — batch/automated imports always block on sensitive content
126
+ const rawWarnings = scanText(text, config);
127
+ const warnings = forceStrict
128
+ ? rawWarnings.map(w => ({ ...w, level: 'block' }))
129
+ : rawWarnings;
130
+ for (const w of warnings) {
131
+ console.warn(`⚠ ${w.message}`);
132
+ if (w.level === 'block') {
133
+ console.error(forceStrict
134
+ ? 'Blocked: sensitive content in automated import. Candidate not created.'
135
+ : 'Blocked: strict redaction is enabled. Candidate not created.');
136
+ process.exit(1);
137
+ }
138
+ }
139
+ // Duplicate detection
140
+ const state = loadState(options.cwd);
141
+ const pending = listCandidates('pending', options.cwd);
142
+ const dupes = detectDuplicates(text, type, state, pending);
143
+ if (dupes.length > 0) {
144
+ console.warn('⚠ Possible duplicates detected:');
145
+ for (const d of dupes) {
146
+ console.warn(` - [${d.id}] (${d.source}) ${d.reason}: "${d.text}"`);
147
+ }
148
+ }
149
+ const { id, short_label } = generateCandidateIdWithLabel(options.cwd);
150
+ const candidate = {
151
+ id,
152
+ short_label,
153
+ type,
154
+ text,
155
+ created_at: nowISO(),
156
+ author: options.author ?? actorIdentity.agent,
157
+ author_id: options.authorId ?? actorIdentity.agent_id,
158
+ project_id: options.projectId ?? actorIdentity.project_id,
159
+ host_id: options.hostId ?? actorIdentity.host_id,
160
+ session_id: options.sessionId ?? actorIdentity.session_id,
161
+ source: options.source,
162
+ tags: options.tag ?? [],
163
+ status: 'pending',
164
+ severity: type === 'trap' ? options.severity ?? 'medium' : undefined,
165
+ from: type === 'handoff' ? options.from : undefined,
166
+ to: type === 'handoff' ? options.to : undefined,
167
+ related_paths: options.path ? [options.path] : undefined,
168
+ star_count: 0,
169
+ starred_by: [],
170
+ usage_count: 0,
171
+ usage_events: [],
172
+ };
173
+ const safety = evaluateReflectionSafety({
174
+ text,
175
+ type,
176
+ tags: candidate.tags,
177
+ relatedPaths: candidate.related_paths,
178
+ projectId: candidate.project_id,
179
+ cwd: options.cwd,
180
+ automation,
181
+ });
182
+ if (safety.contradiction_summary) {
183
+ console.warn(`⚠ ${safety.contradiction_summary}`);
184
+ }
185
+ const candidateWithSafety = {
186
+ ...candidate,
187
+ contradictions_detected: safety.contradictions_detected,
188
+ contradiction_summary: safety.contradiction_summary,
189
+ promotion_blocked_reason: safety.promotion_blocked_reason,
190
+ };
191
+ // Write-through for trusted/curator agents — bypass pending inbox
192
+ if (!forceStrict) {
193
+ try {
194
+ if (!safety.promotion_blocked_reason && agentCanWriteDirect(candidate.author_id ?? candidate.author, options.cwd)) {
195
+ const promotedItemId = promoteCandidateToState(candidateWithSafety, options.cwd);
196
+ appendAuditEntry({
197
+ actor: candidate.author,
198
+ actor_id: candidate.author_id,
199
+ action: 'promote_direct',
200
+ item_id: id,
201
+ item_type: type,
202
+ after: candidate,
203
+ });
204
+ if (printSuccess) {
205
+ console.log(`✔ Direct write: [${id}] (${type}) ${text} (trusted agent — bypassed inbox)`);
206
+ }
207
+ return {
208
+ candidateId: id,
209
+ type,
210
+ writeThrough: true,
211
+ promotedItemId,
212
+ contradictionsDetected: safety.contradictions_detected,
213
+ contradictionSummary: safety.contradiction_summary,
214
+ };
215
+ }
216
+ }
217
+ catch { /* trust check failed — fall through to pending */ }
218
+ }
219
+ saveCandidate(candidateWithSafety, options.cwd);
220
+ if (printSuccess) {
221
+ console.log(`✔ Candidate created: [${id}] (${type}) ${text}`);
222
+ if (safety.promotion_blocked_reason) {
223
+ console.log(` Auto-promotion blocked: ${safety.promotion_blocked_reason}`);
224
+ }
225
+ }
226
+ return {
227
+ candidateId: id,
228
+ type,
229
+ writeThrough: false,
230
+ contradictionsDetected: safety.contradictions_detected,
231
+ contradictionSummary: safety.contradiction_summary,
232
+ promotionBlockedReason: safety.promotion_blocked_reason,
233
+ };
234
+ }
235
+ function promoteCandidateToState(candidate, cwd) {
236
+ const state = loadState(cwd);
237
+ let promotedItemId = '';
238
+ switch (candidate.type) {
239
+ case 'constraint': {
240
+ const { id: cId, short_label } = generateIdWithLabel('active_constraints', cwd);
241
+ const entry = { id: cId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, status: 'active', tags: candidate.tags };
242
+ state.active_constraints.push(entry);
243
+ promotedItemId = entry.id;
244
+ break;
245
+ }
246
+ case 'decision': {
247
+ const { id: dId, short_label } = generateIdWithLabel('recent_decisions', cwd);
248
+ const entry = { id: dId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, related_paths: candidate.related_paths, tags: candidate.tags };
249
+ state.recent_decisions.push(entry);
250
+ promotedItemId = entry.id;
251
+ break;
252
+ }
253
+ case 'trap': {
254
+ const { id: tId, short_label } = generateTrapIdWithLabel(cwd);
255
+ const entry = { id: tId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, status: 'active', severity: candidate.severity ?? 'medium', tags: candidate.tags, visibility: 'shared' };
256
+ state.known_traps.push(entry);
257
+ promotedItemId = entry.id;
258
+ break;
259
+ }
260
+ case 'handoff': {
261
+ const { id: hId, short_label } = generateIdWithLabel('open_handoffs', cwd);
262
+ const entry = { id: hId, short_label, text: candidate.text, created_at: candidate.created_at, author: candidate.author, author_id: candidate.author_id, project_id: candidate.project_id, host_id: candidate.host_id, session_id: candidate.session_id, from: candidate.from ?? '', to: candidate.to ?? '', status: 'open', tags: candidate.tags, related_paths: candidate.related_paths };
263
+ state.open_handoffs.push(entry);
264
+ promotedItemId = entry.id;
265
+ break;
266
+ }
267
+ }
268
+ saveState(state, cwd);
269
+ writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state));
270
+ return promotedItemId;
271
+ }
272
+ export function mapEventTypeToCandidateType(eventType) {
273
+ switch (eventType) {
274
+ case 'risk_detected':
275
+ return 'trap';
276
+ case 'handoff_requested':
277
+ return 'handoff';
278
+ case 'task_started':
279
+ case 'task_finished':
280
+ return 'constraint';
281
+ case 'observation':
282
+ default:
283
+ return 'decision';
284
+ }
285
+ }
286
+ //# sourceMappingURL=reflect.js.map
@@ -0,0 +1,29 @@
1
+ import { registerAgentIdentity, setCurrentAgentIdentity } from '../core/agent-registry.js';
2
+ import { memoryExists } from '../core/io.js';
3
+ export function runRegisterAgent(agentName, options = {}) {
4
+ if (!memoryExists()) {
5
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
6
+ process.exit(1);
7
+ }
8
+ const resolvedTrust = options.curator ? 'curator' : options.trustLevel;
9
+ const agent = registerAgentIdentity({
10
+ agentName,
11
+ kind: options.kind ?? 'unknown',
12
+ capabilities: options.capability,
13
+ replaceCapabilities: options.replaceCapabilities,
14
+ generateFingerprint: options.generateFingerprint,
15
+ trustLevel: resolvedTrust,
16
+ });
17
+ if (options.setCurrent) {
18
+ setCurrentAgentIdentity(agent);
19
+ }
20
+ if (options.json) {
21
+ console.log(JSON.stringify({ ...agent, current: options.setCurrent ?? false }, null, 2));
22
+ return;
23
+ }
24
+ const currentLabel = options.setCurrent ? ' [current]' : '';
25
+ const capabilitiesLabel = agent.capabilities.length > 0 ? `, capabilities=${agent.capabilities.join(',')}` : '';
26
+ const fingerprintLabel = agent.identity_key ? `, fp=${agent.identity_key.fingerprint.slice(0, 12)}` : '';
27
+ console.log(`✔ Agent registered: ${agent.agent_name} (${agent.agent_id}, kind=${agent.kind}${capabilitiesLabel}${fingerprintLabel})${currentLabel}`);
28
+ }
29
+ //# sourceMappingURL=register-agent.js.map
@@ -0,0 +1,52 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { loadCandidate, archiveCandidate, resolveIdOrAlias } from '../core/candidates.js';
3
+ import { nowISO } from '../core/ids.js';
4
+ import { appendAuditEntry } from '../core/audit.js';
5
+ import { requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
6
+ export function runReject(id, reason, by, cwd) {
7
+ try {
8
+ rejectCandidate(id, reason, by, cwd);
9
+ console.log(`✔ Candidate [${id}] rejected and archived.`);
10
+ }
11
+ catch (e) {
12
+ const msg = e instanceof Error ? e.message : String(e);
13
+ console.error(`Error: ${msg}`);
14
+ process.exit(1);
15
+ }
16
+ }
17
+ export function rejectCandidate(id, reason, by, cwd, byId) {
18
+ if (!memoryExists(cwd)) {
19
+ throw new Error('.brainclaw/ not found. Run `brainclaw init` first.');
20
+ }
21
+ const resolvedId = resolveIdOrAlias(id, cwd);
22
+ const candidate = loadCandidate(resolvedId, cwd);
23
+ if (candidate.status !== 'pending') {
24
+ throw new Error(`Candidate '${resolvedId}' is already ${candidate.status}.`);
25
+ }
26
+ const actorIdentity = requireRegisteredAgentIdentity({
27
+ agentName: by,
28
+ agentId: byId,
29
+ cwd,
30
+ allowCurrent: true,
31
+ allowEnv: true,
32
+ });
33
+ requireMinimumTrustLevel(actorIdentity, 'trusted');
34
+ const actor = actorIdentity.agent_name;
35
+ candidate.status = 'rejected';
36
+ candidate.resolved_at = nowISO();
37
+ candidate.resolved_by = actor;
38
+ if (reason) {
39
+ candidate.resolution_reason = reason;
40
+ }
41
+ archiveCandidate(candidate, 'rejected', cwd);
42
+ appendAuditEntry({
43
+ actor,
44
+ actor_id: actorIdentity.agent_id,
45
+ action: 'reject',
46
+ item_id: resolvedId,
47
+ item_type: candidate.type,
48
+ reason,
49
+ }, cwd);
50
+ return { candidate_id: resolvedId, actor };
51
+ }
52
+ //# sourceMappingURL=reject.js.map
@@ -0,0 +1,41 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { memoryPath, writeFileAtomic } from '../core/io.js';
3
+ import { loadClaim, listClaims, releaseClaim } from '../core/claims.js';
4
+ import { generateMarkdown } from '../core/markdown.js';
5
+ import { loadState, saveState } from '../core/state.js';
6
+ export function runReleaseClaim(id, options = {}) {
7
+ if (!memoryExists(options.cwd)) {
8
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
9
+ process.exit(1);
10
+ }
11
+ try {
12
+ const existing = loadClaim(id, options.cwd);
13
+ const claim = releaseClaim(id, options.cwd);
14
+ let state = loadState(options.cwd);
15
+ if (existing.plan_id) {
16
+ const plan = state.plan_items.find((item) => item.id === existing.plan_id);
17
+ if (plan) {
18
+ const otherActiveClaims = listClaims(options.cwd).filter((item) => item.status === 'active' && item.plan_id === existing.plan_id);
19
+ if (options.planStatus) {
20
+ plan.status = options.planStatus;
21
+ }
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
+ }
31
+ }
32
+ writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
33
+ console.log(`✔ Claim [${id}] released (was: ${claim.agent} → ${claim.scope})`);
34
+ }
35
+ catch (e) {
36
+ const msg = e instanceof Error ? e.message : String(e);
37
+ console.error(`Error: ${msg}`);
38
+ process.exit(1);
39
+ }
40
+ }
41
+ //# sourceMappingURL=release-claim.js.map
@@ -0,0 +1,67 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
3
+ import { listClaims, releaseClaim } from '../core/claims.js';
4
+ import { generateMarkdown } from '../core/markdown.js';
5
+ import { loadState, saveState } from '../core/state.js';
6
+ function normPath(p) {
7
+ return p.replace(/\\/g, '/').replace(/^\.\//, '');
8
+ }
9
+ function scopeMatchesFile(scope, file) {
10
+ const f = normPath(file);
11
+ return scope.split(/\s+/).some((s) => {
12
+ const sp = normPath(s).replace(/\/$/, '');
13
+ return f === sp || f.startsWith(sp + '/');
14
+ });
15
+ }
16
+ export function runReleaseClaims(options = {}) {
17
+ if (!memoryExists(options.cwd)) {
18
+ process.exit(0); // silent — called from git hook
19
+ }
20
+ let changedFiles = [];
21
+ if (options.fromGitDiff) {
22
+ const ref1 = options.ref1 ?? 'ORIG_HEAD';
23
+ const ref2 = options.ref2 ?? 'HEAD';
24
+ try {
25
+ const output = execSync(`git diff --name-only ${ref1} ${ref2}`, { encoding: 'utf-8' });
26
+ changedFiles = output.split('\n').map((f) => f.trim()).filter(Boolean);
27
+ }
28
+ catch {
29
+ process.exit(0); // not in git or no diff — skip silently
30
+ }
31
+ }
32
+ if (changedFiles.length === 0)
33
+ process.exit(0);
34
+ const claims = listClaims(options.cwd).filter((c) => c.status === 'active');
35
+ const toRelease = claims.filter((c) => changedFiles.some((f) => scopeMatchesFile(c.scope, f)));
36
+ if (toRelease.length === 0)
37
+ process.exit(0);
38
+ let state = loadState(options.cwd);
39
+ let released = 0;
40
+ for (const claim of toRelease) {
41
+ try {
42
+ releaseClaim(claim.id, options.cwd);
43
+ released++;
44
+ // Revert in_progress plan to todo if no more active claims
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
+ }
55
+ }
56
+ }
57
+ console.log(`✔ Auto-released claim [${claim.id}]: ${claim.scope}`);
58
+ }
59
+ catch { /* skip individual failures */ }
60
+ }
61
+ state = loadState(options.cwd);
62
+ writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
63
+ if (released > 0) {
64
+ console.log(`brainclaw: ${released} claim(s) auto-released after merge.`);
65
+ }
66
+ }
67
+ //# sourceMappingURL=release-claims.js.map