@skaile/workspaces 5.7.0 → 5.9.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 (419) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/app-sdk/index.js +1 -1
  3. package/dist/{asset-feeds-K36CR52Q.js → asset-feeds-XMKKUUK6.js} +54 -30
  4. package/dist/asset-feeds-XMKKUUK6.js.map +1 -0
  5. package/dist/asset-manager/catalog-deployer.js +9 -9
  6. package/dist/asset-manager/index.js +15 -15
  7. package/dist/asset-manager/installer.js +11 -11
  8. package/dist/asset-manager/renderers.js +10 -10
  9. package/dist/asset-manager/src/index.d.ts +268 -24
  10. package/dist/asset-manager/src/index.d.ts.map +1 -1
  11. package/dist/asset-manager/src/install-agent.d.ts +11 -0
  12. package/dist/asset-manager/src/install-agent.d.ts.map +1 -1
  13. package/dist/asset-manager/src/installer.d.ts +2 -2
  14. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  15. package/dist/bridge/drivers/claude-sdk.js +301 -212
  16. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  17. package/dist/bridge/drivers/codex.js +92 -66
  18. package/dist/bridge/drivers/codex.js.map +1 -1
  19. package/dist/bridge/drivers/continue.js +14 -15
  20. package/dist/bridge/drivers/continue.js.map +1 -1
  21. package/dist/bridge/drivers/echo.js +4 -4
  22. package/dist/bridge/drivers/gemini.js +8 -8
  23. package/dist/bridge/drivers/goose.js +14 -15
  24. package/dist/bridge/drivers/goose.js.map +1 -1
  25. package/dist/bridge/drivers/omp.js +70 -104
  26. package/dist/bridge/drivers/omp.js.map +1 -1
  27. package/dist/bridge/drivers/opencode.js +14 -15
  28. package/dist/bridge/drivers/opencode.js.map +1 -1
  29. package/dist/bridge/drivers/qwen.js +8 -8
  30. package/dist/bridge/index.js +16 -17
  31. package/dist/bridge/src/codex/managed-driver.d.ts +13 -10
  32. package/dist/bridge/src/codex/managed-driver.d.ts.map +1 -1
  33. package/dist/bridge/src/drivers/_subprocess-cli.d.ts +12 -2
  34. package/dist/bridge/src/drivers/_subprocess-cli.d.ts.map +1 -1
  35. package/dist/bridge/src/drivers/claude-sdk.d.ts +67 -62
  36. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  37. package/dist/bridge/src/drivers/claude-tool-allowlist.d.ts +43 -0
  38. package/dist/bridge/src/drivers/claude-tool-allowlist.d.ts.map +1 -0
  39. package/dist/bridge/src/drivers/codex.d.ts +26 -10
  40. package/dist/bridge/src/drivers/codex.d.ts.map +1 -1
  41. package/dist/bridge/src/drivers/continue.d.ts +2 -2
  42. package/dist/bridge/src/drivers/continue.d.ts.map +1 -1
  43. package/dist/bridge/src/drivers/gemini.d.ts +2 -2
  44. package/dist/bridge/src/drivers/gemini.d.ts.map +1 -1
  45. package/dist/bridge/src/drivers/goose.d.ts +2 -2
  46. package/dist/bridge/src/drivers/goose.d.ts.map +1 -1
  47. package/dist/bridge/src/drivers/omp.d.ts +24 -35
  48. package/dist/bridge/src/drivers/omp.d.ts.map +1 -1
  49. package/dist/bridge/src/drivers/opencode.d.ts +2 -2
  50. package/dist/bridge/src/drivers/opencode.d.ts.map +1 -1
  51. package/dist/bridge/src/drivers/recovery-policy.d.ts +79 -0
  52. package/dist/bridge/src/drivers/recovery-policy.d.ts.map +1 -0
  53. package/dist/bridge/src/index.d.ts +1 -0
  54. package/dist/bridge/src/index.d.ts.map +1 -1
  55. package/dist/bridge/src/reconfigure-ledger.d.ts +117 -0
  56. package/dist/bridge/src/reconfigure-ledger.d.ts.map +1 -0
  57. package/dist/bridge/src/types.d.ts +77 -7
  58. package/dist/bridge/src/types.d.ts.map +1 -1
  59. package/dist/{chunk-WIEC3ZE4.js → chunk-2FRUUDZY.js} +5 -5
  60. package/dist/{chunk-WIEC3ZE4.js.map → chunk-2FRUUDZY.js.map} +1 -1
  61. package/dist/{chunk-NARITJS2.js → chunk-2H73V6XR.js} +3 -3
  62. package/dist/{chunk-NARITJS2.js.map → chunk-2H73V6XR.js.map} +1 -1
  63. package/dist/{chunk-O4Z7MAW7.js → chunk-2LWYNQK7.js} +3 -3
  64. package/dist/{chunk-O4Z7MAW7.js.map → chunk-2LWYNQK7.js.map} +1 -1
  65. package/dist/{chunk-OOLT6HGP.js → chunk-36AFURKZ.js} +7 -7
  66. package/dist/{chunk-OOLT6HGP.js.map → chunk-36AFURKZ.js.map} +1 -1
  67. package/dist/{chunk-DFFD4BFG.js → chunk-4MQS7CAF.js} +174 -86
  68. package/dist/chunk-4MQS7CAF.js.map +1 -0
  69. package/dist/{chunk-RNPHRRLG.js → chunk-4PTSR4UU.js} +3 -3
  70. package/dist/{chunk-RNPHRRLG.js.map → chunk-4PTSR4UU.js.map} +1 -1
  71. package/dist/{chunk-BTK7ECLH.js → chunk-5NNNV76P.js} +41 -8
  72. package/dist/chunk-5NNNV76P.js.map +1 -0
  73. package/dist/{chunk-IZLQ4GDO.js → chunk-7AI3Q5AQ.js} +43 -21
  74. package/dist/chunk-7AI3Q5AQ.js.map +1 -0
  75. package/dist/{chunk-NFGRW3L7.js → chunk-7RGTAEG6.js} +3 -3
  76. package/dist/{chunk-NFGRW3L7.js.map → chunk-7RGTAEG6.js.map} +1 -1
  77. package/dist/{chunk-XBSQ4KMC.js → chunk-B4X2N7JQ.js} +205 -3
  78. package/dist/chunk-B4X2N7JQ.js.map +1 -0
  79. package/dist/{chunk-FRHE7FFB.js → chunk-B6GIW55K.js} +5 -3
  80. package/dist/chunk-B6GIW55K.js.map +1 -0
  81. package/dist/{chunk-RVIZQ5XD.js → chunk-BTE2V4IW.js} +3 -3
  82. package/dist/{chunk-RVIZQ5XD.js.map → chunk-BTE2V4IW.js.map} +1 -1
  83. package/dist/{chunk-EB4XN3KE.js → chunk-CGT45WN7.js} +3 -3
  84. package/dist/{chunk-EB4XN3KE.js.map → chunk-CGT45WN7.js.map} +1 -1
  85. package/dist/{chunk-FAP63KV7.js → chunk-CKQEIY43.js} +4 -4
  86. package/dist/{chunk-FAP63KV7.js.map → chunk-CKQEIY43.js.map} +1 -1
  87. package/dist/{chunk-NWQB7UTY.js → chunk-DNZYOKOJ.js} +3 -3
  88. package/dist/{chunk-NWQB7UTY.js.map → chunk-DNZYOKOJ.js.map} +1 -1
  89. package/dist/{chunk-AVCUPPG6.js → chunk-EF7NPDDU.js} +4 -4
  90. package/dist/{chunk-AVCUPPG6.js.map → chunk-EF7NPDDU.js.map} +1 -1
  91. package/dist/chunk-EPBKISS5.js +84 -0
  92. package/dist/chunk-EPBKISS5.js.map +1 -0
  93. package/dist/{chunk-DMLDYFDS.js → chunk-F4NJDSR7.js} +9 -5
  94. package/dist/chunk-F4NJDSR7.js.map +1 -0
  95. package/dist/{chunk-PSA6ONFB.js → chunk-FVJK4QWM.js} +4 -4
  96. package/dist/{chunk-PSA6ONFB.js.map → chunk-FVJK4QWM.js.map} +1 -1
  97. package/dist/{chunk-KNWCFEWU.js → chunk-FYXZRVID.js} +20 -7
  98. package/dist/{chunk-KNWCFEWU.js.map → chunk-FYXZRVID.js.map} +1 -1
  99. package/dist/{chunk-LBQ4BBX4.js → chunk-FZNG4V4J.js} +16 -30
  100. package/dist/chunk-FZNG4V4J.js.map +1 -0
  101. package/dist/{chunk-SWO25LTO.js → chunk-GB7WDMH2.js} +4 -4
  102. package/dist/{chunk-SWO25LTO.js.map → chunk-GB7WDMH2.js.map} +1 -1
  103. package/dist/{chunk-EU3Q5AQ4.js → chunk-GKMZ6THE.js} +23 -32
  104. package/dist/chunk-GKMZ6THE.js.map +1 -0
  105. package/dist/{chunk-TTPSDC2G.js → chunk-GTILM2CI.js} +34 -13
  106. package/dist/chunk-GTILM2CI.js.map +1 -0
  107. package/dist/{chunk-LXY4BUA3.js → chunk-GXLQLPE5.js} +9 -5
  108. package/dist/chunk-GXLQLPE5.js.map +1 -0
  109. package/dist/{chunk-D5HOETOB.js → chunk-IMKSM5NL.js} +981 -126
  110. package/dist/chunk-IMKSM5NL.js.map +1 -0
  111. package/dist/{chunk-6UFFKMVC.js → chunk-IR33D6EP.js} +6 -6
  112. package/dist/{chunk-6UFFKMVC.js.map → chunk-IR33D6EP.js.map} +1 -1
  113. package/dist/{chunk-CT6NQSA4.js → chunk-ISHJITXJ.js} +3 -3
  114. package/dist/{chunk-CT6NQSA4.js.map → chunk-ISHJITXJ.js.map} +1 -1
  115. package/dist/chunk-IZCR27MU.js +60 -0
  116. package/dist/chunk-IZCR27MU.js.map +1 -0
  117. package/dist/{chunk-GWXFSLBK.js → chunk-J7U3PIOE.js} +4 -4
  118. package/dist/{chunk-GWXFSLBK.js.map → chunk-J7U3PIOE.js.map} +1 -1
  119. package/dist/{chunk-LCU5U5IW.js → chunk-JAKOMFC6.js} +67 -19
  120. package/dist/chunk-JAKOMFC6.js.map +1 -0
  121. package/dist/{chunk-THEJJBOU.js → chunk-JZ5EHBPI.js} +3 -3
  122. package/dist/{chunk-THEJJBOU.js.map → chunk-JZ5EHBPI.js.map} +1 -1
  123. package/dist/{chunk-BWZF2ZIF.js → chunk-K3CVFRVS.js} +3 -3
  124. package/dist/{chunk-BWZF2ZIF.js.map → chunk-K3CVFRVS.js.map} +1 -1
  125. package/dist/{chunk-VOOI7B2L.js → chunk-LAMCXQGA.js} +241 -73
  126. package/dist/chunk-LAMCXQGA.js.map +1 -0
  127. package/dist/{chunk-37FTLYFP.js → chunk-LNFKL23K.js} +3 -3
  128. package/dist/{chunk-37FTLYFP.js.map → chunk-LNFKL23K.js.map} +1 -1
  129. package/dist/{chunk-XUUYAS3Z.js → chunk-M3WPBTGJ.js} +3 -3
  130. package/dist/{chunk-XUUYAS3Z.js.map → chunk-M3WPBTGJ.js.map} +1 -1
  131. package/dist/{chunk-QZLWJETK.js → chunk-MGGFBZWA.js} +3 -3
  132. package/dist/{chunk-QZLWJETK.js.map → chunk-MGGFBZWA.js.map} +1 -1
  133. package/dist/{chunk-34C42MJX.js → chunk-MR5HBOUF.js} +5 -5
  134. package/dist/{chunk-34C42MJX.js.map → chunk-MR5HBOUF.js.map} +1 -1
  135. package/dist/{chunk-QCI6D7E4.js → chunk-NUELZWCF.js} +266 -170
  136. package/dist/chunk-NUELZWCF.js.map +1 -0
  137. package/dist/{chunk-GIC3G5YG.js → chunk-P67WJVP4.js} +7 -13
  138. package/dist/chunk-P67WJVP4.js.map +1 -0
  139. package/dist/{chunk-G67BOI2J.js → chunk-PMECEZK3.js} +15 -9
  140. package/dist/chunk-PMECEZK3.js.map +1 -0
  141. package/dist/{chunk-OKGUDNDT.js → chunk-Q6E6FM7P.js} +35 -33
  142. package/dist/chunk-Q6E6FM7P.js.map +1 -0
  143. package/dist/{chunk-CWM3MYGX.js → chunk-QVEKLSEH.js} +8 -9
  144. package/dist/chunk-QVEKLSEH.js.map +1 -0
  145. package/dist/{chunk-O7VPNKYN.js → chunk-QVNRCDTV.js} +23 -7
  146. package/dist/chunk-QVNRCDTV.js.map +1 -0
  147. package/dist/{chunk-UX3K4RKE.js → chunk-S53LOR7B.js} +3 -3
  148. package/dist/{chunk-UX3K4RKE.js.map → chunk-S53LOR7B.js.map} +1 -1
  149. package/dist/{chunk-53MSCGR2.js → chunk-SCOT677Q.js} +5 -5
  150. package/dist/{chunk-53MSCGR2.js.map → chunk-SCOT677Q.js.map} +1 -1
  151. package/dist/{chunk-SZWRPEF7.js → chunk-TTRYE6AC.js} +39 -29
  152. package/dist/chunk-TTRYE6AC.js.map +1 -0
  153. package/dist/{chunk-KM7XWC4Y.js → chunk-UAROCOXF.js} +3 -3
  154. package/dist/{chunk-KM7XWC4Y.js.map → chunk-UAROCOXF.js.map} +1 -1
  155. package/dist/{chunk-MKLBK7HI.js → chunk-UM7RIAHO.js} +17 -13
  156. package/dist/chunk-UM7RIAHO.js.map +1 -0
  157. package/dist/{chunk-JMKB7IB4.js → chunk-UMVKJM4Q.js} +4 -4
  158. package/dist/chunk-UMVKJM4Q.js.map +1 -0
  159. package/dist/{chunk-JLSCA5T5.js → chunk-UO7WPLDY.js} +3 -3
  160. package/dist/{chunk-JLSCA5T5.js.map → chunk-UO7WPLDY.js.map} +1 -1
  161. package/dist/{chunk-HCYS7GFP.js → chunk-V2DXZYBB.js} +3 -3
  162. package/dist/{chunk-HCYS7GFP.js.map → chunk-V2DXZYBB.js.map} +1 -1
  163. package/dist/{chunk-47ZD34KB.js → chunk-VMKMFS2Q.js} +28 -7
  164. package/dist/chunk-VMKMFS2Q.js.map +1 -0
  165. package/dist/{chunk-IUUYRXT6.js → chunk-W5DWWE4Y.js} +4 -4
  166. package/dist/{chunk-IUUYRXT6.js.map → chunk-W5DWWE4Y.js.map} +1 -1
  167. package/dist/{chunk-FK6HPLQA.js → chunk-X4BAYGMT.js} +3 -3
  168. package/dist/{chunk-FK6HPLQA.js.map → chunk-X4BAYGMT.js.map} +1 -1
  169. package/dist/{chunk-OJ4N4T4H.js → chunk-XDVR7QIT.js} +20 -6
  170. package/dist/chunk-XDVR7QIT.js.map +1 -0
  171. package/dist/{chunk-7FJQT3GT.js → chunk-Y4N2NUZD.js} +4 -4
  172. package/dist/{chunk-7FJQT3GT.js.map → chunk-Y4N2NUZD.js.map} +1 -1
  173. package/dist/{chunk-6VRJH4MR.js → chunk-Y7QA2OLD.js} +6 -6
  174. package/dist/{chunk-6VRJH4MR.js.map → chunk-Y7QA2OLD.js.map} +1 -1
  175. package/dist/{chunk-74EEFB52.js → chunk-YSHVQGUS.js} +115 -27
  176. package/dist/chunk-YSHVQGUS.js.map +1 -0
  177. package/dist/{chunk-6C654TFI.js → chunk-ZAOGFXDO.js} +36 -8
  178. package/dist/chunk-ZAOGFXDO.js.map +1 -0
  179. package/dist/{chunk-N6FBTXQ5.js → chunk-ZTBAYUDY.js} +2 -2
  180. package/dist/{chunk-N6FBTXQ5.js.map → chunk-ZTBAYUDY.js.map} +1 -1
  181. package/dist/cli/index.js +373 -188
  182. package/dist/cli/index.js.map +1 -1
  183. package/dist/cli/src/asset-feeds.d.ts +5 -2
  184. package/dist/cli/src/asset-feeds.d.ts.map +1 -1
  185. package/dist/cli/src/commands/asset-cmd.d.ts.map +1 -1
  186. package/dist/cli/src/commands/catalog.d.ts +15 -2
  187. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  188. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  189. package/dist/cli/src/commands/library-index.d.ts +8 -0
  190. package/dist/cli/src/commands/library-index.d.ts.map +1 -1
  191. package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
  192. package/dist/cli/src/commands/manage.d.ts.map +1 -1
  193. package/dist/cli/src/commands/outdated.d.ts +7 -2
  194. package/dist/cli/src/commands/outdated.d.ts.map +1 -1
  195. package/dist/cli/src/commands/project.d.ts +2 -0
  196. package/dist/cli/src/commands/project.d.ts.map +1 -1
  197. package/dist/cli/src/commands/publish-core.d.ts +10 -1
  198. package/dist/cli/src/commands/publish-core.d.ts.map +1 -1
  199. package/dist/cli/src/commands/session.d.ts.map +1 -1
  200. package/dist/cli/src/commands/source.d.ts +14 -0
  201. package/dist/cli/src/commands/source.d.ts.map +1 -1
  202. package/dist/cli/src/commands/tree.d.ts.map +1 -1
  203. package/dist/cli/src/commands/update.d.ts.map +1 -1
  204. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  205. package/dist/cli/src/flow-discovery.d.ts +8 -5
  206. package/dist/cli/src/flow-discovery.d.ts.map +1 -1
  207. package/dist/cli/src/open-library.d.ts +0 -11
  208. package/dist/cli/src/open-library.d.ts.map +1 -1
  209. package/dist/cli/src/skill-checks.d.ts +11 -2
  210. package/dist/cli/src/skill-checks.d.ts.map +1 -1
  211. package/dist/client/index.js +1 -1
  212. package/dist/client/src/client.d.ts +10 -3
  213. package/dist/client/src/client.d.ts.map +1 -1
  214. package/dist/connectors/config.js +5 -5
  215. package/dist/connectors/index.js +25 -26
  216. package/dist/connectors/src/access-policy.d.ts +33 -0
  217. package/dist/connectors/src/access-policy.d.ts.map +1 -0
  218. package/dist/connectors/src/connector-manager.d.ts.map +1 -1
  219. package/dist/connectors/src/index.d.ts +2 -0
  220. package/dist/connectors/src/index.d.ts.map +1 -1
  221. package/dist/connectors-C7UQ73BK.js +44 -0
  222. package/dist/{connectors-SSOTIDHT.js.map → connectors-C7UQ73BK.js.map} +1 -1
  223. package/dist/connectors-shared/index.js +2 -2
  224. package/dist/core/index.js +8 -8
  225. package/dist/core/logging.js +1 -1
  226. package/dist/core/manifest.js +2 -2
  227. package/dist/core/models.js +1 -1
  228. package/dist/core/publish-manifest.js +2 -2
  229. package/dist/core/ref.js +1 -1
  230. package/dist/core/runtime-assets.js +5 -5
  231. package/dist/core/src/index.d.ts +3 -3
  232. package/dist/core/src/index.d.ts.map +1 -1
  233. package/dist/core/src/logging/config.d.ts +9 -1
  234. package/dist/core/src/logging/config.d.ts.map +1 -1
  235. package/dist/core/src/manifest.d.ts +11 -1
  236. package/dist/core/src/manifest.d.ts.map +1 -1
  237. package/dist/core/src/models.d.ts +14 -6
  238. package/dist/core/src/models.d.ts.map +1 -1
  239. package/dist/core/src/repo-manager.d.ts +41 -6
  240. package/dist/core/src/repo-manager.d.ts.map +1 -1
  241. package/dist/core/src/store.d.ts +16 -2
  242. package/dist/core/src/store.d.ts.map +1 -1
  243. package/dist/core/src/workspace-config.d.ts +10 -1
  244. package/dist/core/src/workspace-config.d.ts.map +1 -1
  245. package/dist/core/store.js +1 -1
  246. package/dist/core/workspace-config.js +4 -4
  247. package/dist/deploy/index.js +8 -8
  248. package/dist/discovery/index.js +5 -5
  249. package/dist/{ensure-sources-277VI7R3.js → ensure-sources-SRC2DVR5.js} +18 -18
  250. package/dist/{ensure-sources-277VI7R3.js.map → ensure-sources-SRC2DVR5.js.map} +1 -1
  251. package/dist/factory-assets/connectors/box.js +25 -26
  252. package/dist/factory-assets/connectors/deploy.js +25 -26
  253. package/dist/factory-assets/connectors/devserver.js +25 -26
  254. package/dist/factory-assets/connectors/flow/adapter.d.ts +6 -0
  255. package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
  256. package/dist/factory-assets/connectors/flow/adapter.js +25 -26
  257. package/dist/factory-assets/connectors/flow/contract/expression.d.ts +3 -0
  258. package/dist/factory-assets/connectors/flow/contract/expression.d.ts.map +1 -1
  259. package/dist/factory-assets/connectors/flow/contract/output-fields.d.ts +1 -1
  260. package/dist/factory-assets/connectors/flow/contract/output-fields.d.ts.map +1 -1
  261. package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
  262. package/dist/factory-assets/connectors/flow/contract.js +4 -4
  263. package/dist/factory-assets/connectors/flow/engine/flow-output.d.ts +4 -3
  264. package/dist/factory-assets/connectors/flow/engine/flow-output.d.ts.map +1 -1
  265. package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts +6 -1
  266. package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts.map +1 -1
  267. package/dist/factory-assets/connectors/flow/engine.js +12 -12
  268. package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts.map +1 -1
  269. package/dist/factory-assets/connectors/flow/prompt-fragments.js +3 -3
  270. package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
  271. package/dist/factory-assets/connectors/flow/run-flow.js +27 -28
  272. package/dist/factory-assets/connectors/flow.js +25 -26
  273. package/dist/factory-assets/connectors/git/config.d.ts.map +1 -1
  274. package/dist/factory-assets/connectors/git.js +25 -26
  275. package/dist/factory-assets/connectors/gmail.js +25 -26
  276. package/dist/factory-assets/connectors/googledrive.js +25 -26
  277. package/dist/factory-assets/connectors/local.js +25 -26
  278. package/dist/factory-assets/connectors/mattermost.js +25 -26
  279. package/dist/factory-assets/connectors/memory.js +25 -26
  280. package/dist/factory-assets/connectors/minio.js +25 -26
  281. package/dist/factory-assets/connectors/postgres.js +25 -26
  282. package/dist/factory-assets/connectors/s3.js +25 -26
  283. package/dist/factory-assets/connectors/sharepoint.js +25 -26
  284. package/dist/factory-assets/connectors/sqlite.js +25 -26
  285. package/dist/factory-assets/connectors/static-server.js +25 -26
  286. package/dist/factory-assets/connectors/tunnel.js +25 -26
  287. package/dist/factory-assets/connectors/webdav.js +25 -26
  288. package/dist/factory-assets/connectors/xstate-store.js +25 -26
  289. package/dist/factory-assets/connectors/xstate.js +25 -26
  290. package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +10 -7
  291. package/dist/{flows-J2UEU2QM.js → flows-CBYRSAGS.js} +13 -13
  292. package/dist/{flows-J2UEU2QM.js.map → flows-CBYRSAGS.js.map} +1 -1
  293. package/dist/{helpers-KC5O7CML.js → helpers-XAEKCZKT.js} +18 -18
  294. package/dist/{helpers-KC5O7CML.js.map → helpers-XAEKCZKT.js.map} +1 -1
  295. package/dist/library/index.js +12 -12
  296. package/dist/library/install.js +3 -3
  297. package/dist/library/src/index.d.ts +3 -1
  298. package/dist/library/src/index.d.ts.map +1 -1
  299. package/dist/library/src/local/scan-authoring-libraries.d.ts +43 -0
  300. package/dist/library/src/local/scan-authoring-libraries.d.ts.map +1 -0
  301. package/dist/library/src/manifest-writer.d.ts +26 -3
  302. package/dist/library/src/manifest-writer.d.ts.map +1 -1
  303. package/dist/library/src/sync/git-driver.d.ts.map +1 -1
  304. package/dist/{library-publish-FGXF5TJO.js → library-publish-VJSWMOLH.js} +73 -37
  305. package/dist/library-publish-VJSWMOLH.js.map +1 -0
  306. package/dist/open-library-DB2PNL2X.js +40 -0
  307. package/dist/{open-library-P3D337NH.js.map → open-library-DB2PNL2X.js.map} +1 -1
  308. package/dist/open-registry-ZUZJ7MS7.js +32 -0
  309. package/dist/{open-registry-Z3P2X2AB.js.map → open-registry-ZUZJ7MS7.js.map} +1 -1
  310. package/dist/paths-FQ64OLJG.js +22 -0
  311. package/dist/{paths-JKEKXM52.js.map → paths-FQ64OLJG.js.map} +1 -1
  312. package/dist/{plugin-store-WI4MZJLP.js → plugin-store-MPAMD5OT.js} +10 -10
  313. package/dist/{plugin-store-WI4MZJLP.js.map → plugin-store-MPAMD5OT.js.map} +1 -1
  314. package/dist/project-I6NX3NFB.js +41 -0
  315. package/dist/{project-WFUDVYNN.js.map → project-I6NX3NFB.js.map} +1 -1
  316. package/dist/resolver/index.js +9 -9
  317. package/dist/runner/index.js +40 -41
  318. package/dist/runner/src/context-usage/compose.d.ts.map +1 -1
  319. package/dist/runner/src/logging-bootstrap.d.ts +6 -0
  320. package/dist/runner/src/logging-bootstrap.d.ts.map +1 -1
  321. package/dist/runner/src/platform-blocks.d.ts +55 -0
  322. package/dist/runner/src/platform-blocks.d.ts.map +1 -0
  323. package/dist/runner/src/reconfigure-handler.d.ts +38 -17
  324. package/dist/runner/src/reconfigure-handler.d.ts.map +1 -1
  325. package/dist/runner/src/serve.d.ts.map +1 -1
  326. package/dist/runner/src/session-builder.d.ts.map +1 -1
  327. package/dist/runner/src/session-capability-wiring.d.ts +0 -2
  328. package/dist/runner/src/session-capability-wiring.d.ts.map +1 -1
  329. package/dist/sdk/asset-manager.js +15 -15
  330. package/dist/sdk/bridge.js +16 -17
  331. package/dist/sdk/client.js +1 -1
  332. package/dist/sdk/core.js +8 -8
  333. package/dist/sdk/flow.js +12 -12
  334. package/dist/sdk/index.js +69 -46
  335. package/dist/sdk/index.js.map +1 -1
  336. package/dist/sdk/resolver.js +9 -9
  337. package/dist/sdk/runner.js +40 -41
  338. package/dist/sdk/session.js +2 -2
  339. package/dist/sdk/src/agent-session.d.ts +8 -0
  340. package/dist/sdk/src/agent-session.d.ts.map +1 -1
  341. package/dist/sdk/store.js +2 -1
  342. package/dist/sdk/types.js +1 -1
  343. package/dist/session/index.js +2 -2
  344. package/dist/session/src/dispatcher.d.ts +25 -4
  345. package/dist/session/src/dispatcher.d.ts.map +1 -1
  346. package/dist/session/src/index.d.ts +1 -1
  347. package/dist/session/src/index.d.ts.map +1 -1
  348. package/dist/{setup-RAMLTORM.js → setup-B7JUHYJW.js} +26 -27
  349. package/dist/setup-B7JUHYJW.js.map +1 -0
  350. package/dist/skills-TVO26Y4V.js +26 -0
  351. package/dist/{skills-EOHZ2DZ4.js.map → skills-TVO26Y4V.js.map} +1 -1
  352. package/dist/source-PJBMDCKX.js +41 -0
  353. package/dist/{source-FYVSFYEI.js.map → source-PJBMDCKX.js.map} +1 -1
  354. package/dist/{source-manifest-publish-4FPIPPLV.js → source-manifest-publish-LLJWXGID.js} +27 -27
  355. package/dist/{source-manifest-publish-4FPIPPLV.js.map → source-manifest-publish-LLJWXGID.js.map} +1 -1
  356. package/dist/store/index.js +2 -1
  357. package/dist/store/src/store.d.ts +8 -5
  358. package/dist/store/src/store.d.ts.map +1 -1
  359. package/dist/tui/index.js +40 -41
  360. package/dist/tui/index.js.map +1 -1
  361. package/dist/types/index.js +1 -1
  362. package/dist/types/manifests.js +1 -1
  363. package/dist/types/src/ask-slots.d.ts +68 -0
  364. package/dist/types/src/ask-slots.d.ts.map +1 -0
  365. package/dist/types/src/delivery.d.ts.map +1 -1
  366. package/dist/types/src/events.d.ts +30 -2
  367. package/dist/types/src/events.d.ts.map +1 -1
  368. package/dist/types/src/index.d.ts +3 -3
  369. package/dist/types/src/index.d.ts.map +1 -1
  370. package/dist/types/src/manifests/index.d.ts +1 -1
  371. package/dist/types/src/manifests/index.d.ts.map +1 -1
  372. package/dist/types/src/manifests/preset.d.ts +4 -0
  373. package/dist/types/src/manifests/preset.d.ts.map +1 -1
  374. package/dist/types/src/protocol.d.ts +43 -1
  375. package/dist/types/src/protocol.d.ts.map +1 -1
  376. package/dist/types/src/version.d.ts +28 -1
  377. package/dist/types/src/version.d.ts.map +1 -1
  378. package/dist/workspace-plugin/adapters/mcp.js +32 -33
  379. package/dist/workspace-plugin/adapters/omp.js +14 -14
  380. package/dist/workspace-plugin/index.js +2 -2
  381. package/package.json +1 -1
  382. package/dist/asset-feeds-K36CR52Q.js.map +0 -1
  383. package/dist/chunk-47ZD34KB.js.map +0 -1
  384. package/dist/chunk-6C654TFI.js.map +0 -1
  385. package/dist/chunk-74EEFB52.js.map +0 -1
  386. package/dist/chunk-BTK7ECLH.js.map +0 -1
  387. package/dist/chunk-CWM3MYGX.js.map +0 -1
  388. package/dist/chunk-D5HOETOB.js.map +0 -1
  389. package/dist/chunk-DFFD4BFG.js.map +0 -1
  390. package/dist/chunk-DMLDYFDS.js.map +0 -1
  391. package/dist/chunk-EINLFFP6.js +0 -59
  392. package/dist/chunk-EINLFFP6.js.map +0 -1
  393. package/dist/chunk-EU3Q5AQ4.js.map +0 -1
  394. package/dist/chunk-FRHE7FFB.js.map +0 -1
  395. package/dist/chunk-G67BOI2J.js.map +0 -1
  396. package/dist/chunk-GIC3G5YG.js.map +0 -1
  397. package/dist/chunk-IZLQ4GDO.js.map +0 -1
  398. package/dist/chunk-JMKB7IB4.js.map +0 -1
  399. package/dist/chunk-LBQ4BBX4.js.map +0 -1
  400. package/dist/chunk-LCU5U5IW.js.map +0 -1
  401. package/dist/chunk-LXY4BUA3.js.map +0 -1
  402. package/dist/chunk-MKLBK7HI.js.map +0 -1
  403. package/dist/chunk-O7VPNKYN.js.map +0 -1
  404. package/dist/chunk-OJ4N4T4H.js.map +0 -1
  405. package/dist/chunk-OKGUDNDT.js.map +0 -1
  406. package/dist/chunk-QCI6D7E4.js.map +0 -1
  407. package/dist/chunk-SZWRPEF7.js.map +0 -1
  408. package/dist/chunk-TTPSDC2G.js.map +0 -1
  409. package/dist/chunk-VOOI7B2L.js.map +0 -1
  410. package/dist/chunk-XBSQ4KMC.js.map +0 -1
  411. package/dist/connectors-SSOTIDHT.js +0 -45
  412. package/dist/library-publish-FGXF5TJO.js.map +0 -1
  413. package/dist/open-library-P3D337NH.js +0 -40
  414. package/dist/open-registry-Z3P2X2AB.js +0 -32
  415. package/dist/paths-JKEKXM52.js +0 -22
  416. package/dist/project-WFUDVYNN.js +0 -41
  417. package/dist/setup-RAMLTORM.js.map +0 -1
  418. package/dist/skills-EOHZ2DZ4.js +0 -26
  419. package/dist/source-FYVSFYEI.js +0 -41
