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,242 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { listCandidates, saveCandidate } from '../core/candidates.js';
3
+ import { loadConfig } from '../core/config.js';
4
+ import { detectNewItemContradictions, summarizeContradictions } from '../core/contradictions.js';
5
+ import { buildReputationRankingLookup } from '../core/reputation.js';
6
+ import { checkCircuitBreaker } from '../core/circuit-breaker.js';
7
+ import { loadState } from '../core/state.js';
8
+ import { runAccept } from './accept.js';
9
+ export function runReview(options = {}) {
10
+ if (!memoryExists(options.cwd)) {
11
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
12
+ process.exit(1);
13
+ }
14
+ let candidates = listCandidates('pending', options.cwd);
15
+ const config = loadConfig(options.cwd);
16
+ const slaHours = config.governance?.review_sla_hours ?? 24;
17
+ const promotionThreshold = config.reflective_memory?.promotion_stars_threshold ?? 3;
18
+ const promotionUsesThreshold = config.reflective_memory?.promotion_uses_threshold ?? 2;
19
+ const now = Date.now();
20
+ const rankingLookup = buildReputationRankingLookup(options.cwd);
21
+ if (options.type) {
22
+ candidates = candidates.filter(c => c.type === options.type);
23
+ }
24
+ if (options.assignee) {
25
+ const targetAssignee = options.assignee.toLowerCase();
26
+ candidates = candidates.filter((c) => getReviewAssignee(c.tags)?.toLowerCase() === targetAssignee);
27
+ }
28
+ if (options.forCurator) {
29
+ const curator = options.forCurator.toLowerCase();
30
+ candidates = candidates.filter((c) => getReviewAssignee(c.tags)?.toLowerCase() === curator);
31
+ }
32
+ if (options.onlyOverdue) {
33
+ candidates = candidates.filter((c) => {
34
+ const ageHours = Math.floor((now - Date.parse(c.created_at)) / (1000 * 60 * 60));
35
+ return ageHours > slaHours;
36
+ });
37
+ }
38
+ if (options.prioritized) {
39
+ candidates = [...candidates].sort((a, b) => {
40
+ const starDelta = (b.star_count ?? 0) - (a.star_count ?? 0);
41
+ if (starDelta !== 0)
42
+ return starDelta;
43
+ const aRank = priorityRank(a.type);
44
+ const bRank = priorityRank(b.type);
45
+ if (aRank !== bRank)
46
+ return aRank - bRank;
47
+ const trustDelta = rankingLookup.getInternalTrust(b.author_id, b.author) - rankingLookup.getInternalTrust(a.author_id, a.author);
48
+ if (trustDelta !== 0)
49
+ return trustDelta;
50
+ return a.created_at.localeCompare(b.created_at);
51
+ });
52
+ }
53
+ else if (options.take && options.take > 0) {
54
+ candidates = [...candidates].sort((a, b) => a.created_at.localeCompare(b.created_at));
55
+ }
56
+ const totalBeforeTake = candidates.length;
57
+ if (options.take && options.take > 0) {
58
+ candidates = candidates.slice(0, options.take);
59
+ }
60
+ const claimed = [];
61
+ const skipped = [];
62
+ // --auto: auto-promote candidates that meet score threshold
63
+ if (options.auto) {
64
+ const scoreThreshold = config.reflective_memory?.auto_promote_score_threshold ?? 5;
65
+ const autoBy = options.autoBy ?? process.env.USER ?? process.env.USERNAME ?? 'auto';
66
+ const state = loadState(options.cwd);
67
+ const promoted = [];
68
+ const autoSkipped = [];
69
+ for (const c of candidates) {
70
+ const score = (c.star_count ?? 0) + (c.usage_count ?? 0);
71
+ if (score >= scoreThreshold ||
72
+ (c.star_count ?? 0) >= promotionThreshold ||
73
+ (c.usage_count ?? 0) >= promotionUsesThreshold) {
74
+ // Circuit-breaker: skip auto-promote if agent has too many recent rejections
75
+ const breaker = checkCircuitBreaker(c.author_id ?? c.author, options.cwd);
76
+ if (breaker.tripped) {
77
+ autoSkipped.push({ id: c.id, reason: `circuit_breaker:${c.author}(${breaker.rejection_count}/${breaker.threshold} rejections in ${breaker.window_days}d)` });
78
+ continue;
79
+ }
80
+ const contradictions = detectNewItemContradictions(c.text, c.tags, c.related_paths, state, c.project_id);
81
+ const blocking = contradictions.filter((item) => item.severity === 'medium' || item.severity === 'high');
82
+ if (blocking.length > 0) {
83
+ saveCandidate({
84
+ ...c,
85
+ contradictions_detected: contradictions,
86
+ contradiction_summary: summarizeContradictions(contradictions),
87
+ promotion_blocked_reason: 'contradiction_detected',
88
+ }, options.cwd);
89
+ autoSkipped.push({ id: c.id, reason: 'contradiction_detected' });
90
+ continue;
91
+ }
92
+ try {
93
+ runAccept(c.id, autoBy, options.cwd);
94
+ promoted.push(c.id);
95
+ }
96
+ catch {
97
+ autoSkipped.push({ id: c.id, reason: 'accept_failed' });
98
+ }
99
+ }
100
+ else {
101
+ autoSkipped.push({ id: c.id, reason: 'below_threshold' });
102
+ }
103
+ }
104
+ if (options.json) {
105
+ console.log(JSON.stringify({ auto_promoted: promoted, skipped: autoSkipped }));
106
+ }
107
+ else {
108
+ console.log(`✔ Auto-promoted ${promoted.length} candidate(s). Skipped ${autoSkipped.length}.`);
109
+ }
110
+ return;
111
+ }
112
+ if (options.claim) {
113
+ const curator = options.claim.trim();
114
+ for (const c of candidates) {
115
+ const existing = getReviewAssignee(c.tags);
116
+ if (existing && existing.toLowerCase() !== curator.toLowerCase()) {
117
+ skipped.push({ id: c.id, reason: `already assigned to ${existing}` });
118
+ continue;
119
+ }
120
+ const updated = {
121
+ ...c,
122
+ tags: setReviewAssignee(c.tags, curator),
123
+ };
124
+ saveCandidate(updated, options.cwd);
125
+ claimed.push(updated);
126
+ }
127
+ candidates = claimed;
128
+ }
129
+ if (options.json) {
130
+ if (options.claim) {
131
+ console.log(JSON.stringify({
132
+ claimed: candidates.map((c) => {
133
+ const ageHours = Math.floor((now - Date.parse(c.created_at)) / (1000 * 60 * 60));
134
+ return {
135
+ ...c,
136
+ review_assignee: getReviewAssignee(c.tags),
137
+ promotion_stars: c.star_count ?? 0,
138
+ promotion_threshold: promotionThreshold,
139
+ promotion_uses: c.usage_count ?? 0,
140
+ promotion_uses_threshold: promotionUsesThreshold,
141
+ promotion_recommended: (c.star_count ?? 0) >= promotionThreshold || (c.usage_count ?? 0) >= promotionUsesThreshold,
142
+ age_hours: ageHours,
143
+ sla_hours: slaHours,
144
+ overdue: ageHours > slaHours,
145
+ };
146
+ }),
147
+ skipped,
148
+ }, null, 2));
149
+ return;
150
+ }
151
+ const payload = candidates.map((c) => {
152
+ const ageHours = Math.floor((now - Date.parse(c.created_at)) / (1000 * 60 * 60));
153
+ return {
154
+ ...c,
155
+ review_assignee: getReviewAssignee(c.tags),
156
+ promotion_stars: c.star_count ?? 0,
157
+ promotion_threshold: promotionThreshold,
158
+ promotion_uses: c.usage_count ?? 0,
159
+ promotion_uses_threshold: promotionUsesThreshold,
160
+ promotion_recommended: (c.star_count ?? 0) >= promotionThreshold || (c.usage_count ?? 0) >= promotionUsesThreshold,
161
+ age_hours: ageHours,
162
+ sla_hours: slaHours,
163
+ overdue: ageHours > slaHours,
164
+ };
165
+ });
166
+ console.log(JSON.stringify(payload, null, 2));
167
+ return;
168
+ }
169
+ if (candidates.length === 0) {
170
+ if (options.claim) {
171
+ console.log(`No candidates claimed for curator '${options.claim}'.`);
172
+ if (skipped.length > 0) {
173
+ console.log(`Skipped ${skipped.length} candidate(s) already assigned to another curator.`);
174
+ }
175
+ return;
176
+ }
177
+ console.log('No pending candidates.');
178
+ return;
179
+ }
180
+ console.log(`${candidates.length} pending candidate(s):`);
181
+ console.log('');
182
+ if (options.prioritized) {
183
+ console.log(`Priority mode enabled (SLA ${slaHours}h)`);
184
+ console.log('');
185
+ }
186
+ if (options.take && options.take > 0) {
187
+ console.log(`Showing ${candidates.length} of ${totalBeforeTake} candidate(s) (--take ${options.take})`);
188
+ console.log('');
189
+ }
190
+ if (options.claim) {
191
+ console.log(`Claimed ${candidates.length} candidate(s) for curator '${options.claim}'.`);
192
+ if (skipped.length > 0) {
193
+ console.log(`Skipped ${skipped.length} candidate(s) already assigned to another curator.`);
194
+ }
195
+ console.log('');
196
+ }
197
+ for (const c of candidates) {
198
+ const tags = c.tags.length ? ` [${c.tags.join(', ')}]` : '';
199
+ const extra = c.type === 'handoff' ? ` (${c.from} → ${c.to})` : '';
200
+ const assignee = getReviewAssignee(c.tags);
201
+ const assigneePart = assignee ? ` · assignee ${assignee}` : '';
202
+ const stars = c.star_count ?? 0;
203
+ const uses = c.usage_count ?? 0;
204
+ const promote = stars >= promotionThreshold || uses >= promotionUsesThreshold ? ' · PROMOTE?' : '';
205
+ const ageHours = Math.floor((now - Date.parse(c.created_at)) / (1000 * 60 * 60));
206
+ const overdue = ageHours > slaHours ? ' OVERDUE' : '';
207
+ console.log(` [${c.short_label ?? c.id}] (${c.type}) ${c.text}${extra}${tags}`);
208
+ console.log(` by ${c.author} at ${c.created_at}${assigneePart} · stars ${stars}/${promotionThreshold} · uses ${uses}/${promotionUsesThreshold}${promote} · age ${ageHours}h · SLA ${slaHours}h${overdue}`);
209
+ }
210
+ }
211
+ function setReviewAssignee(tags, assignee) {
212
+ const next = [];
213
+ for (const tag of tags) {
214
+ if (!tag.startsWith('assignee:')) {
215
+ next.push(tag);
216
+ }
217
+ }
218
+ next.push(`assignee:${assignee}`);
219
+ return next;
220
+ }
221
+ function getReviewAssignee(tags) {
222
+ for (const tag of tags) {
223
+ if (tag.startsWith('assignee:')) {
224
+ return tag.slice('assignee:'.length).trim() || undefined;
225
+ }
226
+ }
227
+ return undefined;
228
+ }
229
+ function priorityRank(type) {
230
+ switch (type) {
231
+ case 'handoff':
232
+ return 1;
233
+ case 'constraint':
234
+ return 2;
235
+ case 'trap':
236
+ return 3;
237
+ case 'decision':
238
+ default:
239
+ return 4;
240
+ }
241
+ }
242
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1,156 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { readAuditLog } from '../core/audit.js';
3
+ import { loadState, saveState } from '../core/state.js';
4
+ import { loadCandidate, saveCandidate } from '../core/candidates.js';
5
+ import { appendAuditEntry } from '../core/audit.js';
6
+ import { buildOperationalIdentity } from '../core/identity.js';
7
+ export function runRollback(options = {}) {
8
+ if (!memoryExists()) {
9
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
10
+ process.exit(1);
11
+ }
12
+ if (!options.auditId && !options.itemId) {
13
+ console.error('Error: provide --audit-id <id> or --item-id <id> to identify what to roll back.');
14
+ process.exit(1);
15
+ }
16
+ let actor;
17
+ try {
18
+ actor = buildOperationalIdentity(options.actor);
19
+ }
20
+ catch {
21
+ actor = { agent: options.actor ?? process.env.USER ?? process.env.USERNAME ?? 'unknown', agent_id: undefined };
22
+ }
23
+ // Find the target audit entry
24
+ let entries = readAuditLog();
25
+ if (options.auditId) {
26
+ // Look for entry with matching timestamp prefix (first 20 chars are a sortable ISO prefix)
27
+ entries = entries.filter(e => e.timestamp.startsWith(options.auditId) || e.timestamp === options.auditId);
28
+ }
29
+ else if (options.itemId) {
30
+ // Get most recent audit entry for this item that has a `before` snapshot
31
+ entries = entries
32
+ .filter(e => e.item_id === options.itemId && e.before !== undefined)
33
+ .sort((a, b) => b.timestamp.localeCompare(a.timestamp));
34
+ }
35
+ if (entries.length === 0) {
36
+ const result = {
37
+ ok: false,
38
+ message: `No audit entry found for ${options.auditId ?? options.itemId}. Cannot roll back.`,
39
+ };
40
+ if (options.json) {
41
+ console.log(JSON.stringify(result, null, 2));
42
+ }
43
+ else {
44
+ console.error(`Error: ${result.message}`);
45
+ }
46
+ process.exit(1);
47
+ }
48
+ const entry = entries[0];
49
+ if (entry.before === undefined) {
50
+ const result = {
51
+ ok: false,
52
+ item_id: entry.item_id,
53
+ message: `Audit entry at ${entry.timestamp} has no 'before' snapshot. Cannot roll back.`,
54
+ };
55
+ if (options.json) {
56
+ console.log(JSON.stringify(result, null, 2));
57
+ }
58
+ else {
59
+ console.error(`Error: ${result.message}`);
60
+ }
61
+ process.exit(1);
62
+ }
63
+ const result = applyRollback(entry, actor.agent, options);
64
+ if (options.json) {
65
+ console.log(JSON.stringify(result, null, 2));
66
+ }
67
+ else if (result.ok) {
68
+ const dryLabel = options.dryRun ? ' (dry run)' : '';
69
+ console.log(`✔ Rolled back ${result.item_type} [${result.item_id}]${dryLabel}: ${result.message}`);
70
+ }
71
+ else {
72
+ console.error(`Error: ${result.message}`);
73
+ process.exit(1);
74
+ }
75
+ }
76
+ function applyRollback(entry, actorName, options) {
77
+ const { item_id, item_type, before, action } = entry;
78
+ if (!item_id || !item_type || !before) {
79
+ return { ok: false, message: 'Audit entry is missing item_id, item_type, or before snapshot.' };
80
+ }
81
+ if (options.dryRun) {
82
+ return {
83
+ ok: true,
84
+ audit_entry: entry.timestamp,
85
+ item_id,
86
+ item_type,
87
+ action_reversed: action,
88
+ message: `Would restore ${item_type} to state before ${action} at ${entry.timestamp}`,
89
+ };
90
+ }
91
+ try {
92
+ if (item_type === 'candidate') {
93
+ try {
94
+ const existing = loadCandidate(item_id);
95
+ saveCandidate({ ...existing, ...before });
96
+ }
97
+ catch {
98
+ // Candidate may have been deleted — try to recreate from before snapshot
99
+ saveCandidate(before);
100
+ }
101
+ }
102
+ else if (['constraint', 'decision', 'trap'].includes(item_type)) {
103
+ const state = loadState();
104
+ if (item_type === 'constraint') {
105
+ const idx = state.active_constraints.findIndex(c => c.id === item_id);
106
+ if (idx >= 0) {
107
+ state.active_constraints[idx] = before;
108
+ }
109
+ else {
110
+ state.active_constraints.push(before);
111
+ }
112
+ }
113
+ else if (item_type === 'decision') {
114
+ const idx = state.recent_decisions.findIndex(d => d.id === item_id);
115
+ if (idx >= 0) {
116
+ state.recent_decisions[idx] = before;
117
+ }
118
+ else {
119
+ state.recent_decisions.push(before);
120
+ }
121
+ }
122
+ else if (item_type === 'trap') {
123
+ const idx = state.known_traps.findIndex(t => t.id === item_id);
124
+ if (idx >= 0) {
125
+ state.known_traps[idx] = before;
126
+ }
127
+ else {
128
+ state.known_traps.push(before);
129
+ }
130
+ }
131
+ saveState(state);
132
+ }
133
+ else {
134
+ return { ok: false, message: `Rollback not supported for item_type '${item_type}'.` };
135
+ }
136
+ appendAuditEntry({
137
+ actor: actorName,
138
+ action: 'rollback',
139
+ item_id,
140
+ item_type,
141
+ reason: `Rolled back ${action} at ${entry.timestamp}`,
142
+ });
143
+ return {
144
+ ok: true,
145
+ audit_entry: entry.timestamp,
146
+ item_id,
147
+ item_type,
148
+ action_reversed: action,
149
+ message: `Restored to state before '${action}' at ${entry.timestamp}`,
150
+ };
151
+ }
152
+ catch (e) {
153
+ return { ok: false, message: e instanceof Error ? e.message : String(e) };
154
+ }
155
+ }
156
+ //# sourceMappingURL=rollback.js.map
@@ -0,0 +1,144 @@
1
+ import { buildOperationalIdentity } from '../core/identity.js';
2
+ import { memoryExists } from '../core/io.js';
3
+ import { loadConfig } from '../core/config.js';
4
+ import { getAgentTrustLevel, requireMinimumTrustLevel, requireRegisteredAgentIdentity } from '../core/agent-registry.js';
5
+ import { saveRuntimeNote, generateRuntimeNoteId } from '../core/runtime.js';
6
+ import { loadState } from '../core/state.js';
7
+ import { nowISO } from '../core/ids.js';
8
+ import { createCandidateFromInput } from './reflect.js';
9
+ import { suggestCandidateTypes } from './reflect-runtime-note.js';
10
+ import { validateCliInput, validateCliTtl } from '../core/input-validation.js';
11
+ export function runRuntimeNote(text, options) {
12
+ validateCliInput(text, options.tag);
13
+ if (options.ttl) {
14
+ validateCliTtl(options.ttl);
15
+ }
16
+ try {
17
+ return createRuntimeNote(text, options, true);
18
+ }
19
+ catch (error) {
20
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
21
+ process.exit(1);
22
+ }
23
+ }
24
+ export function createRuntimeNote(text, options, printSuccess = false) {
25
+ if (!memoryExists(options.cwd)) {
26
+ throw new Error('.brainclaw/ not found. Run `brainclaw init` first.');
27
+ }
28
+ const registeredAgent = requireRegisteredAgentIdentity({
29
+ agentName: options.agent,
30
+ agentId: options.agentId,
31
+ cwd: options.cwd,
32
+ allowCurrent: true,
33
+ allowEnv: true,
34
+ });
35
+ requireMinimumTrustLevel(registeredAgent, 'contributor');
36
+ const actor = buildOperationalIdentity(registeredAgent.agent_name, options.cwd, {
37
+ agentId: registeredAgent.agent_id,
38
+ sessionId: options.sessionId,
39
+ });
40
+ const state = loadState(options.cwd);
41
+ const plan = options.plan ? state.plan_items.find((item) => item.id === options.plan) : undefined;
42
+ if (options.plan && !plan) {
43
+ throw new Error(`Plan item '${options.plan}' not found.`);
44
+ }
45
+ const id = generateRuntimeNoteId();
46
+ const visibility = options.visibility ?? 'shared';
47
+ const hostId = options.host ?? actor.host_id;
48
+ const expiresAt = options.ttl ? parseTtl(options.ttl) : undefined;
49
+ const note = {
50
+ id,
51
+ agent: actor.agent,
52
+ agent_id: actor.agent_id,
53
+ project_id: actor.project_id,
54
+ session_id: actor.session_id,
55
+ text,
56
+ created_at: nowISO(),
57
+ project: options.project ?? plan?.project,
58
+ plan_id: options.plan,
59
+ tags: options.tag ?? [],
60
+ visibility,
61
+ host_id: hostId,
62
+ expires_at: expiresAt,
63
+ note_type: 'observation',
64
+ };
65
+ saveRuntimeNote(note, options.cwd);
66
+ const result = maybeAutoReflectRuntimeNote(note, options);
67
+ const scopeInfo = visibility === 'shared' ? 'shared' : `${visibility}:${hostId}`;
68
+ const ttlInfo = expiresAt ? ` (expires ${expiresAt})` : '';
69
+ if (printSuccess) {
70
+ console.log(`✔ Runtime note added: [${id}] (${actor.agent}, ${scopeInfo}) ${text}${ttlInfo}`);
71
+ if (result.autoReflectAttempted) {
72
+ if (result.promotedItemId) {
73
+ console.log(` Auto-reflect: promoted ${result.detectedType} via candidate [${result.candidateId}] -> [${result.promotedItemId}]`);
74
+ }
75
+ else if (result.candidateId) {
76
+ console.log(` Auto-reflect: created pending ${result.detectedType} candidate [${result.candidateId}]`);
77
+ }
78
+ else if (result.skipReason) {
79
+ console.log(` Auto-reflect skipped: ${result.skipReason}`);
80
+ }
81
+ }
82
+ }
83
+ return {
84
+ noteId: id,
85
+ agent: actor.agent,
86
+ sessionId: actor.session_id,
87
+ scopeInfo,
88
+ expiresAt,
89
+ ...result,
90
+ };
91
+ }
92
+ function maybeAutoReflectRuntimeNote(note, options) {
93
+ const config = loadConfig(options.cwd);
94
+ const trustLevel = getAgentTrustLevel(note.agent_id ?? note.agent, options.cwd);
95
+ const autoReflectRequested = Boolean(options.autoReflect)
96
+ || (config.auto_reflect_notes === true && (trustLevel === 'trusted' || trustLevel === 'curator'));
97
+ if (!autoReflectRequested) {
98
+ return { autoReflectAttempted: false };
99
+ }
100
+ if (trustLevel === 'observer') {
101
+ return { autoReflectAttempted: true, skipReason: 'observer_not_allowed' };
102
+ }
103
+ const suggestions = suggestCandidateTypes(note.text, note.tags);
104
+ const detected = suggestions.find((entry) => entry.type !== 'handoff');
105
+ if (!detected || detected.score < 4) {
106
+ return { autoReflectAttempted: true, skipReason: 'low_confidence' };
107
+ }
108
+ const creation = createCandidateFromInput(note.text, detected.type, {
109
+ tag: note.tags,
110
+ author: note.agent,
111
+ authorId: note.agent_id,
112
+ projectId: note.project_id,
113
+ hostId: note.host_id,
114
+ sessionId: note.session_id,
115
+ source: `runtime-note:${note.agent}:${note.id}`,
116
+ cwd: options.cwd,
117
+ }, false, false, true);
118
+ return {
119
+ autoReflectAttempted: true,
120
+ detectedType: detected.type,
121
+ candidateId: creation.candidateId,
122
+ promotedItemId: creation.promotedItemId,
123
+ contradictionsDetected: creation.contradictionsDetected?.map((item) => ({
124
+ severity: item.severity,
125
+ reason: item.reason,
126
+ conflicts_with: item.conflicts_with,
127
+ })),
128
+ contradictionSummary: creation.contradictionSummary,
129
+ promotionBlockedReason: creation.promotionBlockedReason,
130
+ };
131
+ }
132
+ /** Parse a TTL string like "30m", "2h", "7d" and return an ISO expiry timestamp. */
133
+ function parseTtl(ttl) {
134
+ const match = /^(\d+)([mhd])$/.exec(ttl.trim().toLowerCase());
135
+ if (!match)
136
+ return undefined;
137
+ const value = parseInt(match[1], 10);
138
+ const unit = match[2];
139
+ const ms = unit === 'm' ? value * 60_000
140
+ : unit === 'h' ? value * 3_600_000
141
+ : value * 86_400_000;
142
+ return new Date(Date.now() + ms).toISOString();
143
+ }
144
+ //# sourceMappingURL=runtime-note.js.map
@@ -0,0 +1,49 @@
1
+ import { resolveCurrentHostId } from '../core/host.js';
2
+ import { memoryExists } from '../core/io.js';
3
+ import { listRuntimeNotes } from '../core/runtime.js';
4
+ export function runRuntimeStatus(options = {}) {
5
+ if (!memoryExists(options.cwd)) {
6
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
7
+ process.exit(1);
8
+ }
9
+ const currentHost = resolveCurrentHostId();
10
+ const notes = listRuntimeNotes({
11
+ agent: options.agent,
12
+ visibility: options.visibility,
13
+ hostId: options.host,
14
+ includeAllHosts: options.allHosts,
15
+ }, options.cwd);
16
+ const filtered = options.plan ? notes.filter((note) => note.plan_id === options.plan) : notes;
17
+ if (options.json) {
18
+ console.log(JSON.stringify(filtered, null, 2));
19
+ return;
20
+ }
21
+ if (filtered.length === 0) {
22
+ console.log('No runtime notes.');
23
+ return;
24
+ }
25
+ // Group by agent
26
+ const byAgent = new Map();
27
+ for (const n of filtered) {
28
+ const list = byAgent.get(n.agent) ?? [];
29
+ list.push(n);
30
+ byAgent.set(n.agent, list);
31
+ }
32
+ const scopeLabel = options.visibility ? ` visibility=${options.visibility}` : ' visibility=shared+machine(current-host)';
33
+ const hostLabel = options.allHosts ? ' host=all' : ` host=${options.host ?? currentHost}`;
34
+ console.log(`${filtered.length} runtime note(s) from ${byAgent.size} agent(s):${scopeLabel}${hostLabel}`);
35
+ console.log('');
36
+ for (const [agent, agentNotes] of byAgent) {
37
+ console.log(` ${agent}:`);
38
+ for (const n of agentNotes.slice(-5)) {
39
+ const tags = n.tags.length ? ` [${n.tags.join(', ')}]` : '';
40
+ const plan = n.plan_id ? ` (plan ${n.plan_id})` : '';
41
+ const scope = n.visibility === 'shared' ? ' [shared]' : ` [${n.visibility}:${n.host_id ?? 'unknown-host'}]`;
42
+ console.log(` [${n.id}] ${n.text}${plan}${scope}${tags}`);
43
+ }
44
+ if (agentNotes.length > 5) {
45
+ console.log(` ... and ${agentNotes.length - 5} more`);
46
+ }
47
+ }
48
+ }
49
+ //# sourceMappingURL=runtime-status.js.map
@@ -0,0 +1,36 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { search } from '../core/search.js';
3
+ export function runSearch(query, options = {}) {
4
+ if (!memoryExists()) {
5
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
6
+ process.exit(1);
7
+ }
8
+ const results = search({
9
+ query,
10
+ section: options.section,
11
+ since: options.since,
12
+ tags: options.tag,
13
+ includePending: options.pending,
14
+ maxResults: options.maxResults ?? 20,
15
+ });
16
+ if (options.json) {
17
+ console.log(JSON.stringify(results, null, 2));
18
+ return;
19
+ }
20
+ if (results.length === 0) {
21
+ console.log(`No results for "${query}".`);
22
+ return;
23
+ }
24
+ console.log(`Found ${results.length} result(s) for "${query}":\n`);
25
+ for (const r of results) {
26
+ const tags = r.tags.length > 0 ? ` [${r.tags.join(', ')}]` : '';
27
+ const paths = r.related_paths && r.related_paths.length > 0 ? ` (${r.related_paths.join(', ')})` : '';
28
+ const score = r.score > 0 ? ` (score: ${r.score})` : '';
29
+ console.log(` [${r.id}] <${r.section}>${tags}${paths}${score}`);
30
+ console.log(` ${r.text}`);
31
+ if (r.author)
32
+ console.log(` — ${r.author}, ${r.created_at.slice(0, 10)}`);
33
+ console.log();
34
+ }
35
+ }
36
+ //# sourceMappingURL=search.js.map