@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
@@ -30,13 +30,40 @@ function makePlan(overrides: Partial<Plan> = {}): Plan {
30
30
  scope: 'Auth module, middleware, and user service. Does not include OAuth providers.',
31
31
  status: 'draft',
32
32
  decisions: [
33
- { decision: 'Use JWT for stateless auth', rationale: 'Because it scales horizontally without shared session store' },
33
+ {
34
+ decision: 'Use JWT for stateless auth',
35
+ rationale: 'Because it scales horizontally without shared session store',
36
+ },
34
37
  ],
35
38
  tasks: [
36
- { id: 'task-1', title: 'Add JWT signing', description: 'Implement JWT sign/verify using built-in crypto module', status: 'pending', updatedAt: Date.now() },
37
- { id: 'task-2', title: 'Add auth middleware', description: 'Create Express middleware that validates JWT from Authorization header', status: 'pending', updatedAt: Date.now() },
38
- { id: 'task-3', title: 'Add login endpoint', description: 'POST /auth/login returns JWT after verifying credentials', status: 'pending', updatedAt: Date.now() },
39
- { id: 'task-4', title: 'Add test coverage', description: 'Test JWT signing, middleware rejection, and login flow end-to-end', status: 'pending', updatedAt: Date.now() },
39
+ {
40
+ id: 'task-1',
41
+ title: 'Add JWT signing',
42
+ description: 'Implement JWT sign/verify using built-in crypto module',
43
+ status: 'pending',
44
+ updatedAt: Date.now(),
45
+ },
46
+ {
47
+ id: 'task-2',
48
+ title: 'Add auth middleware',
49
+ description: 'Create Express middleware that validates JWT from Authorization header',
50
+ status: 'pending',
51
+ updatedAt: Date.now(),
52
+ },
53
+ {
54
+ id: 'task-3',
55
+ title: 'Add login endpoint',
56
+ description: 'POST /auth/login returns JWT after verifying credentials',
57
+ status: 'pending',
58
+ updatedAt: Date.now(),
59
+ },
60
+ {
61
+ id: 'task-4',
62
+ title: 'Add test coverage',
63
+ description: 'Test JWT signing, middleware rejection, and login flow end-to-end',
64
+ status: 'pending',
65
+ updatedAt: Date.now(),
66
+ },
40
67
  ],
41
68
  checks: [],
42
69
  createdAt: Date.now(),
@@ -178,7 +205,9 @@ describe('Pass 1: Structure', () => {
178
205
  it('returns critical gap for missing objective', () => {
179
206
  const gaps = analyzeStructure(makePlan({ objective: '' }));
180
207
  expect(gaps.some((g) => g._trigger === 'missing_or_short_objective')).toBe(true);
181
- expect(gaps.find((g) => g._trigger === 'missing_or_short_objective')?.severity).toBe('critical');
208
+ expect(gaps.find((g) => g._trigger === 'missing_or_short_objective')?.severity).toBe(
209
+ 'critical',
210
+ );
182
211
  });
183
212
 
184
213
  it('returns critical gap for short objective', () => {
@@ -258,10 +287,37 @@ describe('Pass 3: Feasibility', () => {
258
287
  it('does not flag when tasks have dependsOn', () => {
259
288
  const plan = makePlan({
260
289
  tasks: [
261
- { id: 't1', title: 'Setup', description: 'Setup project', status: 'pending', updatedAt: Date.now() },
262
- { id: 't2', title: 'Build', description: 'Build feature', status: 'pending', dependsOn: ['t1'], updatedAt: Date.now() },
263
- { id: 't3', title: 'Test', description: 'Test feature', status: 'pending', dependsOn: ['t2'], updatedAt: Date.now() },
264
- { id: 't4', title: 'Deploy', description: 'Deploy to prod', status: 'pending', dependsOn: ['t3'], updatedAt: Date.now() },
290
+ {
291
+ id: 't1',
292
+ title: 'Setup',
293
+ description: 'Setup project',
294
+ status: 'pending',
295
+ updatedAt: Date.now(),
296
+ },
297
+ {
298
+ id: 't2',
299
+ title: 'Build',
300
+ description: 'Build feature',
301
+ status: 'pending',
302
+ dependsOn: ['t1'],
303
+ updatedAt: Date.now(),
304
+ },
305
+ {
306
+ id: 't3',
307
+ title: 'Test',
308
+ description: 'Test feature',
309
+ status: 'pending',
310
+ dependsOn: ['t2'],
311
+ updatedAt: Date.now(),
312
+ },
313
+ {
314
+ id: 't4',
315
+ title: 'Deploy',
316
+ description: 'Deploy to prod',
317
+ status: 'pending',
318
+ dependsOn: ['t3'],
319
+ updatedAt: Date.now(),
320
+ },
265
321
  ],
266
322
  });
267
323
  const gaps = analyzeFeasibility(plan);
@@ -271,8 +327,20 @@ describe('Pass 3: Feasibility', () => {
271
327
  it('does not flag when 3 or fewer tasks', () => {
272
328
  const plan = makePlan({
273
329
  tasks: [
274
- { id: 't1', title: 'Task 1', description: 'Do something', status: 'pending', updatedAt: Date.now() },
275
- { id: 't2', title: 'Task 2', description: 'Do another thing', status: 'pending', updatedAt: Date.now() },
330
+ {
331
+ id: 't1',
332
+ title: 'Task 1',
333
+ description: 'Do something',
334
+ status: 'pending',
335
+ updatedAt: Date.now(),
336
+ },
337
+ {
338
+ id: 't2',
339
+ title: 'Task 2',
340
+ description: 'Do another thing',
341
+ status: 'pending',
342
+ updatedAt: Date.now(),
343
+ },
276
344
  ],
277
345
  });
278
346
  const gaps = analyzeFeasibility(plan);
@@ -305,7 +373,13 @@ describe('Pass 4: Risk', () => {
305
373
  scope: 'Auth module only. Not including user service.',
306
374
  decisions: [{ decision: 'Use new pattern', rationale: 'Because it is cleaner code' }],
307
375
  tasks: [
308
- { id: 't1', title: 'Refactor auth', description: 'Change the auth flow implementation', status: 'pending', updatedAt: Date.now() },
376
+ {
377
+ id: 't1',
378
+ title: 'Refactor auth',
379
+ description: 'Change the auth flow implementation',
380
+ status: 'pending',
381
+ updatedAt: Date.now(),
382
+ },
309
383
  ],
310
384
  });
311
385
  const gaps = analyzeRisk(plan);
@@ -59,35 +59,81 @@ export function containsAny(text: string, patterns: string[]): boolean {
59
59
  // ─── Pattern Constants (Passes 1-4) ─────────────────────────────
60
60
 
61
61
  export const METRIC_PATTERNS = [
62
- /\d+/, /percent/i, /reduce/i, /increase/i, /measure/i,
63
- /target/i, /goal/i, /kpi/i, /metric/i, /benchmark/i,
62
+ /\d+/,
63
+ /percent/i,
64
+ /reduce/i,
65
+ /increase/i,
66
+ /measure/i,
67
+ /target/i,
68
+ /goal/i,
69
+ /kpi/i,
70
+ /metric/i,
71
+ /benchmark/i,
64
72
  ];
65
73
 
66
74
  export const EXCLUSION_KEYWORDS = [
67
- 'not', 'exclude', 'outside', 'beyond', 'limit', 'except', "won't", 'will not',
75
+ 'not',
76
+ 'exclude',
77
+ 'outside',
78
+ 'beyond',
79
+ 'limit',
80
+ 'except',
81
+ "won't",
82
+ 'will not',
68
83
  ];
69
84
 
70
85
  export const OVERLY_BROAD_PATTERNS = [
71
- 'everything', 'all systems', 'entire codebase', 'complete rewrite',
72
- 'from scratch', 'total overhaul', 'rewrite everything',
86
+ 'everything',
87
+ 'all systems',
88
+ 'entire codebase',
89
+ 'complete rewrite',
90
+ 'from scratch',
91
+ 'total overhaul',
92
+ 'rewrite everything',
73
93
  ];
74
94
 
75
95
  export const DEPENDENCY_KEYWORDS = [
76
- 'depends', 'dependency', 'prerequisite', 'requires', 'blocked', 'before',
96
+ 'depends',
97
+ 'dependency',
98
+ 'prerequisite',
99
+ 'requires',
100
+ 'blocked',
101
+ 'before',
77
102
  ];
78
103
 
79
104
  export const BREAKING_CHANGE_KEYWORDS = [
80
- 'breaking change', 'breaking', 'migration', 'deprecate',
81
- 'remove api', 'remove endpoint', 'schema change', 'database migration',
105
+ 'breaking change',
106
+ 'breaking',
107
+ 'migration',
108
+ 'deprecate',
109
+ 'remove api',
110
+ 'remove endpoint',
111
+ 'schema change',
112
+ 'database migration',
82
113
  ];
83
114
 
84
115
  export const MITIGATION_KEYWORDS = [
85
- 'rollback', 'backward compatible', 'backwards compatible', 'feature flag',
86
- 'gradual', 'phased', 'fallback', 'backup', 'canary', 'blue-green',
116
+ 'rollback',
117
+ 'backward compatible',
118
+ 'backwards compatible',
119
+ 'feature flag',
120
+ 'gradual',
121
+ 'phased',
122
+ 'fallback',
123
+ 'backup',
124
+ 'canary',
125
+ 'blue-green',
87
126
  ];
88
127
 
89
128
  export const VERIFICATION_KEYWORDS = [
90
- 'test', 'verify', 'validate', 'check', 'assert', 'confirm', 'spec', 'coverage',
129
+ 'test',
130
+ 'verify',
131
+ 'validate',
132
+ 'check',
133
+ 'assert',
134
+ 'confirm',
135
+ 'spec',
136
+ 'coverage',
91
137
  ];
92
138
 
93
139
  // ─── Pass 1: Structure ───────────────────────────────────────────
@@ -98,12 +144,14 @@ export function analyzeStructure(plan: Plan): PlanGap[] {
98
144
  if (!plan.objective || plan.objective.trim().length < MIN_OBJECTIVE_LENGTH) {
99
145
  gaps.push(
100
146
  gap(
101
- 'critical', 'structure',
147
+ 'critical',
148
+ 'structure',
102
149
  plan.objective
103
150
  ? `Objective too short (${plan.objective.trim().length} chars, need ${MIN_OBJECTIVE_LENGTH}+).`
104
151
  : 'Plan has no objective.',
105
152
  'Add a clear objective describing what this plan achieves.',
106
- 'objective', 'missing_or_short_objective',
153
+ 'objective',
154
+ 'missing_or_short_objective',
107
155
  ),
108
156
  );
109
157
  }
@@ -111,21 +159,28 @@ export function analyzeStructure(plan: Plan): PlanGap[] {
111
159
  if (!plan.scope || plan.scope.trim().length < MIN_SCOPE_LENGTH) {
112
160
  gaps.push(
113
161
  gap(
114
- 'critical', 'structure',
162
+ 'critical',
163
+ 'structure',
115
164
  plan.scope
116
165
  ? `Scope too short (${plan.scope.trim().length} chars, need ${MIN_SCOPE_LENGTH}+).`
117
166
  : 'Plan has no scope defined.',
118
167
  'Define the scope — what is included and excluded.',
119
- 'scope', 'missing_or_short_scope',
168
+ 'scope',
169
+ 'missing_or_short_scope',
120
170
  ),
121
171
  );
122
172
  }
123
173
 
124
174
  if (plan.tasks.length === 0) {
125
175
  gaps.push(
126
- gap('critical', 'structure', 'Plan has no tasks.',
176
+ gap(
177
+ 'critical',
178
+ 'structure',
179
+ 'Plan has no tasks.',
127
180
  'Add at least one task to make the plan actionable.',
128
- 'tasks', 'no_tasks'),
181
+ 'tasks',
182
+ 'no_tasks',
183
+ ),
129
184
  );
130
185
  }
131
186
 
@@ -139,10 +194,14 @@ export function analyzeCompleteness(plan: Plan): PlanGap[] {
139
194
 
140
195
  if (plan.objective && !METRIC_PATTERNS.some((p) => p.test(plan.objective))) {
141
196
  gaps.push(
142
- gap('minor', 'completeness',
197
+ gap(
198
+ 'minor',
199
+ 'completeness',
143
200
  'Objective has no measurable targets or metrics.',
144
201
  'Include quantifiable success criteria (numbers, percentages, concrete outcomes).',
145
- 'objective', 'no_metrics_in_objective'),
202
+ 'objective',
203
+ 'no_metrics_in_objective',
204
+ ),
146
205
  );
147
206
  }
148
207
 
@@ -152,10 +211,14 @@ export function analyzeCompleteness(plan: Plan): PlanGap[] {
152
211
  const text = decisionText(d);
153
212
  if (text.trim().length < MIN_DECISION_LENGTH) {
154
213
  gaps.push(
155
- gap('major', 'completeness',
214
+ gap(
215
+ 'major',
216
+ 'completeness',
156
217
  `Decision ${i + 1} is too short (${text.trim().length} chars) — lacks rationale.`,
157
218
  'Expand each decision to include the reasoning (why this choice over alternatives).',
158
- `decisions[${i}]`, 'short_decision'),
219
+ `decisions[${i}]`,
220
+ 'short_decision',
221
+ ),
159
222
  );
160
223
  }
161
224
  }
@@ -163,10 +226,14 @@ export function analyzeCompleteness(plan: Plan): PlanGap[] {
163
226
 
164
227
  if (plan.scope && !containsAny(plan.scope, EXCLUSION_KEYWORDS)) {
165
228
  gaps.push(
166
- gap('minor', 'completeness',
229
+ gap(
230
+ 'minor',
231
+ 'completeness',
167
232
  'Scope does not mention what is excluded.',
168
233
  'Add explicit exclusions to prevent scope creep (e.g., "does NOT include…").',
169
- 'scope', 'no_exclusions_in_scope'),
234
+ 'scope',
235
+ 'no_exclusions_in_scope',
236
+ ),
170
237
  );
171
238
  }
172
239
 
@@ -181,10 +248,14 @@ export function analyzeFeasibility(plan: Plan): PlanGap[] {
181
248
 
182
249
  if (containsAny(scopeAndTasks, OVERLY_BROAD_PATTERNS)) {
183
250
  gaps.push(
184
- gap('major', 'feasibility',
251
+ gap(
252
+ 'major',
253
+ 'feasibility',
185
254
  'Scope contains overly broad indicators — risk of unrealistic delivery.',
186
255
  'Narrow the scope to a well-defined subset. Prefer incremental delivery over big-bang rewrites.',
187
- 'scope', 'overly_broad_scope'),
256
+ 'scope',
257
+ 'overly_broad_scope',
258
+ ),
188
259
  );
189
260
  }
190
261
 
@@ -192,10 +263,14 @@ export function analyzeFeasibility(plan: Plan): PlanGap[] {
192
263
  const hasDeps = plan.tasks.some((t) => t.dependsOn && t.dependsOn.length > 0);
193
264
  if (!hasDeps) {
194
265
  gaps.push(
195
- gap('minor', 'feasibility',
266
+ gap(
267
+ 'minor',
268
+ 'feasibility',
196
269
  `${plan.tasks.length} tasks with no dependency mentions — execution order unclear.`,
197
270
  'Identify dependencies between tasks or add explicit ordering notes.',
198
- 'tasks', 'no_dependency_awareness'),
271
+ 'tasks',
272
+ 'no_dependency_awareness',
273
+ ),
199
274
  );
200
275
  }
201
276
  }
@@ -214,19 +289,27 @@ export function analyzeRisk(plan: Plan): PlanGap[] {
214
289
  !containsAny(allText, MITIGATION_KEYWORDS)
215
290
  ) {
216
291
  gaps.push(
217
- gap('major', 'risk',
292
+ gap(
293
+ 'major',
294
+ 'risk',
218
295
  'Plan involves breaking changes but mentions no mitigation strategy.',
219
296
  'Add a rollback plan, feature flags, or phased migration approach.',
220
- undefined, 'breaking_without_mitigation'),
297
+ undefined,
298
+ 'breaking_without_mitigation',
299
+ ),
221
300
  );
222
301
  }
223
302
 
224
303
  if (plan.tasks.length > 0 && !containsAny(allText, VERIFICATION_KEYWORDS)) {
225
304
  gaps.push(
226
- gap('minor', 'risk',
305
+ gap(
306
+ 'minor',
307
+ 'risk',
227
308
  'No verification or testing mentioned in the plan.',
228
309
  'Add at least one task or note about testing/validation.',
229
- 'tasks', 'no_verification_mentioned'),
310
+ 'tasks',
311
+ 'no_verification_mentioned',
312
+ ),
230
313
  );
231
314
  }
232
315
 
@@ -129,7 +129,12 @@ describe('github-projection', () => {
129
129
  ],
130
130
  tasks: [
131
131
  { id: 'task-1', title: 'Design auth schema', description: 'Create DB tables' },
132
- { id: 'task-2', title: 'Implement login', description: 'Login endpoint', dependsOn: ['task-1'] },
132
+ {
133
+ id: 'task-2',
134
+ title: 'Implement login',
135
+ description: 'Login endpoint',
136
+ dependsOn: ['task-1'],
137
+ },
133
138
  ],
134
139
  };
135
140
 
@@ -129,7 +129,12 @@ export function listMilestones(repo: GitHubRepo): GitHubMilestone[] {
129
129
  try {
130
130
  const output = execFileSync(
131
131
  'gh',
132
- ['api', `repos/${repo.owner}/${repo.repo}/milestones`, '--jq', '.[] | {number, title, state}'],
132
+ [
133
+ 'api',
134
+ `repos/${repo.owner}/${repo.repo}/milestones`,
135
+ '--jq',
136
+ '.[] | {number, title, state}',
137
+ ],
133
138
  {
134
139
  encoding: 'utf-8',
135
140
  timeout: 10000,
@@ -157,11 +162,16 @@ export function listOpenIssues(repo: GitHubRepo, limit: number = 100): GitHubIss
157
162
  const output = execFileSync(
158
163
  'gh',
159
164
  [
160
- 'issue', 'list',
161
- '--repo', `${repo.owner}/${repo.repo}`,
162
- '--state', 'open',
163
- '--limit', String(limit),
164
- '--json', 'number,title,state,body',
165
+ 'issue',
166
+ 'list',
167
+ '--repo',
168
+ `${repo.owner}/${repo.repo}`,
169
+ '--state',
170
+ 'open',
171
+ '--limit',
172
+ String(limit),
173
+ '--json',
174
+ 'number,title,state,body',
165
175
  ],
166
176
  {
167
177
  encoding: 'utf-8',
@@ -184,11 +194,7 @@ export function listLabels(repo: GitHubRepo): GitHubLabel[] {
184
194
  try {
185
195
  const output = execFileSync(
186
196
  'gh',
187
- [
188
- 'label', 'list',
189
- '--repo', `${repo.owner}/${repo.repo}`,
190
- '--json', 'name,color',
191
- ],
197
+ ['label', 'list', '--repo', `${repo.owner}/${repo.repo}`, '--json', 'name,color'],
192
198
  {
193
199
  encoding: 'utf-8',
194
200
  timeout: 10000,
@@ -273,10 +279,18 @@ export function findDuplicateIssue(
273
279
  taskTitle: string,
274
280
  existingIssues: GitHubIssue[],
275
281
  ): GitHubIssue | null {
276
- const taskWords = new Set(taskTitle.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
282
+ const taskWords = new Set(
283
+ taskTitle
284
+ .toLowerCase()
285
+ .split(/\s+/)
286
+ .filter((w) => w.length > 2),
287
+ );
277
288
 
278
289
  for (const issue of existingIssues) {
279
- const issueWords = issue.title.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
290
+ const issueWords = issue.title
291
+ .toLowerCase()
292
+ .split(/\s+/)
293
+ .filter((w) => w.length > 2);
280
294
  let overlap = 0;
281
295
  for (const w of issueWords) {
282
296
  if (taskWords.has(w)) overlap++;
@@ -367,10 +381,14 @@ export function createGitHubIssue(
367
381
  ): number | null {
368
382
  try {
369
383
  const args = [
370
- 'issue', 'create',
371
- '--repo', `${repo.owner}/${repo.repo}`,
372
- '--title', title,
373
- '--body', body,
384
+ 'issue',
385
+ 'create',
386
+ '--repo',
387
+ `${repo.owner}/${repo.repo}`,
388
+ '--title',
389
+ title,
390
+ '--body',
391
+ body,
374
392
  ];
375
393
 
376
394
  if (options?.milestone) {
@@ -407,10 +425,13 @@ export function updateGitHubIssueBody(
407
425
  execFileSync(
408
426
  'gh',
409
427
  [
410
- 'issue', 'edit',
411
- '--repo', `${repo.owner}/${repo.repo}`,
428
+ 'issue',
429
+ 'edit',
430
+ '--repo',
431
+ `${repo.owner}/${repo.repo}`,
412
432
  String(issueNumber),
413
- '--body', body,
433
+ '--body',
434
+ body,
414
435
  ],
415
436
  {
416
437
  encoding: 'utf-8',
@@ -14,7 +14,9 @@ function setup() {
14
14
  function teardown() {
15
15
  try {
16
16
  rmSync(TMP, { recursive: true, force: true });
17
- } catch { /* ignore */ }
17
+ } catch {
18
+ /* ignore */
19
+ }
18
20
  }
19
21
 
20
22
  describe('ImpactAnalyzer', () => {
@@ -65,10 +67,7 @@ describe('ImpactAnalyzer', () => {
65
67
  join(TMP, 'src', 'components', 'b.ts'),
66
68
  "import { format } from '../utils/format';",
67
69
  );
68
- writeFileSync(
69
- join(TMP, 'src', 'components', 'c.ts'),
70
- "const f = require('../utils/format');",
71
- );
70
+ writeFileSync(join(TMP, 'src', 'components', 'c.ts'), "const f = require('../utils/format');");
72
71
 
73
72
  const report = analyzer.analyzeImpact(['src/utils/format.ts'], TMP);
74
73
 
@@ -100,11 +99,9 @@ describe('ImpactAnalyzer', () => {
100
99
  it('detects scope violations when files are outside plan scope', () => {
101
100
  writeFileSync(join(TMP, 'src', 'utils', 'misc.ts'), 'export const x = 1;');
102
101
 
103
- const report = analyzer.analyzeImpact(
104
- ['src/utils/misc.ts', 'src/components/button.ts'],
105
- TMP,
106
- ['components'],
107
- );
102
+ const report = analyzer.analyzeImpact(['src/utils/misc.ts', 'src/components/button.ts'], TMP, [
103
+ 'components',
104
+ ]);
108
105
 
109
106
  expect(report.scopeViolations).toContain('src/utils/misc.ts');
110
107
  expect(report.scopeViolations).not.toContain('src/components/button.ts');
@@ -134,11 +131,7 @@ describe('ImpactAnalyzer', () => {
134
131
  it('includes scope violation in recommendations', () => {
135
132
  writeFileSync(join(TMP, 'src', 'utils', 'oops.ts'), 'export const x = 1;');
136
133
 
137
- const report = analyzer.analyzeImpact(
138
- ['src/utils/oops.ts'],
139
- TMP,
140
- ['components'],
141
- );
134
+ const report = analyzer.analyzeImpact(['src/utils/oops.ts'], TMP, ['components']);
142
135
 
143
136
  const recText = report.recommendations.join(' ');
144
137
  expect(recText).toContain('Scope violation');
@@ -148,10 +141,7 @@ describe('ImpactAnalyzer', () => {
148
141
 
149
142
  it('separates test files from untested consumers', () => {
150
143
  writeFileSync(join(TMP, 'src', 'utils', 'api.ts'), 'export const api = 1;');
151
- writeFileSync(
152
- join(TMP, 'src', 'components', 'page.ts'),
153
- "import { api } from '../utils/api';",
154
- );
144
+ writeFileSync(join(TMP, 'src', 'components', 'page.ts'), "import { api } from '../utils/api';");
155
145
  writeFileSync(
156
146
  join(TMP, 'src', '__tests__', 'api.test.ts'),
157
147
  "import { api } from '../utils/api';",
@@ -168,10 +158,7 @@ describe('ImpactAnalyzer', () => {
168
158
  // ─── Graceful when project path doesn't exist ──────────────
169
159
 
170
160
  it('returns low risk when project path does not exist', () => {
171
- const report = analyzer.analyzeImpact(
172
- ['src/foo.ts'],
173
- '/nonexistent/path/12345',
174
- );
161
+ const report = analyzer.analyzeImpact(['src/foo.ts'], '/nonexistent/path/12345');
175
162
 
176
163
  expect(report.riskLevel).toBe('low');
177
164
  expect(report.affectedConsumers).toHaveLength(0);