@soleri/core 9.2.0 → 9.3.1

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 (316) hide show
  1. package/data/flows/build.flow.yaml +8 -9
  2. package/data/flows/deliver.flow.yaml +9 -10
  3. package/data/flows/design.flow.yaml +3 -4
  4. package/data/flows/enhance.flow.yaml +5 -6
  5. package/data/flows/explore.flow.yaml +3 -4
  6. package/data/flows/fix.flow.yaml +5 -6
  7. package/data/flows/plan.flow.yaml +4 -5
  8. package/data/flows/review.flow.yaml +3 -4
  9. package/dist/curator/curator.d.ts.map +1 -1
  10. package/dist/curator/curator.js +98 -22
  11. package/dist/curator/curator.js.map +1 -1
  12. package/dist/engine/bin/soleri-engine.js.map +1 -1
  13. package/dist/engine/module-manifest.d.ts +2 -0
  14. package/dist/engine/module-manifest.d.ts.map +1 -1
  15. package/dist/engine/module-manifest.js +136 -1
  16. package/dist/engine/module-manifest.js.map +1 -1
  17. package/dist/engine/register-engine.d.ts.map +1 -1
  18. package/dist/engine/register-engine.js +25 -1
  19. package/dist/engine/register-engine.js.map +1 -1
  20. package/dist/flows/gate-evaluator.js.map +1 -1
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/operator/operator-profile.d.ts.map +1 -1
  26. package/dist/operator/operator-profile.js +11 -5
  27. package/dist/operator/operator-profile.js.map +1 -1
  28. package/dist/operator/operator-signals.d.ts.map +1 -1
  29. package/dist/operator/operator-signals.js.map +1 -1
  30. package/dist/planning/evidence-collector.js.map +1 -1
  31. package/dist/planning/gap-passes.d.ts.map +1 -1
  32. package/dist/planning/gap-passes.js +23 -6
  33. package/dist/planning/gap-passes.js.map +1 -1
  34. package/dist/planning/gap-patterns.d.ts.map +1 -1
  35. package/dist/planning/gap-patterns.js +57 -11
  36. package/dist/planning/gap-patterns.js.map +1 -1
  37. package/dist/planning/github-projection.d.ts.map +1 -1
  38. package/dist/planning/github-projection.js +39 -20
  39. package/dist/planning/github-projection.js.map +1 -1
  40. package/dist/planning/impact-analyzer.d.ts.map +1 -1
  41. package/dist/planning/impact-analyzer.js +20 -18
  42. package/dist/planning/impact-analyzer.js.map +1 -1
  43. package/dist/planning/plan-lifecycle.d.ts.map +1 -1
  44. package/dist/planning/plan-lifecycle.js +22 -9
  45. package/dist/planning/plan-lifecycle.js.map +1 -1
  46. package/dist/planning/planner.d.ts.map +1 -1
  47. package/dist/planning/planner.js +60 -17
  48. package/dist/planning/planner.js.map +1 -1
  49. package/dist/planning/rationalization-detector.d.ts.map +1 -1
  50. package/dist/planning/rationalization-detector.js.map +1 -1
  51. package/dist/planning/reconciliation-engine.d.ts.map +1 -1
  52. package/dist/planning/reconciliation-engine.js.map +1 -1
  53. package/dist/planning/task-complexity-assessor.d.ts +42 -0
  54. package/dist/planning/task-complexity-assessor.d.ts.map +1 -0
  55. package/dist/planning/task-complexity-assessor.js +132 -0
  56. package/dist/planning/task-complexity-assessor.js.map +1 -0
  57. package/dist/planning/task-verifier.d.ts.map +1 -1
  58. package/dist/planning/task-verifier.js +14 -6
  59. package/dist/planning/task-verifier.js.map +1 -1
  60. package/dist/runtime/admin-ops.d.ts.map +1 -1
  61. package/dist/runtime/admin-ops.js +18 -0
  62. package/dist/runtime/admin-ops.js.map +1 -1
  63. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  64. package/dist/runtime/admin-setup-ops.js +2 -1
  65. package/dist/runtime/admin-setup-ops.js.map +1 -1
  66. package/dist/runtime/branching-ops.d.ts +12 -0
  67. package/dist/runtime/branching-ops.d.ts.map +1 -0
  68. package/dist/runtime/branching-ops.js +100 -0
  69. package/dist/runtime/branching-ops.js.map +1 -0
  70. package/dist/runtime/context-health.d.ts.map +1 -1
  71. package/dist/runtime/context-health.js.map +1 -1
  72. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  73. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  74. package/dist/runtime/facades/branching-facade.js +8 -0
  75. package/dist/runtime/facades/branching-facade.js.map +1 -0
  76. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -1
  77. package/dist/runtime/facades/chat-service-ops.js +3 -1
  78. package/dist/runtime/facades/chat-service-ops.js.map +1 -1
  79. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -1
  80. package/dist/runtime/facades/chat-transport-ops.js.map +1 -1
  81. package/dist/runtime/facades/index.d.ts.map +1 -1
  82. package/dist/runtime/facades/index.js +42 -0
  83. package/dist/runtime/facades/index.js.map +1 -1
  84. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  85. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  86. package/dist/runtime/facades/intake-facade.js +11 -0
  87. package/dist/runtime/facades/intake-facade.js.map +1 -0
  88. package/dist/runtime/facades/links-facade.d.ts +9 -0
  89. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  90. package/dist/runtime/facades/links-facade.js +10 -0
  91. package/dist/runtime/facades/links-facade.js.map +1 -0
  92. package/dist/runtime/facades/operator-facade.d.ts.map +1 -1
  93. package/dist/runtime/facades/operator-facade.js.map +1 -1
  94. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  95. package/dist/runtime/facades/plan-facade.js +4 -1
  96. package/dist/runtime/facades/plan-facade.js.map +1 -1
  97. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  98. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  99. package/dist/runtime/facades/tier-facade.js +8 -0
  100. package/dist/runtime/facades/tier-facade.js.map +1 -0
  101. package/dist/runtime/facades/vault-facade.d.ts +9 -1
  102. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  103. package/dist/runtime/facades/vault-facade.js +44 -187
  104. package/dist/runtime/facades/vault-facade.js.map +1 -1
  105. package/dist/runtime/github-integration.d.ts.map +1 -1
  106. package/dist/runtime/github-integration.js +11 -4
  107. package/dist/runtime/github-integration.js.map +1 -1
  108. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  109. package/dist/runtime/orchestrate-ops.js +75 -42
  110. package/dist/runtime/orchestrate-ops.js.map +1 -1
  111. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  112. package/dist/runtime/planning-extra-ops.js.map +1 -1
  113. package/dist/runtime/runtime.d.ts.map +1 -1
  114. package/dist/runtime/runtime.js +3 -1
  115. package/dist/runtime/runtime.js.map +1 -1
  116. package/dist/runtime/session-briefing.d.ts.map +1 -1
  117. package/dist/runtime/session-briefing.js +5 -1
  118. package/dist/runtime/session-briefing.js.map +1 -1
  119. package/dist/runtime/tier-ops.d.ts +13 -0
  120. package/dist/runtime/tier-ops.d.ts.map +1 -0
  121. package/dist/runtime/tier-ops.js +110 -0
  122. package/dist/runtime/tier-ops.js.map +1 -0
  123. package/dist/skills/sync-skills.d.ts.map +1 -1
  124. package/dist/skills/sync-skills.js +1 -1
  125. package/dist/skills/sync-skills.js.map +1 -1
  126. package/dist/vault/linking.d.ts.map +1 -1
  127. package/dist/vault/linking.js +41 -5
  128. package/dist/vault/linking.js.map +1 -1
  129. package/dist/vault/vault-entries.d.ts.map +1 -1
  130. package/dist/vault/vault-entries.js +68 -26
  131. package/dist/vault/vault-entries.js.map +1 -1
  132. package/dist/vault/vault-maintenance.d.ts.map +1 -1
  133. package/dist/vault/vault-maintenance.js +6 -2
  134. package/dist/vault/vault-maintenance.js.map +1 -1
  135. package/dist/vault/vault-markdown-sync.d.ts.map +1 -1
  136. package/dist/vault/vault-markdown-sync.js.map +1 -1
  137. package/dist/vault/vault-memories.d.ts.map +1 -1
  138. package/dist/vault/vault-memories.js +3 -1
  139. package/dist/vault/vault-memories.js.map +1 -1
  140. package/dist/vault/vault-schema.js +36 -10
  141. package/dist/vault/vault-schema.js.map +1 -1
  142. package/dist/vault/vault.d.ts.map +1 -1
  143. package/dist/vault/vault.js +5 -1
  144. package/dist/vault/vault.js.map +1 -1
  145. package/package.json +7 -7
  146. package/src/agency/agency-manager.test.ts +60 -40
  147. package/src/agency/default-rules.test.ts +17 -9
  148. package/src/capabilities/registry.test.ts +2 -12
  149. package/src/chat/agent-loop.test.ts +33 -43
  150. package/src/chat/mcp-bridge.test.ts +7 -2
  151. package/src/claudemd/inject.test.ts +2 -12
  152. package/src/context/context-engine.test.ts +96 -51
  153. package/src/control/intent-router.test.ts +3 -3
  154. package/src/curator/classifier.test.ts +14 -8
  155. package/src/curator/contradiction-detector.test.ts +30 -5
  156. package/src/curator/curator.ts +278 -56
  157. package/src/curator/duplicate-detector.test.ts +77 -15
  158. package/src/curator/quality-gate.test.ts +71 -31
  159. package/src/curator/tag-manager.test.ts +12 -4
  160. package/src/domain-packs/knowledge-installer.test.ts +2 -10
  161. package/src/domain-packs/token-resolver.test.ts +1 -3
  162. package/src/domain-packs/types.test.ts +16 -2
  163. package/src/enforcement/registry.test.ts +2 -8
  164. package/src/engine/bin/soleri-engine.ts +3 -1
  165. package/src/engine/module-manifest.test.ts +48 -4
  166. package/src/engine/module-manifest.ts +138 -1
  167. package/src/engine/register-engine.test.ts +6 -1
  168. package/src/engine/register-engine.ts +26 -3
  169. package/src/errors/classify.test.ts +6 -2
  170. package/src/errors/retry.test.ts +1 -4
  171. package/src/facades/facade-factory.test.ts +110 -64
  172. package/src/flows/epilogue.test.ts +16 -10
  173. package/src/flows/gate-evaluator.test.ts +12 -6
  174. package/src/flows/gate-evaluator.ts +1 -3
  175. package/src/governance/governance.test.ts +137 -21
  176. package/src/health/health-registry.test.ts +8 -1
  177. package/src/index.ts +8 -0
  178. package/src/intake/content-classifier.test.ts +121 -51
  179. package/src/intake/dedup-gate.test.ts +38 -22
  180. package/src/intake/intake-pipeline.test.ts +5 -3
  181. package/src/intake/text-ingester.test.ts +26 -20
  182. package/src/llm/key-pool.test.ts +1 -3
  183. package/src/llm/llm-client.test.ts +1 -4
  184. package/src/llm/oauth-discovery.test.ts +16 -16
  185. package/src/llm/utils.test.ts +62 -18
  186. package/src/logging/logger.test.ts +4 -1
  187. package/src/loop/loop-manager.test.ts +2 -6
  188. package/src/migrations/migration-runner.edge-cases.test.ts +2 -7
  189. package/src/operator/operator-profile-extended.test.ts +15 -5
  190. package/src/operator/operator-profile.test.ts +26 -8
  191. package/src/operator/operator-profile.ts +38 -22
  192. package/src/operator/operator-signals-extended.test.ts +35 -23
  193. package/src/operator/operator-signals.test.ts +6 -10
  194. package/src/operator/operator-signals.ts +2 -1
  195. package/src/operator/prompts/hook-precompact-operator-dispatch.md +10 -6
  196. package/src/operator/prompts/subagent-soft-signal-extractor.md +5 -0
  197. package/src/operator/prompts/subagent-synthesis-cognition.md +19 -10
  198. package/src/operator/prompts/subagent-synthesis-communication.md +13 -7
  199. package/src/operator/prompts/subagent-synthesis-technical.md +19 -9
  200. package/src/operator/prompts/subagent-synthesis-trust.md +27 -21
  201. package/src/persona/defaults.test.ts +1 -5
  202. package/src/planning/evidence-collector.test.ts +147 -38
  203. package/src/planning/evidence-collector.ts +1 -4
  204. package/src/planning/gap-analysis-alternatives.test.ts +41 -11
  205. package/src/planning/gap-passes.test.ts +215 -33
  206. package/src/planning/gap-passes.ts +115 -46
  207. package/src/planning/gap-patterns.test.ts +87 -13
  208. package/src/planning/gap-patterns.ts +114 -31
  209. package/src/planning/github-projection.test.ts +6 -1
  210. package/src/planning/github-projection.ts +41 -20
  211. package/src/planning/impact-analyzer.test.ts +10 -23
  212. package/src/planning/impact-analyzer.ts +33 -46
  213. package/src/planning/plan-lifecycle.test.ts +103 -36
  214. package/src/planning/plan-lifecycle.ts +49 -18
  215. package/src/planning/planner.test.ts +12 -2
  216. package/src/planning/planner.ts +198 -58
  217. package/src/planning/rationalization-detector.test.ts +5 -20
  218. package/src/planning/rationalization-detector.ts +14 -16
  219. package/src/planning/reconciliation-engine.test.ts +20 -3
  220. package/src/planning/reconciliation-engine.ts +1 -2
  221. package/src/planning/task-complexity-assessor.test.ts +298 -0
  222. package/src/planning/task-complexity-assessor.ts +183 -0
  223. package/src/planning/task-verifier.test.ts +59 -27
  224. package/src/planning/task-verifier.ts +15 -9
  225. package/src/playbooks/playbook-executor.test.ts +1 -3
  226. package/src/plugins/plugin-loader.test.ts +19 -14
  227. package/src/plugins/plugin-registry.test.ts +45 -33
  228. package/src/project/project-registry.test.ts +23 -12
  229. package/src/prompts/template-manager.test.ts +4 -1
  230. package/src/queue/job-queue.test.ts +10 -14
  231. package/src/runtime/admin-extra-ops.test.ts +5 -19
  232. package/src/runtime/admin-ops.test.ts +22 -1
  233. package/src/runtime/admin-ops.ts +19 -0
  234. package/src/runtime/admin-setup-ops.test.ts +3 -4
  235. package/src/runtime/admin-setup-ops.ts +9 -2
  236. package/src/runtime/archive-ops.test.ts +4 -1
  237. package/src/runtime/branching-ops.test.ts +144 -0
  238. package/src/runtime/branching-ops.ts +107 -0
  239. package/src/runtime/capture-ops.test.ts +7 -21
  240. package/src/runtime/chain-ops.test.ts +16 -6
  241. package/src/runtime/claude-md-helpers.test.ts +1 -3
  242. package/src/runtime/context-health.test.ts +1 -3
  243. package/src/runtime/context-health.ts +1 -3
  244. package/src/runtime/curator-extra-ops.test.ts +3 -1
  245. package/src/runtime/domain-ops.test.ts +46 -36
  246. package/src/runtime/facades/admin-facade.test.ts +1 -4
  247. package/src/runtime/facades/archive-facade.test.ts +21 -7
  248. package/src/runtime/facades/brain-facade.test.ts +176 -72
  249. package/src/runtime/facades/branching-facade.test.ts +43 -0
  250. package/src/runtime/facades/branching-facade.ts +11 -0
  251. package/src/runtime/facades/chat-facade.test.ts +81 -28
  252. package/src/runtime/facades/chat-service-ops.test.ts +178 -73
  253. package/src/runtime/facades/chat-service-ops.ts +3 -1
  254. package/src/runtime/facades/chat-session-ops.test.ts +25 -10
  255. package/src/runtime/facades/chat-transport-ops.test.ts +101 -34
  256. package/src/runtime/facades/chat-transport-ops.ts +0 -1
  257. package/src/runtime/facades/context-facade.test.ts +19 -4
  258. package/src/runtime/facades/control-facade.test.ts +3 -3
  259. package/src/runtime/facades/index.ts +42 -0
  260. package/src/runtime/facades/intake-facade.test.ts +215 -0
  261. package/src/runtime/facades/intake-facade.ts +14 -0
  262. package/src/runtime/facades/links-facade.test.ts +203 -0
  263. package/src/runtime/facades/links-facade.ts +13 -0
  264. package/src/runtime/facades/loop-facade.test.ts +22 -5
  265. package/src/runtime/facades/memory-facade.test.ts +19 -5
  266. package/src/runtime/facades/operator-facade.test.ts +17 -4
  267. package/src/runtime/facades/operator-facade.ts +11 -3
  268. package/src/runtime/facades/orchestrate-facade.test.ts +7 -1
  269. package/src/runtime/facades/plan-facade.test.ts +29 -12
  270. package/src/runtime/facades/plan-facade.ts +7 -2
  271. package/src/runtime/facades/tier-facade.test.ts +47 -0
  272. package/src/runtime/facades/tier-facade.ts +11 -0
  273. package/src/runtime/facades/vault-facade.test.ts +174 -242
  274. package/src/runtime/facades/vault-facade.ts +55 -199
  275. package/src/runtime/github-integration.ts +11 -8
  276. package/src/runtime/grading-ops.test.ts +39 -8
  277. package/src/runtime/intake-ops.test.ts +69 -16
  278. package/src/runtime/loop-ops.test.ts +16 -6
  279. package/src/runtime/memory-cross-project-ops.test.ts +25 -14
  280. package/src/runtime/orchestrate-ops.test.ts +204 -0
  281. package/src/runtime/orchestrate-ops.ts +103 -65
  282. package/src/runtime/pack-ops.test.ts +23 -6
  283. package/src/runtime/planning-extra-ops.test.ts +17 -7
  284. package/src/runtime/planning-extra-ops.ts +3 -1
  285. package/src/runtime/playbook-ops.test.ts +26 -3
  286. package/src/runtime/plugin-ops.test.ts +83 -25
  287. package/src/runtime/project-ops.test.ts +26 -6
  288. package/src/runtime/runtime.ts +3 -1
  289. package/src/runtime/session-briefing.test.ts +183 -54
  290. package/src/runtime/session-briefing.ts +8 -2
  291. package/src/runtime/sync-ops.test.ts +3 -12
  292. package/src/runtime/telemetry-ops.test.ts +31 -6
  293. package/src/runtime/tier-ops.test.ts +159 -0
  294. package/src/runtime/tier-ops.ts +119 -0
  295. package/src/runtime/vault-extra-ops.test.ts +32 -8
  296. package/src/runtime/vault-sharing-ops.test.ts +1 -4
  297. package/src/skills/sync-skills.ts +2 -12
  298. package/src/transport/ws-server.test.ts +7 -4
  299. package/src/vault/__tests__/vault-characterization.test.ts +492 -81
  300. package/src/vault/linking.test.ts +50 -17
  301. package/src/vault/linking.ts +48 -7
  302. package/src/vault/obsidian-sync.test.ts +6 -3
  303. package/src/vault/scope-detector.test.ts +1 -3
  304. package/src/vault/vault-branching.test.ts +9 -7
  305. package/src/vault/vault-entries.ts +209 -65
  306. package/src/vault/vault-maintenance.ts +7 -12
  307. package/src/vault/vault-manager.test.ts +10 -10
  308. package/src/vault/vault-markdown-sync.ts +4 -1
  309. package/src/vault/vault-memories.ts +7 -7
  310. package/src/vault/vault-scaling.test.ts +5 -5
  311. package/src/vault/vault-schema.ts +72 -15
  312. package/src/vault/vault.ts +55 -9
  313. package/src/brain/strength-scorer.ts +0 -404
  314. package/src/engine/index.ts +0 -21
  315. package/src/persona/index.ts +0 -9
  316. package/src/vault/vault-interfaces.ts +0 -56
