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,159 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import { memoryExists } from '../core/io.js';
5
+ import { loadState } from '../core/state.js';
6
+ import { listCandidates } from '../core/candidates.js';
7
+ import { listClaims } from '../core/claims.js';
8
+ import { listRuntimeNotes } from '../core/runtime.js';
9
+ import { listOperationalTraps } from '../core/traps.js';
10
+ import { pullRemoteMemory, pushRemoteMemory } from '../core/sync-remote.js';
11
+ export function runSync(options = {}) {
12
+ if (!memoryExists(options.cwd)) {
13
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
14
+ process.exit(1);
15
+ }
16
+ // --remote: pull + show summary + push
17
+ if (options.remote) {
18
+ console.log('Remote sync: pull → summarize → push');
19
+ console.log('');
20
+ const pullResult = pullRemoteMemory({});
21
+ if (pullResult.success) {
22
+ console.log(` Pull: ✔ ${pullResult.message}`);
23
+ }
24
+ else {
25
+ console.error(` Pull: ✗ ${pullResult.message}`);
26
+ process.exit(1);
27
+ }
28
+ const pushResult = pushRemoteMemory({});
29
+ if (pushResult.success) {
30
+ console.log(` Push: ✔ ${pushResult.message}`);
31
+ }
32
+ else {
33
+ console.error(` Push: ✗ ${pushResult.message}`);
34
+ process.exit(1);
35
+ }
36
+ return;
37
+ }
38
+ // Summarize current memory state
39
+ const state = loadState(options.cwd);
40
+ const pending = listCandidates('pending', options.cwd);
41
+ const claims = listClaims(options.cwd).filter(c => c.status === 'active');
42
+ const sharedNotes = listRuntimeNotes({ visibility: 'shared' }, options.cwd);
43
+ const machineNotes = listRuntimeNotes({ visibility: 'machine' }, options.cwd);
44
+ const privateNotes = listRuntimeNotes({ visibility: 'private' }, options.cwd);
45
+ const machineTraps = listOperationalTraps({ visibility: 'machine' }, options.cwd);
46
+ const privateTraps = listOperationalTraps({ visibility: 'private' }, options.cwd);
47
+ const activePlans = state.plan_items.filter((plan) => plan.status !== 'done' && plan.status !== 'dropped');
48
+ console.log('Memory sync summary:');
49
+ console.log('');
50
+ console.log(` State: ${activePlans.length} plans, ${state.active_constraints.length} constraints, ${state.recent_decisions.length} decisions, ${state.known_traps.length} traps, ${state.open_handoffs.length} handoffs`);
51
+ console.log(` Pending candidates: ${pending.length}`);
52
+ console.log(` Active claims: ${claims.length}`);
53
+ console.log(` Runtime notes: ${sharedNotes.length} shared, ${machineNotes.length} machine-local, ${privateNotes.length} private`);
54
+ console.log(` Local traps: ${machineTraps.length} machine-local, ${privateTraps.length} private`);
55
+ const scopePaths = resolveScopePaths(options.scope, options.includeMachineRuntime ?? false, options.cwd);
56
+ const pathSpec = scopePaths.join(' ');
57
+ console.log(` Sync scope: ${pathSpec}`);
58
+ if (options.summaryOnly) {
59
+ console.log('');
60
+ console.log(' Summary-only mode enabled; skipping git status and commit checks.');
61
+ return;
62
+ }
63
+ // Check git status of .brainclaw/
64
+ let gitStatus = '';
65
+ try {
66
+ gitStatus = execSync(`git status --porcelain ${pathSpec}`, {
67
+ encoding: 'utf-8',
68
+ cwd: options.cwd ?? process.cwd(),
69
+ timeout: 5000,
70
+ }).trim();
71
+ }
72
+ catch {
73
+ console.log('');
74
+ console.log(' (not a git repo or git not available — skipping git status)');
75
+ return;
76
+ }
77
+ if (!gitStatus) {
78
+ console.log('');
79
+ console.log(' No uncommitted changes in .brainclaw/');
80
+ return;
81
+ }
82
+ const changed = gitStatus.split('\n').length;
83
+ console.log('');
84
+ console.log(` ${changed} file(s) changed in .brainclaw/`);
85
+ if (options.commit) {
86
+ const msg = options.message ?? `chore: sync brainclaw (${new Date().toISOString().slice(0, 10)})`;
87
+ try {
88
+ execSync(`git add ${pathSpec}`, {
89
+ cwd: options.cwd ?? process.cwd(),
90
+ timeout: 5000,
91
+ });
92
+ execSync(`git commit -m "${msg.replace(/"/g, '\\"')}"`, {
93
+ encoding: 'utf-8',
94
+ cwd: options.cwd ?? process.cwd(),
95
+ timeout: 10000,
96
+ });
97
+ console.log(`✔ Committed .brainclaw/ changes: "${msg}"`);
98
+ console.log(' (not pushed — run `git push` when ready)');
99
+ }
100
+ catch (error) {
101
+ const message = error instanceof Error ? error.message : String(error);
102
+ console.error(`Error: failed to commit .brainclaw/ changes. ${message}`);
103
+ process.exit(1);
104
+ }
105
+ }
106
+ else {
107
+ console.log(' Run with --commit to create a local git commit.');
108
+ }
109
+ }
110
+ export function resolveScopePaths(scope, includeMachineRuntime = false, cwd) {
111
+ switch ((scope ?? 'all').toLowerCase()) {
112
+ case 'all':
113
+ return existingScopePaths([
114
+ '.brainclaw/config.yaml',
115
+ '.brainclaw/project.md',
116
+ '.brainclaw/memory/constraints/',
117
+ '.brainclaw/memory/decisions/',
118
+ '.brainclaw/memory/traps/',
119
+ '.brainclaw/coordination/handoffs/',
120
+ '.brainclaw/coordination/plans/',
121
+ '.brainclaw/memory/instructions/',
122
+ '.brainclaw/coordination/inbox/',
123
+ '.brainclaw/archive/',
124
+ '.brainclaw/coordination/claims/',
125
+ '.brainclaw/coordination/runtime/',
126
+ ...(includeMachineRuntime ? ['.brainclaw/coordination/runtime-hosts/', '.brainclaw/coordination/runtime-private/'] : []),
127
+ ], cwd);
128
+ case 'state':
129
+ return existingScopePaths([
130
+ '.brainclaw/memory/constraints/',
131
+ '.brainclaw/memory/decisions/',
132
+ '.brainclaw/memory/traps/',
133
+ '.brainclaw/coordination/handoffs/',
134
+ ], cwd);
135
+ case 'config':
136
+ return existingScopePaths(['.brainclaw/config.yaml'], cwd);
137
+ case 'project':
138
+ return existingScopePaths(['.brainclaw/project.md'], cwd);
139
+ case 'inbox':
140
+ return existingScopePaths(['.brainclaw/coordination/inbox/'], cwd);
141
+ case 'archive':
142
+ return existingScopePaths(['.brainclaw/archive/'], cwd);
143
+ case 'claims':
144
+ return existingScopePaths(['.brainclaw/coordination/claims/'], cwd);
145
+ case 'runtime':
146
+ return existingScopePaths(['.brainclaw/coordination/runtime/'], cwd);
147
+ case 'runtime-local':
148
+ return existingScopePaths(['.brainclaw/coordination/runtime-hosts/', '.brainclaw/coordination/runtime-private/'], cwd);
149
+ case 'trap-local':
150
+ return existingScopePaths(['.brainclaw/memory/traps-hosts/', '.brainclaw/memory/traps-private/'], cwd);
151
+ default:
152
+ console.warn(`⚠ Unknown sync scope "${scope}"; defaulting to .brainclaw/`);
153
+ return resolveScopePaths('all', includeMachineRuntime, cwd);
154
+ }
155
+ }
156
+ function existingScopePaths(paths, cwd) {
157
+ return paths.filter((scopePath) => fs.existsSync(cwd ? path.join(cwd, scopePath) : scopePath));
158
+ }
159
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1,126 @@
1
+ import { loadState, saveState } from '../core/state.js';
2
+ import { resolveCurrentAgentName } from '../core/agent-registry.js';
3
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
4
+ import { generateMarkdown } from '../core/markdown.js';
5
+ import { generateIdWithLabel, nowISO } from '../core/ids.js';
6
+ import { loadConfig } from '../core/config.js';
7
+ import { scanText } from '../core/security.js';
8
+ import { validateCliInput } from '../core/input-validation.js';
9
+ import { resolveTargetStore } from '../core/store-resolution.js';
10
+ export function runTool(subcommand, args, options = {}) {
11
+ const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
12
+ if (!memoryExists(cwd)) {
13
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
14
+ process.exit(1);
15
+ }
16
+ if (subcommand === 'list') {
17
+ runToolList(cwd);
18
+ }
19
+ else if (subcommand === 'add') {
20
+ const name = args[0];
21
+ const description = args[1];
22
+ if (!name || !description) {
23
+ console.error('Error: tool add requires <name> <description>');
24
+ process.exit(1);
25
+ }
26
+ runToolAdd(name, description, options, cwd);
27
+ }
28
+ else if (subcommand === 'describe') {
29
+ const toolId = args[0];
30
+ if (!toolId) {
31
+ console.error('Error: tool describe requires <id>');
32
+ process.exit(1);
33
+ }
34
+ runToolDescribe(toolId, cwd);
35
+ }
36
+ else if (subcommand === 'search') {
37
+ const query = args[0];
38
+ if (!query) {
39
+ console.error('Error: tool search requires <query>');
40
+ process.exit(1);
41
+ }
42
+ runToolSearch(query, cwd);
43
+ }
44
+ else {
45
+ console.error(`Unknown tool subcommand: ${subcommand}`);
46
+ process.exit(1);
47
+ }
48
+ }
49
+ function runToolList(cwd) {
50
+ const state = loadState(cwd);
51
+ const tools = state.recent_decisions
52
+ .filter((d) => d.tags.includes('tool'))
53
+ .map((d) => ({ id: d.id, name: d.text.split('\n')[0], type: d.tags.find((t) => t !== 'tool') }));
54
+ if (tools.length === 0) {
55
+ console.log('No tools registered yet.');
56
+ return;
57
+ }
58
+ console.log(`\n${tools.length} tool(s):\n`);
59
+ tools.forEach((tool) => {
60
+ console.log(` [${tool.id}] ${tool.name}`);
61
+ if (tool.type) {
62
+ console.log(` type: ${tool.type}`);
63
+ }
64
+ });
65
+ console.log('');
66
+ }
67
+ function runToolAdd(name, description, options, cwd) {
68
+ validateCliInput(name + ' ' + description, options.tag);
69
+ const config = loadConfig(cwd);
70
+ const warnings = scanText(name + ': ' + description, config);
71
+ for (const w of warnings) {
72
+ console.warn(`⚠ ${w.message}`);
73
+ if (w.level === 'block') {
74
+ console.error('Blocked: strict redaction is enabled. Entry not added.');
75
+ process.exit(1);
76
+ }
77
+ }
78
+ const state = loadState(cwd);
79
+ const { id, short_label } = generateIdWithLabel('recent_decisions');
80
+ const toolType = options.type ?? 'utility';
81
+ const entry = {
82
+ id,
83
+ short_label,
84
+ text: name,
85
+ created_at: nowISO(),
86
+ author: options.author ?? resolveCurrentAgentName(cwd),
87
+ tags: ['tool', toolType, ...(options.tag ?? [])],
88
+ };
89
+ // For now, store as decision to avoid schema migration
90
+ // Will migrate to separate tool storage in v0.16
91
+ state.recent_decisions.push(entry);
92
+ saveState(state, cwd);
93
+ writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state));
94
+ console.log(`✔ Tool added: [${id}] ${name}`);
95
+ console.log(' (Stored in decisions for now; will move to dedicated registry in v0.16)');
96
+ }
97
+ function runToolDescribe(toolId, cwd) {
98
+ const state = loadState(cwd);
99
+ const decision = state.recent_decisions.find((d) => d.id === toolId || d.short_label === toolId);
100
+ if (!decision) {
101
+ console.error(`Error: tool '${toolId}' not found`);
102
+ process.exit(1);
103
+ }
104
+ console.log(`\nTool: ${decision.text}`);
105
+ console.log(`ID: ${decision.id}`);
106
+ console.log(`Type: ${decision.tags.find((t) => t !== 'tool')}`);
107
+ console.log(`Author: ${decision.author}`);
108
+ console.log(`Created: ${decision.created_at}`);
109
+ console.log('');
110
+ }
111
+ function runToolSearch(query, cwd) {
112
+ const state = loadState(cwd);
113
+ const tools = state.recent_decisions.filter((d) => d.tags.includes('tool'));
114
+ const results = tools.filter((tool) => tool.text.toLowerCase().includes(query.toLowerCase()) ||
115
+ tool.tags.some((tag) => tag.toLowerCase().includes(query.toLowerCase())));
116
+ if (results.length === 0) {
117
+ console.log(`No tools found matching '${query}'`);
118
+ return;
119
+ }
120
+ console.log(`\n${results.length} tool(s) matching '${query}':\n`);
121
+ results.forEach((result) => {
122
+ console.log(` [${result.id}] ${result.text.split('\n')[0]}`);
123
+ });
124
+ console.log('');
125
+ }
126
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1,74 @@
1
+ import { loadState, saveState } from '../core/state.js';
2
+ import { resolveCurrentAgentName } from '../core/agent-registry.js';
3
+ import { resolveCurrentHostId } from '../core/host.js';
4
+ import { loadConfig } from '../core/config.js';
5
+ import { generateMarkdown } from '../core/markdown.js';
6
+ import { nowISO } from '../core/ids.js';
7
+ import { scanText } from '../core/security.js';
8
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
9
+ import { generateTrapIdWithLabel, saveOperationalTrap } from '../core/traps.js';
10
+ import { validateCliInput, validateCliTtl } from '../core/input-validation.js';
11
+ import { resolveTargetStore } from '../core/store-resolution.js';
12
+ export function runTrap(text, options = {}) {
13
+ const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
14
+ if (!memoryExists(cwd)) {
15
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
16
+ process.exit(1);
17
+ }
18
+ validateCliInput(text, options.tag);
19
+ if (options.ttl) {
20
+ validateCliTtl(options.ttl);
21
+ }
22
+ const config = loadConfig(cwd);
23
+ const warnings = scanText(text, config);
24
+ for (const w of warnings) {
25
+ console.warn(`⚠ ${w.message}`);
26
+ if (w.level === 'block') {
27
+ console.error('Blocked: strict redaction is enabled. Entry not added.');
28
+ process.exit(1);
29
+ }
30
+ }
31
+ const state = loadState(cwd);
32
+ const { id, short_label } = generateTrapIdWithLabel();
33
+ const visibility = options.visibility ?? 'shared';
34
+ const hostId = visibility === 'shared' ? undefined : resolveCurrentHostId(options.host);
35
+ const entry = {
36
+ id,
37
+ short_label,
38
+ text,
39
+ created_at: nowISO(),
40
+ author: options.author ?? resolveCurrentAgentName(cwd),
41
+ status: options.status ?? 'active',
42
+ severity: options.severity ?? 'medium',
43
+ tags: options.tag ?? [],
44
+ related_paths: options.path,
45
+ plan_id: options.plan,
46
+ visibility,
47
+ host_id: hostId,
48
+ expires_at: options.ttl ? parseTtl(options.ttl) : undefined,
49
+ };
50
+ if (visibility === 'shared') {
51
+ state.known_traps.push(entry);
52
+ saveState(state, cwd);
53
+ writeFileAtomic(memoryPath('project.md', cwd), generateMarkdown(state, cwd));
54
+ }
55
+ else {
56
+ saveOperationalTrap(entry, cwd);
57
+ }
58
+ const scopeInfo = visibility === 'shared' ? 'shared' : `${visibility}:${hostId}`;
59
+ const storeLabel = options.store && options.store !== 'local' ? ` [store:${options.store}]` : '';
60
+ console.log(`✔ Trap added: [${id}] (${scopeInfo}) ${text}${storeLabel}`);
61
+ }
62
+ /** Parse a TTL string like "30m", "2h", "7d" and return an ISO expiry timestamp. */
63
+ function parseTtl(ttl) {
64
+ const match = /^(\d+)([mhd])$/.exec(ttl.trim().toLowerCase());
65
+ if (!match)
66
+ return undefined;
67
+ const value = parseInt(match[1], 10);
68
+ const unit = match[2];
69
+ const ms = unit === 'm' ? value * 60_000
70
+ : unit === 'h' ? value * 3_600_000
71
+ : value * 86_400_000;
72
+ return new Date(Date.now() + ms).toISOString();
73
+ }
74
+ //# sourceMappingURL=trap.js.map
@@ -0,0 +1,23 @@
1
+ import { loadState, saveState } from '../core/state.js';
2
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
3
+ import { generateMarkdown } from '../core/markdown.js';
4
+ export function runUpdateHandoff(id, options = {}) {
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 handoff = state.open_handoffs.find((item) => item.id === id);
11
+ if (!handoff) {
12
+ console.error(`Error: Handoff '${id}' not found.`);
13
+ process.exit(1);
14
+ }
15
+ if (options.status)
16
+ handoff.status = options.status;
17
+ if (options.to !== undefined)
18
+ handoff.to = options.to;
19
+ saveState(state);
20
+ writeFileAtomic(memoryPath('project.md'), generateMarkdown(state));
21
+ console.log(`✔ Handoff updated: [${handoff.id}] ${handoff.from} → ${handoff.to} (${handoff.status})`);
22
+ }
23
+ //# sourceMappingURL=update-handoff.js.map
@@ -0,0 +1,37 @@
1
+ import { loadState, saveState } from '../core/state.js';
2
+ import { memoryExists, memoryPath, writeFileAtomic } from '../core/io.js';
3
+ import { generateMarkdown } from '../core/markdown.js';
4
+ import { nowISO } from '../core/ids.js';
5
+ export function runUpdatePlan(id, options = {}) {
6
+ if (!memoryExists(options.cwd)) {
7
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
8
+ process.exit(1);
9
+ }
10
+ const state = loadState(options.cwd);
11
+ const plan = state.plan_items.find((item) => item.id === id);
12
+ if (!plan) {
13
+ console.error(`Error: Plan item '${id}' not found.`);
14
+ process.exit(1);
15
+ }
16
+ const timestamp = nowISO();
17
+ if (options.status) {
18
+ plan.status = options.status;
19
+ if (options.status === 'in_progress' && !plan.started_at)
20
+ plan.started_at = timestamp;
21
+ if (options.status === 'done' && !plan.completed_at)
22
+ plan.completed_at = timestamp;
23
+ }
24
+ if (options.assignee !== undefined)
25
+ plan.assignee = options.assignee;
26
+ if (options.project !== undefined)
27
+ plan.project = options.project;
28
+ if (options.priority)
29
+ plan.priority = options.priority;
30
+ if (options.actualEffort)
31
+ plan.actual_effort = options.actualEffort;
32
+ plan.updated_at = timestamp;
33
+ saveState(state, options.cwd);
34
+ writeFileAtomic(memoryPath('project.md', options.cwd), generateMarkdown(state, options.cwd));
35
+ console.log(`✔ Plan item updated: [${plan.id}] ${plan.text}`);
36
+ }
37
+ //# sourceMappingURL=update-plan.js.map