@@ -1,20 +1,20 @@
1
- import { getStoreManifestsDir } from './chunk-DFFD4BFG.js';
2
- import { readManifestSourceConfig, discoverAssetsInTree } from './chunk-6VRJH4MR.js';
3
- import { installAgent, installTargetOf, deployAll, removeAsset, canonicalEntryRef, createScaffold } from './chunk-74EEFB52.js';
1
+ import { getStoreManifestsDir } from './chunk-4MQS7CAF.js';
2
+ import { readManifestSourceConfig, isManifestMode, discoverAssetsInTree } from './chunk-Y7QA2OLD.js';
3
+ import { installAgent, installTargetOf, canonicalEntryRef, deployAll, removeAsset, createScaffold } from './chunk-YSHVQGUS.js';
4
4
  import { appendHistory, getRecentHistory, clearHistory } from './chunk-KTBKW2FI.js';
5
5
  import { commitChanges } from './chunk-UQ6LFBPZ.js';
6
- import { driverTargetSupportsAgents, CODEX_IDENTITY_DIR } from './chunk-AVCUPPG6.js';
7
- import { resolveConfiguredDriverTarget, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, writeLock, buildLockFile, readLock, verifyLock, mergeLockFile, extractForPatch, generatePatch, savePatch, applyPatch } from './chunk-OOLT6HGP.js';
8
- import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-SWO25LTO.js';
9
- import { deployedBase, deployedDir, isDeployed } from './chunk-G67BOI2J.js';
10
- import { resolveSkWorkspaceConfig, effectiveAgentInstances, resolveAgentDefinitionDir, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, coerceVersion, resolveAll, buildProvenanceIndex, ensureRepo, sourceShortName, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, resolveAsset, readLinks, storeManifestSlug } from './chunk-LCU5U5IW.js';
6
+ import { driverTargetSupportsAgents, CODEX_IDENTITY_DIR } from './chunk-EF7NPDDU.js';
7
+ import { resolveConfiguredDriverTarget, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, buildLockFile, writeLock, readLock, verifyLock, mergeLockFile, extractForPatch, generatePatch, savePatch, applyPatch } from './chunk-36AFURKZ.js';
8
+ import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-GB7WDMH2.js';
9
+ import { deployedBase, deployedDir, isDeployed, deployedPaths } from './chunk-PMECEZK3.js';
10
+ import { resolveSkWorkspaceConfig, effectiveAgentInstances, resolveAgentDefinitionDir, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, resolveAll, buildProvenanceIndex, ensureRepo, sourceShortName, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, coerceVersion, resolveAsset, readLinks, MAX_PRESET_DEPTH, PresetDepthError, storeManifestSlug } from './chunk-JAKOMFC6.js';
11
11
  import { globalSkaileDir, globalCacheRoot, migrateLegacyDir } from './chunk-L5VSIPQD.js';
