@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
@@ -268,14 +268,18 @@ export function buildSpecReviewPrompt(
268
268
  ? `\n\nAcceptance Criteria:\n${task.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}`
269
269
  : '';
270
270
  return [
271
- `# Spec Compliance Review`, ``, `## Task: ${task.title}`,
271
+ `# Spec Compliance Review`,
272
+ ``,
273
+ `## Task: ${task.title}`,
272
274
  `**Description:** ${task.description}`,
273
- `**Plan Objective:** ${planObjective}${criteria}`, ``,
275
+ `**Plan Objective:** ${planObjective}${criteria}`,
276
+ ``,
274
277
  `## Review Checklist`,
275
278
  `1. Does the implementation match the task description?`,
276
279
  `2. Are all acceptance criteria satisfied?`,
277
280
  `3. Does it align with the plan's overall objective?`,
278
- `4. Are there any spec deviations?`, ``,
281
+ `4. Are there any spec deviations?`,
282
+ ``,
279
283
  `Provide: outcome (approved/rejected/needs_changes) and detailed comments.`,
280
284
  ].join('\n');
281
285
  }
@@ -284,12 +288,13 @@ export function buildSpecReviewPrompt(
284
288
  * Generate a code quality review prompt for a task.
285
289
  * Pure function — no persistence side-effects.
286
290
  */
287
- export function buildQualityReviewPrompt(
288
- task: Pick<PlanTask, 'title' | 'description'>,
289
- ): string {
291
+ export function buildQualityReviewPrompt(task: Pick<PlanTask, 'title' | 'description'>): string {
290
292
  return [
291
- `# Code Quality Review`, ``, `## Task: ${task.title}`,
292
- `**Description:** ${task.description}`, ``,
293
+ `# Code Quality Review`,
294
+ ``,
295
+ `## Task: ${task.title}`,
296
+ `**Description:** ${task.description}`,
297
+ ``,
293
298
  `## Quality Checklist`,
294
299
  `1. **Correctness** — Does it work as intended?`,
295
300
  `2. **Security** — No injection, XSS, or OWASP top 10 vulnerabilities?`,
@@ -297,7 +302,8 @@ export function buildQualityReviewPrompt(
297
302
  `4. **Maintainability** — Clear naming, appropriate abstractions, documented intent?`,
298
303
  `5. **Testing** — Adequate test coverage for the changes?`,
299
304
  `6. **Error Handling** — Graceful degradation, no swallowed errors?`,
300
- `7. **Conventions** — Follows project coding standards?`, ``,
305
+ `7. **Conventions** — Follows project coding standards?`,
306
+ ``,
301
307
  `Provide: outcome (approved/rejected/needs_changes) and detailed comments.`,
302
308
  ].join('\n');
303
309
  }
@@ -19,9 +19,7 @@ function makePlaybook(overrides: Partial<PlaybookDefinition> = {}): PlaybookDefi
19
19
  tags: [],
20
20
  matchIntents: ['BUILD'],
21
21
  matchKeywords: [],
22
- gates: [
23
- { phase: 'completion', requirement: 'Tests pass', checkType: 'test-pass' },
24
- ],
22
+ gates: [{ phase: 'completion', requirement: 'Tests pass', checkType: 'test-pass' }],
25
23
  taskTemplates: [],
26
24
  toolInjections: ['search_intelligent'],
27
25
  verificationCriteria: ['All tests pass'],
@@ -13,7 +13,10 @@ import type { LoadedPlugin, PluginManifest } from './types.js';
13
13
  let testDirs: string[] = [];
14
14
 
15
15
  function makeTempDir(): string {
16
- const dir = join(tmpdir(), `soleri-loader-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
16
+ const dir = join(
17
+ tmpdir(),
18
+ `soleri-loader-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
19
+ );
17
20
  mkdirSync(dir, { recursive: true });
18
21
  testDirs.push(dir);
19
22
  return dir;
@@ -26,13 +29,15 @@ function writePlugin(parentDir: string, id: string, manifest: Record<string, unk
26
29
  return dir;
27
30
  }
28
31
 
29
- function makeLoaded(overrides: Partial<LoadedPlugin & { manifest: Partial<PluginManifest> }> = {}): LoadedPlugin {
32
+ function makeLoaded(
33
+ overrides: Partial<LoadedPlugin & { manifest: Partial<PluginManifest> }> = {},
34
+ ): LoadedPlugin {
30
35
  return {
31
36
  manifest: pluginManifestSchema.parse({
32
37
  id: 'test-plugin',
33
38
  name: 'Test',
34
39
  version: '1.0.0',
35
- ...(overrides.manifest),
40
+ ...overrides.manifest,
36
41
  }),
37
42
  directory: overrides.directory ?? '/tmp/test',
38
43
  provenance: overrides.provenance ?? 'global',
@@ -41,7 +46,11 @@ function makeLoaded(overrides: Partial<LoadedPlugin & { manifest: Partial<Plugin
41
46
 
42
47
  afterEach(() => {
43
48
  for (const dir of testDirs) {
44
- try { rmSync(dir, { recursive: true, force: true }); } catch { /* noop */ }
49
+ try {
50
+ rmSync(dir, { recursive: true, force: true });
51
+ } catch {
52
+ /* noop */
53
+ }
45
54
  }
46
55
  testDirs = [];
47
56
  });
@@ -62,11 +71,11 @@ describe('loadPlugins — colocated', () => {
62
71
  const result = loadPlugins('test-agent', undefined, [dir1, dir2]);
63
72
 
64
73
  expect(result.loaded).toHaveLength(2);
65
- const ids = result.loaded.map(p => p.manifest.id);
74
+ const ids = result.loaded.map((p) => p.manifest.id);
66
75
  expect(ids).toContain('alpha');
67
76
  expect(ids).toContain('beta');
68
77
  // First occurrence wins
69
- expect(result.loaded.find(p => p.manifest.id === 'alpha')!.manifest.name).toBe('Alpha');
78
+ expect(result.loaded.find((p) => p.manifest.id === 'alpha')!.manifest.name).toBe('Alpha');
70
79
  });
71
80
 
72
81
  it('skips files (non-directories) inside plugin dir', () => {
@@ -154,18 +163,14 @@ describe('validateDependencies — colocated', () => {
154
163
  });
155
164
 
156
165
  it('detects multiple missing dependencies', () => {
157
- const plugins = [
158
- makeLoaded({ manifest: { id: 'lonely', dependencies: ['dep-a', 'dep-b'] } }),
159
- ];
166
+ const plugins = [makeLoaded({ manifest: { id: 'lonely', dependencies: ['dep-a', 'dep-b'] } })];
160
167
  const errors = validateDependencies(plugins);
161
168
  expect(errors).toHaveLength(2);
162
- expect(errors.map(e => e.missingDep)).toEqual(['dep-a', 'dep-b']);
169
+ expect(errors.map((e) => e.missingDep)).toEqual(['dep-a', 'dep-b']);
163
170
  });
164
171
 
165
172
  it('handles plugins with no dependencies', () => {
166
- const plugins = [
167
- makeLoaded({ manifest: { id: 'standalone' } }),
168
- ];
173
+ const plugins = [makeLoaded({ manifest: { id: 'standalone' } })];
169
174
  expect(validateDependencies(plugins)).toEqual([]);
170
175
  });
171
176
  });
@@ -181,7 +186,7 @@ describe('sortByDependencies — colocated', () => {
181
186
  const a = makeLoaded({ manifest: { id: 'a' } });
182
187
 
183
188
  const sorted = sortByDependencies([c, b, a]);
184
- const ids = sorted.map(p => p.manifest.id);
189
+ const ids = sorted.map((p) => p.manifest.id);
185
190
 
186
191
  expect(ids.indexOf('a')).toBeLessThan(ids.indexOf('b'));
187
192
  expect(ids.indexOf('b')).toBeLessThan(ids.indexOf('c'));
@@ -73,14 +73,16 @@ describe('PluginRegistry — colocated', () => {
73
73
  it('builds static facades from manifest when no index.js exists', async () => {
74
74
  const loaded = makeLoaded({
75
75
  manifest: makeManifest({
76
- facades: [{
77
- name: 'my_facade',
78
- description: 'Test facade',
79
- ops: [
80
- { name: 'do_thing', description: 'Does a thing', auth: 'read' },
81
- { name: 'admin_thing', description: 'Admin action', auth: 'admin' },
82
- ],
83
- }],
76
+ facades: [
77
+ {
78
+ name: 'my_facade',
79
+ description: 'Test facade',
80
+ ops: [
81
+ { name: 'do_thing', description: 'Does a thing', auth: 'read' },
82
+ { name: 'admin_thing', description: 'Admin action', auth: 'admin' },
83
+ ],
84
+ },
85
+ ],
84
86
  }),
85
87
  });
86
88
 
@@ -98,11 +100,13 @@ describe('PluginRegistry — colocated', () => {
98
100
  it('static facade ops return error message when called', async () => {
99
101
  const loaded = makeLoaded({
100
102
  manifest: makeManifest({
101
- facades: [{
102
- name: 'f',
103
- description: '',
104
- ops: [{ name: 'op1', description: '', auth: 'read' }],
105
- }],
103
+ facades: [
104
+ {
105
+ name: 'f',
106
+ description: '',
107
+ ops: [{ name: 'op1', description: '', auth: 'read' }],
108
+ },
109
+ ],
106
110
  }),
107
111
  });
108
112
 
@@ -126,9 +130,9 @@ describe('PluginRegistry — colocated', () => {
126
130
  });
127
131
 
128
132
  it('throws for unregistered plugin', async () => {
129
- await expect(
130
- registry.activate('ghost', makeContext(makeLoaded())),
131
- ).rejects.toThrow('not registered');
133
+ await expect(registry.activate('ghost', makeContext(makeLoaded()))).rejects.toThrow(
134
+ 'not registered',
135
+ );
132
136
  });
133
137
 
134
138
  it('sets error status when index.js fails to load', async () => {
@@ -152,11 +156,13 @@ describe('PluginRegistry — colocated', () => {
152
156
  it('deactivates an active plugin and clears facades', async () => {
153
157
  const loaded = makeLoaded({
154
158
  manifest: makeManifest({
155
- facades: [{
156
- name: 'f',
157
- description: '',
158
- ops: [{ name: 'op', description: '', auth: 'read' }],
159
- }],
159
+ facades: [
160
+ {
161
+ name: 'f',
162
+ description: '',
163
+ ops: [{ name: 'op', description: '', auth: 'read' }],
164
+ },
165
+ ],
160
166
  }),
161
167
  });
162
168
 
@@ -203,11 +209,13 @@ describe('PluginRegistry — colocated', () => {
203
209
  const active = makeLoaded({
204
210
  manifest: makeManifest({
205
211
  id: 'active-one',
206
- facades: [{
207
- name: 'af',
208
- description: '',
209
- ops: [{ name: 'aop', description: '', auth: 'read' }],
210
- }],
212
+ facades: [
213
+ {
214
+ name: 'af',
215
+ description: '',
216
+ ops: [{ name: 'aop', description: '', auth: 'read' }],
217
+ },
218
+ ],
211
219
  }),
212
220
  });
213
221
  const inactive = makeLoaded({ manifest: makeManifest({ id: 'inactive-one' }) });
@@ -227,10 +235,14 @@ describe('PluginRegistry — colocated', () => {
227
235
  id: 'multi',
228
236
  facades: [
229
237
  { name: 'f1', description: '', ops: [{ name: 'a', description: '', auth: 'read' }] },
230
- { name: 'f2', description: '', ops: [
231
- { name: 'b', description: '', auth: 'write' },
232
- { name: 'c', description: '', auth: 'admin' },
233
- ]},
238
+ {
239
+ name: 'f2',
240
+ description: '',
241
+ ops: [
242
+ { name: 'b', description: '', auth: 'write' },
243
+ { name: 'c', description: '', auth: 'admin' },
244
+ ],
245
+ },
234
246
  ],
235
247
  }),
236
248
  });
@@ -240,7 +252,7 @@ describe('PluginRegistry — colocated', () => {
240
252
 
241
253
  const ops = registry.getActiveOps();
242
254
  expect(ops).toHaveLength(3);
243
- expect(ops.map(o => o.name)).toEqual(['a', 'b', 'c']);
255
+ expect(ops.map((o) => o.name)).toEqual(['a', 'b', 'c']);
244
256
  });
245
257
 
246
258
  it('returns empty when no active plugins', () => {
@@ -265,8 +277,8 @@ describe('PluginRegistry — colocated', () => {
265
277
 
266
278
  const list = registry.list();
267
279
  expect(list).toHaveLength(2);
268
- expect(list.find(p => p.id === 'a')!.status).toBe('active');
269
- expect(list.find(p => p.id === 'b')!.status).toBe('registered');
280
+ expect(list.find((p) => p.id === 'a')!.status).toBe('active');
281
+ expect(list.find((p) => p.id === 'b')!.status).toBe('registered');
270
282
  });
271
283
  });
272
284
  });
@@ -33,9 +33,7 @@ function createMockProvider(): PersistenceProvider {
33
33
  // Check for duplicate
34
34
  const existing = store.project_links.find(
35
35
  (r) =>
36
- r.source_project_id === p[0] &&
37
- r.target_project_id === p[1] &&
38
- r.link_type === p[2],
36
+ r.source_project_id === p[0] && r.target_project_id === p[1] && r.link_type === p[2],
39
37
  );
40
38
  if (existing) return { changes: 0, lastInsertRowid: existing.id as number };
41
39
  autoIncrementId++;
@@ -97,7 +95,11 @@ function createMockProvider(): PersistenceProvider {
97
95
  return { changes: before - store.project_rules.length, lastInsertRowid: 0 };
98
96
  }
99
97
 
100
- if (sql.startsWith('DELETE FROM project_links WHERE source_project_id = ? AND target_project_id = ? AND link_type')) {
98
+ if (
99
+ sql.startsWith(
100
+ 'DELETE FROM project_links WHERE source_project_id = ? AND target_project_id = ? AND link_type',
101
+ )
102
+ ) {
101
103
  const before = store.project_links.length;
102
104
  store.project_links = store.project_links.filter(
103
105
  (r) =>
@@ -106,16 +108,23 @@ function createMockProvider(): PersistenceProvider {
106
108
  return { changes: before - store.project_links.length, lastInsertRowid: 0 };
107
109
  }
108
110
 
109
- if (sql.startsWith('DELETE FROM project_links WHERE source_project_id = ? AND target_project_id = ?')) {
111
+ if (
112
+ sql.startsWith(
113
+ 'DELETE FROM project_links WHERE source_project_id = ? AND target_project_id = ?',
114
+ )
115
+ ) {
110
116
  const before = store.project_links.length;
111
117
  store.project_links = store.project_links.filter(
112
- (r) =>
113
- !(r.source_project_id === p[0] && r.target_project_id === p[1]),
118
+ (r) => !(r.source_project_id === p[0] && r.target_project_id === p[1]),
114
119
  );
115
120
  return { changes: before - store.project_links.length, lastInsertRowid: 0 };
116
121
  }
117
122
 
118
- if (sql.startsWith('DELETE FROM project_links WHERE source_project_id = ? OR target_project_id = ?')) {
123
+ if (
124
+ sql.startsWith(
125
+ 'DELETE FROM project_links WHERE source_project_id = ? OR target_project_id = ?',
126
+ )
127
+ ) {
119
128
  const before = store.project_links.length;
120
129
  store.project_links = store.project_links.filter(
121
130
  (r) => r.source_project_id !== p[0] && r.target_project_id !== p[1],
@@ -141,12 +150,14 @@ function createMockProvider(): PersistenceProvider {
141
150
  if (sql.includes('FROM registered_projects WHERE path')) {
142
151
  return store.registered_projects.find((r) => r.path === p[0]) as T | undefined;
143
152
  }
144
- if (sql.includes('FROM project_links WHERE source_project_id = ? AND target_project_id = ? AND link_type')) {
153
+ if (
154
+ sql.includes(
155
+ 'FROM project_links WHERE source_project_id = ? AND target_project_id = ? AND link_type',
156
+ )
157
+ ) {
145
158
  return store.project_links.find(
146
159
  (r) =>
147
- r.source_project_id === p[0] &&
148
- r.target_project_id === p[1] &&
149
- r.link_type === p[2],
160
+ r.source_project_id === p[0] && r.target_project_id === p[1] && r.link_type === p[2],
150
161
  ) as T | undefined;
151
162
  }
152
163
 
@@ -8,7 +8,10 @@ describe('TemplateManager', () => {
8
8
  let tempDir: string;
9
9
 
10
10
  beforeEach(() => {
11
- tempDir = join(tmpdir(), `tmpl-mgr-colocated-${Date.now()}-${Math.random().toString(36).slice(2)}`);
11
+ tempDir = join(
12
+ tmpdir(),
13
+ `tmpl-mgr-colocated-${Date.now()}-${Math.random().toString(36).slice(2)}`,
14
+ );
12
15
  mkdirSync(tempDir, { recursive: true });
13
16
  });
14
17
 
@@ -57,7 +57,11 @@ function createMockProvider(): PersistenceProvider {
57
57
  row.completed_at = new Date().toISOString();
58
58
  }
59
59
  }
60
- if (sql.includes('UPDATE') && sql.includes("status = 'pending'") && sql.includes('retry_count')) {
60
+ if (
61
+ sql.includes('UPDATE') &&
62
+ sql.includes("status = 'pending'") &&
63
+ sql.includes('retry_count')
64
+ ) {
61
65
  const id = p[0] as string;
62
66
  const row = rows.get(id);
63
67
  if (row) {
@@ -144,15 +148,7 @@ describe('JobQueue', () => {
144
148
  queue.enqueue('groom');
145
149
  expect(provider.run).toHaveBeenCalledWith(
146
150
  expect.stringContaining('INSERT INTO job_queue'),
147
- expect.arrayContaining([
148
- expect.any(String),
149
- 'groom',
150
- null,
151
- '{}',
152
- '[]',
153
- null,
154
- 3,
155
- ]),
151
+ expect.arrayContaining([expect.any(String), 'groom', null, '{}', '[]', null, 3]),
156
152
  );
157
153
  });
158
154
 
@@ -219,10 +215,10 @@ describe('JobQueue', () => {
219
215
  const id = queue.enqueue('groom');
220
216
  queue.dequeue();
221
217
  queue.complete(id);
222
- expect(provider.run).toHaveBeenCalledWith(
223
- expect.stringContaining("status = 'completed'"),
224
- [null, id],
225
- );
218
+ expect(provider.run).toHaveBeenCalledWith(expect.stringContaining("status = 'completed'"), [
219
+ null,
220
+ id,
221
+ ]);
226
222
  });
227
223
  });
228
224
 
@@ -67,9 +67,7 @@ function createMockRuntime(): AgentRuntime {
67
67
  overall: 'healthy',
68
68
  subsystems: { vault: { status: 'healthy' }, brain: { status: 'healthy' } },
69
69
  })),
70
- get: vi.fn((name: string) =>
71
- name === 'vault' ? { status: 'healthy', failures: 0 } : null,
72
- ),
70
+ get: vi.fn((name: string) => (name === 'vault' ? { status: 'healthy', failures: 0 } : null)),
73
71
  },
74
72
  flags: {
75
73
  getAll: vi.fn(() => ({
@@ -396,10 +394,7 @@ describe('createAdminExtraOps', () => {
396
394
 
397
395
  describe('admin_hot_reload', () => {
398
396
  it('reloads brain, vault FTS, and templates', async () => {
399
- const result = (await findOp(ops, 'admin_hot_reload').handler({})) as Record<
400
- string,
401
- unknown
402
- >;
397
+ const result = (await findOp(ops, 'admin_hot_reload').handler({})) as Record<string, unknown>;
403
398
  expect((result.reloaded as string[]).length).toBe(3);
404
399
  expect(result.brainTerms).toBe(500);
405
400
  expect(result.templateCount).toBe(2);
@@ -409,10 +404,7 @@ describe('createAdminExtraOps', () => {
409
404
  vi.mocked(runtime.vault.rebuildFtsIndex).mockImplementation(() => {
410
405
  throw new Error('fail');
411
406
  });
412
- const result = (await findOp(ops, 'admin_hot_reload').handler({})) as Record<
413
- string,
414
- unknown
415
- >;
407
+ const result = (await findOp(ops, 'admin_hot_reload').handler({})) as Record<string, unknown>;
416
408
  expect(result.reloaded).not.toContain('vault_fts');
417
409
  });
418
410
  });
@@ -455,10 +447,7 @@ describe('createAdminExtraOps', () => {
455
447
 
456
448
  describe('admin_list_flags', () => {
457
449
  it('returns all feature flags', async () => {
458
- const result = (await findOp(ops, 'admin_list_flags').handler({})) as Record<
459
- string,
460
- unknown
461
- >;
450
+ const result = (await findOp(ops, 'admin_list_flags').handler({})) as Record<string, unknown>;
462
451
  expect(result).toHaveProperty('auth-enforcement');
463
452
  });
464
453
  });
@@ -516,10 +505,7 @@ describe('createAdminExtraOps', () => {
516
505
 
517
506
  describe('admin_setup_run', () => {
518
507
  it('runs setup actions', async () => {
519
- const result = (await findOp(ops, 'admin_setup_run').handler({})) as Record<
520
- string,
521
- unknown
522
- >;
508
+ const result = (await findOp(ops, 'admin_setup_run').handler({})) as Record<string, unknown>;
523
509
  expect(result.setup).toBe(true);
524
510
  expect((result.actions as string[]).length).toBeGreaterThan(0);
525
511
  });
@@ -133,11 +133,32 @@ describe('createAdminOps', () => {
133
133
  expect(grouped.vault).toContain('vault_search');
134
134
  });
135
135
 
136
- it('returns verbose format when verbose=true', async () => {
136
+ it('returns routing hints in grouped mode', async () => {
137
137
  const op = findOp(ops, 'admin_tool_list');
138
138
  const allOps = [
139
139
  { name: 'admin_health', description: 'Health check', auth: 'read' },
140
140
  ];
141
+ const result = (await op.handler({ _allOps: allOps })) as Record<string, unknown>;
142
+ const routing = result.routing as Record<string, string>;
143
+ expect(routing).toBeDefined();
144
+ expect(typeof routing).toBe('object');
145
+ // Spot-check a few known intent signals
146
+ expect(routing['search knowledge']).toBe('vault.search_intelligent');
147
+ expect(routing['plan this']).toBe('plan.create_plan');
148
+ expect(routing['health check']).toBe('admin.admin_health');
149
+ });
150
+
151
+ it('returns routing hints in fallback mode', async () => {
152
+ const op = findOp(ops, 'admin_tool_list');
153
+ const result = (await op.handler({})) as Record<string, unknown>;
154
+ const routing = result.routing as Record<string, string>;
155
+ expect(routing).toBeDefined();
156
+ expect(Object.keys(routing).length).toBeGreaterThan(0);
157
+ });
158
+
159
+ it('returns verbose format when verbose=true', async () => {
160
+ const op = findOp(ops, 'admin_tool_list');
161
+ const allOps = [{ name: 'admin_health', description: 'Health check', auth: 'read' }];
141
162
  const result = (await op.handler({
142
163
  _allOps: allOps,
143
164
  verbose: true,
@@ -10,6 +10,7 @@ import { join, dirname } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
11
  import type { OpDefinition } from '../facades/types.js';
12
12
  import type { AgentRuntime } from './types.js';
13
+ import { ENGINE_MODULE_MANIFEST } from '../engine/module-manifest.js';
13
14
 
14
15
  /**
15
16
  * Resolve the @soleri/core package.json version.
@@ -113,6 +114,7 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
113
114
  return {
114
115
  count: allOps.length,
115
116
  ops: grouped,
117
+ routing: buildRoutingHints(),
116
118
  };
117
119
  }
118
120
  // Fallback — just describe admin ops
@@ -130,6 +132,7 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
130
132
  'admin_diagnostic',
131
133
  ],
132
134
  },
135
+ routing: buildRoutingHints(),
133
136
  };
134
137
  },
135
138
  },
@@ -321,6 +324,22 @@ function formatBytes(bytes: number): string {
321
324
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
322
325
  }
323
326
 
327
+ /**
328
+ * Build a flat routing map from ENGINE_MODULE_MANIFEST intentSignals.
329
+ * Keys are natural-language phrases, values are `{suffix}.{op}` paths.
330
+ */
331
+ function buildRoutingHints(): Record<string, string> {
332
+ const routing: Record<string, string> = {};
333
+ for (const mod of ENGINE_MODULE_MANIFEST) {
334
+ if (mod.intentSignals) {
335
+ for (const [phrase, op] of Object.entries(mod.intentSignals)) {
336
+ routing[phrase] = `${mod.suffix}.${op}`;
337
+ }
338
+ }
339
+ }
340
+ return routing;
341
+ }
342
+
324
343
  function formatUptime(seconds: number): string {
325
344
  if (seconds < 60) return `${seconds}s`;
326
345
  const minutes = Math.floor(seconds / 60);
@@ -42,9 +42,7 @@ vi.mock('./claude-md-helpers.js', () => ({
42
42
  }));
43
43
 
44
44
  vi.mock('../skills/sync-skills.js', () => ({
45
- discoverSkills: vi.fn(() => [
46
- { name: 'skill-1', path: '/mock/skills/skill-1' },
47
- ]),
45
+ discoverSkills: vi.fn(() => [{ name: 'skill-1', path: '/mock/skills/skill-1' }]),
48
46
  syncSkillsToClaudeCode: vi.fn(() => ({
49
47
  installed: ['skill-1'],
50
48
  updated: [],
@@ -158,7 +156,8 @@ describe('createAdminSetupOps', () => {
158
156
  });
159
157
 
160
158
  it('updates existing agent sections', async () => {
161
- mockFs['/some/project/CLAUDE.md'] = '# Project\n<!-- agent:test-agent -->\nOld\n<!-- /agent:test-agent -->';
159
+ mockFs['/some/project/CLAUDE.md'] =
160
+ '# Project\n<!-- agent:test-agent -->\nOld\n<!-- /agent:test-agent -->';
162
161
  const { hasSections } = await import('./claude-md-helpers.js');
163
162
  vi.mocked(hasSections).mockReturnValueOnce(true);
164
163
  const result = (await findOp(ops, 'admin_inject_claude_md').handler({
@@ -393,10 +393,17 @@ export function createAdminSetupOps(runtime: AgentRuntime): OpDefinition[] {
393
393
  }
394
394
 
395
395
  // 2. Skills — use shared sync (same logic as engine startup)
396
- let skillsResults: { installed: string[]; updated: string[]; skipped: string[]; failed: string[] };
396
+ let skillsResults: {
397
+ installed: string[];
398
+ updated: string[];
399
+ skipped: string[];
400
+ failed: string[];
401
+ };
397
402
  if (!hooksOnly && !settingsJsonOnly) {
398
403
  if (install) {
399
- const agentName = runtime.persona?.name ?? config.agentId.charAt(0).toUpperCase() + config.agentId.slice(1);
404
+ const agentName =
405
+ runtime.persona?.name ??
406
+ config.agentId.charAt(0).toUpperCase() + config.agentId.slice(1);
400
407
  skillsResults = syncSkillsToClaudeCode(skillsSourceDirs, agentName);
401
408
  } else {
402
409
  // Dry run — just discover what would be synced
@@ -182,7 +182,10 @@ describe('createArchiveOps', () => {
182
182
 
183
183
  describe('vault_find_expired', () => {
184
184
  it('finds expired entries', async () => {
185
- const result = (await findOp(ops, 'vault_find_expired').handler({})) as Record<string, unknown>;
185
+ const result = (await findOp(ops, 'vault_find_expired').handler({})) as Record<
186
+ string,
187
+ unknown
188
+ >;
186
189
  expect(result.count).toBe(1);
187
190
  });
188
191
  });