@@ -8,9 +8,7 @@ function makeMockRuntime(overrides: Record<string, unknown> = {}) {
8
8
  vault: {
9
9
  get: vi.fn().mockReturnValue({ id: 'e1', tags: ['pattern'] }),
10
10
  update: vi.fn(),
11
- searchMemories: vi.fn().mockReturnValue([
12
- { id: 'm1', content: 'memory 1' },
13
- ]),
11
+ searchMemories: vi.fn().mockReturnValue([{ id: 'm1', content: 'memory 1' }]),
14
12
  search: vi.fn().mockReturnValue([
15
13
  { entry: { id: 'g1', tags: ['_global'] }, score: 0.9 },
16
14
  { entry: { id: 'g2', tags: ['other'] }, score: 0.8 },
@@ -25,9 +23,9 @@ function makeMockRuntime(overrides: Record<string, unknown> = {}) {
25
23
  metadata: { memoryConfig: { crossProjectEnabled: true, extraPaths: ['/extra'] } },
26
24
  }),
27
25
  register: vi.fn(),
28
- getLinkedProjects: vi.fn().mockReturnValue([
29
- { project: { path: '/linked', name: 'linked-project' } },
30
- ]),
26
+ getLinkedProjects: vi
27
+ .fn()
28
+ .mockReturnValue([{ project: { path: '/linked', name: 'linked-project' } }]),
31
29
  },
32
30
  } as unknown as AgentRuntime;
33
31
  }