12
12
  import { DRIVER_TARGETS } from './chunk-CPSIPIX7.js';
13
- import { scanDirectory, parseRequires } from './chunk-TTPSDC2G.js';
14
- import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName } from './chunk-O7VPNKYN.js';
13
+ import { scanDirectory, parseRequires } from './chunk-GTILM2CI.js';
14
+ import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr } from './chunk-QVNRCDTV.js';
15
15
  import { spawnSync } from 'child_process';
16
16
  import { existsSync, rmSync, readdirSync, lstatSync, unlinkSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
17
- import path, { join, resolve, dirname, sep, posix } from 'path';
17
+ import path, { join, resolve, dirname, sep, relative, posix } from 'path';
18
18
  import { gt } from 'semver';
19
19
  import { stringify, parse } from 'yaml';
20
20
  import fs from 'fs/promises';
@@ -375,6 +375,26 @@ function libraryDiskHashes(libPath, cache, registry) {
375
375
  }
376
376
  return hashes;
377
377
  }
378
+ function withLibraryFiles(entry, libPath, cache, registry) {
379
+ let byManifest = cache.get(libPath);
380
+ if (!byManifest) {
381
+ byManifest = /* @__PURE__ */ new Map();
382
+ const sourceConfig = readManifestSourceConfig(libPath, "library");
383
+ if (sourceConfig && isManifestMode(sourceConfig)) {
384
+ for (const a of discoverAssetsInTree(libPath, { sourceConfig, registry }).assets) {
385
+ const { assetRoot, files: files2 } = a.fileFilter;
386
+ byManifest.set(
387
+ resolve(a.manifestPath),
388
+ filesUnderManifestDir(files2, relative(assetRoot, a.manifestPath).split(sep).join("/"))
389
+ );
390
+ }
391
+ }
392
+ cache.set(libPath, byManifest);
393
+ }
394
+ const files = byManifest.get(resolve(entry.source));
395
+ if (files) entry.files = files;
396
+ return entry;
397
+ }
378
398
  function newestLibraryDef(snap, libraryName, id) {
379
399
  const lib = snap?.libraries.find((l) => l.name === libraryName);
380
400
  if (!snap || !lib) return void 0;
@@ -386,6 +406,17 @@ function newestLibraryDef(snap, libraryName, id) {
386
406
  }
387
407
  return best;
388
408
  }
409
+ function libraryPreference(isDefaultLib) {
410
+ return (a, b) => {
411
+ const va = coerceVersion(a.entry.version ?? "");
412
+ const vb = coerceVersion(b.entry.version ?? "");
413
+ if (gt(va, vb)) return -1;
414
+ if (gt(vb, va)) return 1;
415
+ const da = isDefaultLib.get(a.library) ? 0 : 1;
416
+ const db = isDefaultLib.get(b.library) ? 0 : 1;
417
+ return da - db || a.library.localeCompare(b.library);
418
+ };
419
+ }
389
420
  function qualifyDepRef(ref, fallback) {
390
421
  const hash = ref.indexOf("#");
391
422
  const head = hash === -1 ? ref : ref.slice(0, hash);
@@ -407,7 +438,7 @@ function libraryEntryDepRefs(e) {
407
438
  }
408
439
  for (const r of e.requires ?? []) {
409
440
  const p = r.publisher ?? publisher;
410
- if (p) refs.add(assetRefToStr({ kind: r.kind, name: r.name, publisher: p }));
441
+ if (p) refs.add(assetRefToStr({ kind: r.kind, name: r.name, publisher: p, pin: r.pin }));
411
442
  }
412
443
  return [...refs];
413
444
  }
@@ -489,6 +520,56 @@ function parseRefTolerant(ref) {
489
520
  return { kind, name, publisher: void 0 };
490
521
  }
491
522
  }
