brainclaw 0.29.2 → 1.5.3

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 (195) hide show
  1. package/README.md +193 -170
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli.js +673 -24
  4. package/dist/commands/accept.js +3 -0
  5. package/dist/commands/add-step.js +11 -26
  6. package/dist/commands/agent-board.js +70 -3
  7. package/dist/commands/audit.js +19 -0
  8. package/dist/commands/check-policy.js +54 -0
  9. package/dist/commands/check-security-mcp.js +145 -0
  10. package/dist/commands/check-security.js +106 -0
  11. package/dist/commands/claim-resource.js +1 -0
  12. package/dist/commands/codev.js +672 -0
  13. package/dist/commands/compact.js +74 -0
  14. package/dist/commands/complete-step.js +16 -26
  15. package/dist/commands/constraint.js +8 -20
  16. package/dist/commands/decision.js +9 -20
  17. package/dist/commands/delete-plan.js +10 -12
  18. package/dist/commands/delete-step.js +16 -0
  19. package/dist/commands/dispatch.js +163 -0
  20. package/dist/commands/doctor.js +1122 -49
  21. package/dist/commands/enable-agent.js +1 -0
  22. package/dist/commands/export.js +280 -22
  23. package/dist/commands/handoff.js +33 -0
  24. package/dist/commands/harvest.js +189 -0
  25. package/dist/commands/hooks.js +82 -25
  26. package/dist/commands/inbox.js +169 -0
  27. package/dist/commands/init.js +38 -31
  28. package/dist/commands/install-hooks.js +71 -44
  29. package/dist/commands/link.js +89 -0
  30. package/dist/commands/list-claims.js +48 -3
  31. package/dist/commands/list-plans.js +129 -25
  32. package/dist/commands/loops-handlers.js +409 -0
  33. package/dist/commands/mcp-read-handlers.js +1628 -0
  34. package/dist/commands/mcp-schemas.generated.js +74 -0
  35. package/dist/commands/mcp.js +4221 -1501
  36. package/dist/commands/plan-resource.js +64 -0
  37. package/dist/commands/plan.js +12 -26
  38. package/dist/commands/prune.js +37 -2
  39. package/dist/commands/reflect.js +20 -7
  40. package/dist/commands/release-claim.js +11 -6
  41. package/dist/commands/release-notes.js +170 -0
  42. package/dist/commands/repair.js +210 -0
  43. package/dist/commands/run-profile.js +57 -0
  44. package/dist/commands/sequence.js +113 -0
  45. package/dist/commands/session-end.js +423 -14
  46. package/dist/commands/session-start.js +214 -41
  47. package/dist/commands/setup-security.js +103 -0
  48. package/dist/commands/setup.js +42 -4
  49. package/dist/commands/stale.js +109 -0
  50. package/dist/commands/switch.js +100 -2
  51. package/dist/commands/trap.js +14 -31
  52. package/dist/commands/update-handoff.js +63 -4
  53. package/dist/commands/update-plan.js +21 -28
  54. package/dist/commands/update-step.js +37 -0
  55. package/dist/commands/upgrade.js +313 -6
  56. package/dist/commands/usage.js +102 -0
  57. package/dist/commands/version.js +20 -0
  58. package/dist/commands/who.js +33 -5
  59. package/dist/commands/worktree.js +105 -0
  60. package/dist/core/actions.js +315 -0
  61. package/dist/core/agent-capability.js +610 -17
  62. package/dist/core/agent-context.js +7 -1
  63. package/dist/core/agent-files.js +1169 -85
  64. package/dist/core/agent-integrations.js +160 -5
  65. package/dist/core/agent-inventory.js +2 -0
  66. package/dist/core/agent-profiles.js +93 -0
  67. package/dist/core/agent-registry.js +162 -30
  68. package/dist/core/agentrun-reconciler.js +345 -0
  69. package/dist/core/agentruns.js +424 -0
  70. package/dist/core/ai-agent-detection.js +31 -10
  71. package/dist/core/archival.js +77 -0
  72. package/dist/core/assignment-sweeper.js +82 -0
  73. package/dist/core/assignments.js +367 -0
  74. package/dist/core/audit.js +30 -0
  75. package/dist/core/brainclaw-version.js +94 -2
  76. package/dist/core/candidates.js +93 -2
  77. package/dist/core/claims.js +419 -0
  78. package/dist/core/codev-metrics.js +77 -0
  79. package/dist/core/codev-personas.js +31 -0
  80. package/dist/core/codev-plan-gen.js +35 -0
  81. package/dist/core/codev-prompts.js +74 -0
  82. package/dist/core/codev-responses.js +62 -0
  83. package/dist/core/codev-rounds.js +218 -0
  84. package/dist/core/config.js +4 -0
  85. package/dist/core/context.js +381 -34
  86. package/dist/core/coordination.js +201 -6
  87. package/dist/core/cross-project.js +230 -16
  88. package/dist/core/default-profiles/doctor.yaml +11 -0
  89. package/dist/core/default-profiles/janitor.yaml +11 -0
  90. package/dist/core/default-profiles/onboarder.yaml +11 -0
  91. package/dist/core/default-profiles/reviewer.yaml +13 -0
  92. package/dist/core/dispatcher.js +1189 -0
  93. package/dist/core/duplicates.js +2 -2
  94. package/dist/core/entity-operations.js +450 -0
  95. package/dist/core/entity-registry.js +344 -0
  96. package/dist/core/events.js +106 -2
  97. package/dist/core/execution-adapters.js +154 -0
  98. package/dist/core/execution-context.js +63 -0
  99. package/dist/core/execution-profile.js +270 -0
  100. package/dist/core/execution.js +255 -0
  101. package/dist/core/facade-schema.js +81 -0
  102. package/dist/core/federation-cloud.js +99 -0
  103. package/dist/core/federation-message.js +52 -0
  104. package/dist/core/federation-transport.js +65 -0
  105. package/dist/core/gc-semantic.js +482 -0
  106. package/dist/core/governance.js +247 -0
  107. package/dist/core/guards.js +19 -0
  108. package/dist/core/ideation.js +72 -0
  109. package/dist/core/identity.js +110 -25
  110. package/dist/core/ids.js +6 -0
  111. package/dist/core/input-validation.js +2 -2
  112. package/dist/core/instruction-templates.js +344 -136
  113. package/dist/core/io.js +90 -11
  114. package/dist/core/lock.js +6 -2
  115. package/dist/core/loops/brief-assembly.js +213 -0
  116. package/dist/core/loops/facade-schema.js +148 -0
  117. package/dist/core/loops/index.js +7 -0
  118. package/dist/core/loops/iteration-engine.js +139 -0
  119. package/dist/core/loops/lock.js +385 -0
  120. package/dist/core/loops/store.js +201 -0
  121. package/dist/core/loops/types.js +403 -0
  122. package/dist/core/loops/verbs.js +534 -0
  123. package/dist/core/markdown.js +15 -3
  124. package/dist/core/memory-compactor.js +432 -0
  125. package/dist/core/memory-git.js +152 -8
  126. package/dist/core/messaging.js +278 -0
  127. package/dist/core/migration.js +32 -1
  128. package/dist/core/mutation-pipeline.js +4 -2
  129. package/dist/core/operations/memory-mutation.js +129 -0
  130. package/dist/core/operations/memory-write.js +78 -0
  131. package/dist/core/operations/plan.js +190 -0
  132. package/dist/core/policy.js +169 -0
  133. package/dist/core/reputation.js +9 -3
  134. package/dist/core/schema.js +491 -6
  135. package/dist/core/search.js +21 -2
  136. package/dist/core/security-cache.js +71 -0
  137. package/dist/core/security-guard.js +152 -0
  138. package/dist/core/security-scoring.js +86 -0
  139. package/dist/core/sequence.js +130 -0
  140. package/dist/core/socket-client.js +113 -0
  141. package/dist/core/staleness.js +246 -0
  142. package/dist/core/state.js +98 -22
  143. package/dist/core/store-resolution.js +43 -11
  144. package/dist/core/toml-writer.js +76 -0
  145. package/dist/core/upgrades/backup.js +232 -0
  146. package/dist/core/upgrades/health-check.js +169 -0
  147. package/dist/core/upgrades/patches/candidate-archive.js +145 -0
  148. package/dist/core/upgrades/patches/handoff-review-strip.js +128 -0
  149. package/dist/core/upgrades/patches/provenance-rollout.js +136 -0
  150. package/dist/core/upgrades/schema-version.js +97 -0
  151. package/dist/core/worktree.js +606 -0
  152. package/dist/facts.js +114 -0
  153. package/dist/facts.json +111 -0
  154. package/docs/architecture/project-refs.md +5 -1
  155. package/docs/cli.md +690 -43
  156. package/docs/concepts/ideation-loop.md +317 -0
  157. package/docs/concepts/loop-engine.md +456 -0
  158. package/docs/concepts/mcp-governance.md +268 -0
  159. package/docs/concepts/memory-staleness.md +122 -0
  160. package/docs/concepts/multi-agent-workflows.md +166 -0
  161. package/docs/concepts/plans-and-claims.md +31 -6
  162. package/docs/concepts/project-md-convention.md +35 -0
  163. package/docs/concepts/troubleshooting.md +220 -0
  164. package/docs/concepts/upgrade-cli.md +202 -0
  165. package/docs/concepts/upgrade-dogfood-procedure.md +114 -0
  166. package/docs/context-format-changelog.md +2 -2
  167. package/docs/context-format.md +2 -2
  168. package/docs/index.md +68 -0
  169. package/docs/integrations/agents.md +15 -16
  170. package/docs/integrations/cline.md +88 -0
  171. package/docs/integrations/codex.md +75 -23
  172. package/docs/integrations/continue.md +60 -0
  173. package/docs/integrations/copilot.md +67 -9
  174. package/docs/integrations/kilocode.md +72 -0
  175. package/docs/integrations/mcp.md +304 -21
  176. package/docs/integrations/mistral-vibe.md +122 -0
  177. package/docs/integrations/opencode.md +84 -0
  178. package/docs/integrations/overview.md +23 -8
  179. package/docs/integrations/roo.md +74 -0
  180. package/docs/integrations/windsurf.md +83 -0
  181. package/docs/mcp-schema-changelog.md +191 -1
  182. package/docs/playbooks/integration/index.md +121 -0
  183. package/docs/playbooks/productivity/index.md +102 -0
  184. package/docs/playbooks/team/index.md +122 -0
  185. package/docs/product/agent-first-model.md +184 -0
  186. package/docs/product/entity-model-audit.md +462 -0
  187. package/docs/quickstart-existing-project.md +135 -0
  188. package/docs/quickstart.md +124 -37
  189. package/docs/release-maintenance.md +79 -0
  190. package/docs/review.md +2 -0
  191. package/docs/server-operations.md +118 -0
  192. package/package.json +20 -12
  193. package/dist/commands/claude-desktop-extension.js +0 -18
  194. package/dist/commands/diff.js +0 -99
  195. package/dist/core/claude-desktop-extension.js +0 -224
