@soleri/core 2.9.0 → 2.11.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 (503) hide show
  1. package/dist/agency/agency-manager.d.ts +47 -0
  2. package/dist/agency/agency-manager.d.ts.map +1 -0
  3. package/dist/agency/agency-manager.js +281 -0
  4. package/dist/agency/agency-manager.js.map +1 -0
  5. package/dist/agency/index.d.ts +3 -0
  6. package/dist/agency/index.d.ts.map +1 -0
  7. package/dist/agency/index.js +2 -0
  8. package/dist/agency/index.js.map +1 -0
  9. package/dist/agency/types.d.ts +69 -0
  10. package/dist/agency/types.d.ts.map +1 -0
  11. package/dist/agency/types.js +5 -0
  12. package/dist/agency/types.js.map +1 -0
  13. package/dist/brain/brain.d.ts +0 -1
  14. package/dist/brain/brain.d.ts.map +1 -1
  15. package/dist/brain/brain.js +14 -0
  16. package/dist/brain/brain.js.map +1 -1
  17. package/dist/brain/intelligence.d.ts +5 -1
  18. package/dist/brain/intelligence.d.ts.map +1 -1
  19. package/dist/brain/intelligence.js +83 -0
  20. package/dist/brain/intelligence.js.map +1 -1
  21. package/dist/brain/types.d.ts +21 -0
  22. package/dist/brain/types.d.ts.map +1 -1
  23. package/dist/chat/agent-loop-types.d.ts +82 -0
  24. package/dist/chat/agent-loop-types.d.ts.map +1 -0
  25. package/dist/chat/agent-loop-types.js +8 -0
  26. package/dist/chat/agent-loop-types.js.map +1 -0
  27. package/dist/chat/agent-loop.d.ts +19 -0
  28. package/dist/chat/agent-loop.d.ts.map +1 -0
  29. package/dist/chat/agent-loop.js +261 -0
  30. package/dist/chat/agent-loop.js.map +1 -0
  31. package/dist/chat/auth-manager.d.ts +49 -0
  32. package/dist/chat/auth-manager.d.ts.map +1 -0
  33. package/dist/chat/auth-manager.js +152 -0
  34. package/dist/chat/auth-manager.js.map +1 -0
  35. package/dist/chat/browser-session.d.ts +86 -0
  36. package/dist/chat/browser-session.d.ts.map +1 -0
  37. package/dist/chat/browser-session.js +143 -0
  38. package/dist/chat/browser-session.js.map +1 -0
  39. package/dist/chat/cancellation.d.ts +54 -0
  40. package/dist/chat/cancellation.d.ts.map +1 -0
  41. package/dist/chat/cancellation.js +80 -0
  42. package/dist/chat/cancellation.js.map +1 -0
  43. package/dist/chat/chat-session.d.ts +86 -0
  44. package/dist/chat/chat-session.d.ts.map +1 -0
  45. package/dist/chat/chat-session.js +252 -0
  46. package/dist/chat/chat-session.js.map +1 -0
  47. package/dist/chat/file-handler.d.ts +63 -0
  48. package/dist/chat/file-handler.d.ts.map +1 -0
  49. package/dist/chat/file-handler.js +182 -0
  50. package/dist/chat/file-handler.js.map +1 -0
  51. package/dist/chat/fragment-buffer.d.ts +49 -0
  52. package/dist/chat/fragment-buffer.d.ts.map +1 -0
  53. package/dist/chat/fragment-buffer.js +130 -0
  54. package/dist/chat/fragment-buffer.js.map +1 -0
  55. package/dist/chat/index.d.ts +24 -0
  56. package/dist/chat/index.d.ts.map +1 -0
  57. package/dist/chat/index.js +15 -0
  58. package/dist/chat/index.js.map +1 -0
  59. package/dist/chat/mcp-bridge.d.ts +60 -0
  60. package/dist/chat/mcp-bridge.d.ts.map +1 -0
  61. package/dist/chat/mcp-bridge.js +111 -0
  62. package/dist/chat/mcp-bridge.js.map +1 -0
  63. package/dist/chat/notifications.d.ts +82 -0
  64. package/dist/chat/notifications.d.ts.map +1 -0
  65. package/dist/chat/notifications.js +119 -0
  66. package/dist/chat/notifications.js.map +1 -0
  67. package/dist/chat/output-compressor.d.ts +30 -0
  68. package/dist/chat/output-compressor.d.ts.map +1 -0
  69. package/dist/chat/output-compressor.js +95 -0
  70. package/dist/chat/output-compressor.js.map +1 -0
  71. package/dist/chat/queue.d.ts +91 -0
  72. package/dist/chat/queue.d.ts.map +1 -0
  73. package/dist/chat/queue.js +146 -0
  74. package/dist/chat/queue.js.map +1 -0
  75. package/dist/chat/response-chunker.d.ts +29 -0
  76. package/dist/chat/response-chunker.d.ts.map +1 -0
  77. package/dist/chat/response-chunker.js +163 -0
  78. package/dist/chat/response-chunker.js.map +1 -0
  79. package/dist/chat/self-update.d.ts +62 -0
  80. package/dist/chat/self-update.d.ts.map +1 -0
  81. package/dist/chat/self-update.js +90 -0
  82. package/dist/chat/self-update.js.map +1 -0
  83. package/dist/chat/types.d.ts +105 -0
  84. package/dist/chat/types.d.ts.map +1 -0
  85. package/dist/chat/types.js +8 -0
  86. package/dist/chat/types.js.map +1 -0
  87. package/dist/chat/voice.d.ts +39 -0
  88. package/dist/chat/voice.d.ts.map +1 -0
  89. package/dist/chat/voice.js +80 -0
  90. package/dist/chat/voice.js.map +1 -0
  91. package/dist/claudemd/compose.d.ts +31 -0
  92. package/dist/claudemd/compose.d.ts.map +1 -0
  93. package/dist/claudemd/compose.js +105 -0
  94. package/dist/claudemd/compose.js.map +1 -0
  95. package/dist/claudemd/index.d.ts +5 -0
  96. package/dist/claudemd/index.d.ts.map +1 -0
  97. package/dist/claudemd/index.js +3 -0
  98. package/dist/claudemd/index.js.map +1 -0
  99. package/dist/claudemd/inject.d.ts +31 -0
  100. package/dist/claudemd/inject.d.ts.map +1 -0
  101. package/dist/claudemd/inject.js +157 -0
  102. package/dist/claudemd/inject.js.map +1 -0
  103. package/dist/claudemd/types.d.ts +41 -0
  104. package/dist/claudemd/types.d.ts.map +1 -0
  105. package/dist/claudemd/types.js +5 -0
  106. package/dist/claudemd/types.js.map +1 -0
  107. package/dist/context/context-engine.d.ts +31 -0
  108. package/dist/context/context-engine.d.ts.map +1 -0
  109. package/dist/context/context-engine.js +245 -0
  110. package/dist/context/context-engine.js.map +1 -0
  111. package/dist/context/index.d.ts +3 -0
  112. package/dist/context/index.d.ts.map +1 -0
  113. package/dist/context/index.js +2 -0
  114. package/dist/context/index.js.map +1 -0
  115. package/dist/context/types.d.ts +54 -0
  116. package/dist/context/types.d.ts.map +1 -0
  117. package/dist/context/types.js +5 -0
  118. package/dist/context/types.js.map +1 -0
  119. package/dist/enforcement/adapters/claude-code.d.ts +18 -0
  120. package/dist/enforcement/adapters/claude-code.d.ts.map +1 -0
  121. package/dist/enforcement/adapters/claude-code.js +106 -0
  122. package/dist/enforcement/adapters/claude-code.js.map +1 -0
  123. package/dist/enforcement/adapters/index.d.ts +2 -0
  124. package/dist/enforcement/adapters/index.d.ts.map +1 -0
  125. package/dist/enforcement/adapters/index.js +2 -0
  126. package/dist/enforcement/adapters/index.js.map +1 -0
  127. package/dist/enforcement/index.d.ts +4 -0
  128. package/dist/enforcement/index.d.ts.map +1 -0
  129. package/dist/enforcement/index.js +3 -0
  130. package/dist/enforcement/index.js.map +1 -0
  131. package/dist/enforcement/registry.d.ts +23 -0
  132. package/dist/enforcement/registry.d.ts.map +1 -0
  133. package/dist/enforcement/registry.js +63 -0
  134. package/dist/enforcement/registry.js.map +1 -0
  135. package/dist/enforcement/types.d.ts +51 -0
  136. package/dist/enforcement/types.d.ts.map +1 -0
  137. package/dist/enforcement/types.js +8 -0
  138. package/dist/enforcement/types.js.map +1 -0
  139. package/dist/facades/facade-factory.d.ts +10 -3
  140. package/dist/facades/facade-factory.d.ts.map +1 -1
  141. package/dist/facades/facade-factory.js +94 -5
  142. package/dist/facades/facade-factory.js.map +1 -1
  143. package/dist/facades/types.d.ts +15 -1
  144. package/dist/facades/types.d.ts.map +1 -1
  145. package/dist/facades/types.js +6 -0
  146. package/dist/facades/types.js.map +1 -1
  147. package/dist/health/health-registry.d.ts +40 -0
  148. package/dist/health/health-registry.d.ts.map +1 -0
  149. package/dist/health/health-registry.js +134 -0
  150. package/dist/health/health-registry.js.map +1 -0
  151. package/dist/health/index.d.ts +5 -0
  152. package/dist/health/index.d.ts.map +1 -0
  153. package/dist/health/index.js +3 -0
  154. package/dist/health/index.js.map +1 -0
  155. package/dist/health/vault-integrity.d.ts +13 -0
  156. package/dist/health/vault-integrity.d.ts.map +1 -0
  157. package/dist/health/vault-integrity.js +49 -0
  158. package/dist/health/vault-integrity.js.map +1 -0
  159. package/dist/index.d.ts +67 -6
  160. package/dist/index.d.ts.map +1 -1
  161. package/dist/index.js +51 -3
  162. package/dist/index.js.map +1 -1
  163. package/dist/intake/intake-pipeline.d.ts +0 -7
  164. package/dist/intake/intake-pipeline.d.ts.map +1 -1
  165. package/dist/intake/intake-pipeline.js +1 -1
  166. package/dist/intake/intake-pipeline.js.map +1 -1
  167. package/dist/intelligence/types.d.ts +1 -0
  168. package/dist/intelligence/types.d.ts.map +1 -1
  169. package/dist/migrations/index.d.ts +6 -0
  170. package/dist/migrations/index.d.ts.map +1 -0
  171. package/dist/migrations/index.js +5 -0
  172. package/dist/migrations/index.js.map +1 -0
  173. package/dist/migrations/migration-runner.d.ts +51 -0
  174. package/dist/migrations/migration-runner.d.ts.map +1 -0
  175. package/dist/migrations/migration-runner.js +141 -0
  176. package/dist/migrations/migration-runner.js.map +1 -0
  177. package/dist/packs/index.d.ts +10 -0
  178. package/dist/packs/index.d.ts.map +1 -0
  179. package/dist/packs/index.js +8 -0
  180. package/dist/packs/index.js.map +1 -0
  181. package/dist/packs/lockfile.d.ts +97 -0
  182. package/dist/packs/lockfile.d.ts.map +1 -0
  183. package/dist/packs/lockfile.js +129 -0
  184. package/dist/packs/lockfile.js.map +1 -0
  185. package/dist/packs/pack-installer.d.ts +41 -0
  186. package/dist/packs/pack-installer.d.ts.map +1 -0
  187. package/dist/packs/pack-installer.js +253 -0
  188. package/dist/packs/pack-installer.js.map +1 -0
  189. package/dist/packs/resolver.d.ts +51 -0
  190. package/dist/packs/resolver.d.ts.map +1 -0
  191. package/dist/packs/resolver.js +195 -0
  192. package/dist/packs/resolver.js.map +1 -0
  193. package/dist/packs/types.d.ts +186 -0
  194. package/dist/packs/types.d.ts.map +1 -0
  195. package/dist/packs/types.js +69 -0
  196. package/dist/packs/types.js.map +1 -0
  197. package/dist/persistence/postgres-provider.d.ts +42 -7
  198. package/dist/persistence/postgres-provider.d.ts.map +1 -1
  199. package/dist/persistence/postgres-provider.js +187 -46
  200. package/dist/persistence/postgres-provider.js.map +1 -1
  201. package/dist/planning/gap-analysis.d.ts.map +1 -1
  202. package/dist/planning/gap-analysis.js +3 -1
  203. package/dist/planning/gap-analysis.js.map +1 -1
  204. package/dist/playbooks/index.d.ts +2 -0
  205. package/dist/playbooks/index.d.ts.map +1 -1
  206. package/dist/playbooks/index.js +2 -0
  207. package/dist/playbooks/index.js.map +1 -1
  208. package/dist/playbooks/playbook-executor.d.ts +100 -0
  209. package/dist/playbooks/playbook-executor.d.ts.map +1 -0
  210. package/dist/playbooks/playbook-executor.js +207 -0
  211. package/dist/playbooks/playbook-executor.js.map +1 -0
  212. package/dist/plugins/index.d.ts +7 -0
  213. package/dist/plugins/index.d.ts.map +1 -0
  214. package/dist/plugins/index.js +7 -0
  215. package/dist/plugins/index.js.map +1 -0
  216. package/dist/plugins/plugin-loader.d.ts +28 -0
  217. package/dist/plugins/plugin-loader.d.ts.map +1 -0
  218. package/dist/plugins/plugin-loader.js +150 -0
  219. package/dist/plugins/plugin-loader.js.map +1 -0
  220. package/dist/plugins/plugin-registry.d.ts +58 -0
  221. package/dist/plugins/plugin-registry.d.ts.map +1 -0
  222. package/dist/plugins/plugin-registry.js +157 -0
  223. package/dist/plugins/plugin-registry.js.map +1 -0
  224. package/dist/plugins/types.d.ts +180 -0
  225. package/dist/plugins/types.d.ts.map +1 -0
  226. package/dist/plugins/types.js +48 -0
  227. package/dist/plugins/types.js.map +1 -0
  228. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  229. package/dist/runtime/admin-extra-ops.js +181 -8
  230. package/dist/runtime/admin-extra-ops.js.map +1 -1
  231. package/dist/runtime/capture-ops.d.ts.map +1 -1
  232. package/dist/runtime/capture-ops.js +106 -7
  233. package/dist/runtime/capture-ops.js.map +1 -1
  234. package/dist/runtime/deprecation.d.ts +33 -0
  235. package/dist/runtime/deprecation.d.ts.map +1 -0
  236. package/dist/runtime/deprecation.js +41 -0
  237. package/dist/runtime/deprecation.js.map +1 -0
  238. package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
  239. package/dist/runtime/facades/admin-facade.js +12 -1
  240. package/dist/runtime/facades/admin-facade.js.map +1 -1
  241. package/dist/runtime/facades/agency-facade.d.ts +7 -0
  242. package/dist/runtime/facades/agency-facade.d.ts.map +1 -0
  243. package/dist/runtime/facades/agency-facade.js +103 -0
  244. package/dist/runtime/facades/agency-facade.js.map +1 -0
  245. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  246. package/dist/runtime/facades/brain-facade.js +58 -0
  247. package/dist/runtime/facades/brain-facade.js.map +1 -1
  248. package/dist/runtime/facades/chat-facade.d.ts +7 -0
  249. package/dist/runtime/facades/chat-facade.d.ts.map +1 -0
  250. package/dist/runtime/facades/chat-facade.js +808 -0
  251. package/dist/runtime/facades/chat-facade.js.map +1 -0
  252. package/dist/runtime/facades/context-facade.d.ts +7 -0
  253. package/dist/runtime/facades/context-facade.d.ts.map +1 -0
  254. package/dist/runtime/facades/context-facade.js +45 -0
  255. package/dist/runtime/facades/context-facade.js.map +1 -0
  256. package/dist/runtime/facades/index.d.ts.map +1 -1
  257. package/dist/runtime/facades/index.js +18 -0
  258. package/dist/runtime/facades/index.js.map +1 -1
  259. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  260. package/dist/runtime/facades/vault-facade.js +247 -1
  261. package/dist/runtime/facades/vault-facade.js.map +1 -1
  262. package/dist/runtime/feature-flags.d.ts +18 -0
  263. package/dist/runtime/feature-flags.d.ts.map +1 -0
  264. package/dist/runtime/feature-flags.js +90 -0
  265. package/dist/runtime/feature-flags.js.map +1 -0
  266. package/dist/runtime/pack-ops.d.ts +9 -0
  267. package/dist/runtime/pack-ops.d.ts.map +1 -0
  268. package/dist/runtime/pack-ops.js +76 -0
  269. package/dist/runtime/pack-ops.js.map +1 -0
  270. package/dist/runtime/playbook-ops.d.ts +3 -7
  271. package/dist/runtime/playbook-ops.d.ts.map +1 -1
  272. package/dist/runtime/playbook-ops.js +101 -10
  273. package/dist/runtime/playbook-ops.js.map +1 -1
  274. package/dist/runtime/plugin-ops.d.ts +9 -0
  275. package/dist/runtime/plugin-ops.d.ts.map +1 -0
  276. package/dist/runtime/plugin-ops.js +235 -0
  277. package/dist/runtime/plugin-ops.js.map +1 -0
  278. package/dist/runtime/runtime.d.ts.map +1 -1
  279. package/dist/runtime/runtime.js +72 -5
  280. package/dist/runtime/runtime.js.map +1 -1
  281. package/dist/runtime/telemetry-ops.d.ts +10 -0
  282. package/dist/runtime/telemetry-ops.d.ts.map +1 -0
  283. package/dist/runtime/telemetry-ops.js +53 -0
  284. package/dist/runtime/telemetry-ops.js.map +1 -0
  285. package/dist/runtime/types.d.ts +35 -0
  286. package/dist/runtime/types.d.ts.map +1 -1
  287. package/dist/runtime/vault-sharing-ops.d.ts +13 -0
  288. package/dist/runtime/vault-sharing-ops.d.ts.map +1 -0
  289. package/dist/runtime/vault-sharing-ops.js +345 -0
  290. package/dist/runtime/vault-sharing-ops.js.map +1 -0
  291. package/dist/streams/index.d.ts +1 -1
  292. package/dist/streams/index.d.ts.map +1 -1
  293. package/dist/streams/index.js +1 -1
  294. package/dist/streams/index.js.map +1 -1
  295. package/dist/streams/replayable-stream.d.ts +13 -1
  296. package/dist/streams/replayable-stream.d.ts.map +1 -1
  297. package/dist/streams/replayable-stream.js +27 -3
  298. package/dist/streams/replayable-stream.js.map +1 -1
  299. package/dist/text/similarity.d.ts +0 -1
  300. package/dist/text/similarity.d.ts.map +1 -1
  301. package/dist/text/similarity.js +1 -1
  302. package/dist/text/similarity.js.map +1 -1
  303. package/dist/transport/http-server.d.ts +56 -0
  304. package/dist/transport/http-server.d.ts.map +1 -0
  305. package/dist/transport/http-server.js +210 -0
  306. package/dist/transport/http-server.js.map +1 -0
  307. package/dist/transport/index.d.ts +11 -0
  308. package/dist/transport/index.d.ts.map +1 -0
  309. package/dist/transport/index.js +10 -0
  310. package/dist/transport/index.js.map +1 -0
  311. package/dist/transport/lsp-server.d.ts +140 -0
  312. package/dist/transport/lsp-server.d.ts.map +1 -0
  313. package/dist/transport/lsp-server.js +239 -0
  314. package/dist/transport/lsp-server.js.map +1 -0
  315. package/dist/transport/rate-limiter.d.ts +35 -0
  316. package/dist/transport/rate-limiter.d.ts.map +1 -0
  317. package/dist/transport/rate-limiter.js +72 -0
  318. package/dist/transport/rate-limiter.js.map +1 -0
  319. package/dist/transport/session-manager.d.ts +49 -0
  320. package/dist/transport/session-manager.d.ts.map +1 -0
  321. package/dist/transport/session-manager.js +83 -0
  322. package/dist/transport/session-manager.js.map +1 -0
  323. package/dist/transport/token-auth.d.ts +29 -0
  324. package/dist/transport/token-auth.d.ts.map +1 -0
  325. package/dist/transport/token-auth.js +84 -0
  326. package/dist/transport/token-auth.js.map +1 -0
  327. package/dist/transport/types.d.ts +61 -0
  328. package/dist/transport/types.d.ts.map +1 -0
  329. package/dist/transport/types.js +5 -0
  330. package/dist/transport/types.js.map +1 -0
  331. package/dist/transport/ws-server.d.ts +78 -0
  332. package/dist/transport/ws-server.d.ts.map +1 -0
  333. package/dist/transport/ws-server.js +342 -0
  334. package/dist/transport/ws-server.js.map +1 -0
  335. package/dist/vault/git-vault-sync.d.ts +107 -0
  336. package/dist/vault/git-vault-sync.d.ts.map +1 -0
  337. package/dist/vault/git-vault-sync.js +251 -0
  338. package/dist/vault/git-vault-sync.js.map +1 -0
  339. package/dist/vault/knowledge-review.d.ts +67 -0
  340. package/dist/vault/knowledge-review.d.ts.map +1 -0
  341. package/dist/vault/knowledge-review.js +133 -0
  342. package/dist/vault/knowledge-review.js.map +1 -0
  343. package/dist/vault/obsidian-sync.d.ts +94 -0
  344. package/dist/vault/obsidian-sync.d.ts.map +1 -0
  345. package/dist/vault/obsidian-sync.js +247 -0
  346. package/dist/vault/obsidian-sync.js.map +1 -0
  347. package/dist/vault/scope-detector.d.ts +31 -0
  348. package/dist/vault/scope-detector.d.ts.map +1 -0
  349. package/dist/vault/scope-detector.js +182 -0
  350. package/dist/vault/scope-detector.js.map +1 -0
  351. package/dist/vault/vault-branching.d.ts +71 -0
  352. package/dist/vault/vault-branching.d.ts.map +1 -0
  353. package/dist/vault/vault-branching.js +180 -0
  354. package/dist/vault/vault-branching.js.map +1 -0
  355. package/dist/vault/vault-manager.d.ts +89 -0
  356. package/dist/vault/vault-manager.d.ts.map +1 -0
  357. package/dist/vault/vault-manager.js +199 -0
  358. package/dist/vault/vault-manager.js.map +1 -0
  359. package/dist/vault/vault-types.d.ts +30 -0
  360. package/dist/vault/vault-types.d.ts.map +1 -0
  361. package/dist/vault/vault-types.js +10 -0
  362. package/dist/vault/vault-types.js.map +1 -0
  363. package/dist/vault/vault.d.ts +10 -0
  364. package/dist/vault/vault.d.ts.map +1 -1
  365. package/dist/vault/vault.js +36 -3
  366. package/dist/vault/vault.js.map +1 -1
  367. package/package.json +1 -1
  368. package/src/__tests__/admin-extra-ops.test.ts +31 -11
  369. package/src/__tests__/agency-manager.test.ts +374 -0
  370. package/src/__tests__/agent-loop.test.ts +256 -0
  371. package/src/__tests__/capture-ops.test.ts +275 -0
  372. package/src/__tests__/chat-differentiators.test.ts +251 -0
  373. package/src/__tests__/chat-enhanced.test.ts +390 -0
  374. package/src/__tests__/chat-transport.test.ts +665 -0
  375. package/src/__tests__/claudemd.test.ts +282 -0
  376. package/src/__tests__/context-engine.test.ts +256 -0
  377. package/src/__tests__/core-ops.test.ts +97 -5
  378. package/src/__tests__/deprecation.test.ts +78 -0
  379. package/src/__tests__/enforcement.test.ts +153 -0
  380. package/src/__tests__/facade-factory.test.ts +271 -0
  381. package/src/__tests__/feature-flags.test.ts +138 -0
  382. package/src/__tests__/git-vault-sync.test.ts +230 -0
  383. package/src/__tests__/health-registry.test.ts +173 -0
  384. package/src/__tests__/knowledge-review.test.ts +104 -0
  385. package/src/__tests__/lsp-transport.test.ts +442 -0
  386. package/src/__tests__/migration-runner.test.ts +170 -0
  387. package/src/__tests__/normalize.test.ts +10 -0
  388. package/src/__tests__/obsidian-sync.test.ts +354 -0
  389. package/src/__tests__/pack-lockfile.test.ts +261 -0
  390. package/src/__tests__/pack-ops.test.ts +146 -0
  391. package/src/__tests__/pack-system.test.ts +423 -0
  392. package/src/__tests__/playbook-executor.test.ts +249 -0
  393. package/src/__tests__/playbook-ops-execution.test.ts +189 -0
  394. package/src/__tests__/plugin-ops.test.ts +411 -0
  395. package/src/__tests__/plugin-system.test.ts +509 -0
  396. package/src/__tests__/postgres-provider.test.ts +64 -6
  397. package/src/__tests__/replayable-stream.test.ts +112 -1
  398. package/src/__tests__/scope-detector.test.ts +121 -0
  399. package/src/__tests__/session-lifecycle.test.ts +259 -0
  400. package/src/__tests__/transport.test.ts +758 -0
  401. package/src/__tests__/vault-branching.test.ts +274 -0
  402. package/src/__tests__/vault-connect.test.ts +179 -0
  403. package/src/__tests__/vault-integrity.test.ts +71 -0
  404. package/src/__tests__/vault-manager.test.ts +238 -0
  405. package/src/__tests__/vault-scaling.test.ts +281 -0
  406. package/src/__tests__/vault-sharing.test.ts +270 -0
  407. package/src/__tests__/ws-transport.test.ts +479 -0
  408. package/src/agency/agency-manager.ts +326 -0
  409. package/src/agency/index.ts +13 -0
  410. package/src/agency/types.ts +88 -0
  411. package/src/brain/brain.ts +15 -11
  412. package/src/brain/intelligence.ts +103 -0
  413. package/src/brain/types.ts +26 -0
  414. package/src/chat/agent-loop-types.ts +99 -0
  415. package/src/chat/agent-loop.ts +357 -0
  416. package/src/chat/auth-manager.ts +171 -0
  417. package/src/chat/browser-session.ts +188 -0
  418. package/src/chat/cancellation.ts +99 -0
  419. package/src/chat/chat-session.ts +283 -0
  420. package/src/chat/file-handler.ts +230 -0
  421. package/src/chat/fragment-buffer.ts +160 -0
  422. package/src/chat/index.ts +72 -0
  423. package/src/chat/mcp-bridge.ts +135 -0
  424. package/src/chat/notifications.ts +164 -0
  425. package/src/chat/output-compressor.ts +116 -0
  426. package/src/chat/queue.ts +208 -0
  427. package/src/chat/response-chunker.ts +200 -0
  428. package/src/chat/self-update.ts +117 -0
  429. package/src/chat/types.ts +126 -0
  430. package/src/chat/voice.ts +134 -0
  431. package/src/claudemd/compose.ts +142 -0
  432. package/src/claudemd/index.ts +17 -0
  433. package/src/claudemd/inject.ts +170 -0
  434. package/src/claudemd/types.ts +45 -0
  435. package/src/context/context-engine.ts +302 -0
  436. package/src/context/index.ts +11 -0
  437. package/src/context/types.ts +69 -0
  438. package/src/enforcement/adapters/claude-code.ts +135 -0
  439. package/src/enforcement/adapters/index.ts +1 -0
  440. package/src/enforcement/index.ts +10 -0
  441. package/src/enforcement/registry.ts +82 -0
  442. package/src/enforcement/types.ts +56 -0
  443. package/src/facades/facade-factory.ts +138 -5
  444. package/src/facades/types.ts +21 -0
  445. package/src/health/health-registry.ts +165 -0
  446. package/src/health/index.ts +11 -0
  447. package/src/health/vault-integrity.ts +66 -0
  448. package/src/index.ts +294 -2
  449. package/src/intake/intake-pipeline.ts +1 -1
  450. package/src/intelligence/types.ts +1 -0
  451. package/src/migrations/index.ts +6 -0
  452. package/src/migrations/migration-runner.ts +185 -0
  453. package/src/packs/index.ts +20 -0
  454. package/src/packs/lockfile.ts +180 -0
  455. package/src/packs/pack-installer.ts +289 -0
  456. package/src/packs/resolver.ts +237 -0
  457. package/src/packs/types.ts +125 -0
  458. package/src/persistence/postgres-provider.ts +211 -58
  459. package/src/planning/gap-analysis.ts +52 -7
  460. package/src/playbooks/index.ts +11 -0
  461. package/src/playbooks/playbook-executor.ts +301 -0
  462. package/src/plugins/index.ts +19 -0
  463. package/src/plugins/plugin-loader.ts +183 -0
  464. package/src/plugins/plugin-registry.ts +187 -0
  465. package/src/plugins/types.ts +119 -0
  466. package/src/runtime/admin-extra-ops.ts +193 -8
  467. package/src/runtime/capture-ops.ts +113 -8
  468. package/src/runtime/deprecation.ts +58 -0
  469. package/src/runtime/facades/admin-facade.ts +16 -1
  470. package/src/runtime/facades/agency-facade.ts +111 -0
  471. package/src/runtime/facades/brain-facade.ts +60 -0
  472. package/src/runtime/facades/chat-facade.ts +918 -0
  473. package/src/runtime/facades/context-facade.ts +55 -0
  474. package/src/runtime/facades/index.ts +22 -1
  475. package/src/runtime/facades/vault-facade.ts +261 -1
  476. package/src/runtime/feature-flags.ts +101 -0
  477. package/src/runtime/pack-ops.ts +85 -0
  478. package/src/runtime/playbook-ops.ts +113 -9
  479. package/src/runtime/plugin-ops.ts +258 -0
  480. package/src/runtime/runtime.ts +84 -5
  481. package/src/runtime/telemetry-ops.ts +57 -0
  482. package/src/runtime/types.ts +35 -0
  483. package/src/runtime/vault-sharing-ops.ts +372 -0
  484. package/src/streams/index.ts +1 -1
  485. package/src/streams/replayable-stream.ts +34 -3
  486. package/src/text/similarity.ts +1 -1
  487. package/src/transport/http-server.ts +269 -0
  488. package/src/transport/index.ts +48 -0
  489. package/src/transport/lsp-server.ts +401 -0
  490. package/src/transport/rate-limiter.ts +97 -0
  491. package/src/transport/session-manager.ts +120 -0
  492. package/src/transport/token-auth.ts +96 -0
  493. package/src/transport/types.ts +66 -0
  494. package/src/transport/ws-server.ts +415 -0
  495. package/src/vault/git-vault-sync.ts +318 -0
  496. package/src/vault/knowledge-review.ts +221 -0
  497. package/src/vault/obsidian-sync.ts +346 -0
  498. package/src/vault/scope-detector.ts +219 -0
  499. package/src/vault/vault-branching.ts +264 -0
  500. package/src/vault/vault-manager.ts +237 -0
  501. package/src/vault/vault-types.ts +50 -0
  502. package/src/vault/vault.ts +41 -3
  503. package/src/governance/index.ts +0 -18
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Pack Lockfile — persists installed pack metadata to `soleri.lock`.
3
+ *
4
+ * Tracks installed pack versions, types, sources, and integrity hashes
5
+ * for reproducible agent setups. The lockfile enables:
6
+ * - `soleri pack install --frozen` (CI mode — fail if lockfile is stale)
7
+ * - `soleri pack outdated` (compare installed vs. latest available)
8
+ * - Persistence across restarts (unlike the in-memory PackInstaller registry)
9
+ */
10
+
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
12
+ import { dirname } from 'node:path';
13
+ import { createHash } from 'node:crypto';
14
+
15
+ // ─── Types ────────────────────────────────────────────────────────────
16
+
17
+ export interface LockEntry {
18
+ /** Pack ID (from manifest) */
19
+ id: string;
20
+ /** Installed version (semver) */
21
+ version: string;
22
+ /** Pack type: hooks, skills, knowledge, domain, bundle */
23
+ type: PackType;
24
+ /** Where it was installed from */
25
+ source: PackSource;
26
+ /** Path to installed pack directory */
27
+ directory: string;
28
+ /** SHA-256 of the manifest file at install time */
29
+ integrity: string;
30
+ /** ISO timestamp of install */
31
+ installedAt: string;
32
+ /** Number of vault entries seeded */
33
+ vaultEntries: number;
34
+ /** Skill names discovered */
35
+ skills: string[];
36
+ /** Hook names discovered */
37
+ hooks: string[];
38
+ /** Whether facades were registered */
39
+ facadesRegistered: boolean;
40
+ /** Compatible @soleri/core version range (from manifest "soleri" field) */
41
+ soleriRange?: string;
42
+ }
43
+
44
+ export type PackType = 'hooks' | 'skills' | 'knowledge' | 'domain' | 'bundle';
45
+ export type PackSource = 'built-in' | 'local' | 'npm';
46
+
47
+ export interface LockfileData {
48
+ /** Lockfile format version */
49
+ version: 1;
50
+ /** Map of packId → lock entry */
51
+ packs: Record<string, LockEntry>;
52
+ }
53
+
54
+ // ─── Lockfile Manager ─────────────────────────────────────────────────
55
+
56
+ export class PackLockfile {
57
+ private data: LockfileData;
58
+ private dirty = false;
59
+
60
+ constructor(private readonly filePath: string) {
61
+ this.data = this.load();
62
+ }
63
+
64
+ /**
65
+ * Get a lock entry by pack ID.
66
+ */
67
+ get(packId: string): LockEntry | undefined {
68
+ return this.data.packs[packId];
69
+ }
70
+
71
+ /**
72
+ * Set (add or update) a lock entry.
73
+ */
74
+ set(entry: LockEntry): void {
75
+ this.data.packs[entry.id] = entry;
76
+ this.dirty = true;
77
+ }
78
+
79
+ /**
80
+ * Remove a lock entry.
81
+ */
82
+ remove(packId: string): boolean {
83
+ if (!(packId in this.data.packs)) return false;
84
+ delete this.data.packs[packId];
85
+ this.dirty = true;
86
+ return true;
87
+ }
88
+
89
+ /**
90
+ * List all lock entries.
91
+ */
92
+ list(): LockEntry[] {
93
+ return Object.values(this.data.packs);
94
+ }
95
+
96
+ /**
97
+ * Check if a pack is locked.
98
+ */
99
+ has(packId: string): boolean {
100
+ return packId in this.data.packs;
101
+ }
102
+
103
+ /**
104
+ * Get number of locked packs.
105
+ */
106
+ get size(): number {
107
+ return Object.keys(this.data.packs).length;
108
+ }
109
+
110
+ /**
111
+ * Save lockfile to disk (only if dirty).
112
+ */
113
+ save(): boolean {
114
+ if (!this.dirty) return false;
115
+ const dir = dirname(this.filePath);
116
+ if (!existsSync(dir)) {
117
+ mkdirSync(dir, { recursive: true });
118
+ }
119
+ writeFileSync(this.filePath, JSON.stringify(this.data, null, 2) + '\n', 'utf-8');
120
+ this.dirty = false;
121
+ return true;
122
+ }
123
+
124
+ /**
125
+ * Force reload from disk.
126
+ */
127
+ reload(): void {
128
+ this.data = this.load();
129
+ this.dirty = false;
130
+ }
131
+
132
+ /**
133
+ * Compute integrity hash for a manifest file.
134
+ */
135
+ static computeIntegrity(manifestPath: string): string {
136
+ if (!existsSync(manifestPath)) return '';
137
+ const content = readFileSync(manifestPath, 'utf-8');
138
+ return 'sha256-' + createHash('sha256').update(content).digest('hex');
139
+ }
140
+
141
+ private load(): LockfileData {
142
+ if (!existsSync(this.filePath)) {
143
+ return { version: 1, packs: {} };
144
+ }
145
+ try {
146
+ const raw = JSON.parse(readFileSync(this.filePath, 'utf-8'));
147
+ if (raw.version === 1 && typeof raw.packs === 'object') {
148
+ return raw as LockfileData;
149
+ }
150
+ return { version: 1, packs: {} };
151
+ } catch {
152
+ return { version: 1, packs: {} };
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Infer pack type from manifest content.
159
+ */
160
+ export function inferPackType(manifest: {
161
+ vault?: unknown;
162
+ skills?: unknown;
163
+ hooks?: unknown;
164
+ facades?: unknown[];
165
+ domains?: string[];
166
+ }): PackType {
167
+ const hasVault = !!manifest.vault;
168
+ const hasSkills = !!manifest.skills;
169
+ const hasHooks = !!manifest.hooks;
170
+ const hasFacades = (manifest.facades?.length ?? 0) > 0;
171
+
172
+ // Bundle if multiple content types
173
+ const types = [hasVault, hasSkills, hasHooks, hasFacades].filter(Boolean).length;
174
+ if (types > 1) return 'bundle';
175
+
176
+ if (hasHooks) return 'hooks';
177
+ if (hasSkills) return 'skills';
178
+ if (hasFacades) return 'domain';
179
+ return 'knowledge';
180
+ }
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Pack Installer — validates, loads vault entries, registers facades.
3
+ *
4
+ * Install flow:
5
+ * 1. Read and validate soleri-pack.json manifest
6
+ * 2. Load vault intelligence bundles from vault/ subdirectory
7
+ * 3. Register facades via plugin system
8
+ * 4. Discover skills and hooks
9
+ * 5. Return install summary
10
+ */
11
+
12
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
13
+ import { join, basename } from 'node:path';
14
+ import {
15
+ packManifestSchema,
16
+ type InstalledPack,
17
+ type InstallResult,
18
+ type ValidateResult,
19
+ } from './types.js';
20
+ import { loadIntelligenceData } from '../intelligence/loader.js';
21
+ import type { Vault } from '../vault/vault.js';
22
+ import type { PluginRegistry } from '../plugins/plugin-registry.js';
23
+ import type { PluginContext } from '../plugins/types.js';
24
+
25
+ const MANIFEST_FILENAME = 'soleri-pack.json';
26
+
27
+ // =============================================================================
28
+ // PACK REGISTRY (in-memory)
29
+ // =============================================================================
30
+
31
+ export class PackInstaller {
32
+ private packs = new Map<string, InstalledPack>();
33
+
34
+ constructor(
35
+ private vault: Vault,
36
+ private pluginRegistry: PluginRegistry,
37
+ ) {}
38
+
39
+ /**
40
+ * Validate a pack directory without installing.
41
+ */
42
+ validate(packDir: string): ValidateResult {
43
+ const errors: string[] = [];
44
+ const warnings: string[] = [];
45
+
46
+ // 1. Check manifest
47
+ const manifestPath = join(packDir, MANIFEST_FILENAME);
48
+ if (!existsSync(manifestPath)) {
49
+ return { valid: false, errors: [`No ${MANIFEST_FILENAME} found in ${packDir}`], warnings };
50
+ }
51
+
52
+ let raw: unknown;
53
+ try {
54
+ raw = JSON.parse(readFileSync(manifestPath, 'utf-8'));
55
+ } catch (e) {
56
+ return {
57
+ valid: false,
58
+ errors: [`Invalid JSON: ${e instanceof Error ? e.message : String(e)}`],
59
+ warnings,
60
+ };
61
+ }
62
+
63
+ const parseResult = packManifestSchema.safeParse(raw);
64
+ if (!parseResult.success) {
65
+ const issues = parseResult.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`);
66
+ return { valid: false, errors: issues, warnings };
67
+ }
68
+
69
+ const manifest = parseResult.data;
70
+
71
+ // 2. Count vault entries
72
+ let vaultEntries = 0;
73
+ const vaultDir = join(packDir, manifest.vault?.dir ?? 'vault');
74
+ if (existsSync(vaultDir)) {
75
+ const entries = loadIntelligenceData(vaultDir);
76
+ vaultEntries = entries.length;
77
+ } else if (manifest.vault) {
78
+ warnings.push(`Vault directory "${manifest.vault.dir}" declared but not found`);
79
+ }
80
+
81
+ // 3. Count skills
82
+ const skillsDir = join(packDir, manifest.skills?.dir ?? 'skills');
83
+ const skills = existsSync(skillsDir) ? listMarkdownFiles(skillsDir) : [];
84
+ if (manifest.skills && skills.length === 0) {
85
+ warnings.push(`Skills directory "${manifest.skills.dir}" declared but empty or not found`);
86
+ }
87
+
88
+ // 4. Count hooks
89
+ const hooksDir = join(packDir, manifest.hooks?.dir ?? 'hooks');
90
+ const hooks = existsSync(hooksDir) ? listMarkdownFiles(hooksDir) : [];
91
+ if (manifest.hooks && hooks.length === 0) {
92
+ warnings.push(`Hooks directory "${manifest.hooks.dir}" declared but empty or not found`);
93
+ }
94
+
95
+ // 5. Count facades/ops
96
+ const facades = manifest.facades.length;
97
+ const ops = manifest.facades.reduce((sum, f) => sum + f.ops.length, 0);
98
+
99
+ return {
100
+ valid: true,
101
+ manifest,
102
+ errors,
103
+ warnings,
104
+ counts: { vaultEntries, skills: skills.length, hooks: hooks.length, facades, ops },
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Install a knowledge pack from a directory.
110
+ */
111
+ async install(packDir: string, runtimeCtx?: unknown): Promise<InstallResult> {
112
+ // Validate first
113
+ const validation = this.validate(packDir);
114
+ if (!validation.valid || !validation.manifest) {
115
+ return {
116
+ id: 'unknown',
117
+ installed: false,
118
+ vaultEntries: 0,
119
+ skills: [],
120
+ hooks: [],
121
+ facades: 0,
122
+ error: validation.errors.join('; '),
123
+ };
124
+ }
125
+
126
+ const manifest = validation.manifest;
127
+
128
+ // Check duplicate
129
+ if (this.packs.has(manifest.id)) {
130
+ return {
131
+ id: manifest.id,
132
+ installed: false,
133
+ vaultEntries: 0,
134
+ skills: [],
135
+ hooks: [],
136
+ facades: 0,
137
+ error: `Pack "${manifest.id}" is already installed`,
138
+ };
139
+ }
140
+
141
+ try {
142
+ // 1. Seed vault entries
143
+ let vaultEntries = 0;
144
+ const vaultDir = join(packDir, manifest.vault?.dir ?? 'vault');
145
+ if (existsSync(vaultDir)) {
146
+ const entries = loadIntelligenceData(vaultDir);
147
+ if (entries.length > 0) {
148
+ this.vault.seed(entries);
149
+ vaultEntries = entries.length;
150
+ }
151
+ }
152
+
153
+ // 2. Register facades via plugin system
154
+ let facadesRegistered = false;
155
+ if (manifest.facades.length > 0) {
156
+ // Convert pack manifest to plugin-compatible format for registry
157
+ const pluginLoaded = {
158
+ manifest: {
159
+ id: manifest.id,
160
+ name: manifest.name,
161
+ version: manifest.version,
162
+ description: manifest.description,
163
+ domain: manifest.domains[0],
164
+ dependencies: manifest.dependencies,
165
+ facades: manifest.facades,
166
+ intelligence: [],
167
+ },
168
+ directory: packDir,
169
+ provenance: 'custom' as const,
170
+ };
171
+
172
+ if (!this.pluginRegistry.get(manifest.id)) {
173
+ this.pluginRegistry.register(pluginLoaded);
174
+ }
175
+
176
+ const ctx: PluginContext = {
177
+ runtime: runtimeCtx ?? {},
178
+ manifest: pluginLoaded.manifest,
179
+ directory: packDir,
180
+ };
181
+
182
+ await this.pluginRegistry.activate(manifest.id, ctx);
183
+ facadesRegistered = true;
184
+ }
185
+
186
+ // 3. Discover skills
187
+ const skillsDir = join(packDir, manifest.skills?.dir ?? 'skills');
188
+ const skills = existsSync(skillsDir) ? listMarkdownFiles(skillsDir) : [];
189
+
190
+ // 4. Discover hooks
191
+ const hooksDir = join(packDir, manifest.hooks?.dir ?? 'hooks');
192
+ const hooks = existsSync(hooksDir) ? listMarkdownFiles(hooksDir) : [];
193
+
194
+ // Track installed pack
195
+ const installed: InstalledPack = {
196
+ id: manifest.id,
197
+ manifest,
198
+ directory: packDir,
199
+ status: 'installed',
200
+ vaultEntries,
201
+ skills,
202
+ hooks,
203
+ facadesRegistered,
204
+ installedAt: Date.now(),
205
+ };
206
+ this.packs.set(manifest.id, installed);
207
+
208
+ return {
209
+ id: manifest.id,
210
+ installed: true,
211
+ vaultEntries,
212
+ skills,
213
+ hooks,
214
+ facades: manifest.facades.length,
215
+ };
216
+ } catch (e) {
217
+ const error = e instanceof Error ? e.message : String(e);
218
+
219
+ this.packs.set(manifest.id, {
220
+ id: manifest.id,
221
+ manifest,
222
+ directory: packDir,
223
+ status: 'error',
224
+ error,
225
+ vaultEntries: 0,
226
+ skills: [],
227
+ hooks: [],
228
+ facadesRegistered: false,
229
+ installedAt: Date.now(),
230
+ });
231
+
232
+ return {
233
+ id: manifest.id,
234
+ installed: false,
235
+ vaultEntries: 0,
236
+ skills: [],
237
+ hooks: [],
238
+ facades: 0,
239
+ error,
240
+ };
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Uninstall a pack — deactivates facades, removes from registry.
246
+ * Note: vault entries are NOT removed (they become part of knowledge).
247
+ */
248
+ uninstall(packId: string): boolean {
249
+ const pack = this.packs.get(packId);
250
+ if (!pack) return false;
251
+
252
+ // Deactivate facades
253
+ if (pack.facadesRegistered) {
254
+ this.pluginRegistry.deactivate(packId);
255
+ }
256
+
257
+ pack.status = 'uninstalled';
258
+ this.packs.delete(packId);
259
+ return true;
260
+ }
261
+
262
+ /**
263
+ * Get an installed pack by ID.
264
+ */
265
+ get(packId: string): InstalledPack | undefined {
266
+ return this.packs.get(packId);
267
+ }
268
+
269
+ /**
270
+ * List all installed packs.
271
+ */
272
+ list(): InstalledPack[] {
273
+ return Array.from(this.packs.values());
274
+ }
275
+ }
276
+
277
+ // =============================================================================
278
+ // HELPERS
279
+ // =============================================================================
280
+
281
+ function listMarkdownFiles(dir: string): string[] {
282
+ try {
283
+ return readdirSync(dir)
284
+ .filter((f) => f.endsWith('.md'))
285
+ .map((f) => basename(f, '.md'));
286
+ } catch {
287
+ return [];
288
+ }
289
+ }
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Pack Resolver — resolution chain for discovering packs.
3
+ *
4
+ * Resolution order:
5
+ * 1. Local file path (absolute or relative)
6
+ * 2. Built-in packs (bundled with the agent)
7
+ * 3. npm registry (@soleri/pack-* or soleri-pack-*)
8
+ *
9
+ * Each source implements the PackSource interface.
10
+ */
11
+
12
+ import { existsSync } from 'node:fs';
13
+ import { resolve, join } from 'node:path';
14
+ import { execFileSync } from 'node:child_process';
15
+ import { mkdirSync, readdirSync } from 'node:fs';
16
+ import { tmpdir } from 'node:os';
17
+
18
+ // ─── Types ────────────────────────────────────────────────────────────
19
+
20
+ export interface ResolvedPack {
21
+ /** Local directory path to the pack */
22
+ directory: string;
23
+ /** Where it was resolved from */
24
+ source: 'local' | 'built-in' | 'npm';
25
+ /** npm package name (if resolved from npm) */
26
+ npmPackage?: string;
27
+ /** Resolved version (if from npm) */
28
+ resolvedVersion?: string;
29
+ }
30
+
31
+ export interface ResolveOptions {
32
+ /** Built-in pack directories to search */
33
+ builtinDirs?: string[];
34
+ /** Whether to try npm resolution. Default: true */
35
+ npm?: boolean;
36
+ /** Specific version to install (e.g., "1.2.0") */
37
+ version?: string;
38
+ /** npm timeout in ms. Default: 30_000 */
39
+ npmTimeout?: number;
40
+ }
41
+
42
+ // ─── Resolver ─────────────────────────────────────────────────────────
43
+
44
+ /**
45
+ * Resolve a pack identifier to a local directory path.
46
+ *
47
+ * @param pack - Local path, built-in name, or npm package name
48
+ * @param options - Resolution options
49
+ * @returns Resolved pack directory and source metadata
50
+ */
51
+ export function resolvePack(pack: string, options: ResolveOptions = {}): ResolvedPack {
52
+ // 1. Local path — absolute or relative, or existing directory
53
+ if (pack.startsWith('/') || pack.startsWith('.') || existsSync(pack)) {
54
+ const dir = resolve(pack);
55
+ if (!existsSync(dir)) {
56
+ throw new Error(`Local pack directory not found: ${dir}`);
57
+ }
58
+ return { directory: dir, source: 'local' };
59
+ }
60
+
61
+ // 2. Built-in packs — search configured directories
62
+ if (options.builtinDirs) {
63
+ for (const builtinDir of options.builtinDirs) {
64
+ const packDir = join(builtinDir, pack);
65
+ if (existsSync(packDir) && existsSync(join(packDir, 'soleri-pack.json'))) {
66
+ return { directory: packDir, source: 'built-in' };
67
+ }
68
+ }
69
+ }
70
+
71
+ // 3. npm registry
72
+ if (options.npm !== false) {
73
+ return resolveFromNpm(pack, options);
74
+ }
75
+
76
+ throw new Error(
77
+ `Pack "${pack}" not found. Checked: local path, built-in directories${options.npm !== false ? ', npm registry' : ''}.`,
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Resolve a pack from the npm registry.
83
+ * Downloads via `npm pack` and extracts to a temp directory.
84
+ */
85
+ function resolveFromNpm(pack: string, options: ResolveOptions): ResolvedPack {
86
+ // Normalize npm package name
87
+ const npmName = pack.startsWith('@')
88
+ ? pack.replace(/@[^/]*$/, '') // Strip version from scoped: @scope/name@1.0 → @scope/name
89
+ : pack.includes('/')
90
+ ? pack
91
+ : `@soleri/pack-${pack.replace(/@.*$/, '')}`;
92
+
93
+ // Extract version
94
+ const version =
95
+ options.version ??
96
+ (pack.includes('@') && !pack.startsWith('@')
97
+ ? pack.split('@').pop()
98
+ : pack.startsWith('@') && pack.split('@').length === 3
99
+ ? pack.split('@').pop()
100
+ : undefined);
101
+
102
+ const spec = version ? `${npmName}@${version}` : npmName;
103
+ const tmpDir = join(tmpdir(), `soleri-pack-${Date.now()}`);
104
+ mkdirSync(tmpDir, { recursive: true });
105
+
106
+ try {
107
+ execFileSync('npm', ['pack', spec, '--pack-destination', tmpDir], {
108
+ stdio: 'pipe',
109
+ timeout: options.npmTimeout ?? 30_000,
110
+ });
111
+
112
+ // Find the tarball
113
+ const files = readdirSync(tmpDir).filter((f) => f.endsWith('.tgz'));
114
+ if (files.length === 0) {
115
+ throw new Error(`No tarball found after npm pack ${spec}`);
116
+ }
117
+
118
+ const extractDir = join(tmpDir, 'extracted');
119
+ mkdirSync(extractDir, { recursive: true });
120
+ execFileSync('tar', ['xzf', join(tmpDir, files[0]), '-C', extractDir], {
121
+ stdio: 'pipe',
122
+ timeout: 15_000,
123
+ });
124
+
125
+ // npm pack extracts to a 'package/' subdirectory
126
+ const packageDir = join(extractDir, 'package');
127
+ if (!existsSync(packageDir)) {
128
+ throw new Error(`Extracted package directory not found at ${packageDir}`);
129
+ }
130
+
131
+ // Extract resolved version from package.json if present
132
+ let resolvedVersion: string | undefined;
133
+ try {
134
+ const pkgPath = join(packageDir, 'package.json');
135
+ if (existsSync(pkgPath)) {
136
+ const { readFileSync } = require('node:fs');
137
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
138
+ resolvedVersion = pkg.version;
139
+ }
140
+ } catch {
141
+ // Version extraction is best-effort
142
+ }
143
+
144
+ return {
145
+ directory: packageDir,
146
+ source: 'npm',
147
+ npmPackage: npmName,
148
+ resolvedVersion,
149
+ };
150
+ } catch (e) {
151
+ const msg = e instanceof Error ? e.message : String(e);
152
+ throw new Error(`Failed to resolve "${spec}" from npm: ${msg}`, { cause: e });
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Check basic semver compatibility: does `version` satisfy `range`?
158
+ *
159
+ * Supports: ">=1.0.0", ">=1.0.0 <2.0.0", "^1.2.3", exact version "1.0.0".
160
+ * Returns true if range is empty/undefined (no constraint).
161
+ */
162
+ export function checkVersionCompat(version: string, range?: string): boolean {
163
+ if (!range) return true;
164
+ const r = range.trim();
165
+ if (!r) return true;
166
+
167
+ const v = parseVer(version);
168
+ if (!v) return true; // Can't parse → don't block
169
+
170
+ // Handle caret: ^1.2.3 means >=1.2.3 <2.0.0
171
+ if (r.startsWith('^')) {
172
+ const base = parseVer(r.slice(1));
173
+ if (!base) return true;
174
+ return compareVer(v, base) >= 0 && v[0] === base[0];
175
+ }
176
+
177
+ // Handle tilde: ~1.2.3 means >=1.2.3 <1.3.0
178
+ if (r.startsWith('~')) {
179
+ const base = parseVer(r.slice(1));
180
+ if (!base) return true;
181
+ return compareVer(v, base) >= 0 && v[0] === base[0] && v[1] === base[1];
182
+ }
183
+
184
+ // Handle compound ranges: ">=1.0.0 <2.0.0"
185
+ const parts = r.split(/\s+/).filter(Boolean);
186
+ return parts.every((part) => {
187
+ if (part.startsWith('>=')) {
188
+ const base = parseVer(part.slice(2));
189
+ return base ? compareVer(v, base) >= 0 : true;
190
+ }
191
+ if (part.startsWith('>')) {
192
+ const base = parseVer(part.slice(1));
193
+ return base ? compareVer(v, base) > 0 : true;
194
+ }
195
+ if (part.startsWith('<=')) {
196
+ const base = parseVer(part.slice(2));
197
+ return base ? compareVer(v, base) <= 0 : true;
198
+ }
199
+ if (part.startsWith('<')) {
200
+ const base = parseVer(part.slice(1));
201
+ return base ? compareVer(v, base) < 0 : true;
202
+ }
203
+ // Exact match
204
+ const exact = parseVer(part);
205
+ return exact ? compareVer(v, exact) === 0 : true;
206
+ });
207
+ }
208
+
209
+ type SemVer = [number, number, number];
210
+
211
+ function parseVer(s: string): SemVer | null {
212
+ const m = s.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
213
+ return m ? [+m[1], +m[2], +m[3]] : null;
214
+ }
215
+
216
+ function compareVer(a: SemVer, b: SemVer): number {
217
+ for (let i = 0; i < 3; i++) {
218
+ if (a[i] !== b[i]) return a[i] - b[i];
219
+ }
220
+ return 0;
221
+ }
222
+
223
+ /**
224
+ * Check if a newer version is available on npm.
225
+ * Returns the latest version string, or null if check fails.
226
+ */
227
+ export function checkNpmVersion(npmPackage: string, timeout = 10_000): string | null {
228
+ try {
229
+ const output = execFileSync('npm', ['view', npmPackage, 'version'], {
230
+ stdio: 'pipe',
231
+ timeout,
232
+ });
233
+ return output.toString().trim() || null;
234
+ } catch {
235
+ return null;
236
+ }
237
+ }