@soleri/core 9.2.0 → 9.3.0

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 (298) 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.map +1 -1
  14. package/dist/engine/module-manifest.js +21 -1
  15. package/dist/engine/module-manifest.js.map +1 -1
  16. package/dist/engine/register-engine.d.ts.map +1 -1
  17. package/dist/engine/register-engine.js +25 -1
  18. package/dist/engine/register-engine.js.map +1 -1
  19. package/dist/flows/gate-evaluator.js.map +1 -1
  20. package/dist/operator/operator-profile.d.ts.map +1 -1
  21. package/dist/operator/operator-profile.js +11 -5
  22. package/dist/operator/operator-profile.js.map +1 -1
  23. package/dist/operator/operator-signals.d.ts.map +1 -1
  24. package/dist/operator/operator-signals.js.map +1 -1
  25. package/dist/planning/evidence-collector.js.map +1 -1
  26. package/dist/planning/gap-passes.d.ts.map +1 -1
  27. package/dist/planning/gap-passes.js +23 -6
  28. package/dist/planning/gap-passes.js.map +1 -1
  29. package/dist/planning/gap-patterns.d.ts.map +1 -1
  30. package/dist/planning/gap-patterns.js +57 -11
  31. package/dist/planning/gap-patterns.js.map +1 -1
  32. package/dist/planning/github-projection.d.ts.map +1 -1
  33. package/dist/planning/github-projection.js +39 -20
  34. package/dist/planning/github-projection.js.map +1 -1
  35. package/dist/planning/impact-analyzer.d.ts.map +1 -1
  36. package/dist/planning/impact-analyzer.js +20 -18
  37. package/dist/planning/impact-analyzer.js.map +1 -1
  38. package/dist/planning/plan-lifecycle.d.ts.map +1 -1
  39. package/dist/planning/plan-lifecycle.js +22 -9
  40. package/dist/planning/plan-lifecycle.js.map +1 -1
  41. package/dist/planning/planner.d.ts.map +1 -1
  42. package/dist/planning/planner.js +60 -17
  43. package/dist/planning/planner.js.map +1 -1
  44. package/dist/planning/rationalization-detector.d.ts.map +1 -1
  45. package/dist/planning/rationalization-detector.js.map +1 -1
  46. package/dist/planning/reconciliation-engine.d.ts.map +1 -1
  47. package/dist/planning/reconciliation-engine.js.map +1 -1
  48. package/dist/planning/task-verifier.d.ts.map +1 -1
  49. package/dist/planning/task-verifier.js +14 -6
  50. package/dist/planning/task-verifier.js.map +1 -1
  51. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  52. package/dist/runtime/admin-setup-ops.js +2 -1
  53. package/dist/runtime/admin-setup-ops.js.map +1 -1
  54. package/dist/runtime/branching-ops.d.ts +12 -0
  55. package/dist/runtime/branching-ops.d.ts.map +1 -0
  56. package/dist/runtime/branching-ops.js +100 -0
  57. package/dist/runtime/branching-ops.js.map +1 -0
  58. package/dist/runtime/context-health.d.ts.map +1 -1
  59. package/dist/runtime/context-health.js.map +1 -1
  60. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  61. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  62. package/dist/runtime/facades/branching-facade.js +8 -0
  63. package/dist/runtime/facades/branching-facade.js.map +1 -0
  64. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -1
  65. package/dist/runtime/facades/chat-service-ops.js +3 -1
  66. package/dist/runtime/facades/chat-service-ops.js.map +1 -1
  67. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -1
  68. package/dist/runtime/facades/chat-transport-ops.js.map +1 -1
  69. package/dist/runtime/facades/index.d.ts.map +1 -1
  70. package/dist/runtime/facades/index.js +42 -0
  71. package/dist/runtime/facades/index.js.map +1 -1
  72. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  73. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  74. package/dist/runtime/facades/intake-facade.js +11 -0
  75. package/dist/runtime/facades/intake-facade.js.map +1 -0
  76. package/dist/runtime/facades/links-facade.d.ts +9 -0
  77. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  78. package/dist/runtime/facades/links-facade.js +10 -0
  79. package/dist/runtime/facades/links-facade.js.map +1 -0
  80. package/dist/runtime/facades/operator-facade.d.ts.map +1 -1
  81. package/dist/runtime/facades/operator-facade.js.map +1 -1
  82. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  83. package/dist/runtime/facades/plan-facade.js +4 -1
  84. package/dist/runtime/facades/plan-facade.js.map +1 -1
  85. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  86. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  87. package/dist/runtime/facades/tier-facade.js +8 -0
  88. package/dist/runtime/facades/tier-facade.js.map +1 -0
  89. package/dist/runtime/facades/vault-facade.d.ts +9 -1
  90. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  91. package/dist/runtime/facades/vault-facade.js +44 -187
  92. package/dist/runtime/facades/vault-facade.js.map +1 -1
  93. package/dist/runtime/github-integration.d.ts.map +1 -1
  94. package/dist/runtime/github-integration.js +11 -4
  95. package/dist/runtime/github-integration.js.map +1 -1
  96. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  97. package/dist/runtime/orchestrate-ops.js +32 -10
  98. package/dist/runtime/orchestrate-ops.js.map +1 -1
  99. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  100. package/dist/runtime/planning-extra-ops.js.map +1 -1
  101. package/dist/runtime/runtime.d.ts.map +1 -1
  102. package/dist/runtime/runtime.js +3 -1
  103. package/dist/runtime/runtime.js.map +1 -1
  104. package/dist/runtime/session-briefing.d.ts.map +1 -1
  105. package/dist/runtime/session-briefing.js +5 -1
  106. package/dist/runtime/session-briefing.js.map +1 -1
  107. package/dist/runtime/tier-ops.d.ts +13 -0
  108. package/dist/runtime/tier-ops.d.ts.map +1 -0
  109. package/dist/runtime/tier-ops.js +110 -0
  110. package/dist/runtime/tier-ops.js.map +1 -0
  111. package/dist/skills/sync-skills.d.ts.map +1 -1
  112. package/dist/skills/sync-skills.js +1 -1
  113. package/dist/skills/sync-skills.js.map +1 -1
  114. package/dist/vault/linking.d.ts.map +1 -1
  115. package/dist/vault/linking.js +41 -5
  116. package/dist/vault/linking.js.map +1 -1
  117. package/dist/vault/vault-entries.d.ts.map +1 -1
  118. package/dist/vault/vault-entries.js +68 -26
  119. package/dist/vault/vault-entries.js.map +1 -1
  120. package/dist/vault/vault-maintenance.d.ts.map +1 -1
  121. package/dist/vault/vault-maintenance.js +6 -2
  122. package/dist/vault/vault-maintenance.js.map +1 -1
  123. package/dist/vault/vault-markdown-sync.d.ts.map +1 -1
  124. package/dist/vault/vault-markdown-sync.js.map +1 -1
  125. package/dist/vault/vault-memories.d.ts.map +1 -1
  126. package/dist/vault/vault-memories.js +3 -1
  127. package/dist/vault/vault-memories.js.map +1 -1
  128. package/dist/vault/vault-schema.js +36 -10
  129. package/dist/vault/vault-schema.js.map +1 -1
  130. package/dist/vault/vault.d.ts.map +1 -1
  131. package/dist/vault/vault.js +5 -1
  132. package/dist/vault/vault.js.map +1 -1
  133. package/package.json +7 -7
  134. package/src/agency/agency-manager.test.ts +60 -40
  135. package/src/agency/default-rules.test.ts +17 -9
  136. package/src/capabilities/registry.test.ts +2 -12
  137. package/src/chat/agent-loop.test.ts +33 -43
  138. package/src/chat/mcp-bridge.test.ts +7 -2
  139. package/src/claudemd/inject.test.ts +2 -12
  140. package/src/context/context-engine.test.ts +96 -51
  141. package/src/control/intent-router.test.ts +3 -3
  142. package/src/curator/classifier.test.ts +14 -8
  143. package/src/curator/contradiction-detector.test.ts +30 -5
  144. package/src/curator/curator.ts +278 -56
  145. package/src/curator/duplicate-detector.test.ts +77 -15
  146. package/src/curator/quality-gate.test.ts +71 -31
  147. package/src/curator/tag-manager.test.ts +12 -4
  148. package/src/domain-packs/knowledge-installer.test.ts +2 -10
  149. package/src/domain-packs/token-resolver.test.ts +1 -3
  150. package/src/domain-packs/types.test.ts +16 -2
  151. package/src/enforcement/registry.test.ts +2 -8
  152. package/src/engine/bin/soleri-engine.ts +3 -1
  153. package/src/engine/module-manifest.test.ts +5 -4
  154. package/src/engine/module-manifest.ts +21 -1
  155. package/src/engine/register-engine.test.ts +6 -1
  156. package/src/engine/register-engine.ts +26 -3
  157. package/src/errors/classify.test.ts +6 -2
  158. package/src/errors/retry.test.ts +1 -4
  159. package/src/facades/facade-factory.test.ts +110 -64
  160. package/src/flows/epilogue.test.ts +16 -10
  161. package/src/flows/gate-evaluator.test.ts +12 -6
  162. package/src/flows/gate-evaluator.ts +1 -3
  163. package/src/governance/governance.test.ts +137 -21
  164. package/src/health/health-registry.test.ts +8 -1
  165. package/src/intake/content-classifier.test.ts +121 -51
  166. package/src/intake/dedup-gate.test.ts +38 -22
  167. package/src/intake/intake-pipeline.test.ts +5 -3
  168. package/src/intake/text-ingester.test.ts +26 -20
  169. package/src/llm/key-pool.test.ts +1 -3
  170. package/src/llm/llm-client.test.ts +1 -4
  171. package/src/llm/oauth-discovery.test.ts +16 -16
  172. package/src/llm/utils.test.ts +62 -18
  173. package/src/logging/logger.test.ts +4 -1
  174. package/src/loop/loop-manager.test.ts +2 -6
  175. package/src/migrations/migration-runner.edge-cases.test.ts +2 -7
  176. package/src/operator/operator-profile-extended.test.ts +15 -5
  177. package/src/operator/operator-profile.test.ts +26 -8
  178. package/src/operator/operator-profile.ts +38 -22
  179. package/src/operator/operator-signals-extended.test.ts +35 -23
  180. package/src/operator/operator-signals.test.ts +6 -10
  181. package/src/operator/operator-signals.ts +2 -1
  182. package/src/operator/prompts/hook-precompact-operator-dispatch.md +10 -6
  183. package/src/operator/prompts/subagent-soft-signal-extractor.md +5 -0
  184. package/src/operator/prompts/subagent-synthesis-cognition.md +19 -10
  185. package/src/operator/prompts/subagent-synthesis-communication.md +13 -7
  186. package/src/operator/prompts/subagent-synthesis-technical.md +19 -9
  187. package/src/operator/prompts/subagent-synthesis-trust.md +27 -21
  188. package/src/persona/defaults.test.ts +1 -5
  189. package/src/planning/evidence-collector.test.ts +147 -38
  190. package/src/planning/evidence-collector.ts +1 -4
  191. package/src/planning/gap-analysis-alternatives.test.ts +41 -11
  192. package/src/planning/gap-passes.test.ts +215 -33
  193. package/src/planning/gap-passes.ts +115 -46
  194. package/src/planning/gap-patterns.test.ts +87 -13
  195. package/src/planning/gap-patterns.ts +114 -31
  196. package/src/planning/github-projection.test.ts +6 -1
  197. package/src/planning/github-projection.ts +41 -20
  198. package/src/planning/impact-analyzer.test.ts +10 -23
  199. package/src/planning/impact-analyzer.ts +33 -46
  200. package/src/planning/plan-lifecycle.test.ts +103 -36
  201. package/src/planning/plan-lifecycle.ts +49 -18
  202. package/src/planning/planner.test.ts +12 -2
  203. package/src/planning/planner.ts +198 -58
  204. package/src/planning/rationalization-detector.test.ts +5 -20
  205. package/src/planning/rationalization-detector.ts +14 -16
  206. package/src/planning/reconciliation-engine.test.ts +20 -3
  207. package/src/planning/reconciliation-engine.ts +1 -2
  208. package/src/planning/task-verifier.test.ts +59 -27
  209. package/src/planning/task-verifier.ts +15 -9
  210. package/src/playbooks/playbook-executor.test.ts +1 -3
  211. package/src/plugins/plugin-loader.test.ts +19 -14
  212. package/src/plugins/plugin-registry.test.ts +45 -33
  213. package/src/project/project-registry.test.ts +23 -12
  214. package/src/prompts/template-manager.test.ts +4 -1
  215. package/src/queue/job-queue.test.ts +10 -14
  216. package/src/runtime/admin-extra-ops.test.ts +5 -19
  217. package/src/runtime/admin-ops.test.ts +1 -3
  218. package/src/runtime/admin-setup-ops.test.ts +3 -4
  219. package/src/runtime/admin-setup-ops.ts +9 -2
  220. package/src/runtime/archive-ops.test.ts +4 -1
  221. package/src/runtime/branching-ops.test.ts +144 -0
  222. package/src/runtime/branching-ops.ts +107 -0
  223. package/src/runtime/capture-ops.test.ts +7 -21
  224. package/src/runtime/chain-ops.test.ts +16 -6
  225. package/src/runtime/claude-md-helpers.test.ts +1 -3
  226. package/src/runtime/context-health.test.ts +1 -3
  227. package/src/runtime/context-health.ts +1 -3
  228. package/src/runtime/curator-extra-ops.test.ts +3 -1
  229. package/src/runtime/domain-ops.test.ts +46 -36
  230. package/src/runtime/facades/admin-facade.test.ts +1 -4
  231. package/src/runtime/facades/archive-facade.test.ts +21 -7
  232. package/src/runtime/facades/brain-facade.test.ts +176 -72
  233. package/src/runtime/facades/branching-facade.test.ts +43 -0
  234. package/src/runtime/facades/branching-facade.ts +11 -0
  235. package/src/runtime/facades/chat-facade.test.ts +81 -28
  236. package/src/runtime/facades/chat-service-ops.test.ts +178 -73
  237. package/src/runtime/facades/chat-service-ops.ts +3 -1
  238. package/src/runtime/facades/chat-session-ops.test.ts +25 -10
  239. package/src/runtime/facades/chat-transport-ops.test.ts +101 -34
  240. package/src/runtime/facades/chat-transport-ops.ts +0 -1
  241. package/src/runtime/facades/context-facade.test.ts +19 -4
  242. package/src/runtime/facades/control-facade.test.ts +3 -3
  243. package/src/runtime/facades/index.ts +42 -0
  244. package/src/runtime/facades/intake-facade.test.ts +215 -0
  245. package/src/runtime/facades/intake-facade.ts +14 -0
  246. package/src/runtime/facades/links-facade.test.ts +203 -0
  247. package/src/runtime/facades/links-facade.ts +13 -0
  248. package/src/runtime/facades/loop-facade.test.ts +22 -5
  249. package/src/runtime/facades/memory-facade.test.ts +19 -5
  250. package/src/runtime/facades/operator-facade.test.ts +17 -4
  251. package/src/runtime/facades/operator-facade.ts +11 -3
  252. package/src/runtime/facades/orchestrate-facade.test.ts +7 -1
  253. package/src/runtime/facades/plan-facade.test.ts +29 -12
  254. package/src/runtime/facades/plan-facade.ts +7 -2
  255. package/src/runtime/facades/tier-facade.test.ts +47 -0
  256. package/src/runtime/facades/tier-facade.ts +11 -0
  257. package/src/runtime/facades/vault-facade.test.ts +174 -242
  258. package/src/runtime/facades/vault-facade.ts +55 -199
  259. package/src/runtime/github-integration.ts +11 -8
  260. package/src/runtime/grading-ops.test.ts +39 -8
  261. package/src/runtime/intake-ops.test.ts +69 -16
  262. package/src/runtime/loop-ops.test.ts +16 -6
  263. package/src/runtime/memory-cross-project-ops.test.ts +25 -14
  264. package/src/runtime/orchestrate-ops.ts +54 -27
  265. package/src/runtime/pack-ops.test.ts +23 -6
  266. package/src/runtime/planning-extra-ops.test.ts +17 -7
  267. package/src/runtime/planning-extra-ops.ts +3 -1
  268. package/src/runtime/playbook-ops.test.ts +26 -3
  269. package/src/runtime/plugin-ops.test.ts +83 -25
  270. package/src/runtime/project-ops.test.ts +26 -6
  271. package/src/runtime/runtime.ts +3 -1
  272. package/src/runtime/session-briefing.test.ts +183 -54
  273. package/src/runtime/session-briefing.ts +8 -2
  274. package/src/runtime/sync-ops.test.ts +3 -12
  275. package/src/runtime/telemetry-ops.test.ts +31 -6
  276. package/src/runtime/tier-ops.test.ts +159 -0
  277. package/src/runtime/tier-ops.ts +119 -0
  278. package/src/runtime/vault-extra-ops.test.ts +32 -8
  279. package/src/runtime/vault-sharing-ops.test.ts +1 -4
  280. package/src/skills/sync-skills.ts +2 -12
  281. package/src/transport/ws-server.test.ts +7 -4
  282. package/src/vault/__tests__/vault-characterization.test.ts +492 -81
  283. package/src/vault/linking.test.ts +50 -17
  284. package/src/vault/linking.ts +48 -7
  285. package/src/vault/obsidian-sync.test.ts +6 -3
  286. package/src/vault/scope-detector.test.ts +1 -3
  287. package/src/vault/vault-branching.test.ts +9 -7
  288. package/src/vault/vault-entries.ts +209 -65
  289. package/src/vault/vault-maintenance.ts +7 -12
  290. package/src/vault/vault-manager.test.ts +10 -10
  291. package/src/vault/vault-markdown-sync.ts +4 -1
  292. package/src/vault/vault-memories.ts +7 -7
  293. package/src/vault/vault-schema.ts +72 -15
  294. package/src/vault/vault.ts +55 -9
  295. package/src/brain/strength-scorer.ts +0 -404
  296. package/src/engine/index.ts +0 -21
  297. package/src/persona/index.ts +0 -9
  298. package/src/vault/vault-interfaces.ts +0 -56