@@ -60,7 +58,9 @@ describe('createMemoryCrossProjectOps', () => {
60
58
  it('promotes entry by adding _global tag', async () => {
61
59
  const runtime = makeMockRuntime();
62
60
  ops = createMemoryCrossProjectOps(runtime);
63
- const result = (await findOp('memory_promote_to_global').handler({ entryId: 'e1' })) as Record<string, unknown>;
61
+ const result = (await findOp('memory_promote_to_global').handler({
62
+ entryId: 'e1',
63
+ })) as Record<string, unknown>;
64
64
  expect(result.promoted).toBe(true);
65
65
  expect(result.tags).toContain('_global');
66
66
  expect(runtime.vault.update).toHaveBeenCalledWith('e1', { tags: ['pattern', '_global'] });
@@ -69,7 +69,9 @@ describe('createMemoryCrossProjectOps', () => {
69
69
  it('returns error when entry not found', async () => {
70
70
  const runtime = makeMockRuntime({ get: vi.fn().mockReturnValue(null) });
71
71
  ops = createMemoryCrossProjectOps(runtime);
72
- const result = (await findOp('memory_promote_to_global').handler({ entryId: 'missing' })) as Record<string, unknown>;
72
+ const result = (await findOp('memory_promote_to_global').handler({
73
+ entryId: 'missing',
74
+ })) as Record<string, unknown>;
73
75
  expect(result.promoted).toBe(false);
74
76
  expect(result.error).toContain('Entry not found');
75
77
  });
@@ -79,7 +81,9 @@ describe('createMemoryCrossProjectOps', () => {
79
81
  get: vi.fn().mockReturnValue({ id: 'e1', tags: ['_global'] }),
80
82
  });
81
83
  ops = createMemoryCrossProjectOps(runtime);
82
- const result = (await findOp('memory_promote_to_global').handler({ entryId: 'e1' })) as Record<string, unknown>;
84
+ const result = (await findOp('memory_promote_to_global').handler({
85
+ entryId: 'e1',
86
+ })) as Record<string, unknown>;
83
87
  expect(result.promoted).toBe(false);
84
88
  expect(result.message).toContain('already promoted');
85
89
  });
@@ -132,12 +136,15 @@ describe('createMemoryCrossProjectOps', () => {
132
136
  const runtime = makeMockRuntime();
133
137
  ops = createMemoryCrossProjectOps(runtime);
134
138
  await findOp('memory_cross_project_search').handler({
135
- query: 'test', projectPath: '/test',
139
+ query: 'test',
140
+ projectPath: '/test',
136
141
  });
137
142
 
138
143
  // Should search current project, linked project, and extra path
139
144
  const searchCalls = (runtime.vault.searchMemories as ReturnType<typeof vi.fn>).mock.calls;
140
- const searchedPaths = searchCalls.map((c: unknown[]) => (c[1] as Record<string, unknown>).projectPath);
145
+ const searchedPaths = searchCalls.map(
146
+ (c: unknown[]) => (c[1] as Record<string, unknown>).projectPath,
147
+ );
141
148
  expect(searchedPaths).toContain('/test');
142
149
  expect(searchedPaths).toContain('/linked');
143
150
  expect(searchedPaths).toContain('/extra');
@@ -151,7 +158,8 @@ describe('createMemoryCrossProjectOps', () => {
151
158
  ]);
152
159
  ops = createMemoryCrossProjectOps(runtime);
153
160
  const result = (await findOp('memory_cross_project_search').handler({
154
- query: 'test', projectPath: '/test',
161
+ query: 'test',
162
+ projectPath: '/test',
155
163
  })) as Record<string, unknown>;
156
164
 
157
165
  // linkedMemories should be empty because m1 was already in current results
@@ -162,12 +170,15 @@ describe('createMemoryCrossProjectOps', () => {
162
170
  it('skips linked search when cross-project disabled', async () => {
163
171
  const runtime = makeMockRuntime();
164
172
  (runtime.projectRegistry.getByPath as ReturnType<typeof vi.fn>).mockReturnValue({
165
- id: 'proj-1', name: 'test', path: '/test',
173
+ id: 'proj-1',
174
+ name: 'test',
175
+ path: '/test',
166
176
  metadata: { memoryConfig: { crossProjectEnabled: false } },
167
177
  });
168
178
  ops = createMemoryCrossProjectOps(runtime);
169
179
  const result = (await findOp('memory_cross_project_search').handler({
170
- query: 'test', projectPath: '/test',
180
+ query: 'test',
181
+ projectPath: '/test',
171
182
  })) as Record<string, unknown>;
172
183
 
173
184
  expect(runtime.projectRegistry.getLinkedProjects).not.toHaveBeenCalled();
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { createOrchestrateOps } from './orchestrate-ops.js';
3
+ import { assessTaskComplexity } from '../planning/task-complexity-assessor.js';
3
4
  import type { AgentRuntime } from './types.js';
4
5
 
5
6
  // ---------------------------------------------------------------------------
@@ -241,6 +242,73 @@ describe('createOrchestrateOps', () => {
241
242
  await op.handler({ planId: 'plan-1', sessionId: 'session-1' });
242
243
  expect(rt.brainIntelligence.extractKnowledge).toHaveBeenCalledWith('session-1');
243
244
  });
245
+
246
+ it('works without a preceding plan', async () => {
247
+ const op = findOp(ops, 'orchestrate_complete');
248
+ const result = (await op.handler({
249
+ sessionId: 'session-1',
250
+ outcome: 'completed',
251
+ summary: 'Fixed a typo in the README',
252
+ })) as Record<string, unknown>;
253
+
254
+ // Should not call planner.complete
255
+ expect(rt.planner.complete).not.toHaveBeenCalled();
256
+
257
+ // Should return a lightweight completion record
258
+ const plan = result.plan as Record<string, unknown>;
259
+ expect(plan.status).toBe('completed');
260
+ expect(plan.objective).toBe('Fixed a typo in the README');
261
+ });
262
+
263
+ it('captures knowledge even without plan', async () => {
264
+ const op = findOp(ops, 'orchestrate_complete');
265
+ await op.handler({
266
+ sessionId: 'session-1',
267
+ summary: 'Refactored utility function',
268
+ });
269
+
270
+ // Brain session end and knowledge extraction still run
271
+ expect(rt.brainIntelligence.lifecycle).toHaveBeenCalledWith(
272
+ expect.objectContaining({ action: 'end', sessionId: 'session-1' }),
273
+ );
274
+ expect(rt.brainIntelligence.extractKnowledge).toHaveBeenCalledWith('session-1');
275
+ });
276
+
277
+ it('skips anti-rationalization gate when no criteria', async () => {
278
+ const { detectRationalizations } = await import('../planning/rationalization-detector.js');
279
+ const op = findOp(ops, 'orchestrate_complete');
280
+
281
+ await op.handler({
282
+ sessionId: 'session-1',
283
+ outcome: 'completed',
284
+ summary: 'This was basically done already',
285
+ });
286
+
287
+ // detectRationalizations should never be called since there are no criteria
288
+ expect(detectRationalizations).not.toHaveBeenCalled();
289
+ // Should still complete successfully
290
+ expect(rt.brainIntelligence.lifecycle).toHaveBeenCalled();
291
+ });
292
+
293
+ it('still runs brain session end without plan', async () => {
294
+ const op = findOp(ops, 'orchestrate_complete');
295
+ const result = (await op.handler({
296
+ sessionId: 'session-1',
297
+ outcome: 'completed',
298
+ toolsUsed: ['grep', 'edit'],
299
+ filesModified: [],
300
+ })) as Record<string, unknown>;
301
+
302
+ expect(rt.brainIntelligence.lifecycle).toHaveBeenCalledWith(
303
+ expect.objectContaining({
304
+ action: 'end',
305
+ sessionId: 'session-1',
306
+ planOutcome: 'completed',
307
+ toolsUsed: ['grep', 'edit'],
308
+ }),
309
+ );
310
+ expect(result.session).toBeDefined();
311
+ });
244
312
  });
245
313
 
246
314
  // ─── orchestrate_status ───────────────────────────────────────
@@ -299,4 +367,140 @@ describe('createOrchestrateOps', () => {
299
367
  await expect(op.handler({ planId: 'missing' })).rejects.toThrow('not found');
300
368
  });
301
369
  });
370
+
371
+ // ─── task auto-assessment routing ────────────────────────────
372
+ //
373
+ // Integration-style tests that verify the full assess → route → complete flow:
374
+ // 1. Use TaskComplexityAssessor to classify the task
375
+ // 2. Route to direct execution (simple) or planning (complex)
376
+ // 3. Complete via orchestrate_complete in both paths
377
+
378
+ describe('task auto-assessment routing', () => {
379
+ it('simple task routes to direct execution + complete', async () => {
380
+ // Step 1: Assess — "fix typo in README" should be simple
381
+ const assessment = assessTaskComplexity({ prompt: 'fix typo in README' });
382
+ expect(assessment.classification).toBe('simple');
383
+
384
+ // Step 2: Skip planning, go straight to complete without a planId
385
+ const completeOp = findOp(ops, 'orchestrate_complete');
386
+ const result = (await completeOp.handler({
387
+ sessionId: 'session-simple',
388
+ outcome: 'completed',
389
+ summary: 'Fixed typo in README',
390
+ })) as Record<string, unknown>;
391
+
392
+ // Should not touch the planner at all
393
+ expect(rt.planner.complete).not.toHaveBeenCalled();
394
+
395
+ // Should still produce a valid completion record
396
+ const plan = result.plan as Record<string, unknown>;
397
+ expect(plan.status).toBe('completed');
398
+ expect(plan.objective).toBe('Fixed typo in README');
399
+
400
+ // Knowledge should still be captured
401
+ expect(rt.brainIntelligence.extractKnowledge).toHaveBeenCalledWith('session-simple');
402
+ });
403
+
404
+ it('complex task routes through planning + complete', async () => {
405
+ // Step 1: Assess — cross-cutting auth task should be complex
406
+ const assessment = assessTaskComplexity({
407
+ prompt: 'add authentication across all API routes',
408
+ filesEstimated: 8,
409
+ });
410
+ expect(assessment.classification).toBe('complex');
411
+
412
+ // Step 2: Create a plan via orchestrate_plan
413
+ const planOp = findOp(ops, 'orchestrate_plan');
414
+ const planResult = (await planOp.handler({
415
+ prompt: 'add authentication across all API routes',
416
+ })) as Record<string, unknown>;
417
+ expect(planResult).toHaveProperty('plan');
418
+ expect(planResult).toHaveProperty('flow');
419
+
420
+ // Step 3: Complete with the planId
421
+ const completeOp = findOp(ops, 'orchestrate_complete');
422
+ const result = (await completeOp.handler({
423
+ planId: 'plan-1',
424
+ sessionId: 'session-complex',
425
+ outcome: 'completed',
426
+ summary: 'Added authentication middleware to all API routes',
427
+ })) as Record<string, unknown>;
428
+
429
+ // Should complete via the planner lifecycle
430
+ expect(rt.planner.complete).toHaveBeenCalledWith('plan-1');
431
+
432
+ // Knowledge should be captured
433
+ expect(rt.brainIntelligence.lifecycle).toHaveBeenCalledWith(
434
+ expect.objectContaining({ action: 'end', sessionId: 'session-complex' }),
435
+ );
436
+ expect(rt.brainIntelligence.extractKnowledge).toHaveBeenCalledWith('session-complex');
437
+
438
+ // Plan should be marked completed
439
+ const completedPlan = result.plan as Record<string, unknown>;
440
+ expect(completedPlan.status).toBe('completed');
441
+ });
442
+
443
+ it('orchestrate_complete captures knowledge in both paths', async () => {
444
+ const completeOp = findOp(ops, 'orchestrate_complete');
445
+
446
+ // ── Simple path (no planId) ──
447
+ vi.clearAllMocks();
448
+ rt = mockRuntime();
449
+ ops = createOrchestrateOps(rt);
450
+
451
+ await findOp(ops, 'orchestrate_complete').handler({
452
+ sessionId: 'session-simple',
453
+ outcome: 'completed',
454
+ summary: 'Renamed a variable',
455
+ });
456
+
457
+ // Brain session end called
458
+ expect(rt.brainIntelligence.lifecycle).toHaveBeenCalledWith(
459
+ expect.objectContaining({ action: 'end', sessionId: 'session-simple' }),
460
+ );
461
+ // Knowledge extraction called
462
+ expect(rt.brainIntelligence.extractKnowledge).toHaveBeenCalledWith('session-simple');
463
+ // Planner.complete NOT called (no plan)
464
+ expect(rt.planner.complete).not.toHaveBeenCalled();
465
+
466
+ // ── Complex path (with planId) ──
467
+ vi.clearAllMocks();
468
+ rt = mockRuntime();
469
+ ops = createOrchestrateOps(rt);
470
+
471
+ await findOp(ops, 'orchestrate_complete').handler({
472
+ planId: 'plan-1',
473
+ sessionId: 'session-complex',
474
+ outcome: 'completed',
475
+ summary: 'Implemented full auth layer',
476
+ });
477
+
478
+ // Brain session end called
479
+ expect(rt.brainIntelligence.lifecycle).toHaveBeenCalledWith(
480
+ expect.objectContaining({ action: 'end', sessionId: 'session-complex' }),
481
+ );
482
+ // Knowledge extraction called
483
+ expect(rt.brainIntelligence.extractKnowledge).toHaveBeenCalledWith('session-complex');
484
+ // Planner.complete IS called (has plan)
485
+ expect(rt.planner.complete).toHaveBeenCalledWith('plan-1');
486
+ });
487
+
488
+ it('assessment result includes non-empty reasoning for simple tasks', () => {
489
+ const result = assessTaskComplexity({ prompt: 'fix typo in README' });
490
+ expect(result.classification).toBe('simple');
491
+ expect(typeof result.reasoning).toBe('string');
492
+ expect(result.reasoning.length).toBeGreaterThan(0);
493
+ });
494
+
495
+ it('assessment result includes non-empty reasoning for complex tasks', () => {
496
+ const result = assessTaskComplexity({
497
+ prompt: 'add authentication across all API routes',
498
+ filesEstimated: 8,
499
+ domains: ['auth', 'api', 'middleware'],
500
+ });
501
+ expect(result.classification).toBe('complex');
502
+ expect(typeof result.reasoning).toBe('string');
503
+ expect(result.reasoning.length).toBeGreaterThan(0);
504
+ });
505
+ });
302
506
  });
@@ -182,10 +182,7 @@ function buildHealthWarning(
182
182
  * Collect all acceptance criteria from a plan's tasks.
183
183
  * Returns empty array if plan not found or has no criteria (graceful skip).
184
184
  */
185
- function collectAcceptanceCriteria(
186
- plannerRef: AgentRuntime['planner'],
187
- planId: string,
188
- ): string[] {
185
+ function collectAcceptanceCriteria(plannerRef: AgentRuntime['planner'], planId: string): string[] {
189
186
  const plan = plannerRef.get(planId);
190
187
  if (!plan) return [];
191
188
  const criteria: string[] = [];
@@ -210,7 +207,8 @@ function captureRationalizationAntiPattern(
210
207
  vaultRef.add({
211
208
  id: `antipattern-rationalization-${Date.now()}`,
212
209
  title: 'Rationalization detected in completion claim',
213
- description: `Detected rationalization patterns: ${patterns}. ` +
210
+ description:
211
+ `Detected rationalization patterns: ${patterns}. ` +
214
212
  `Items: ${report.items.map((i) => `"${i.phrase}" (${i.pattern})`).join('; ')}.`,
215
213
  type: 'anti-pattern',
216
214
  domain: 'planning',
@@ -247,7 +245,10 @@ export function createOrchestrateOps(
247
245
  'a pruned orchestration plan with gate-guarded steps.',
248
246
  auth: 'write',
249
247
  schema: z.object({
250
- prompt: z.string().optional().describe('Natural language description of what to do (or use objective)'),
248
+ prompt: z
249
+ .string()
250
+ .optional()
251
+ .describe('Natural language description of what to do (or use objective)'),
251
252
  projectPath: z.string().optional().default('.').describe('Project root path'),
252
253
  // Legacy params — still accepted for backward compat
253
254
  objective: z.string().optional().describe('(Legacy) Plan objective — use prompt instead'),
@@ -277,9 +278,10 @@ export function createOrchestrateOps(
277
278
  recommendations = raw.map((r) => {
278
279
  // Look up vault entry ID by title for feedback tracking
279
280
  const entries = vault.search(r.pattern, { limit: 1 });
280
- const entryId = entries.length > 0 && entries[0].entry.title === r.pattern
281
- ? entries[0].entry.id
282
- : undefined;
281
+ const entryId =
282
+ entries.length > 0 && entries[0].entry.title === r.pattern
283
+ ? entries[0].entry.id
284
+ : undefined;
283
285
  return { pattern: r.pattern, strength: r.strength, entryId };
284
286
  });
285
287
  } catch {
@@ -307,12 +309,10 @@ export function createOrchestrateOps(
307
309
  planStore.set(plan.planId, { plan, createdAt: Date.now() });
308
310
 
309
311
  // 5. Also create a planner plan for lifecycle tracking (backward compat)
310
- const decisions = recommendations.map(
311
- (r) => {
312
- const base = `Brain pattern: ${r.pattern} (strength: ${r.strength.toFixed(1)})`;
313
- return r.entryId ? `${base} [entryId:${r.entryId}]` : base;
314
- },
315
- );
312
+ const decisions = recommendations.map((r) => {
313
+ const base = `Brain pattern: ${r.pattern} (strength: ${r.strength.toFixed(1)})`;
314
+ return r.entryId ? `${base} [entryId:${r.entryId}]` : base;
315
+ });
316
316
  const tasks = (params.tasks as Array<{ title: string; description: string }>) ?? [];
317
317
 
318
318
  // 5b. Extract GitHub issue context if prompt references #NNN
@@ -333,7 +333,8 @@ export function createOrchestrateOps(
333
333
  }
334
334
  }
335
335
 
336
- const planObjective = (params as Record<string, unknown>)._enrichedObjective as string | undefined ?? prompt;
336
+ const planObjective =
337
+ ((params as Record<string, unknown>)._enrichedObjective as string | undefined) ?? prompt;
337
338
 
338
339
  let legacyPlan;
339
340
  try {
@@ -471,22 +472,32 @@ export function createOrchestrateOps(
471
472
  'end brain session, and clean up.',
472
473
  auth: 'write',
473
474
  schema: z.object({
474
- planId: z.string().describe('ID of the executing plan to complete'),
475
+ planId: z.string().optional().describe('ID of the executing plan to complete (optional for direct tasks)'),
475
476
  sessionId: z.string().describe('ID of the brain session to end'),
476
477
  outcome: z
477
478
  .enum(['completed', 'abandoned', 'partial'])
478
479
  .optional()
479
480
  .default('completed')
480
481
  .describe('Plan outcome'),
481
- summary: z.string().optional().describe('Completion summary — checked for rationalization language'),
482
+ summary: z
483
+ .string()
484
+ .optional()
485
+ .describe('Completion summary — checked for rationalization language'),
482
486
  toolsUsed: z.array(z.string()).optional().describe('Tools used during execution'),
483
487
  filesModified: z.array(z.string()).optional().describe('Files modified during execution'),
484
- projectPath: z.string().optional().default('.').describe('Project root path for impact analysis'),
485
- overrideRationalization: z.boolean().optional().default(false)
488
+ projectPath: z
489
+ .string()
490
+ .optional()
491
+ .default('.')
492
+ .describe('Project root path for impact analysis'),
493
+ overrideRationalization: z
494
+ .boolean()
495
+ .optional()
496
+ .default(false)
486
497
  .describe('Set true to bypass rationalization gate and impact warnings after review'),
487
498
  }),
488
499
  handler: async (params) => {
489
- const planId = params.planId as string;
500
+ const planId = params.planId as string | undefined;
490
501
  const sessionId = params.sessionId as string;
491
502
  const outcome = (params.outcome as string) ?? 'completed';
492
503
  const completionSummary = (params.summary as string) ?? '';
@@ -494,20 +505,21 @@ export function createOrchestrateOps(
494
505
  const filesModified = (params.filesModified as string[]) ?? [];
495
506
  const overrideRationalization = (params.overrideRationalization as boolean) ?? false;
496
507
 
497
- // Anti-rationalization gate: check completion summary before completing
498
- if (outcome === 'completed' && !overrideRationalization) {
499
- const criteria = collectAcceptanceCriteria(planner, planId);
500
- if (criteria.length > 0 && completionSummary) {
501
- const report = detectRationalizations(criteria, completionSummary);
502
- if (report.detected) {
503
- captureRationalizationAntiPattern(vault, report);
504
- return {
505
- blocked: true,
506
- reason: 'Rationalization language detected in completion summary',
507
- rationalization: report,
508
- hint: 'Address the unmet criteria, or set overrideRationalization: true to bypass this gate.',
509
- };
510
- }
508
+ // Look up plan optional for direct tasks that skipped planning
509
+ const planObj = planId ? planner.get(planId) : null;
510
+
511
+ // Anti-rationalization gate: only if we have acceptance criteria from a plan
512
+ const criteria = planObj && planId ? collectAcceptanceCriteria(planner, planId) : [];
513
+ if (outcome === 'completed' && criteria.length > 0 && completionSummary && !overrideRationalization) {
514
+ const report = detectRationalizations(criteria, completionSummary);
515
+ if (report.detected) {
516
+ captureRationalizationAntiPattern(vault, report);
517
+ return {
518
+ blocked: true,
519
+ reason: 'Rationalization language detected in completion summary',
520
+ rationalization: report,
521
+ hint: 'Address the unmet criteria, or set overrideRationalization: true to bypass this gate.',
522
+ };
511
523
  }
512
524
  }
513
525
 
@@ -516,10 +528,7 @@ export function createOrchestrateOps(
516
528
  if (filesModified.length > 0) {
517
529
  try {
518
530
  const analyzer = new ImpactAnalyzer();
519
- const planObj = planner.get(planId);
520
- const scopeHints = planObj?.scope
521
- ? [planObj.scope]
522
- : undefined;
531
+ const scopeHints = planObj?.scope ? [planObj.scope] : undefined;
523
532
  impactReport = analyzer.analyzeImpact(
524
533
  filesModified,
525
534
  (params.projectPath as string) ?? '.',
@@ -540,10 +549,19 @@ export function createOrchestrateOps(
540
549
  }
541
550
  }
542
551
 
543
- // Complete the planner plan (legacy lifecycle)
544
- const plan = planner.complete(planId);
552
+ // Complete the planner plan (legacy lifecycle) — only if plan exists
553
+ let completedPlan;
554
+ if (planObj && planId) {
555
+ completedPlan = planner.complete(planId);
556
+ } else {
557
+ completedPlan = {
558
+ id: planId ?? `direct-${Date.now()}`,
559
+ status: 'completed',
560
+ objective: completionSummary || 'Direct execution',
561
+ };
562
+ }
545
563
 
546
- // End brain session
564
+ // End brain session — runs regardless of plan existence
547
565
  const session = brainIntelligence.lifecycle({
548
566
  action: 'end',
549
567
  sessionId,
@@ -553,7 +571,7 @@ export function createOrchestrateOps(
553
571
  filesModified,
554
572
  });
555
573
 
556
- // Extract knowledge
574
+ // Extract knowledge — runs regardless of plan existence
557
575
  let extraction = null;
558
576
  try {
559
577
  extraction = brainIntelligence.extractKnowledge(sessionId);
@@ -563,27 +581,29 @@ export function createOrchestrateOps(
563
581
 
564
582
  // Run flow-engine epilogue if we have a flow plan
565
583
  let epilogueResult = null;
566
- const entry = planStore.get(planId);
567
- if (entry) {
568
- try {
569
- const dispatch = buildDispatch(agentId, runtime, facades);
570
- const summary = `${outcome}: ${entry.plan.summary}. Tools: ${toolsUsed.join(', ') || 'none'}. Files: ${filesModified.join(', ') || 'none'}.`;
571
- epilogueResult = await runEpilogue(
572
- dispatch,
573
- entry.plan.context.probes,
574
- entry.plan.context.projectPath,
575
- summary,
576
- );
577
- } catch {
578
- // Epilogue is best-effort
579
- }
584
+ if (planId) {
585
+ const entry = planStore.get(planId);
586
+ if (entry) {
587
+ try {
588
+ const dispatch = buildDispatch(agentId, runtime, facades);
589
+ const summary = `${outcome}: ${entry.plan.summary}. Tools: ${toolsUsed.join(', ') || 'none'}. Files: ${filesModified.join(', ') || 'none'}.`;
590
+ epilogueResult = await runEpilogue(
591
+ dispatch,
592
+ entry.plan.context.probes,
593
+ entry.plan.context.projectPath,
594
+ summary,
595
+ );
596
+ } catch {
597
+ // Epilogue is best-effort
598
+ }
580
599
 
581
- // Clean up plan store
582
- planStore.delete(planId);
600
+ // Clean up plan store
601
+ planStore.delete(planId);
602
+ }
583
603
  }
584
604
 
585
605
  return {
586
- plan,
606
+ plan: completedPlan,
587
607
  session,
588
608
  extraction,
589
609
  epilogue: epilogueResult,
@@ -710,11 +730,22 @@ export function createOrchestrateOps(
710
730
  auth: 'write',
711
731
  schema: z.object({
712
732
  planId: z.string().describe('ID of the plan to project to GitHub'),
713
- projectPath: z.string().optional().default('.').describe('Project root path for git detection'),
733
+ projectPath: z
734
+ .string()
735
+ .optional()
736
+ .default('.')
737
+ .describe('Project root path for git detection'),
714
738
  milestone: z.number().optional().describe('GitHub milestone number to assign issues to'),
715
739
  labels: z.array(z.string()).optional().describe('Labels to apply to created issues'),
716
- linkToIssue: z.number().optional().describe('Existing issue number to link plan to instead of creating new issues'),
717
- dryRun: z.boolean().optional().default(false).describe('Preview what would be created without actually creating issues'),
740
+ linkToIssue: z
741
+ .number()
742
+ .optional()
743
+ .describe('Existing issue number to link plan to instead of creating new issues'),
744
+ dryRun: z
745
+ .boolean()
746
+ .optional()
747
+ .default(false)
748
+ .describe('Preview what would be created without actually creating issues'),
718
749
  }),
719
750
  handler: async (params) => {
720
751
  const planId = params.planId as string;
@@ -729,7 +760,9 @@ export function createOrchestrateOps(
729
760
  if (!plan) throw new Error(`Plan not found: ${planId}`);
730
761
 
731
762
  if (plan.tasks.length === 0) {
732
- throw new Error('Plan has no tasks — run plan_split first to define tasks before projecting to GitHub');
763
+ throw new Error(
764
+ 'Plan has no tasks — run plan_split first to define tasks before projecting to GitHub',
765
+ );
733
766
  }
734
767
 
735
768
  // 2. Detect GitHub context
@@ -807,7 +840,12 @@ export function createOrchestrateOps(
807
840
 
808
841
  // 6. Create issues per task (with duplicate detection)
809
842
  const created: Array<{ taskId: string; issueNumber: number; title: string }> = [];
810
- const skipped: Array<{ taskId: string; title: string; existingIssue: number; reason: string }> = [];
843
+ const skipped: Array<{
844
+ taskId: string;
845
+ title: string;
846
+ existingIssue: number;
847
+ reason: string;
848
+ }> = [];
811
849
  const failed: Array<{ taskId: string; title: string; reason: string }> = [];
812
850
 
813
851
  for (const task of plan.tasks) {