523
+ function keptLockEntries(prev, missing) {
524
+ const out = { assets: {}, sources: [], refs: /* @__PURE__ */ new Set() };
525
+ if (!prev) return out;
526
+ for (const ref of missing) {
527
+ const want = parseRefTolerant(ref.split(" ")[0] ?? ref);
528
+ if (!want) continue;
529
+ for (const [key, entry] of Object.entries(prev.assets)) {
530
+ if (entry.library?.installed) continue;
531
+ const id = parseCanonicalLockKey(key);
532
+ if (id.kind !== want.kind || id.name !== want.name) continue;
533
+ if (want.publisher && id.publisher !== want.publisher) continue;
534
+ out.assets[key] = entry;
535
+ out.refs.add(ref);
536
+ const src = entry.source ? prev.sources.find((s) => s.url === entry.source?.url) : void 0;
537
+ if (src) out.sources.push(src);
538
+ }
539
+ }
540
+ return out;
541
+ }
542
+ function withKeptLockEntries(lock, kept) {
543
+ if (Object.keys(kept.assets).length === 0) return lock;
544
+ const sources = new Map(lock.sources.map((s) => [s.url, s]));
545
+ for (const s of kept.sources) if (!sources.has(s.url)) sources.set(s.url, s);
546
+ return {
547
+ ...lock,
548
+ // v4 once a library-origin entry is present (see buildLockFile).
549
+ schema_version: lock.schema_version === 4 || Object.values(kept.assets).some((e) => e.library) ? 4 : 3,
550
+ assets: { ...lock.assets, ...kept.assets },
551
+ sources: [...sources.values()].sort((a, b) => a.url.localeCompare(b.url))
552
+ };
553
+ }
554
+ function relockedRefs(prev, next) {
555
+ if (!prev) return [];
556
+ const fingerprint = (e) => [e.library?.version ?? "", e.library?.sha256 ?? e.sha256, e.source?.commit ?? ""].join("\0");
557
+ const out = [];
558
+ for (const [key, entry] of Object.entries(next.assets)) {
559
+ if (entry.library?.installed) continue;
560
+ const id = parseCanonicalLockKey(key);
561
+ const before = prev.assets[key] ? [key, prev.assets[key]] : Object.entries(prev.assets).find(([k]) => {
562
+ const o = parseCanonicalLockKey(k);
563
+ return o.kind === id.kind && o.name === id.name && o.publisher === id.publisher;
564
+ });
565
+ if (!before) continue;
566
+ if (before[0] !== key || fingerprint(before[1]) !== fingerprint(entry)) out.push(key);
567
+ }
568
+ return out;
569
+ }
570
+ function unregisteredLibraryFix(library, ref) {
571
+ return `register library "${library}" again with \`skaile library init ${library} --path <dir>\`, or drop the dependency with \`skaile remove ${ref}\``;
572
+ }
492
573
  function memberIdentity(ref) {
493
574
  const parsed = parseRefTolerant(ref.trim());
494
575
  if (!parsed) return null;
@@ -828,6 +909,46 @@ var AssetManager = class {
828
909
  const resolved = /* @__PURE__ */ new Set();
829
910
  const libraryOf = /* @__PURE__ */ new Map();
830
911
  if (missing.length === 0) return { entries: [], resolved, libraryOf };
912
+ const scan = await this.scanLibraries();
913
+ if (!scan) return { entries: [], resolved, libraryOf };
914
+ const { byKey, isDefaultLib, libraryDir } = scan;
915
+ const entries = [];
916
+ const fileSets = /* @__PURE__ */ new Map();
917
+ const allCandidates = [...byKey.values()].flat();
918
+ for (const ref of missing) {
919
+ const parsed = parseRefTolerant(ref);
920
+ if (!parsed) continue;
921
+ const hash = ref.indexOf("#");
922
+ const pin = parsed.pin ?? (hash >= 0 ? ref.slice(hash + 1) : void 0);
923
+ const key = parsed.publisher ? `${parsed.kind}:@${parsed.publisher}/${parsed.name}` : void 0;
924
+ const pool = key ? byKey.get(key) ?? [] : allCandidates.filter((c) => c.entry.kind === parsed.kind && c.entry.name === parsed.name);
925
+ const candidates = pool.filter((c) => !pin || matchPin(pin, c.entry.version ?? ""));
926
+ if (candidates.length === 0) continue;
927
+ candidates.sort(libraryPreference(isDefaultLib));
928
+ const hit = candidates[0];
929
+ if (!opts.quiet) {
930
+ this.warnLibraryCollision(
931
+ `${hit.entry.kind}:@${hit.entry.publisher}/${hit.entry.name}`,
932
+ candidates,
933
+ hit
934
+ );
935
+ }
936
+ withLibraryFiles(hit.entry, libraryDir.get(hit.library), fileSets, this.registry);
937
+ entries.push(hit.entry);
938
+ libraryOf.set(hit.entry, hit.library);
939
+ resolved.add(ref);
940
+ }
941
+ return { entries, resolved, libraryOf };
942
+ }
943
+ /**
944
+ * Scan every candidate library (the set {@link resolveLibraryEntries} resolves from)
945
+ * into `kind:@publisher/name` → every holder, across all libraries and versions.
946
+ *
947
+ * @param minRoots - Return `null` without scanning when fewer candidate libraries
948
+ * exist (a cross-library question needs at least two).
949
+ * @returns `null` when there is no candidate library or none holds an asset.
950
+ */
951
+ async scanLibraries(minRoots = 1) {
831
952
  const roots = [];
832
953
  for (const l of (await this.loadLibraryIndex())?.libraries ?? []) {
833
954
  if (l.name === GLOBAL_INSTALL_DIR_NAME) continue;
@@ -849,8 +970,9 @@ var AssetManager = class {
849
970
  }
850
971
  } catch {
851
972
  }
852
- if (roots.length === 0) return { entries: [], resolved, libraryOf };
973
+ if (roots.length === 0 || roots.length < minRoots) return null;
853
974
  const byKey = /* @__PURE__ */ new Map();
975
+ const libraryDir = new Map(roots.map((r) => [r.name, r.dir]));
854
976
  const isDefaultLib = new Map(roots.map((r) => [r.name, r.isDefault]));
855
977
  for (const { name, dir } of roots) {
856
978
  const publisher = readDeclaredPublisher(dir) ?? slugifyAssetName(name);
@@ -861,56 +983,49 @@ var AssetManager = class {
861
983
  else byKey.set(key, [{ entry, library: name }]);
862
984
  }
863
985
  }
864
- if (byKey.size === 0) return { entries: [], resolved, libraryOf };
865
- const entries = [];
866
- const allCandidates = [...byKey.values()].flat();
867
- for (const ref of missing) {
868
- const parsed = parseRefTolerant(ref);
869
- if (!parsed) continue;
870
- const hash = ref.indexOf("#");
871
- const pin = parsed.pin ?? (hash >= 0 ? ref.slice(hash + 1) : void 0);
872
- const key = parsed.publisher ? `${parsed.kind}:@${parsed.publisher}/${parsed.name}` : void 0;
873
- const pool = key ? byKey.get(key) ?? [] : allCandidates.filter((c) => c.entry.kind === parsed.kind && c.entry.name === parsed.name);
874
- const candidates = pool.filter((c) => !pin || matchPin(pin, c.entry.version ?? ""));
875
- if (candidates.length === 0) continue;
876
- candidates.sort((a, b) => {
877
- const va = coerceVersion(a.entry.version ?? "");
878
- const vb = coerceVersion(b.entry.version ?? "");
879
- if (gt(va, vb)) return -1;
880
- if (gt(vb, va)) return 1;
881
- const da = isDefaultLib.get(a.library) ? 0 : 1;
882
- const db = isDefaultLib.get(b.library) ? 0 : 1;
883
- return da - db || a.library.localeCompare(b.library);
884
- });
885
- const hit = candidates[0];
886
- if (!opts.quiet) {
887
- this.warnLibraryCollision(
888
- `${hit.entry.kind}:@${hit.entry.publisher}/${hit.entry.name}`,
889
- candidates,
890
- hit
891
- );
892
- }
893
- entries.push(hit.entry);
894
- libraryOf.set(hit.entry, hit.library);
895
- resolved.add(ref);
986
+ return byKey.size === 0 ? null : { byKey, isDefaultLib, libraryDir };
987
+ }
988
+ /**
989
+ * Every ref that more than one registered library holds, with one row per holder
990
+ * (all libraries, all versions) in the order `add` prefers them: the first row is
991
+ * the one `add <ref>` deploys. The local index keeps a single row per ref and
992
+ * version, so `search` reads this to show equal-version copies in both libraries.
993
+ *
994
+ * @returns `kind:@publisher/name` → holders; refs held by one library are absent.
995
+ */
996
+ async libraryHolders() {
997
+ const out = /* @__PURE__ */ new Map();
998
+ const scan = await this.scanLibraries(2);
999
+ if (!scan) return out;
1000
+ for (const [key, candidates] of scan.byKey) {
1001
+ if (new Set(candidates.map((c) => c.library)).size < 2) continue;
1002
+ const sorted = [...candidates].sort(libraryPreference(scan.isDefaultLib));
1003
+ out.set(
1004
+ key,
1005
+ sorted.map((c, i) => ({
1006
+ library: c.library,
1007
+ version: c.entry.version ?? "",
1008
+ description: c.entry.description ?? "",
1009
+ picked: i === 0
1010
+ }))
1011
+ );
896
1012
  }
897
- return { entries, resolved, libraryOf };
1013
+ return out;
898
1014
  }
899
1015
  /**
900
- * Explain misses caused by a publisher change, for every missed publisher-qualified
901
- * ref at once (one library scan, one lock read). Per ref:
902
- * - `did you mean <ref>?` when a registered library holds the same kind + name
903
- * under another publisher;
904
- * - `locked as <lockRef>; run skaile install to re-resolve` when the lock holds the
905
- * same kind + name under another publisher, records the library it came from, and
906
- * that library now holds the asset under yet another publisher than the locked
907
- * one. An unrelated asset that merely shares kind + name does not qualify.
1016
+ * Where each missed publisher-qualified ref went when its publisher changed, for every
1017
+ * ref at once (one library scan, one lock read):
1018
+ * - `moved` when a registered library holds the same kind + name under another publisher;
1019
+ * - `lockedAs` when the lock holds the same kind + name under another publisher, records
1020
+ * the library it came from, and that library now holds the asset under yet another
1021
+ * publisher than the locked one. An unrelated asset that merely shares kind + name does
1022
+ * not qualify.
908
1023
  *
909
1024
  * @param refs - Missed refs; a trailing `(reason)` after the first space is ignored.
910
- * @returns hint text per ref, only for refs that have one. Never warns.
1025
+ * @returns one finding per ref that has one. Never warns.
911
1026
  */
912
- async publisherChangeHints(refs) {
913
- const out = {};
1027
+ async publisherChangeFindings(refs) {
1028
+ const out = /* @__PURE__ */ new Map();
914
1029
  const wanted = /* @__PURE__ */ new Map();
915
1030
  for (const ref of refs) {
916
1031
  const parsed = parseRefTolerant(ref.split(" ")[0] ?? ref);
@@ -925,20 +1040,83 @@ var AssetManager = class {
925
1040
  for (const [ref, w] of wanted) {
926
1041
  const moved = lib.entries.find((e) => e.kind === w.kind && e.name === w.name);
927
1042
  const movedLibrary = moved ? lib.libraryOf.get(moved) : void 0;
928
- const hints = [];
1043
+ const finding = {};
929
1044
  for (const [key, entry] of lockEntries) {
930
1045
  const id = parseCanonicalLockKey(key);
931
1046
  if (id.kind === w.kind && id.name === w.name && id.publisher && id.publisher !== w.publisher && entry.library && moved && movedLibrary === entry.library.name && moved.publisher !== id.publisher) {
932
- hints.push(
933
- `locked as ${id.kind}:@${id.publisher}/${id.name}; run skaile install to re-resolve`
934
- );
1047
+ finding.lockedAs = `${id.kind}:@${id.publisher}/${id.name}`;
935
1048
  break;
936
1049
  }
937
1050
  }
938
1051
  if (moved && moved.publisher !== w.publisher) {
939
- hints.push(`did you mean ${moved.kind}:@${moved.publisher}/${moved.name}?`);
1052
+ finding.moved = {
1053
+ ref: `${moved.kind}:@${moved.publisher}/${moved.name}`,
1054
+ ...movedLibrary ? { library: movedLibrary } : {}
1055
+ };
1056
+ }
1057
+ if (finding.lockedAs || finding.moved) out.set(ref, finding);
1058
+ }
1059
+ return out;
1060
+ }
1061
+ /**
1062
+ * Explain misses caused by a publisher change, as parenthesized hint text per ref:
1063
+ * `did you mean <ref>?` when a registered library holds the same kind + name under
1064
+ * another publisher, and `locked as <lockRef>; run skaile install to re-resolve` when
1065
+ * the lock holds it under the library's previous publisher (see
1066
+ * {@link AssetManager.publisherChangeFindings}).
1067
+ *
1068
+ * @param refs - Missed refs; a trailing `(reason)` after the first space is ignored.
1069
+ * @returns hint text per ref, only for refs that have one. Never warns.
1070
+ */
1071
+ async publisherChangeHints(refs) {
1072
+ const out = {};
1073
+ for (const [ref, f] of await this.publisherChangeFindings(refs)) {
1074
+ const hints = [];
1075
+ if (f.lockedAs) hints.push(`locked as ${f.lockedAs}; run skaile install to re-resolve`);
1076
+ if (f.moved) hints.push(`did you mean ${f.moved.ref}?`);
1077
+ out[ref] = hints.map((h) => ` (${h})`).join("");
1078
+ }
1079
+ return out;
1080
+ }
1081
+ /**
1082
+ * What the user has to change for each ref that is locked from a library but resolves
1083
+ * nowhere any more, where `skaile install` cannot help: the library is not registered
1084
+ * (renamed away or removed), the library now publishes the asset under another
1085
+ * publisher, or the library no longer holds it. A ref the lock does not tie to a
1086
+ * library has no entry.
1087
+ *
1088
+ * @param refs - Declared or locked refs, `kind:@publisher/name` (a trailing `(reason)`
1089
+ * after the first space is ignored).
1090
+ * @returns a phrase per ref that follows the ref name, without parentheses.
1091
+ */
1092
+ async unresolvedFixes(refs) {
1093
+ const out = /* @__PURE__ */ new Map();
1094
+ if (refs.length === 0) return out;
1095
+ const lockEntries = Object.entries(this.tryReadLock()?.assets ?? {});
1096
+ const snap = await this.loadLibraryIndex();
1097
+ const findings = await this.publisherChangeFindings(refs);
1098
+ for (const ref of refs) {
1099
+ const want = parseRefTolerant(ref.split(" ")[0] ?? ref);
1100
+ if (!want) continue;
1101
+ const origin = lockEntries.map(([key, e]) => ({ id: parseCanonicalLockKey(key), library: e.library })).find(
1102
+ ({ id, library }) => library && !library.installed && id.kind === want.kind && id.name === want.name && (!want.publisher || id.publisher === want.publisher)
1103
+ )?.library;
1104
+ if (!origin) continue;
1105
+ const plain = want.publisher ? `${want.kind}:@${want.publisher}/${want.name}` : `${want.kind}:${want.name}`;
1106
+ const moved = findings.get(ref)?.moved;
1107
+ if (moved?.library && want.publisher) {
1108
+ out.set(
1109
+ ref,
1110
+ `did you mean ${moved.ref}? Change the ref in skaile.yaml to it, or keep the old ref with \`skaile library set ${moved.library} publisher=${want.publisher}\``
1111
+ );
1112
+ } else if (!this.libraryPath(origin.name, snap)) {
1113
+ out.set(ref, unregisteredLibraryFix(origin.name, plain));
1114
+ } else {
1115
+ out.set(
1116
+ ref,
1117
+ `library "${origin.name}" no longer holds it \u2014 restore it there, or drop the dependency with \`skaile remove ${plain}\``
1118
+ );
940
1119
  }
941
- if (hints.length > 0) out[ref] = hints.map((h) => ` (${h})`).join("");
942
1120
  }
943
1121
  return out;
944
1122
  }
@@ -958,6 +1136,56 @@ var AssetManager = class {
958
1136
  if (this.onLibraryCollision) this.onLibraryCollision(msg);
959
1137
  else console.warn(`skaile: ${msg}`);
960
1138
  }
1139
+ /** Kind, name and bare publisher of a deploy entry; the publisher comes from its canonical ref. */
1140
+ entryIdentity(entry) {
1141
+ const ref = parseAssetRefLoose(canonicalEntryRef(entry));
1142
+ return {
1143
+ kind: entry.kind,
1144
+ name: entry.name,
1145
+ publisher: ref?.publisher ?? entry.publisher?.replace(/^@/, "")
1146
+ };
1147
+ }
1148
+ /**
1149
+ * Refuse assets of one kind and name from different publishers: they deploy into the
1150
+ * same folder, so the later one would silently overwrite the earlier. Throws one line
1151
+ * naming the folder and the asset that holds it.
1152
+ *
1153
+ * @param incoming - Assets about to be deployed. Two of them clashing is refused.
1154
+ * @param held - Assets the project already declares or has locked; one of them
1155
+ * clashing with an incoming asset is refused when its folder is on disk.
1156
+ */
1157
+ assertNoFolderCollision(incoming, held = []) {
1158
+ const refOf = (a) => assetRefToStr({ kind: a.kind, name: a.name, publisher: a.publisher });
1159
+ const differs = (a, b) => !!a.publisher && !!b.publisher && a.publisher !== b.publisher;
1160
+ const shown = (dir) => this.global ? dir : relative(this.projectDir, dir);
1161
+ const seen = /* @__PURE__ */ new Map();
1162
+ for (const a of incoming) {
1163
+ if (!a.kind || !a.name) continue;
1164
+ const component = installTargetOf(this.registry, a.kind);
1165
+ if (!component) continue;
1166
+ const slot = `${component}/${a.name}`;
1167
+ const prior = seen.get(slot);
1168
+ if (prior && differs(prior, a)) {
1169
+ const dir2 = deployedDir(component, a.name, this.driverTarget, this.global, this.projectDir);
1170
+ throw new Error(
1171
+ `${refOf(prior)} and ${refOf(a)} both deploy to ${shown(dir2)} \u2014 keep only one of them`
1172
+ );
1173
+ }
1174
+ if (!prior) seen.set(slot, a);
1175
+ const holder = held.find(
1176
+ (h) => !!h.kind && h.name === a.name && installTargetOf(this.registry, h.kind) === component && differs(h, a)
1177
+ );
1178
+ if (!holder) continue;
1179
+ const target = this.targetHarnesses().find(
1180
+ (t) => isDeployed(component, a.name, t, this.global, this.projectDir)
1181
+ );
1182
+ if (!target) continue;
1183
+ const dir = deployedDir(component, a.name, target, this.global, this.projectDir);
1184
+ throw new Error(
1185
+ `${refOf(a)} would overwrite ${shown(dir)}, which ${refOf(holder)} already holds \u2014 run skaile remove ${refOf(holder)} first`
1186
+ );
1187
+ }
1188
+ }
961
1189
  /**
962
1190
  * Open the local index and snapshot its libraries + asset definitions.
963
1191
  * Best-effort: no index file (never indexed), or any open/guard failure,
@@ -1022,6 +1250,7 @@ var AssetManager = class {
1022
1250
  if (locked.length === 0) return { entries, missing };
1023
1251
  const snap = await this.loadLibraryIndex();
1024
1252
  const diskHashes = /* @__PURE__ */ new Map();
1253
+ const fileSets = /* @__PURE__ */ new Map();
1025
1254
  for (const [ref, e] of locked) {
1026
1255
  const origin = e.library;
1027
1256
  const { publisher, kind, name } = parseCanonicalLockKey(ref);
@@ -1032,7 +1261,7 @@ var AssetManager = class {
1032
1261
  const libPath = this.libraryPath(origin.name, snap);
1033
1262
  if (!libPath) {
1034
1263
  missing.push(
1035
- `${ref} (library "${origin.name}" is not registered \u2014 renamed or removed? re-lock with \`skaile install\`)`
1264
+ `${ref} (library "${origin.name}" is not registered \u2014 renamed or removed? ${unregisteredLibraryFix(origin.name, `${kind}:@${publisher}/${name}`)}; \`skaile install\` re-locks it when another library holds it now)`
1036
1265
  );
1037
1266
  continue;
1038
1267
  }
@@ -1060,7 +1289,7 @@ var AssetManager = class {
1060
1289
  continue;
1061
1290
  }
1062
1291
  }
1063
- entries.push(found);
1292
+ entries.push(withLibraryFiles(found, libPath, fileSets, this.registry));
1064
1293
  }
1065
1294
  return { entries, missing };
1066
1295
  }
@@ -1099,7 +1328,8 @@ var AssetManager = class {
1099
1328
  stores: opts.stores,
1100
1329
  storeFetcher: this.storeFetcherFor(opts.stores),
1101
1330
  projectDir: this.projectDir,
1102
- knownKinds: this.knownKinds
1331
+ knownKinds: this.knownKinds,
1332
+ declaredRefs: opts.declaredRefs
1103
1333
  });
1104
1334
  if (first.missing.length === 0) return first;
1105
1335
  const fallback = await this.globalStoreFallback(opts.stores);
@@ -1112,7 +1342,8 @@ var AssetManager = class {
1112
1342
  stores,
1113
1343
  storeFetcher: this.storeFetcherFor(stores),
1114
1344
  projectDir: this.projectDir,
1115
- knownKinds: this.knownKinds
1345
+ knownKinds: this.knownKinds,
1346
+ declaredRefs: opts.declaredRefs
1116
1347
  });
1117
1348
  return retry.missing.length < first.missing.length ? retry : first;
1118
1349
  } catch {
@@ -1146,16 +1377,50 @@ var AssetManager = class {
1146
1377
  const missing = /* @__PURE__ */ new Set();
1147
1378
  const seen = /* @__PURE__ */ new Set();
1148
1379
  const covered = /* @__PURE__ */ new Set();
1380
+ const declaredById = /* @__PURE__ */ new Map();
1381
+ for (const d of opts.declaredRefs ?? []) {
1382
+ const id = refIdentity(d);
1383
+ if (id !== null) declaredById.set(id, d);
1384
+ }
1385
+ const presetParent = /* @__PURE__ */ new Map();
1149
1386
  const absorb = (hit) => {
1150
1387
  const next = [];
1151
1388
  for (const e of hit.entries) {
1152
- lib.entries.push(e);
1153
1389
  const origin = hit.libraryOf.get(e);
1154
- if (origin) lib.libraryOf.set(e, origin);
1155
1390
  const id = `${e.publisher}/${e.kind}:${e.name}`;
1156
1391
  seen.add(id);
1157
1392
  covered.add(id);
1158
- next.push(...libraryEntryDepRefs(e));
1393
+ const deps2 = libraryEntryDepRefs(e);
1394
+ if (e.kind === "preset") {
1395
+ const chain = [id];
1396
+ for (let up = presetParent.get(id); up !== void 0; up = presetParent.get(up)) {
1397
+ chain.unshift(up);
1398
+ }
1399
+ if (chain.length > MAX_PRESET_DEPTH) {
1400
+ throw new PresetDepthError("depth", canonicalEntryRef(e), chain);
1401
+ }
1402
+ for (const dep of deps2) {
1403
+ const depId = refIdentity(dep);
1404
+ if (depId === null || !depId.includes("/preset:")) continue;
1405
+ if (chain.includes(depId)) throw new PresetDepthError("cycle", dep, [...chain, depId]);
1406
+ if (!presetParent.has(depId)) presetParent.set(depId, id);
1407
+ }
1408
+ presets.push({
1409
+ ref: canonicalEntryRef(e),
1410
+ assets: deps2.filter((d) => !d.startsWith("preset:")).length,
1411
+ skipped: e.skippedItems ?? 0,
1412
+ ...origin ? { library: origin } : {}
1413
+ });
1414
+ } else {
1415
+ lib.entries.push(e);
1416
+ if (origin) lib.libraryOf.set(e, origin);
1417
+ }
1418
+ next.push(
1419
+ ...deps2.map((dep) => {
1420
+ const depId = refIdentity(dep);
1421
+ return depId !== null && declaredById.get(depId) || dep;
1422
+ })
1423
+ );
1159
1424
  }
1160
1425
  for (const r of hit.resolved) lib.resolved.add(r);
1161
1426
  return next;
@@ -1175,7 +1440,7 @@ var AssetManager = class {
1175
1440
  const libraryTried = new Set(
1176
1441
  fresh.filter((ref) => {
1177
1442
  const id = refIdentity(ref);
1178
- return id !== null && !id.includes("/preset:") && (opts.provenanceIndex.get(id)?.length ?? 0) === 0;
1443
+ return id !== null && (opts.provenanceIndex.get(id)?.length ?? 0) === 0;
1179
1444
  })
1180
1445
  );
1181
1446
  const hit = await this.resolveLibraryEntries([...libraryTried]);
@@ -1371,7 +1636,7 @@ var AssetManager = class {
1371
1636
  async install(opts) {
1372
1637
  const { repositories, sources, stores, dependencies, overrides, patches } = this.loadConfig();
1373
1638
  if (opts?.locked) {
1374
- return this.installLocked(repositories, patches);
1639
+ return this.installLocked(repositories, patches, dependencies);
1375
1640
  }
1376
1641
  const cloneError = this.ensureSourceClones(repositories, sources);
1377
1642
  if (cloneError) return cloneError;
@@ -1381,13 +1646,30 @@ var AssetManager = class {
1381
1646
  });
1382
1647
  const { resolved, missing, overridesApplied, lib, presets } = await this.resolveLibraryFirst(
1383
1648
  dependencies,
1384
- { provenanceIndex, overrides, stores }
1649
+ { provenanceIndex, overrides, stores, declaredRefs: dependencies }
1385
1650
  );
1651
+ this.assertNoFolderCollision([...resolved, ...lib.entries.map((e) => this.entryIdentity(e))]);
1386
1652
  const stillMissing = missing.filter((m) => !lib.resolved.has(m));
1387
1653
  const missingHints = await this.publisherChangeHints(stillMissing);
1654
+ const prevLock = this.tryReadLock();
1655
+ const kept = keptLockEntries(prevLock, stillMissing);
1656
+ const fixes = await this.unresolvedFixes(stillMissing);
1657
+ for (const ref of /* @__PURE__ */ new Set([...kept.refs, ...fixes.keys()])) {
1658
+ const fix = fixes.get(ref);
1659
+ const notes = [
1660
+ kept.refs.has(ref) ? "the deployed copy and its lock entry are kept" : void 0,
1661
+ fix
1662
+ ].filter((n) => n !== void 0);
1663
+ missingHints[ref] = `${fix ? "" : missingHints[ref] ?? ""} (${notes.join("; ")})`;
1664
+ }
1388
1665
  const directLock = this.directInstallLockEntries();
1389
1666
  const libraryLock = { ...directLock, ...await this.libraryLockEntries(lib) };
1390
- const removed = this.reconcileRemovedAssets(resolved, new Set(Object.keys(libraryLock)));
1667
+ const freshLock = buildLockFile(resolved, overridesApplied, libraryLock);
1668
+ await this.keepRequiredByKept(prevLock, kept, Object.keys(freshLock.assets));
1669
+ const removed = this.reconcileRemovedAssets(
1670
+ resolved,
1671
+ /* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)])
1672
+ );
1391
1673
  const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
1392
1674
  const deployed = [
1393
1675
  ...new Set(
@@ -1399,8 +1681,11 @@ var AssetManager = class {
1399
1681
  await this.renderDriverMcpConfig();
1400
1682
  const agentRender = this.renderAgentInstances();
1401
1683
  const lockWritten = resolved.length > 0 || lib.entries.length > 0 || existsSync(this.lockPath);
1684
+ let relocked = [];
1402
1685
  if (lockWritten) {
1403
- writeLock(this.lockPath, buildLockFile(resolved, overridesApplied, libraryLock));
1686
+ const lockData = withKeptLockEntries(freshLock, kept);
1687
+ relocked = relockedRefs(prevLock, lockData);
1688
+ writeLock(this.lockPath, lockData);
1404
1689
  }
1405
1690
  this.recordInstallHistory(deployed, removed);
1406
1691
  const { npmInstalled, npmFailed } = await this.installRuntimeAssetDeps();
@@ -1409,6 +1694,7 @@ var AssetManager = class {
1409
1694
  removed,
1410
1695
  missing: stillMissing,
1411
1696
  ...Object.keys(missingHints).length > 0 ? { missingHints } : {},
1697
+ ...relocked.length > 0 ? { relocked } : {},
1412
1698
  lockWritten,
1413
1699
  npmInstalled,
1414
1700
  npmFailed,
@@ -1416,6 +1702,49 @@ var AssetManager = class {
1416
1702
  ...agentRender
1417
1703
  };
1418
1704
  }
1705
+ /**
1706
+ * Extend `kept` (see {@link keptLockEntries}) with every previous lock entry a kept asset
1707
+ * requires, transitively, that the new lock (`freshKeys`) does not already carry. The lock
1708
+ * records no edges, so they are read from the kept asset's deployed copy, where the
1709
+ * requirement is publisher-qualified the same way a library scan does it. A copy that
1710
+ * cannot be read has no known requirements.
1711
+ */
1712
+ async keepRequiredByKept(prev, kept, freshKeys) {
1713
+ if (!prev) return;
1714
+ const fresh = freshKeys.map(parseCanonicalLockKey);
1715
+ const queue = Object.keys(kept.assets);
1716
+ for (const key of queue) {
1717
+ for (const dep of await this.deployedRequirements(key)) {
1718
+ const want = memberIdentity(dep);
1719
+ if (!want) continue;
1720
+ const matches = (id) => id.kind === want.kind && id.name === want.name && (!want.publisher || !id.publisher || id.publisher === want.publisher);
1721
+ if (fresh.some(matches)) continue;
1722
+ for (const [depKey, entry] of Object.entries(prev.assets)) {
1723
+ if (depKey in kept.assets || entry.library?.installed) continue;
1724
+ if (!matches(parseCanonicalLockKey(depKey))) continue;
1725
+ kept.assets[depKey] = entry;
1726
+ queue.push(depKey);
1727
+ const src = entry.source ? prev.sources.find((s) => s.url === entry.source?.url) : void 0;
1728
+ if (src) kept.sources.push(src);
1729
+ }
1730
+ }
1731
+ }
1732
+ }
1733
+ /** Publisher-qualified refs the deployed copy of the asset behind lock `key` requires. */
1734
+ async deployedRequirements(key) {
1735
+ const { kind, name, publisher } = parseCanonicalLockKey(key);
1736
+ const component = kind ? this.installTarget(kind) : void 0;
1737
+ if (!kind || !name || !component) return [];
1738
+ const dir = deployedDir(component, name, this.driverTarget, this.global, this.projectDir);
1739
+ if (!existsSync(dir)) return [];
1740
+ try {
1741
+ const scanned = await scanDirectory(dir, publisher ?? "", { knownKinds: this.knownKinds });
1742
+ const own = scanned.find((e) => e.kind === kind && e.name === name);
1743
+ return own ? libraryEntryDepRefs(own) : [];
1744
+ } catch {
1745
+ return [];
1746
+ }
1747
+ }
1419
1748
  /**
1420
1749
  * Ensure all source clones are present/up to date. Returns an early
1421
1750
  * {@link InstallResult} describing the failed source on the first error, or
@@ -1480,6 +1809,7 @@ var AssetManager = class {
1480
1809
  for (const ref of Object.keys(oldLock.assets)) {
1481
1810
  if (!resolvedRefs.has(ref)) {
1482
1811
  const { kind, name } = parseCanonicalLockKey(ref);
1812
+ if (kind === "preset") continue;
1483
1813
  if (kind && name) {
1484
1814
  for (const t of this.targetHarnesses()) removeAsset(kind, name, this.deployOptsFor(t));
1485
1815
  removed.push(ref);
@@ -1605,11 +1935,12 @@ var AssetManager = class {
1605
1935
  }
1606
1936
  return { npmInstalled, npmFailed };
1607
1937
  }
1608
- async installLocked(repositories, patches) {
1938
+ async installLocked(repositories, patches, dependencies) {
1609
1939
  const lock = this.tryReadLock();
1610
1940
  if (!lock) {
1611
1941
  throw new Error("No lock file found. Run `skaile install` first.");
1612
1942
  }
1943
+ this.assertNoFolderCollision(Object.keys(lock.assets).map(parseCanonicalLockKey));
1613
1944
  const declBySlug = new Map(Object.entries(repositories));
1614
1945
  const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
1615
1946
  const factoryDir = Object.values(repositories).find((d) => d.factory)?.path ?? null;
@@ -1634,6 +1965,16 @@ var AssetManager = class {
1634
1965
  throw new Error(`Lock file verification failed:
1635
1966
  ${issues.join("\n ")}`);
1636
1967
  }
1968
+ const presetGaps = await this.declaredPresetGaps(dependencies, lock);
1969
+ if (presetGaps.size > 0) {
1970
+ const lines = [...presetGaps].flatMap(
1971
+ ([preset, gaps2]) => gaps2.map((g) => `${preset} item ${g.ref}`)
1972
+ );
1973
+ throw new Error(
1974
+ `Lock file does not pin every item of the declared presets (not resolved by \`skaile install\`):
1975
+ ${lines.join("\n ")}`
1976
+ );
1977
+ }
1637
1978
  const resolved = [];
1638
1979
  for (const [ref, entry] of Object.entries(lock.assets)) {
1639
1980
  if (!entry.source) continue;
@@ -1672,10 +2013,20 @@ var AssetManager = class {
1672
2013
  ];
1673
2014
  await this.renderDriverMcpConfig();
1674
2015
  const agentRender = this.renderAgentInstances();
2016
+ const alreadyMissing = new Set(
2017
+ library.missing.map((m) => {
2018
+ const p = parseRefTolerant(m.split(" ")[0] ?? m);
2019
+ return p ? `${p.kind}:${p.name}` : m;
2020
+ })
2021
+ );
2022
+ const gaps = (await this.lockGaps()).filter((g) => {
2023
+ const p = parseRefTolerant(g.ref);
2024
+ return !(p && alreadyMissing.has(`${p.kind}:${p.name}`));
2025
+ }).map((g) => g.message);
1675
2026
  return {
1676
2027
  deployed,
1677
2028
  removed: [],
1678
- missing: library.missing,
2029
+ missing: [...library.missing, ...gaps],
1679
2030
  lockWritten: false,
1680
2031
  ...agentRender
1681
2032
  };
@@ -1693,27 +2044,41 @@ var AssetManager = class {
1693
2044
  * store/factory sources. Defaults to copy.
1694
2045
  * @param opts.onLibraryOrigin - Called once per deployed library-origin asset with its
1695
2046
  * canonical ref and the name of the library it was resolved from.
2047
+ * @param opts.onPresets - Called once with the presets expanded into this add (the
2048
+ * added preset and any it nests); a library-held preset names its library.
1696
2049
  * @returns Array of asset refs that were deployed in this call.
1697
- * @throws When the asset cannot be found in any configured repository.
2050
+ * @throws When the asset cannot be found in any configured repository, or when a
2051
+ * preset's items do not all resolve (nothing is deployed or recorded then).
1698
2052
  */
1699
2053
  async add(ref, opts) {
1700
- const { repositories, sources, stores, overrides, patches } = this.loadConfig();
1701
- parseAssetRef(ref);
2054
+ const { repositories, sources, stores, dependencies, overrides, patches } = this.loadConfig();
2055
+ const parsed = parseAssetRef(ref);
1702
2056
  this.ensureSourceClonesQuiet(repositories, sources);
1703
2057
  const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
1704
2058
  projectDir: this.projectDir,
1705
2059
  knownKinds: this.knownKinds
1706
2060
  });
1707
- const { resolved, missing, overridesApplied, lib } = await this.resolveLibraryFirst([ref], {
1708
- provenanceIndex,
1709
- overrides,
1710
- stores
1711
- });
1712
- if (resolved.length === 0 && lib.entries.length === 0) {
2061
+ const { resolved, missing, overridesApplied, lib, presets } = await this.resolveLibraryFirst(
2062
+ [ref],
2063
+ { provenanceIndex, overrides, stores, declaredRefs: dependencies }
2064
+ );
2065
+ if (resolved.length === 0 && lib.entries.length === 0 && presets.length === 0) {
1713
2066
  throw new Error(
1714
2067
  `Asset not found: ${ref}${(await this.publisherChangeHints([ref]))[ref] ?? ""}${missing.length ? ` (missing: ${missing.join(", ")})` : ""}`
1715
2068
  );
1716
2069
  }
2070
+ if (parsed.kind === "preset" && missing.length > 0) {
2071
+ throw new Error(
2072
+ `Preset ${ref} has items that do not resolve: ${missing.join(", ")} \u2014 nothing was added`
2073
+ );
2074
+ }
2075
+ this.assertNoFolderCollision(
2076
+ [...resolved, ...lib.entries.map((e) => this.entryIdentity(e))],
2077
+ [
2078
+ ...Object.keys(this.tryReadLock()?.assets ?? {}).map(parseCanonicalLockKey),
2079
+ ...dependencies.map((d) => parseAssetRefLoose(d) ?? {})
2080
+ ]
2081
+ );
1717
2082
  if (missing.length > 0) {
1718
2083
  console.warn(
1719
2084
  `skaile: ${ref} was added without unresolved dependencies: ${missing.join(", ")}`
@@ -1734,6 +2099,7 @@ var AssetManager = class {
1734
2099
  if (origin && deployed.includes(deployedRef)) opts.onLibraryOrigin(deployedRef, origin);
1735
2100
  }
1736
2101
  }
2102
+ if (presets.length > 0) opts?.onPresets?.(presets);
1737
2103
  this.addDependencyToConfig(ref);
1738
2104
  await this.renderDriverMcpConfig();
1739
2105
  this.renderAgentInstances();
@@ -1802,7 +2168,7 @@ var AssetManager = class {
1802
2168
  const out = [];
1803
2169
  const seen = /* @__PURE__ */ new Set();
1804
2170
  for (const p of await this.search(void 0, "preset")) {
1805
- const members = [...p.dependencies, ...p.requires.map((d) => `${d.kind}:${d.name}`)];
2171
+ const members = [...p.dependencies, ...p.requires.map(depToStr)];
1806
2172
  if (!members.some((m) => sameAsset(memberIdentity(m), target))) continue;
1807
2173
  const publisher = p.publisher?.replace(/^@/, "");
1808
2174
  const presetId = { kind: "preset", name: p.name, publisher };
@@ -1988,20 +2354,103 @@ var AssetManager = class {
1988
2354
  * more (e.g. its library file was deleted): the declaration and lock entry
1989
2355
  * would otherwise be stuck behind a "not deployed" refusal.
1990
2356
  *
2357
+ * Removes only `ref` itself; use {@link removeWithDependencies} to also drop the
2358
+ * dependencies it leaves orphaned.
2359
+ *
1991
2360
  * Deliberately does NOT re-render the driver target's MCP config: the render
1992
2361
  * step is async (the declaration resolver is), and making this synchronous
1993
2362
  * method async would break every external caller. A removed MCP server's
1994
2363
  * generated entry is dropped by the next {@link AssetManager.install} — see
1995
2364
  * {@link renderMcpServerConfig}.
1996
2365
  *
1997
- * @param ref - Asset ref in `kind:name` form (e.g. `skill:my-skill`).
2366
+ * @param ref - Asset ref: `kind:@publisher/name`, or the short `kind:name` /
2367
+ * `name` form when exactly one declared, locked or deployed asset matches it.
1998
2368
  * @returns `true` if anything changed (files undeployed, declaration or lock
1999
2369
  * entry removed), `false` if the asset was neither deployed nor declared.
2000
2370
  * A `preset:` ref has no deployed files: only its declaration goes — its
2001
2371
  * expanded items leave with the next `skaile install`.
2372
+ * @throws When `ref` is malformed, or a short ref matches several assets (the
2373
+ * message lists them).
2002
2374
  */
2003
2375
  remove(ref) {
2004
- const parsed = parseAssetRef(ref);
2376
+ const target = this.resolveRemoveTarget(ref);
2377
+ return target ? this.removeResolved(target) : false;
2378
+ }
2379
+ /**
2380
+ * {@link remove} `ref`, then drop the dependencies that removal left orphaned:
2381
+ * locked assets `ref` required (directly or through one another) that
2382
+ * `skaile.yaml` no longer declares and no remaining locked asset requires. A
2383
+ * preset's items count as its requirements. They are undeployed and unlocked;
2384
+ * a dependency still declared or required elsewhere stays.
2385
+ *
2386
+ * @returns `changed` as {@link remove}; `pruned` the canonical refs
2387
+ * (`kind:@publisher/name`) of the dependencies dropped as well.
2388
+ */
2389
+ async removeWithDependencies(ref) {
2390
+ const target = this.resolveRemoveTarget(ref);
2391
+ if (!target) return { changed: false, pruned: [] };
2392
+ const asked = target.publisher ? assetRefToStr({ kind: target.kind, name: target.name, publisher: target.publisher }) : `${target.kind}:${target.name}`;
2393
+ let entry = null;
2394
+ try {
2395
+ entry = await this.info(asked);
2396
+ } catch {
2397
+ }
2398
+ const requiredRefs = entry ? libraryEntryDepRefs(entry) : [];
2399
+ const changed = this.removeResolved(target);
2400
+ if (!changed || requiredRefs.length === 0) return { changed, pruned: [] };
2401
+ return { changed, pruned: await this.pruneOrphans(requiredRefs) };
2402
+ }
2403
+ /**
2404
+ * Resolve the identity {@link remove} acts on. A publisher-qualified ref is taken
2405
+ * as written; a short ref (`kind:name`, or just `name`) is matched on kind + name
2406
+ * against the declared dependencies, the lock and the deployed assets, like
2407
+ * `why` / `diff` do.
2408
+ *
2409
+ * @returns the identity, or `null` when a name-only ref matches nothing.
2410
+ * @throws On a malformed ref, or when a short ref matches more than one asset.
2411
+ */
2412
+ resolveRemoveTarget(ref) {
2413
+ const trimmed = ref.trim();
2414
+ try {
2415
+ return parseAssetRef(trimmed);
2416
+ } catch (err) {
2417
+ if (!(err instanceof Error) || !err.message.startsWith("publisher required")) throw err;
2418
+ const colon = trimmed.indexOf(":");
2419
+ const rawKind = colon === -1 ? void 0 : trimmed.slice(0, colon).trim();
2420
+ const kind = rawKind === "mcp" ? "mcp-server" : rawKind;
2421
+ const hash = trimmed.indexOf("#", colon + 1);
2422
+ const name = trimmed.slice(colon + 1, hash === -1 ? void 0 : hash).trim();
2423
+ if (!name || /[@/\s]/.test(name)) throw err;
2424
+ const found = [];
2425
+ const note = (id) => {
2426
+ if (!id?.kind || id.name !== name || kind && id.kind !== kind) return;
2427
+ found.push({ kind: id.kind, name: id.name, publisher: id.publisher });
2428
+ };
2429
+ try {
2430
+ for (const dep of this.loadConfig().dependencies) note(memberIdentity(dep));
2431
+ } catch {
2432
+ }
2433
+ for (const key of Object.keys(this.tryReadLock()?.assets ?? {})) {
2434
+ note(parseCanonicalLockKey(key));
2435
+ }
2436
+ for (const e of this.listDeployed()) note(e);
2437
+ const qualified = found.filter((f) => f.publisher);
2438
+ const distinct = /* @__PURE__ */ new Map();
2439
+ for (const f of found) {
2440
+ if (!f.publisher && qualified.some((q) => q.kind === f.kind)) continue;
2441
+ distinct.set(`${f.kind}:${f.publisher ?? ""}/${f.name}`, f);
2442
+ }
2443
+ const matches = [...distinct.values()];
2444
+ if (matches.length > 1) {
2445
+ const list = matches.map((m) => m.publisher ? `${m.kind}:@${m.publisher}/${m.name}` : `${m.kind}:${m.name}`).join(", ");
2446
+ throw new Error(`"${ref}" matches several assets (${list}) \u2014 name one with its full ref.`);
2447
+ }
2448
+ if (matches.length === 1) return matches[0];
2449
+ return kind ? { kind, name } : null;
2450
+ }
2451
+ }
2452
+ /** Undeploy `parsed` from every target harness and drop its declaration and lock entry. */
2453
+ removeResolved(parsed) {
2005
2454
  let undeployed = false;
2006
2455
  for (const t of this.targetHarnesses()) {
2007
2456
  if (removeAsset(parsed.kind, parsed.name, this.deployOptsFor(t))) undeployed = true;
@@ -2019,6 +2468,38 @@ var AssetManager = class {
2019
2468
  }
2020
2469
  return changed;
2021
2470
  }
2471
+ /**
2472
+ * Drop the locked assets among `requiredRefs` (and what they require in turn) that
2473
+ * nothing needs any more: not declared in `skaile.yaml`, not required by a locked
2474
+ * asset outside that set. Call after the requiring asset is gone.
2475
+ *
2476
+ * @returns the canonical refs of the assets dropped.
2477
+ */
2478
+ async pruneOrphans(requiredRefs) {
2479
+ const lock = this.tryReadLock();
2480
+ if (!lock) return [];
2481
+ const { requires, roots, findKey } = await this.lockedGraph(lock);
2482
+ const closure = (seeds) => {
2483
+ const out = new Set(seeds);
2484
+ for (const node of out) for (const dep of requires.get(node) ?? []) out.add(dep);
2485
+ return out;
2486
+ };
2487
+ const candidates = closure(requiredRefs.map(findKey).filter((k) => !!k));
2488
+ const needed = closure([
2489
+ ...roots,
2490
+ ...Object.keys(lock.assets).filter((k) => !candidates.has(k))
2491
+ ]);
2492
+ const pruned = [];
2493
+ for (const key of candidates) {
2494
+ if (needed.has(key) || !lock.assets[key]) continue;
2495
+ const { kind, name, publisher } = parseCanonicalLockKey(key);
2496
+ if (!kind || !name) continue;
2497
+ this.removeResolved({ kind, name, publisher });
2498
+ this.pruneLockEntry({ kind, name, publisher });
2499
+ pruned.push(publisher ? assetRefToStr({ kind, name, publisher }) : `${kind}:${name}`);
2500
+ }
2501
+ return pruned;
2502
+ }
2022
2503
  /**
2023
2504
  * Drop every lock entry matching a removed asset's identity, version-agnostic so
2024
2505
  * a content-sha pseudo-version key is pruned too. Symmetric with {@link add}'s
@@ -2199,9 +2680,15 @@ var AssetManager = class {
2199
2680
  for (const { kind, name, component: k } of tracked.values()) {
2200
2681
  const legacy = deployedDir(k, name, "claude-code", false, this.projectDir);
2201
2682
  if (legacy === deployedDir(k, name, this.driverTarget, false, this.projectDir)) continue;
2202
- if (isDeployed(k, name, "claude-code", false, this.projectDir)) {
2203
- out.push({ kind, name, path: legacy });
2204
- }
2683
+ const found = deployedPaths(k, name, "claude-code", false, this.projectDir).find((p) => {
2684
+ try {
2685
+ lstatSync(p);
2686
+ return true;
2687
+ } catch {
2688
+ return false;
2689
+ }
2690
+ });
2691
+ if (found) out.push({ kind, name, path: found });
2205
2692
  }
2206
2693
  return out.sort((a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
2207
2694
  }
@@ -2235,6 +2722,7 @@ var AssetManager = class {
2235
2722
  const dependencies = resolveSkWorkspaceConfig(this.projectDir).dependencies ?? [];
2236
2723
  const lock = this.tryReadLock();
2237
2724
  const lockKeys = lock ? Object.keys(lock.assets).map(parseCanonicalLockKey) : [];
2725
+ const presetGaps = await this.declaredPresetGaps(dependencies, lock);
2238
2726
  let wiredMcps;
2239
2727
  const wiredMcpNames = async () => {
2240
2728
  if (wiredMcps === void 0) {
@@ -2257,7 +2745,7 @@ var AssetManager = class {
2257
2745
  continue;
2258
2746
  }
2259
2747
  if (kind && parsed.kind !== kind) continue;
2260
- const locked = parsed.kind === "preset" ? lock !== null : lockKeys.some(
2748
+ const locked = parsed.kind === "preset" ? lock !== null && !presetGaps.get(ref)?.length : lockKeys.some(
2261
2749
  (k) => k.kind === parsed.kind && k.name === parsed.name && (!parsed.publisher || k.publisher === parsed.publisher)
2262
2750
  );
2263
2751
  let state;
@@ -2285,6 +2773,38 @@ var AssetManager = class {
2285
2773
  }
2286
2774
  return out;
2287
2775
  }
2776
+ /**
2777
+ * Library-locked assets whose deployed directory lacks files the deploy writes —
2778
+ * a deleted `build.py` / `scripts/` helper next to an intact `SKILL.md`. The
2779
+ * expected set is the library entry's file set (every file under its manifest
2780
+ * `root:`), the same bytes the lock hashes; only presence is checked, so a
2781
+ * local edit never counts. An asset with no deployed directory is not listed
2782
+ * (`listDeclared` reports it as drift); an agent is rendered, not copied, so it
2783
+ * has no file mirror to compare. `skaile install` restores what is listed.
2784
+ *
2785
+ * @returns One {@link IncompleteDeploy} per affected asset; empty when healthy
2786
+ * or when the project has no lock.
2787
+ */
2788
+ async incompleteDeploys() {
2789
+ const lock = this.tryReadLock();
2790
+ if (!lock) return [];
2791
+ const locked = Object.entries(lock.assets).filter(([, e]) => e.library !== void 0 && !e.library.installed).map(([key]) => key);
2792
+ if (locked.length === 0) return [];
2793
+ const lib = await this.resolveLibraryEntries(locked, { quiet: true });
2794
+ const out = [];
2795
+ for (const entry of lib.entries) {
2796
+ const component = this.installTarget(entry.kind);
2797
+ if (!entry.files || !component || component === "agent") continue;
2798
+ const dest = join(
2799
+ deployedBase(component, this.driverTarget, this.global, this.projectDir),
2800
+ entry.name
2801
+ );
2802
+ if (!existsSync(dest)) continue;
2803
+ const missing = entry.files.filter((rel) => !existsSync(join(dest, rel)));
2804
+ if (missing.length > 0) out.push({ ref: canonicalEntryRef(entry), missing });
2805
+ }
2806
+ return out;
2807
+ }
2288
2808
  /**
2289
2809
  * Per-harness reconciliation of the **global** declared deps
2290
2810
  * (`~/.skaile/skaile.yaml`) against each global harness's deploy dir. The
@@ -2382,29 +2902,115 @@ var AssetManager = class {
2382
2902
  }
2383
2903
  return repoStatuses;
2384
2904
  }
2905
+ /**
2906
+ * Requirement graph of the locked assets, for {@link tree} / {@link why} / removal pruning.
2907
+ *
2908
+ * The lock records no edges, so they are rebuilt from each locked asset's own
2909
+ * declaration (`requires`, manifest `dependencies`, a preset's items) — the same
2910
+ * source the install walk reads, via {@link info}. An asset that no source or
2911
+ * library can be found for has no known requirements. Nodes are lock keys, plus
2912
+ * each declared `preset:` that the lock does not carry (a preset is expanded at
2913
+ * install, never locked), keyed by its declared ref.
2914
+ *
2915
+ * @returns `requires` (node → the lock keys it requires), `roots` (nodes
2916
+ * `skaile.yaml` declares) and `findKey` (a ref → its lock key, version-agnostic).
2917
+ */
2918
+ async lockedGraph(lock) {
2919
+ const locked = Object.keys(lock.assets).map((key) => ({ key, ...parseCanonicalLockKey(key) }));
2920
+ const findKey = (ref) => {
2921
+ const id = memberIdentity(ref);
2922
+ if (!id) return void 0;
2923
+ return locked.find(
2924
+ (l) => l.kind === id.kind && l.name === id.name && (!id.publisher || !l.publisher || l.publisher === id.publisher)
2925
+ )?.key;
2926
+ };
2927
+ const requires = /* @__PURE__ */ new Map();
2928
+ const link = async (node, ref) => {
2929
+ const out = [];
2930
+ try {
2931
+ const entry = await this.info(ref);
2932
+ for (const dep of entry ? libraryEntryDepRefs(entry) : []) {
2933
+ const key = findKey(dep);
2934
+ if (key && key !== node && !out.includes(key)) out.push(key);
2935
+ }
2936
+ } catch {
2937
+ }
2938
+ requires.set(node, out);
2939
+ };
2940
+ for (const l of locked) {
2941
+ await link(
2942
+ l.key,
2943
+ l.publisher ? `${l.kind}:@${l.publisher}/${l.name}` : `${l.kind}:${l.name}`
2944
+ );
2945
+ }
2946
+ const roots = [];
2947
+ let declared = [];
2948
+ try {
2949
+ declared = this.loadConfig().dependencies;
2950
+ } catch {
2951
+ }
2952
+ for (const dep of declared) {
2953
+ const key = findKey(dep);
2954
+ if (key) {
2955
+ if (!roots.includes(key)) roots.push(key);
2956
+ } else if (memberIdentity(dep)?.kind === "preset" && !roots.includes(dep)) {
2957
+ await link(dep, dep);
2958
+ roots.push(dep);
2959
+ }
2960
+ }
2961
+ return { requires, roots, findKey };
2962
+ }
2385
2963
  /**
2386
2964
  * Build the resolved dependency tree from the lock file.
2387
2965
  *
2966
+ * Required assets nest under the asset that requires them (see {@link lockedGraph});
2967
+ * an asset required by several parents appears under each, and a dependency cycle
2968
+ * is cut where it would repeat.
2969
+ *
2388
2970
  * @returns Root {@link DependencyNode} with `ref === "project"` whose children
2389
- * are the directly-declared dependencies, each with their own transitive subtrees.
2971
+ * are the directly-declared assets, followed by locked assets nothing requires,
2972
+ * each with the assets it requires as children.
2390
2973
  * Returns a single node with `ref === "(no lock file)"` when no lock exists.
2391
2974
  */
2392
- tree() {
2975
+ async tree() {
2393
2976
  const lock = this.tryReadLock();
2394
2977
  if (!lock) return { ref: "(no lock file)", children: [] };
2395
- return {
2396
- ref: "project",
2397
- children: Object.keys(lock.assets).map((ref) => ({ ref, children: [] }))
2978
+ const { requires, roots } = await this.lockedGraph(lock);
2979
+ const required = new Set([...requires.values()].flat());
2980
+ const build = (ref, path2) => ({
2981
+ ref,
2982
+ children: (requires.get(ref) ?? []).filter((child) => !path2.has(child)).map((child) => build(child, /* @__PURE__ */ new Set([...path2, child])))
2983
+ });
2984
+ const shown = /* @__PURE__ */ new Set();
2985
+ const top = [];
2986
+ const addTop = (ref) => {
2987
+ const node = build(ref, /* @__PURE__ */ new Set([ref]));
2988
+ top.push(node);
2989
+ const walk = (n) => {
2990
+ shown.add(n.ref);
2991
+ for (const c of n.children) walk(c);
2992
+ };
2993
+ walk(node);
2398
2994
  };
2995
+ const lockKeys = Object.keys(lock.assets);
2996
+ for (const ref of roots) addTop(ref);
2997
+ for (const ref of lockKeys) if (!roots.includes(ref) && !required.has(ref)) addTop(ref);
2998
+ for (const ref of lockKeys) if (!shown.has(ref)) addTop(ref);
2999
+ return { ref: "project", children: top };
2399
3000
  }
2400
3001
  /**
2401
- * Explain why an asset is installed by tracing its dependency chain in the lock file.
3002
+ * Explain why an asset is installed by tracing its dependency chain.
3003
+ *
3004
+ * The chain is read from the requirement graph ({@link lockedGraph}): the asset,
3005
+ * then the asset that requires it, and so on up to a dependency `skaile.yaml`
3006
+ * declares (the shortest such path; cycles are not followed twice).
2402
3007
  *
2403
3008
  * @param ref - Asset ref in `kind:name` form.
2404
- * @returns Chain of refs from the asset back to `"direct (skaile.yaml)"`, or `[]` when
2405
- * the asset is not in the lock file.
3009
+ * @returns Chain of refs from the asset back to `"direct (skaile.yaml)"`; an asset
3010
+ * no declared dependency reaches ends in `"not required by a declared dependency"`
3011
+ * instead. `[]` when the asset is not in the lock file.
2406
3012
  */
2407
- why(ref) {
3013
+ async why(ref) {
2408
3014
  const lock = this.tryReadLock();
2409
3015
  if (!lock) return [];
2410
3016
  const bare = (s) => s.includes("#") ? s.slice(0, s.indexOf("#")) : s;
@@ -2417,7 +3023,29 @@ var AssetManager = class {
2417
3023
  };
2418
3024
  const key = lock.assets[ref] ? ref : Object.keys(lock.assets).find(matchesIdentity);
2419
3025
  if (!key) return [];
2420
- return [key, "direct (skaile.yaml)"];
3026
+ const { requires, roots } = await this.lockedGraph(lock);
3027
+ if (roots.includes(key)) return [key, "direct (skaile.yaml)"];
3028
+ const requirers = /* @__PURE__ */ new Map();
3029
+ for (const [node, deps] of requires) {
3030
+ for (const dep of deps) requirers.set(dep, [...requirers.get(dep) ?? [], node]);
3031
+ }
3032
+ const child = /* @__PURE__ */ new Map();
3033
+ const seen = /* @__PURE__ */ new Set([key]);
3034
+ const queue = [key];
3035
+ for (let node = queue.shift(); node !== void 0; node = queue.shift()) {
3036
+ for (const parent of requirers.get(node) ?? []) {
3037
+ if (seen.has(parent)) continue;
3038
+ seen.add(parent);
3039
+ child.set(parent, node);
3040
+ if (roots.includes(parent)) {
3041
+ const chain = [];
3042
+ for (let c = parent; c !== void 0; c = child.get(c)) chain.push(c);
3043
+ return [...chain.reverse(), "direct (skaile.yaml)"];
3044
+ }
3045
+ queue.push(parent);
3046
+ }
3047
+ }
3048
+ return [key, "not required by a declared dependency"];
2421
3049
  }
2422
3050
  /**
2423
3051
  * List installed assets that have a newer upstream available.
@@ -2431,13 +3059,16 @@ var AssetManager = class {
2431
3059
  * than the locked one (`latestVersion`, `library` set), or the same version with
2432
3060
  * different bytes (`contentChanged`). `behind` is 0. An unregistered library is a
2433
3061
  * `problem` row.
3062
+ * - **Lock gaps** — every {@link AssetManager.lockGaps} gap (a declared dependency or a
3063
+ * deployed asset's `requires` with no lock entry, a lock that contradicts a
3064
+ * `skaile.yaml` pin) as a `problem` row, so `outdated` agrees with `check`.
2434
3065
  *
2435
3066
  * @returns Array of {@link OutdatedEntry} objects.
2436
3067
  */
2437
3068
  async outdated() {
2438
3069
  const { repositories } = this.loadConfig();
2439
3070
  const lock = this.tryReadLock();
2440
- if (!lock) return [];
3071
+ if (!lock) return this.addLockGapRows([]);
2441
3072
  const result = [];
2442
3073
  const reported = /* @__PURE__ */ new Set();
2443
3074
  const commitCounts = /* @__PURE__ */ new Map();
@@ -2479,6 +3110,9 @@ var AssetManager = class {
2479
3110
  if (!publisher || !kind || !name) continue;
2480
3111
  const libPath = this.libraryPath(origin.name, snap);
2481
3112
  if (!libPath) {
3113
+ const canonical = `${kind}:@${publisher}/${name}`;
3114
+ const heldElsewhere = (await this.resolveLibraryEntries([canonical], { quiet: true })).resolved.size > 0;
3115
+ const fix = heldElsewhere ? void 0 : (await this.unresolvedFixes([canonical])).get(canonical) ?? unregisteredLibraryFix(origin.name, canonical);
2482
3116
  result.push({
2483
3117
  kind,
2484
3118
  name,
@@ -2486,7 +3120,12 @@ var AssetManager = class {
2486
3120
  currentVersion: origin.version,
2487
3121
  behind: 0,
2488
3122
  library: origin.name,
2489
- problem: `library "${origin.name}" is not registered (renamed or removed?)`
3123
+ ...fix === void 0 ? {
3124
+ problem: `library "${origin.name}" is not registered (renamed?) \u2014 run \`skaile install\` to re-lock it against the library that holds it now`
3125
+ } : {
3126
+ problem: `library "${origin.name}" is not registered (renamed or removed?) \u2014 ${fix}`,
3127
+ manualFix: true
3128
+ }
2490
3129
  });
2491
3130
  reported.add(ref);
2492
3131
  continue;
@@ -2550,7 +3189,30 @@ var AssetManager = class {
2550
3189
  problem: "deployed copy missing"
2551
3190
  });
2552
3191
  }
2553
- return result;
3192
+ return this.addLockGapRows(result);
3193
+ }
3194
+ /**
3195
+ * Append one `problem` row per {@link AssetManager.lockGaps} gap whose asset is not
3196
+ * listed yet, so `outdated` reports the same set `check` and `install --locked` fail on.
3197
+ */
3198
+ async addLockGapRows(rows) {
3199
+ const listed = new Set(rows.map((r) => `${r.kind}:${r.name}`));
3200
+ for (const gap of await this.lockGaps()) {
3201
+ const parsed = parseRefTolerant(gap.ref);
3202
+ const id = parsed ? `${parsed.kind}:${parsed.name}` : gap.ref;
3203
+ if (listed.has(id)) continue;
3204
+ listed.add(id);
3205
+ rows.push({
3206
+ kind: parsed?.kind ?? "",
3207
+ name: parsed?.name ?? gap.ref,
3208
+ publisher: parsed?.publisher ?? "",
3209
+ currentVersion: "",
3210
+ behind: 0,
3211
+ problem: gap.reason,
3212
+ ...gap.manualFix ? { manualFix: true } : {}
3213
+ });
3214
+ }
3215
+ return rows;
2554
3216
  }
2555
3217
  /**
2556
3218
  * Produce a unified diff from the deployed asset to its catalog source: `-` lines
@@ -2578,10 +3240,20 @@ var AssetManager = class {
2578
3240
  parsed.name
2579
3241
  );
2580
3242
  if (!existsSync(deployedPath)) return { status: "not-deployed" };
2581
- const r = spawnSync("diff", ["-ruN", deployedPath, entry.source], { encoding: "utf8" });
2582
- if (r.status === 1) return { status: "differs", diff: r.stdout };
2583
- if (r.status === 0) return { status: "identical" };
2584
- throw new Error(`diff failed for ${ref}: ${r.stderr || r.error?.message || "unknown error"}`);
3243
+ const pairs = entry.files ? entry.files.map((rel) => [join(deployedPath, rel), join(dirname(entry.source), rel)]) : [[deployedPath, entry.source]];
3244
+ const diffs = [];
3245
+ for (const [from, to] of pairs) {
3246
+ const r = spawnSync("diff", [entry.files ? "-uN" : "-ruN", from, to], {
3247
+ encoding: "utf8"
3248
+ });
3249
+ if (r.status === 1) diffs.push(r.stdout);
3250
+ else if (r.status !== 0) {
3251
+ throw new Error(
3252
+ `diff failed for ${ref}: ${r.stderr || r.error?.message || "unknown error"}`
3253
+ );
3254
+ }
3255
+ }
3256
+ return diffs.length > 0 ? { status: "differs", diff: diffs.join("") } : { status: "identical" };
2585
3257
  }
2586
3258
  // ── Lock ───────────────────────────────────────────────────────────────
2587
3259
  /**
@@ -2602,7 +3274,8 @@ var AssetManager = class {
2602
3274
  const { resolved, overridesApplied, lib } = await this.resolveLibraryFirst(dependencies, {
2603
3275
  provenanceIndex,
2604
3276
  overrides,
2605
- stores
3277
+ stores,
3278
+ declaredRefs: dependencies
2606
3279
  });
2607
3280
  const libraryLock = await this.libraryLockEntries(lib);
2608
3281
  const lockData = buildLockFile(resolved, overridesApplied, libraryLock);
@@ -2709,7 +3382,7 @@ var AssetManager = class {
2709
3382
  * @returns Array of {@link DepIssue} objects. An empty array means the workspace is healthy.
2710
3383
  */
2711
3384
  async doctor() {
2712
- const { repositories } = this.loadConfig();
3385
+ const { repositories, dependencies } = this.loadConfig();
2713
3386
  const deployed = this.listDeployed();
2714
3387
  const issues = [];
2715
3388
  const lock = this.tryReadLock();
@@ -2725,20 +3398,23 @@ var AssetManager = class {
2725
3398
  );
2726
3399
  if (!full) {
2727
3400
  if (publisher) {
3401
+ const canonical = `${entry.kind}:@${publisher}/${entry.name}`;
3402
+ const fix = (await this.unresolvedFixes([canonical])).get(canonical);
2728
3403
  issues.push({
2729
3404
  assetKind: entry.kind,
2730
3405
  assetName: entry.name,
2731
3406
  depKind: entry.kind,
2732
3407
  depName: entry.name,
2733
3408
  inRepos: false,
2734
- unresolved: true
3409
+ unresolved: true,
3410
+ ...fix ? { fix } : {}
2735
3411
  });
2736
3412
  }
2737
3413
  continue;
2738
3414
  }
2739
3415
  const gaps = [];
2740
3416
  for (const req of full.requires) {
2741
- const dep = memberIdentity(`${req.kind}:${req.name}`);
3417
+ const dep = memberIdentity(depToStr(req));
2742
3418
  if (!dep) continue;
2743
3419
  const depComponent = this.installTarget(dep.kind);
2744
3420
  if (depComponent && !isDeployed(depComponent, dep.name, this.driverTarget, this.global, this.projectDir)) {
@@ -2749,18 +3425,191 @@ var AssetManager = class {
2749
3425
  assetKind: entry.kind,
2750
3426
  assetName: entry.name,
2751
3427
  depKind: req.kind,
2752
- depName: req.name,
3428
+ depName: req.publisher ? `@${req.publisher}/${req.name}` : req.name,
2753
3429
  inRepos
2754
3430
  };
2755
3431
  issues.push(issue);
2756
- if (!inRepos) gaps.push({ issue, ref: `${dep.kind}:${dep.name}` });
3432
+ if (!inRepos) gaps.push({ issue, ref: depToStr(req) });
2757
3433
  }
2758
3434
  }
2759
3435
  const inLibrary = await this.resolveLibraryEntries(gaps.map((g) => g.ref));
2760
3436
  for (const g of gaps) g.issue.inRepos = inLibrary.resolved.has(g.ref);
2761
3437
  }
3438
+ for (const [presetRef, gaps] of await this.declaredPresetGaps(dependencies, lock)) {
3439
+ const preset = parseRefTolerant(presetRef);
3440
+ if (!preset) continue;
3441
+ for (const g of gaps) {
3442
+ const item = parseRefTolerant(g.ref);
3443
+ if (!item) continue;
3444
+ issues.push({
3445
+ assetKind: "preset",
3446
+ assetName: preset.name,
3447
+ depKind: item.kind,
3448
+ depName: item.publisher ? `@${item.publisher}/${item.name}` : item.name,
3449
+ inRepos: g.resolvable
3450
+ });
3451
+ }
3452
+ }
2762
3453
  return issues;
2763
3454
  }
3455
+ /**
3456
+ * Everything the lock does not cover or contradicts, in one list: the set
3457
+ * `skaile check`, `skaile install --locked` and `skaile outdated` all report.
3458
+ *
3459
+ * - `declared` — a `skaile.yaml` dependency with no lock entry (library or source gone,
3460
+ * unresolvable ref, never installed);
3461
+ * - `invalid` — a `dependencies:` entry that is not a parseable ref;
3462
+ * - `drift` — declared + locked but its deployed files are missing;
3463
+ * - `pin` — a declared `#pin` that no lock entry for that asset satisfies;
3464
+ * - `requires` — a deployed asset's `requires` that is not deployed (so not locked either);
3465
+ * - `unresolved` — a lock-managed asset that no source or library holds any more.
3466
+ *
3467
+ * Project mode only: a global manager (`global: true`) returns `[]`. A project without
3468
+ * `skaile.yaml` has no declared set, so only the `requires` / `unresolved` gaps of
3469
+ * deployed assets can appear.
3470
+ *
3471
+ * @returns One {@link LockGap} per gap; empty when the lock covers everything declared.
3472
+ */
3473
+ async lockGaps() {
3474
+ if (this.global) return [];
3475
+ const gaps = [];
3476
+ const issues = await this.doctor();
3477
+ for (const i of issues) {
3478
+ if (i.unresolved) {
3479
+ const ref = `${i.assetKind}:${i.assetName}`;
3480
+ const reason = `is locked but no source or library provides it any more \u2014 its requirements cannot be checked${i.fix ? ` (${i.fix})` : ""}`;
3481
+ gaps.push({
3482
+ type: "unresolved",
3483
+ ref,
3484
+ reason,
3485
+ message: `${ref} ${reason}`,
3486
+ ...i.fix ? { manualFix: true } : {}
3487
+ });
3488
+ continue;
3489
+ }
3490
+ const asset = `${i.assetKind}:${i.assetName}`;
3491
+ const dep = `${i.depKind}:${i.depName}`;
3492
+ const fix = i.inRepos ? "fixable" : "not found";
3493
+ gaps.push({
3494
+ type: "requires",
3495
+ ref: dep,
3496
+ reason: `is required by ${asset} but neither deployed nor locked (${fix})`,
3497
+ message: `${asset} requires ${dep} (${fix})`
3498
+ });
3499
+ }
3500
+ let declared = [];
3501
+ try {
3502
+ declared = await this.listDeclared();
3503
+ } catch {
3504
+ return gaps;
3505
+ }
3506
+ const unlocked = declared.filter((d) => d.state === "declared").map((d) => d.ref);
3507
+ const hints = await this.publisherChangeHints(unlocked);
3508
+ const lockKeys = Object.keys(this.tryReadLock()?.assets ?? {});
3509
+ for (const d of declared) {
3510
+ if (d.kind === "preset" && issues.some((i) => i.assetKind === "preset" && i.assetName === d.name)) {
3511
+ continue;
3512
+ }
3513
+ let gap;
3514
+ if (d.state === "invalid") {
3515
+ gap = {
3516
+ type: "invalid",
3517
+ reason: "is not a valid asset ref (expected kind:@<publisher>/<name>#<version>)"
3518
+ };
3519
+ } else if (d.state === "drift") {
3520
+ gap = {
3521
+ type: "drift",
3522
+ reason: "is declared and locked but not materialized \u2014 run `skaile install`"
3523
+ };
3524
+ } else if (d.state === "declared") {
3525
+ const hint = hints[d.ref];
3526
+ gap = hint ? { type: "declared", reason: `is declared but does not resolve${hint}`, manualFix: true } : {
3527
+ type: "declared",
3528
+ reason: "is declared but does not resolve \u2014 run `skaile install`"
3529
+ };
3530
+ } else {
3531
+ const want = parseAssetRef(d.ref);
3532
+ if (!want.pin || want.kind === "preset") continue;
3533
+ const held = lockKeys.map((key) => ({ key, id: parseCanonicalLockKey(key) })).filter(
3534
+ ({ id }) => id.kind === want.kind && id.name === want.name && (!want.publisher || id.publisher === want.publisher)
3535
+ );
3536
+ if (held.length > 0 && !held.some(({ id }) => !id.version || matchPin(want.pin, id.version))) {
3537
+ gap = {
3538
+ type: "pin",
3539
+ reason: `is pinned in skaile.yaml but the lock holds ${held.map((h) => h.key).join(", ")} \u2014 run \`skaile install\``
3540
+ };
3541
+ }
3542
+ }
3543
+ if (gap) gaps.push({ ...gap, ref: d.ref, message: `${d.ref} ${gap.reason}` });
3544
+ }
3545
+ return gaps;
3546
+ }
3547
+ /**
3548
+ * Items of each declared `preset:` dependency that the lock does not cover.
3549
+ *
3550
+ * A preset is never locked itself; the lock lists only its expanded items, so an
3551
+ * item missing from the lock never resolved (or was declared after the last
3552
+ * install). Presets are read from the local sources and libraries (nested presets
3553
+ * followed); one only a store holds cannot be read without the network and
3554
+ * reports no gaps. Without a lock nothing is reported: `list` and `check` already
3555
+ * show a never-installed declaration as `declared`.
3556
+ *
3557
+ * @param declared - The `dependencies:` refs as written in `skaile.yaml`.
3558
+ * @param lock - The current lock, or `null` when none exists.
3559
+ * @returns Declared preset ref → its uncovered items; `resolvable` is `true` when a
3560
+ * local source or library offers the item (so `skaile install` can fix it).
3561
+ * Presets without gaps are absent.
3562
+ */
3563
+ async declaredPresetGaps(declared, lock) {
3564
+ const gaps = /* @__PURE__ */ new Map();
3565
+ if (!lock) return gaps;
3566
+ const presetRefs = declared.filter((d) => parseRefTolerant(d)?.kind === "preset");
3567
+ if (presetRefs.length === 0) return gaps;
3568
+ const { repositories } = this.loadConfig();
3569
+ const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
3570
+ projectDir: this.projectDir,
3571
+ knownKinds: this.knownKinds
3572
+ });
3573
+ const lockKeys = Object.keys(lock.assets).map(parseCanonicalLockKey);
3574
+ const sourceCandidate = (ref) => {
3575
+ const p = parseRefTolerant(ref);
3576
+ if (!p?.publisher) return void 0;
3577
+ const hash = ref.indexOf("#");
3578
+ const pin = p.pin ?? (hash >= 0 ? ref.slice(hash + 1) : void 0);
3579
+ return (provenanceIndex.get(`${p.publisher}/${p.kind}:${p.name}`) ?? []).filter((c) => matchPin(pin, c.version)).sort((a, b) => {
3580
+ const va = coerceVersion(a.version);
3581
+ const vb = coerceVersion(b.version);
3582
+ return gt(va, vb) ? -1 : gt(vb, va) ? 1 : 0;
3583
+ })[0];
3584
+ };
3585
+ const itemsOf = async (ref) => {
3586
+ const src = sourceCandidate(ref);
3587
+ if (src) return (src.deps ?? []).map((d) => qualifyDepRef(d, src.publisher));
3588
+ const entry = (await this.resolveLibraryEntries([ref], { quiet: true })).entries[0];
3589
+ return entry ? libraryEntryDepRefs(entry) : null;
3590
+ };
3591
+ const resolvableLocally = async (ref) => sourceCandidate(ref) !== void 0 || (await this.resolveLibraryEntries([ref], { quiet: true })).resolved.has(ref);
3592
+ const isLocked = (item) => lockKeys.some(
3593
+ (k) => k.kind === item.kind && k.name === item.name && (!item.publisher || k.publisher === item.publisher)
3594
+ );
3595
+ for (const presetRef of presetRefs) {
3596
+ const out = [];
3597
+ const visited = /* @__PURE__ */ new Set();
3598
+ const walk = async (ref) => {
3599
+ if (visited.has(ref)) return;
3600
+ visited.add(ref);
3601
+ for (const item of await itemsOf(ref) ?? []) {
3602
+ const p = parseRefTolerant(item);
3603
+ if (!p) continue;
3604
+ if (p.kind === "preset") await walk(item);
3605
+ else if (!isLocked(p)) out.push({ ref: item, resolvable: await resolvableLocally(item) });
3606
+ }
3607
+ };
3608
+ await walk(presetRef);
3609
+ if (out.length > 0) gaps.set(presetRef, out);
3610
+ }
3611
+ return gaps;
3612
+ }
2764
3613
  // ── Scaffold ───────────────────────────────────────────────────────────
2765
3614
  /**
2766
3615
  * Create a minimal asset scaffold directory (delegates to {@link createScaffold}).
@@ -2829,15 +3678,21 @@ var AssetManager = class {
2829
3678
  const { kind, name } = parseCanonicalLockKey(ref);
2830
3679
  const component = kind ? this.installTarget(kind) : void 0;
2831
3680
  if (!kind || !name || !component) continue;
2832
- const dest = deployedDir(component, name, this.driverTarget, this.global, this.projectDir);
2833
- try {
2834
- const stat = lstatSync(dest);
2835
- if (stat.isSymbolicLink()) unlinkSync(dest);
2836
- else rmSync(dest, { recursive: true, force: true });
2837
- removed.push(ref);
2838
- } catch {
2839
- removed.push(ref);
3681
+ for (const dest of deployedPaths(
3682
+ component,
3683
+ name,
3684
+ this.driverTarget,
3685
+ this.global,
3686
+ this.projectDir
3687
+ )) {
3688
+ try {
3689
+ const stat = lstatSync(dest);
3690
+ if (stat.isSymbolicLink()) unlinkSync(dest);
3691
+ else rmSync(dest, { recursive: true, force: true });
3692
+ } catch {
3693
+ }
2840
3694
  }
3695
+ removed.push(ref);
2841
3696
  }
2842
3697
  return removed;
2843
3698
  }
@@ -2973,5 +3828,5 @@ var AssetManager = class {
2973
3828
  };
2974
3829
 
2975
3830
  export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, installAgentInstances, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName };
2976
- //# sourceMappingURL=chunk-D5HOETOB.js.map
2977
- //# sourceMappingURL=chunk-D5HOETOB.js.map
3831
+ //# sourceMappingURL=chunk-IMKSM5NL.js.map
3832
+ //# sourceMappingURL=chunk-IMKSM5NL.js.map