@@ -31,21 +31,20 @@ export interface ImpactReport {
31
31
  // Constants
32
32
  // ---------------------------------------------------------------------------
33
33
 
34
- const CODE_EXTENSIONS = new Set([
35
- '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
36
- '.vue', '.svelte',
37
- ]);
34
+ const CODE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.vue', '.svelte']);
38
35
 
39
- const TEST_PATTERNS = [
40
- /\.test\./,
41
- /\.spec\./,
42
- /__tests__/,
43
- /\.stories\./,
44
- ];
36
+ const TEST_PATTERNS = [/\.test\./, /\.spec\./, /__tests__/, /\.stories\./];
45
37
 
46
38
  const SKIP_DIRS = new Set([
47
- 'node_modules', '.git', 'dist', 'build', 'coverage',
48
- '.next', '.nuxt', '.svelte-kit', '.turbo',
39
+ 'node_modules',
40
+ '.git',
41
+ 'dist',
42
+ 'build',
43
+ 'coverage',
44
+ '.next',
45
+ '.nuxt',
46
+ '.svelte-kit',
47
+ '.turbo',
49
48
  ]);
50
49
 
51
50
  const MAX_FILES = 5000;
@@ -58,11 +57,7 @@ export class ImpactAnalyzer {
58
57
  /**
59
58
  * Analyze the downstream impact of modified files.
60
59
  */
61
- analyzeImpact(
62
- modifiedFiles: string[],
63
- projectPath: string,
64
- planScope?: string[],
65
- ): ImpactReport {
60
+ analyzeImpact(modifiedFiles: string[], projectPath: string, planScope?: string[]): ImpactReport {
66
61
  if (modifiedFiles.length === 0 || !existsSync(projectPath)) {
67
62
  return emptyReport(modifiedFiles);
68
63
  }
@@ -72,11 +67,7 @@ export class ImpactAnalyzer {
72
67
  const untestedConsumers = filterUntested(consumers);
73
68
  const scopeViolations = detectScopeViolations(modifiedFiles, planScope);
74
69
  const riskLevel = assessRisk(consumers.length);
75
- const recommendations = buildRecommendations(
76
- consumers,
77
- untestedConsumers,
78
- scopeViolations,
79
- );
70
+ const recommendations = buildRecommendations(consumers, untestedConsumers, scopeViolations);
80
71
 
81
72
  return {
82
73
  modifiedFiles,
@@ -166,18 +157,17 @@ function findConsumers(
166
157
  /**
167
158
  * Build regex-friendly stems from modified file paths.
168
159
  */
169
- function buildImportPatterns(
170
- modifiedFiles: string[],
171
- projectPath: string,
172
- ): string[] {
173
- return modifiedFiles.map((f) => {
174
- const rel = f.startsWith('/') ? relative(projectPath, f) : f;
175
- // Strip extension for import matching
176
- const stem = rel.replace(/\.[^.]+$/, '');
177
- // Also match the bare filename without extension
178
- const bare = basename(rel).replace(/\.[^.]+$/, '');
179
- return bare.length >= 3 ? bare : stem;
180
- }).filter((p) => p.length >= 3);
160
+ function buildImportPatterns(modifiedFiles: string[], projectPath: string): string[] {
161
+ return modifiedFiles
162
+ .map((f) => {
163
+ const rel = f.startsWith('/') ? relative(projectPath, f) : f;
164
+ // Strip extension for import matching
165
+ const stem = rel.replace(/\.[^.]+$/, '');
166
+ // Also match the bare filename without extension
167
+ const bare = basename(rel).replace(/\.[^.]+$/, '');
168
+ return bare.length >= 3 ? bare : stem;
169
+ })
170
+ .filter((p) => p.length >= 3);
181
171
  }
182
172
 
183
173
  /**
@@ -191,34 +181,28 @@ function matchImports(filePath: string, patterns: string[]): string[] {
191
181
  return [];
192
182
  }
193
183
 
194
- const importRegex = /(?:import\s+.*?from\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g;
184
+ const importRegex =
185
+ /(?:import\s+.*?from\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g;
195
186
  const importPaths: string[] = [];
196
187
  let match: RegExpExecArray | null;
197
188
  while ((match = importRegex.exec(content)) !== null) {
198
189
  importPaths.push(match[1] ?? match[2]);
199
190
  }
200
191
 
201
- return patterns.filter((p) =>
202
- importPaths.some((imp) => imp.includes(p)),
203
- );
192
+ return patterns.filter((p) => importPaths.some((imp) => imp.includes(p)));
204
193
  }
205
194
 
206
195
  /**
207
196
  * Filter consumers that are NOT test files.
208
197
  */
209
198
  function filterUntested(consumers: AffectedConsumer[]): string[] {
210
- return consumers
211
- .map((c) => c.file)
212
- .filter((f) => !TEST_PATTERNS.some((p) => p.test(f)));
199
+ return consumers.map((c) => c.file).filter((f) => !TEST_PATTERNS.some((p) => p.test(f)));
213
200
  }
214
201
 
215
202
  /**
216
203
  * Flag modified files not in the declared plan scope.
217
204
  */
218
- function detectScopeViolations(
219
- modifiedFiles: string[],
220
- planScope?: string[],
221
- ): string[] {
205
+ function detectScopeViolations(modifiedFiles: string[], planScope?: string[]): string[] {
222
206
  if (!planScope || planScope.length === 0) return [];
223
207
 
224
208
  return modifiedFiles.filter((f) => {
@@ -248,7 +232,10 @@ function buildRecommendations(
248
232
 
249
233
  if (consumers.length > 0) {
250
234
  recs.push(
251
- `Run tests for ${consumers.length} affected consumer(s): ${consumers.map((c) => c.file).slice(0, 5).join(', ')}`,
235
+ `Run tests for ${consumers.length} affected consumer(s): ${consumers
236
+ .map((c) => c.file)
237
+ .slice(0, 5)
238
+ .join(', ')}`,
252
239
  );
253
240
  }
254
241
 
@@ -110,8 +110,15 @@ describe('plan-lifecycle', () => {
110
110
 
111
111
  describe('PlanGradeRejectionError', () => {
112
112
  it('contains grade, score, minGrade and gaps', () => {
113
- const gaps = [{ id: 'g1', severity: 'critical' as const, category: 'structure',
114
- description: 'Missing structure', recommendation: 'Fix it' }];
113
+ const gaps = [
114
+ {
115
+ id: 'g1',
116
+ severity: 'critical' as const,
117
+ category: 'structure',
118
+ description: 'Missing structure',
119
+ recommendation: 'Fix it',
120
+ },
121
+ ];
115
122
  const err = new PlanGradeRejectionError('C', 65, 'A', gaps);
116
123
  expect(err.grade).toBe('C');
117
124
  expect(err.score).toBe(65);
@@ -122,8 +129,20 @@ describe('plan-lifecycle', () => {
122
129
  });
123
130
  it('includes critical and major gaps in message', () => {
124
131
  const gaps = [
125
- { id: 'g1', severity: 'critical' as const, category: 'x', description: 'Crit gap', recommendation: '' },
126
- { id: 'g2', severity: 'minor' as const, category: 'x', description: 'Minor gap', recommendation: '' },
132
+ {
133
+ id: 'g1',
134
+ severity: 'critical' as const,
135
+ category: 'x',
136
+ description: 'Crit gap',
137
+ recommendation: '',
138
+ },
139
+ {
140
+ id: 'g2',
141
+ severity: 'minor' as const,
142
+ category: 'x',
143
+ description: 'Minor gap',
144
+ recommendation: '',
145
+ },
127
146
  ];
128
147
  const err = new PlanGradeRejectionError('D', 55, 'A', gaps);
129
148
  expect(err.message).toContain('Crit gap');
@@ -133,24 +152,33 @@ describe('plan-lifecycle', () => {
133
152
 
134
153
  describe('hasCircularDependencies', () => {
135
154
  it('returns false for no dependencies', () => {
136
- expect(hasCircularDependencies([
137
- { id: 'a' }, { id: 'b' },
138
- ])).toBe(false);
155
+ expect(hasCircularDependencies([{ id: 'a' }, { id: 'b' }])).toBe(false);
139
156
  });
140
157
  it('returns false for linear dependencies', () => {
141
- expect(hasCircularDependencies([
142
- { id: 'a' }, { id: 'b', dependsOn: ['a'] }, { id: 'c', dependsOn: ['b'] },
143
- ])).toBe(false);
158
+ expect(
159
+ hasCircularDependencies([
160
+ { id: 'a' },
161
+ { id: 'b', dependsOn: ['a'] },
162
+ { id: 'c', dependsOn: ['b'] },
163
+ ]),
164
+ ).toBe(false);
144
165
  });
145
166
  it('returns true for direct cycle', () => {
146
- expect(hasCircularDependencies([
147
- { id: 'a', dependsOn: ['b'] }, { id: 'b', dependsOn: ['a'] },
148
- ])).toBe(true);
167
+ expect(
168
+ hasCircularDependencies([
169
+ { id: 'a', dependsOn: ['b'] },
170
+ { id: 'b', dependsOn: ['a'] },
171
+ ]),
172
+ ).toBe(true);
149
173
  });
150
174
  it('returns true for indirect cycle', () => {
151
- expect(hasCircularDependencies([
152
- { id: 'a', dependsOn: ['c'] }, { id: 'b', dependsOn: ['a'] }, { id: 'c', dependsOn: ['b'] },
153
- ])).toBe(true);
175
+ expect(
176
+ hasCircularDependencies([
177
+ { id: 'a', dependsOn: ['c'] },
178
+ { id: 'b', dependsOn: ['a'] },
179
+ { id: 'c', dependsOn: ['b'] },
180
+ ]),
181
+ ).toBe(true);
154
182
  });
155
183
  it('returns true for self-dependency', () => {
156
184
  expect(hasCircularDependencies([{ id: 'a', dependsOn: ['a'] }])).toBe(true);
@@ -162,29 +190,60 @@ describe('plan-lifecycle', () => {
162
190
  expect(calculateScore([])).toBe(100);
163
191
  });
164
192
  it('deducts critical gaps at weight 30', () => {
165
- const gaps = [{ id: 'g', severity: 'critical' as const, category: 'structure',
166
- description: 'x', recommendation: 'y' }];
193
+ const gaps = [
194
+ {
195
+ id: 'g',
196
+ severity: 'critical' as const,
197
+ category: 'structure',
198
+ description: 'x',
199
+ recommendation: 'y',
200
+ },
201
+ ];
167
202
  expect(calculateScore(gaps)).toBe(70);
168
203
  });
169
204
  it('treats minor gaps as free on iteration 1', () => {
170
- const gaps = [{ id: 'g', severity: 'minor' as const, category: 'clarity',
171
- description: 'x', recommendation: 'y' }];
205
+ const gaps = [
206
+ {
207
+ id: 'g',
208
+ severity: 'minor' as const,
209
+ category: 'clarity',
210
+ description: 'x',
211
+ recommendation: 'y',
212
+ },
213
+ ];
172
214
  expect(calculateScore(gaps, 1)).toBe(100);
173
215
  });
174
216
  it('treats minor gaps at half weight on iteration 2', () => {
175
- const gaps = [{ id: 'g', severity: 'minor' as const, category: 'clarity',
176
- description: 'x', recommendation: 'y' }];
217
+ const gaps = [
218
+ {
219
+ id: 'g',
220
+ severity: 'minor' as const,
221
+ category: 'clarity',
222
+ description: 'x',
223
+ recommendation: 'y',
224
+ },
225
+ ];
177
226
  expect(calculateScore(gaps, 2)).toBe(99);
178
227
  });
179
228
  it('treats minor gaps at full weight on iteration 3', () => {
180
- const gaps = [{ id: 'g', severity: 'minor' as const, category: 'clarity',
181
- description: 'x', recommendation: 'y' }];
229
+ const gaps = [
230
+ {
231
+ id: 'g',
232
+ severity: 'minor' as const,
233
+ category: 'clarity',
234
+ description: 'x',
235
+ recommendation: 'y',
236
+ },
237
+ ];
182
238
  expect(calculateScore(gaps, 3)).toBe(98);
183
239
  });
184
240
  it('floors at 0', () => {
185
241
  const gaps = Array.from({ length: 5 }, (_, i) => ({
186
- id: `g${i}`, severity: 'critical' as const, category: `cat${i}`,
187
- description: 'x', recommendation: 'y',
242
+ id: `g${i}`,
243
+ severity: 'critical' as const,
244
+ category: `cat${i}`,
245
+ description: 'x',
246
+ recommendation: 'y',
188
247
  }));
189
248
  expect(calculateScore(gaps)).toBe(0);
190
249
  });
@@ -201,7 +260,9 @@ describe('plan-lifecycle', () => {
201
260
  });
202
261
  it('adds tasks with correct IDs', () => {
203
262
  const plan = makePlan();
204
- plan.tasks = [{ id: 'task-1', title: 'T1', description: 'd', status: 'pending', updatedAt: 0 }];
263
+ plan.tasks = [
264
+ { id: 'task-1', title: 'T1', description: 'd', status: 'pending', updatedAt: 0 },
265
+ ];
205
266
  applyIteration(plan, { addTasks: [{ title: 'T2', description: 'd2' }] });
206
267
  expect(plan.tasks).toHaveLength(2);
207
268
  expect(plan.tasks[1].id).toBe('task-2');
@@ -230,22 +291,24 @@ describe('plan-lifecycle', () => {
230
291
  });
231
292
  it('throws on unknown dependency', () => {
232
293
  const plan = createPlanObject({ objective: 'test', scope: 'test' });
233
- expect(() => applySplitTasks(plan, [
234
- { title: 'A', description: 'a', dependsOn: ['task-99'] },
235
- ])).toThrow('depends on unknown task');
294
+ expect(() =>
295
+ applySplitTasks(plan, [{ title: 'A', description: 'a', dependsOn: ['task-99'] }]),
296
+ ).toThrow('depends on unknown task');
236
297
  });
237
298
  it('preserves acceptance criteria', () => {
238
299
  const plan = createPlanObject({ objective: 'test', scope: 'test' });
239
- applySplitTasks(plan, [
240
- { title: 'A', description: 'a', acceptanceCriteria: ['cr1', 'cr2'] },
241
- ]);
300
+ applySplitTasks(plan, [{ title: 'A', description: 'a', acceptanceCriteria: ['cr1', 'cr2'] }]);
242
301
  expect(plan.tasks[0].acceptanceCriteria).toEqual(['cr1', 'cr2']);
243
302
  });
244
303
  });
245
304
 
246
305
  describe('applyTaskStatusUpdate', () => {
247
306
  const makeTask = (): PlanTask => ({
248
- id: 'task-1', title: 'T', description: 'd', status: 'pending', updatedAt: 0,
307
+ id: 'task-1',
308
+ title: 'T',
309
+ description: 'd',
310
+ status: 'pending',
311
+ updatedAt: 0,
249
312
  });
250
313
 
251
314
  it('sets startedAt on first in_progress', () => {
@@ -295,8 +358,12 @@ describe('plan-lifecycle', () => {
295
358
  });
296
359
  it('creates numbered tasks', () => {
297
360
  const plan = createPlanObject({
298
- objective: 'x', scope: 'y',
299
- tasks: [{ title: 'A', description: 'a' }, { title: 'B', description: 'b' }],
361
+ objective: 'x',
362
+ scope: 'y',
363
+ tasks: [
364
+ { title: 'A', description: 'a' },
365
+ { title: 'B', description: 'b' },
366
+ ],
300
367
  });
301
368
  expect(plan.tasks[0].id).toBe('task-1');
302
369
  expect(plan.tasks[1].id).toBe('task-2');
@@ -229,10 +229,17 @@ export function hasCircularDependencies(
229
229
  import type { PlanTask, TaskStatus, Plan, PlanDecision } from './planner-types.js';
230
230
 
231
231
  export interface IterateChanges {
232
- objective?: string; scope?: string; decisions?: (string | PlanDecision)[];
233
- addTasks?: Array<{ title: string; description: string }>; removeTasks?: string[];
234
- approach?: string; context?: string; success_criteria?: string[];
235
- tool_chain?: string[]; flow?: string; target_mode?: string;
232
+ objective?: string;
233
+ scope?: string;
234
+ decisions?: (string | PlanDecision)[];
235
+ addTasks?: Array<{ title: string; description: string }>;
236
+ removeTasks?: string[];
237
+ approach?: string;
238
+ context?: string;
239
+ success_criteria?: string[];
240
+ tool_chain?: string[];
241
+ flow?: string;
242
+ target_mode?: string;
236
243
  }
237
244
 
238
245
  /**
@@ -261,8 +268,11 @@ export function applyIteration(plan: Plan, changes: IterateChanges): void {
261
268
  }, 0);
262
269
  for (let i = 0; i < changes.addTasks.length; i++) {
263
270
  plan.tasks.push({
264
- id: `task-${maxIndex + i + 1}`, title: changes.addTasks[i].title,
265
- description: changes.addTasks[i].description, status: 'pending' as TaskStatus, updatedAt: now,
271
+ id: `task-${maxIndex + i + 1}`,
272
+ title: changes.addTasks[i].title,
273
+ description: changes.addTasks[i].description,
274
+ status: 'pending' as TaskStatus,
275
+ updatedAt: now,
266
276
  });
267
277
  }
268
278
  }
@@ -295,23 +305,32 @@ export function applyTaskStatusUpdate(task: PlanTask, status: TaskStatus): void
295
305
  * Create a new Plan object (factory). Does not persist.
296
306
  */
297
307
  export function createPlanObject(params: {
298
- objective: string; scope: string;
308
+ objective: string;
309
+ scope: string;
299
310
  decisions?: (string | PlanDecision)[];
300
311
  tasks?: Array<{ title: string; description: string }>;
301
- approach?: string; context?: string; success_criteria?: string[];
302
- tool_chain?: string[]; flow?: string; target_mode?: string;
312
+ approach?: string;
313
+ context?: string;
314
+ success_criteria?: string[];
315
+ tool_chain?: string[];
316
+ flow?: string;
317
+ target_mode?: string;
303
318
  alternatives?: import('./planner-types.js').PlanAlternative[];
304
319
  initialStatus?: 'brainstorming' | 'draft';
305
320
  }): Plan {
306
321
  const now = Date.now();
307
322
  return {
308
323
  id: `plan-${now}-${Math.random().toString(36).slice(2, 8)}`,
309
- objective: params.objective, scope: params.scope,
324
+ objective: params.objective,
325
+ scope: params.scope,
310
326
  status: params.initialStatus ?? 'draft',
311
327
  decisions: params.decisions ?? [],
312
328
  tasks: (params.tasks ?? []).map((t, i) => ({
313
- id: `task-${i + 1}`, title: t.title, description: t.description,
314
- status: 'pending' as TaskStatus, updatedAt: now,
329
+ id: `task-${i + 1}`,
330
+ title: t.title,
331
+ description: t.description,
332
+ status: 'pending' as TaskStatus,
333
+ updatedAt: now,
315
334
  })),
316
335
  ...(params.approach !== undefined && { approach: params.approach }),
317
336
  ...(params.context !== undefined && { context: params.context }),
@@ -320,25 +339,37 @@ export function createPlanObject(params: {
320
339
  ...(params.flow !== undefined && { flow: params.flow }),
321
340
  ...(params.target_mode !== undefined && { target_mode: params.target_mode }),
322
341
  ...(params.alternatives !== undefined && { alternatives: params.alternatives }),
323
- checks: [], createdAt: now, updatedAt: now,
342
+ checks: [],
343
+ createdAt: now,
344
+ updatedAt: now,
324
345
  };
325
346
  }
326
347
 
327
348
  export function applySplitTasks(
328
349
  plan: Plan,
329
- tasks: Array<{ title: string; description: string; dependsOn?: string[]; acceptanceCriteria?: string[] }>,
350
+ tasks: Array<{
351
+ title: string;
352
+ description: string;
353
+ dependsOn?: string[];
354
+ acceptanceCriteria?: string[];
355
+ }>,
330
356
  ): void {
331
357
  const now = Date.now();
332
358
  plan.tasks = tasks.map((t, i) => ({
333
- id: `task-${i + 1}`, title: t.title, description: t.description,
334
- status: 'pending' as TaskStatus, dependsOn: t.dependsOn,
335
- ...(t.acceptanceCriteria && { acceptanceCriteria: t.acceptanceCriteria }), updatedAt: now,
359
+ id: `task-${i + 1}`,
360
+ title: t.title,
361
+ description: t.description,
362
+ status: 'pending' as TaskStatus,
363
+ dependsOn: t.dependsOn,
364
+ ...(t.acceptanceCriteria && { acceptanceCriteria: t.acceptanceCriteria }),
365
+ updatedAt: now,
336
366
  }));
337
367
  const taskIds = new Set(plan.tasks.map((t) => t.id));
338
368
  for (const task of plan.tasks) {
339
369
  if (task.dependsOn) {
340
370
  for (const dep of task.dependsOn) {
341
- if (!taskIds.has(dep)) throw new Error(`Task '${task.id}' depends on unknown task '${dep}'`);
371
+ if (!taskIds.has(dep))
372
+ throw new Error(`Task '${task.id}' depends on unknown task '${dep}'`);
342
373
  }
343
374
  }
344
375
  }
@@ -9,8 +9,18 @@ import { tmpdir } from 'node:os';
9
9
 
10
10
  /** Two well-structured alternatives to satisfy pass 8. */
11
11
  const TWO_ALTERNATIVES: PlanAlternative[] = [
12
- { approach: 'Use alternative A', pros: ['Pro A'], cons: ['Con A'], rejected_reason: 'Not suitable for our use case' },
13
- { approach: 'Use alternative B', pros: ['Pro B'], cons: ['Con B'], rejected_reason: 'Too complex for the scope' },
12
+ {
13
+ approach: 'Use alternative A',
14
+ pros: ['Pro A'],
15
+ cons: ['Con A'],
16
+ rejected_reason: 'Not suitable for our use case',
17
+ },
18
+ {
19
+ approach: 'Use alternative B',
20
+ pros: ['Pro B'],
21
+ cons: ['Con B'],
22
+ rejected_reason: 'Too complex for the scope',
23
+ },
14
24
  ];
15
25
 
16
26
  describe('Planner', () => {