@@ -2,12 +2,89 @@ import path from 'node:path';
2
2
  import { loadActiveProject, saveActiveProject, clearActiveProject } from '../core/active-project.js';
3
3
  import { loadCurrentSession, saveCurrentSession } from '../core/identity.js';
4
4
  import { memoryExists } from '../core/io.js';
5
- import { resolveProjectRef, resolveWorkspaceRoot } from '../core/store-resolution.js';
5
+ import { resolveProjectRef } from '../core/store-resolution.js';
6
6
  import { scanNestedBrainclawProjects } from '../core/workspace-projects.js';
7
7
  import { loadConfig } from '../core/config.js';
8
+ /**
9
+ * Switch to a project programmatically.
10
+ * When sessionOnly=true (default for MCP), ONLY writes to the session state.
11
+ * This prevents agents from cross-contaminating each other's active project.
12
+ */
13
+ export function switchProject(projectRef, options = {}) {
14
+ const cwd = options.cwd ?? process.cwd();
15
+ const wsRoot = findOutermostWorkspaceRoot(cwd);
16
+ if (!wsRoot) {
17
+ throw new Error('No brainclaw workspace found. Run `brainclaw init` first.');
18
+ }
19
+ const resolved = resolveProjectRef(projectRef, cwd);
20
+ if (!resolved) {
21
+ throw new Error(`Cannot resolve project "${projectRef}". Use bclaw_switch with list=true to see available projects.`);
22
+ }
23
+ let projectName;
24
+ try {
25
+ const config = loadConfig(resolved);
26
+ projectName = config.project_name;
27
+ }
28
+ catch { /* name is optional */ }
29
+ const now = new Date().toISOString();
30
+ const session = loadCurrentSession(cwd);
31
+ const sessionOnly = options.sessionOnly ?? true;
32
+ if (session && sessionOnly) {
33
+ saveCurrentSession({
34
+ ...session,
35
+ active_project: { path: resolved, name: projectName, switched_at: now },
36
+ }, cwd);
37
+ return { switched: true, path: resolved, name: projectName, scope: 'session', workspace_root: wsRoot };
38
+ }
39
+ if (session) {
40
+ // Also write to session even when not sessionOnly
41
+ saveCurrentSession({
42
+ ...session,
43
+ active_project: { path: resolved, name: projectName, switched_at: now },
44
+ }, cwd);
45
+ }
46
+ saveActiveProject(wsRoot, {
47
+ path: resolved,
48
+ name: projectName,
49
+ switched_at: now,
50
+ switched_by: process.env.BRAINCLAW_AGENT_NAME ?? process.env.USER ?? 'unknown',
51
+ });
52
+ return { switched: true, path: resolved, name: projectName, scope: 'global', workspace_root: wsRoot };
53
+ }
54
+ /**
55
+ * List available projects in the workspace.
56
+ */
57
+ export function listAvailableProjects(cwd) {
58
+ const wsRoot = findOutermostWorkspaceRoot(cwd ?? process.cwd());
59
+ if (!wsRoot) {
60
+ throw new Error('No brainclaw workspace found.');
61
+ }
62
+ const active = loadActiveProject(wsRoot);
63
+ const projects = [];
64
+ if (memoryExists(wsRoot)) {
65
+ try {
66
+ const config = loadConfig(wsRoot);
67
+ projects.push({ name: config.project_name, path: wsRoot, relative_path: '.', active: active?.path === wsRoot });
68
+ }
69
+ catch {
70
+ projects.push({ path: wsRoot, relative_path: '.', active: active?.path === wsRoot });
71
+ }
72
+ }
73
+ const children = scanNestedBrainclawProjects(wsRoot, 7);
74
+ for (const child of children) {
75
+ const childPath = path.resolve(child.path);
76
+ if (childPath === wsRoot)
77
+ continue;
78
+ const rel = path.relative(wsRoot, childPath) || '.';
79
+ projects.push({ name: child.project_name, path: childPath, relative_path: rel, active: active?.path === childPath });
80
+ }
81
+ return { workspace_root: wsRoot, projects };
82
+ }
8
83
  export function runSwitch(projectRef, options = {}) {
84
+ // Use real cwd, not effective cwd — switch must see the full workspace
9
85
  const cwd = options.cwd ?? process.cwd();
10
- const wsRoot = resolveWorkspaceRoot(cwd);
86
+ // Walk up from real cwd to find the outermost .brainclaw/ (workspace root)
87
+ const wsRoot = findOutermostWorkspaceRoot(cwd);
11
88
  if (!wsRoot) {
12
89
  console.error('Error: no brainclaw workspace found. Run `brainclaw init` first.');
13
90
  process.exit(1);
@@ -161,4 +238,25 @@ function listProjects(wsRoot, json) {
161
238
  console.log('\nNo active project. Use `brainclaw switch <project>` to set one.');
162
239
  }
163
240
  }
241
+ /**
242
+ * Find the outermost .brainclaw/ workspace root by walking UP from cwd.
243
+ * Unlike resolveWorkspaceRoot which may return the closest store,
244
+ * this returns the farthest one — the true multi-project workspace root.
245
+ */
246
+ function findOutermostWorkspaceRoot(startDir) {
247
+ let dir = path.resolve(startDir);
248
+ const root = path.parse(dir).root;
249
+ const home = process.env.HOME || process.env.USERPROFILE || root;
250
+ let outermost;
251
+ while (dir !== root && dir !== home) {
252
+ if (memoryExists(dir)) {
253
+ outermost = dir; // keep going — we want the outermost
254
+ }
255
+ const parent = path.dirname(dir);
256
+ if (parent === dir)
257
+ break;
258
+ dir = parent;
259
+ }
260
+ return outermost;
261
+ }
164
262
  //# sourceMappingURL=switch.js.map
@@ -1,19 +1,14 @@
1
- import { loadState, persistState } from '../core/state.js';
2
1
  import { resolveCurrentAgentName } from '../core/agent-registry.js';
3
2
  import { resolveCurrentHostId } from '../core/host.js';
4
3
  import { loadConfig } from '../core/config.js';
5
- import { nowISO } from '../core/ids.js';
6
4
  import { scanText } from '../core/security.js';
7
- import { memoryExists } from '../core/io.js';
8
- import { generateTrapIdWithLabel, saveOperationalTrap } from '../core/traps.js';
5
+ import { requireInitialized } from '../core/guards.js';
9
6
  import { validateCliInput, validateCliTtl } from '../core/input-validation.js';
10
7
  import { resolveTargetStore } from '../core/store-resolution.js';
8
+ import { createTrap } from '../core/operations/memory-write.js';
11
9
  export function runTrap(text, options = {}) {
12
10
  const cwd = resolveTargetStore(options.cwd ?? process.cwd(), options.store ?? 'local');
13
- if (!memoryExists(cwd)) {
14
- console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
15
- process.exit(1);
16
- }
11
+ requireInitialized(cwd);
17
12
  validateCliInput(text, options.tag);
18
13
  if (options.ttl) {
19
14
  validateCliTtl(options.ttl);
@@ -27,35 +22,23 @@ export function runTrap(text, options = {}) {
27
22
  process.exit(1);
28
23
  }
29
24
  }
30
- const state = loadState(cwd);
31
- const { id, short_label } = generateTrapIdWithLabel();
32
25
  const visibility = options.visibility ?? 'shared';
33
26
  const hostId = visibility === 'shared' ? undefined : resolveCurrentHostId(options.host);
34
- const entry = {
35
- id,
36
- short_label,
27
+ const result = createTrap({
37
28
  text,
38
- created_at: nowISO(),
39
29
  author: options.author ?? resolveCurrentAgentName(cwd),
40
- status: options.status ?? 'active',
41
- severity: options.severity ?? 'medium',
42
- tags: options.tag ?? [],
43
- related_paths: options.path,
44
- plan_id: options.plan,
30
+ status: options.status,
31
+ severity: options.severity,
32
+ tags: options.tag,
33
+ relatedPaths: options.path,
34
+ planId: options.plan,
45
35
  visibility,
46
- host_id: hostId,
47
- expires_at: options.ttl ? parseTtl(options.ttl) : undefined,
48
- };
49
- if (visibility === 'shared') {
50
- state.known_traps.push(entry);
51
- persistState(state, cwd);
52
- }
53
- else {
54
- saveOperationalTrap(entry, cwd);
55
- }
56
- const scopeInfo = visibility === 'shared' ? 'shared' : `${visibility}:${hostId}`;
36
+ hostId,
37
+ expiresAt: options.ttl ? parseTtl(options.ttl) : undefined,
38
+ }, cwd);
39
+ const scopeInfo = result.visibility === 'shared' ? 'shared' : `${result.visibility}:${result.hostId}`;
57
40
  const storeLabel = options.store && options.store !== 'local' ? ` [store:${options.store}]` : '';
58
- console.log(`✔ Trap added: [${id}] (${scopeInfo}) ${text}${storeLabel}`);
41
+ console.log(`✔ Trap added: [${result.id}] (${scopeInfo}) ${text}${storeLabel}`);
59
42
  }
60
43
  /** Parse a TTL string like "30m", "2h", "7d" and return an ISO expiry timestamp. */
61
44
  function parseTtl(ttl) {
@@ -1,5 +1,67 @@
1
1
  import { loadState, persistState } from '../core/state.js';
2
2
  import { memoryExists } from '../core/io.js';
3
+ import { nowISO } from '../core/ids.js';
4
+ export function applyHandoffUpdates(handoff, options = {}) {
5
+ if (options.status)
6
+ handoff.status = options.status;
7
+ if (options.to !== undefined)
8
+ handoff.to = options.to;
9
+ if (options.narrative !== undefined)
10
+ handoff.narrative = options.narrative;
11
+ const contractUpdates = {};
12
+ for (const key of ['files_touched', 'pre_conditions', 'post_conditions', 'tests_to_verify', 'linked_plans']) {
13
+ const value = options[key];
14
+ if (Array.isArray(value)) {
15
+ contractUpdates[key] = value;
16
+ }
17
+ }
18
+ if (Object.keys(contractUpdates).length > 0) {
19
+ handoff.contract = { ...handoff.contract, ...contractUpdates };
20
+ }
21
+ const hasReviewUpdate = options.reviewer !== undefined ||
22
+ options.requester !== undefined ||
23
+ options.requested_at !== undefined ||
24
+ options.review_thread_id !== undefined ||
25
+ options.review_message_id !== undefined ||
26
+ options.review_verdict !== undefined ||
27
+ options.reviewed_by !== undefined ||
28
+ options.review_summary !== undefined ||
29
+ options.blocking_issues !== undefined ||
30
+ options.suggestions !== undefined;
31
+ if (hasReviewUpdate) {
32
+ const review = { ...(handoff.review ?? {}) };
33
+ if (options.reviewer !== undefined)
34
+ review.reviewer = options.reviewer;
35
+ if (options.requester !== undefined)
36
+ review.requester = options.requester;
37
+ if (options.requested_at !== undefined)
38
+ review.requested_at = options.requested_at;
39
+ if (options.review_thread_id !== undefined)
40
+ review.thread_id = options.review_thread_id;
41
+ if (options.review_message_id !== undefined)
42
+ review.message_id = options.review_message_id;
43
+ if (options.review_verdict !== undefined)
44
+ review.verdict = options.review_verdict;
45
+ if (options.reviewed_by !== undefined)
46
+ review.reviewed_by = options.reviewed_by;
47
+ if (options.review_summary !== undefined)
48
+ review.summary = options.review_summary;
49
+ if (options.blocking_issues !== undefined)
50
+ review.blocking_issues = options.blocking_issues;
51
+ if (options.suggestions !== undefined)
52
+ review.suggestions = options.suggestions;
53
+ const reviewCompleted = options.review_verdict !== undefined ||
54
+ options.reviewed_by !== undefined ||
55
+ options.review_summary !== undefined ||
56
+ options.blocking_issues !== undefined ||
57
+ options.suggestions !== undefined;
58
+ if (reviewCompleted) {
59
+ review.reviewed_at = nowISO();
60
+ }
61
+ handoff.review = review;
62
+ }
63
+ return handoff;
64
+ }
3
65
  export function runUpdateHandoff(id, options = {}) {
4
66
  if (!memoryExists()) {
5
67
  console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
@@ -11,10 +73,7 @@ export function runUpdateHandoff(id, options = {}) {
11
73
  console.error(`Error: Handoff '${id}' not found.`);
12
74
  process.exit(1);
13
75
  }
14
- if (options.status)
15
- handoff.status = options.status;
16
- if (options.to !== undefined)
17
- handoff.to = options.to;
76
+ applyHandoffUpdates(handoff, options);
18
77
  persistState(state);
19
78
  console.log(`✔ Handoff updated: [${handoff.id}] ${handoff.from} → ${handoff.to} (${handoff.status})`);
20
79
  }
@@ -1,35 +1,28 @@
1
- import { loadState, persistState } from '../core/state.js';
2
- import { memoryExists } from '../core/io.js';
3
- import { nowISO } from '../core/ids.js';
1
+ import { requireInitialized } from '../core/guards.js';
2
+ import { updatePlan } from '../core/operations/plan.js';
4
3
  export function runUpdatePlan(id, options = {}) {
5
- if (!memoryExists(options.cwd)) {
6
- console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
4
+ const cwd = options.cwd ?? process.cwd();
5
+ requireInitialized(cwd);
6
+ if (id.startsWith('stp_')) {
7
+ console.error(`Error: '${id}' looks like a step ID, not a plan ID.`);
8
+ console.error(' Use: brainclaw complete-step <planId> <stepId>');
9
+ console.error(' Inspect the parent plan with: brainclaw plan show <planId>');
7
10
  process.exit(1);
8
11
  }
9
- const state = loadState(options.cwd);
10
- const plan = state.plan_items.find((item) => item.id === id);
11
- if (!plan) {
12
- console.error(`Error: Plan item '${id}' not found.`);
13
- process.exit(1);
12
+ try {
13
+ const result = updatePlan({
14
+ id,
15
+ status: options.status,
16
+ assignee: options.assignee,
17
+ priority: options.priority,
18
+ actualEffort: options.actualEffort,
19
+ }, cwd);
20
+ console.log(`✔ Plan item updated: [${result.id}] ${result.text}`);
14
21
  }
15
- const timestamp = nowISO();
16
- if (options.status) {
17
- plan.status = options.status;
18
- if (options.status === 'in_progress' && !plan.started_at)
19
- plan.started_at = timestamp;
20
- if (options.status === 'done' && !plan.completed_at)
21
- plan.completed_at = timestamp;
22
+ catch (error) {
23
+ const msg = error instanceof Error ? error.message : String(error);
24
+ console.error(`Error: ${msg}`);
25
+ process.exit(1);
22
26
  }
23
- if (options.assignee !== undefined)
24
- plan.assignee = options.assignee;
25
- if (options.project !== undefined)
26
- plan.project = options.project;
27
- if (options.priority)
28
- plan.priority = options.priority;
29
- if (options.actualEffort)
30
- plan.actual_effort = options.actualEffort;
31
- plan.updated_at = timestamp;
32
- persistState(state, options.cwd);
33
- console.log(`✔ Plan item updated: [${plan.id}] ${plan.text}`);
34
27
  }
35
28
  //# sourceMappingURL=update-plan.js.map
@@ -0,0 +1,37 @@
1
+ import { requireInitialized } from '../core/guards.js';
2
+ import { updateStep } from '../core/operations/plan.js';
3
+ export function runUpdateStep(planId, stepId, options) {
4
+ requireInitialized(process.cwd());
5
+ const validStatuses = ['todo', 'in_progress', 'testing', 'done', 'blocked'];
6
+ if (options.status && !validStatuses.includes(options.status)) {
7
+ console.error(`Error: Invalid status '${options.status}'. Valid: ${validStatuses.join(', ')}`);
8
+ process.exit(1);
9
+ }
10
+ try {
11
+ const result = updateStep({
12
+ planId,
13
+ stepId,
14
+ status: options.status,
15
+ text: options.text,
16
+ assignee: options.assign,
17
+ });
18
+ const changes = [];
19
+ if (options.status)
20
+ changes.push(`status=${options.status}`);
21
+ if (options.text)
22
+ changes.push('text updated');
23
+ if (options.assign !== undefined)
24
+ changes.push(`assignee=${options.assign || 'unassigned'}`);
25
+ console.log(`✔ Step updated: [${result.stepId}] ${changes.join(', ')}`);
26
+ console.log(` Plan [${result.planId}] progress: ${result.doneSteps}/${result.totalSteps} steps done`);
27
+ if (result.planAutoCompleted) {
28
+ console.log(` All steps done — plan auto-completed.`);
29
+ }
30
+ }
31
+ catch (error) {
32
+ const msg = error instanceof Error ? error.message : String(error);
33
+ console.error(`Error: ${msg}`);
34
+ process.exit(1);
35
+ }
36
+ }
37
+ //# sourceMappingURL=update-step.js.map