@skaile/workspaces 5.10.0 → 5.12.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 (425) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +14 -2
  3. package/dist/{asset-feeds-ELEBTNUS.js → asset-feeds-AM6PSSGM.js} +24 -21
  4. package/dist/asset-feeds-AM6PSSGM.js.map +1 -0
  5. package/dist/asset-manager/catalog-deployer.js +8 -8
  6. package/dist/asset-manager/fragments.js +1 -1
  7. package/dist/asset-manager/index.js +16 -16
  8. package/dist/asset-manager/installer.js +11 -11
  9. package/dist/asset-manager/renderers.js +9 -9
  10. package/dist/asset-manager/src/fragments.d.ts +5 -0
  11. package/dist/asset-manager/src/fragments.d.ts.map +1 -1
  12. package/dist/asset-manager/src/index.d.ts +197 -11
  13. package/dist/asset-manager/src/index.d.ts.map +1 -1
  14. package/dist/asset-manager/src/install-agent.d.ts +9 -1
  15. package/dist/asset-manager/src/install-agent.d.ts.map +1 -1
  16. package/dist/asset-manager/src/install-instances.d.ts.map +1 -1
  17. package/dist/asset-manager/src/installer.d.ts +33 -3
  18. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  19. package/dist/asset-manager/src/renderers.d.ts +5 -0
  20. package/dist/asset-manager/src/renderers.d.ts.map +1 -1
  21. package/dist/bridge/drivers/claude-sdk.js +16 -13
  22. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  23. package/dist/bridge/drivers/codex.js +43 -16
  24. package/dist/bridge/drivers/codex.js.map +1 -1
  25. package/dist/bridge/drivers/continue.js +10 -10
  26. package/dist/bridge/drivers/echo.js +2 -2
  27. package/dist/bridge/drivers/gemini.js +7 -7
  28. package/dist/bridge/drivers/goose.js +10 -10
  29. package/dist/bridge/drivers/omp.js +52 -9
  30. package/dist/bridge/drivers/omp.js.map +1 -1
  31. package/dist/bridge/drivers/opencode.js +10 -10
  32. package/dist/bridge/drivers/qwen.js +7 -7
  33. package/dist/bridge/index.js +12 -12
  34. package/dist/bridge/src/codex/config-toml.d.ts +8 -3
  35. package/dist/bridge/src/codex/config-toml.d.ts.map +1 -1
  36. package/dist/bridge/src/codex/managed-driver.d.ts.map +1 -1
  37. package/dist/bridge/src/drivers/claude-sdk.d.ts +1 -1
  38. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  39. package/dist/bridge/src/drivers/codex.d.ts.map +1 -1
  40. package/dist/bridge/src/drivers/omp-models-yml.d.ts.map +1 -1
  41. package/dist/bridge/src/drivers/omp.d.ts +9 -0
  42. package/dist/bridge/src/drivers/omp.d.ts.map +1 -1
  43. package/dist/bridge/src/provider-materialize/credential-env.d.ts +15 -0
  44. package/dist/bridge/src/provider-materialize/credential-env.d.ts.map +1 -1
  45. package/dist/{chunk-7CR6HT2X.js → chunk-27L6BBAT.js} +9 -9
  46. package/dist/{chunk-7CR6HT2X.js.map → chunk-27L6BBAT.js.map} +1 -1
  47. package/dist/{chunk-QG3HOJEK.js → chunk-2JFIFDDO.js} +3 -3
  48. package/dist/{chunk-QG3HOJEK.js.map → chunk-2JFIFDDO.js.map} +1 -1
  49. package/dist/{chunk-IJHH4DSW.js → chunk-2X3JBVGD.js} +11 -10
  50. package/dist/chunk-2X3JBVGD.js.map +1 -0
  51. package/dist/{chunk-G54USEUP.js → chunk-3HUV2LJF.js} +4 -4
  52. package/dist/{chunk-G54USEUP.js.map → chunk-3HUV2LJF.js.map} +1 -1
  53. package/dist/{chunk-JFXQEVLF.js → chunk-3LVB3HGH.js} +3 -3
  54. package/dist/{chunk-JFXQEVLF.js.map → chunk-3LVB3HGH.js.map} +1 -1
  55. package/dist/{chunk-32XKCNCL.js → chunk-6VLHITAM.js} +146 -4
  56. package/dist/chunk-6VLHITAM.js.map +1 -0
  57. package/dist/{chunk-GEWAIQZ5.js → chunk-7CR2OZPA.js} +10 -12
  58. package/dist/chunk-7CR2OZPA.js.map +1 -0
  59. package/dist/{chunk-6HHE63CO.js → chunk-7QWQTA5S.js} +89 -12
  60. package/dist/chunk-7QWQTA5S.js.map +1 -0
  61. package/dist/{chunk-7I5VRVDP.js → chunk-7XWUJC3A.js} +4 -4
  62. package/dist/{chunk-7I5VRVDP.js.map → chunk-7XWUJC3A.js.map} +1 -1
  63. package/dist/{chunk-3BEY4WK3.js → chunk-C3BMXXTN.js} +203 -31
  64. package/dist/chunk-C3BMXXTN.js.map +1 -0
  65. package/dist/{chunk-IXWODEED.js → chunk-CTP3XBMF.js} +47 -8
  66. package/dist/chunk-CTP3XBMF.js.map +1 -0
  67. package/dist/{chunk-HOHUEKES.js → chunk-DCHQ6BAZ.js} +3 -3
  68. package/dist/{chunk-HOHUEKES.js.map → chunk-DCHQ6BAZ.js.map} +1 -1
  69. package/dist/{chunk-DALV4HHP.js → chunk-DKXNKFKP.js} +3 -2
  70. package/dist/{chunk-DALV4HHP.js.map → chunk-DKXNKFKP.js.map} +1 -1
  71. package/dist/{chunk-3MKPZZRE.js → chunk-G3X4MLDB.js} +3 -3
  72. package/dist/{chunk-3MKPZZRE.js.map → chunk-G3X4MLDB.js.map} +1 -1
  73. package/dist/{chunk-SBFW3CJH.js → chunk-GWD3QNVS.js} +5 -5
  74. package/dist/{chunk-SBFW3CJH.js.map → chunk-GWD3QNVS.js.map} +1 -1
  75. package/dist/chunk-H5U7LNTJ.js +122 -0
  76. package/dist/chunk-H5U7LNTJ.js.map +1 -0
  77. package/dist/{chunk-SX5U422E.js → chunk-HYMERG57.js} +3 -3
  78. package/dist/{chunk-SX5U422E.js.map → chunk-HYMERG57.js.map} +1 -1
  79. package/dist/{chunk-V5TZH4GF.js → chunk-ICIYSJBQ.js} +4 -4
  80. package/dist/{chunk-V5TZH4GF.js.map → chunk-ICIYSJBQ.js.map} +1 -1
  81. package/dist/{chunk-YBQONMH2.js → chunk-ID745F3R.js} +353 -193
  82. package/dist/chunk-ID745F3R.js.map +1 -0
  83. package/dist/{chunk-CLTHEKHR.js → chunk-JQ3DXNQV.js} +4 -4
  84. package/dist/{chunk-CLTHEKHR.js.map → chunk-JQ3DXNQV.js.map} +1 -1
  85. package/dist/chunk-K4VOEYOR.js +21 -0
  86. package/dist/chunk-K4VOEYOR.js.map +1 -0
  87. package/dist/{chunk-XCNK4UE5.js → chunk-KVSDRB35.js} +5 -5
  88. package/dist/{chunk-XCNK4UE5.js.map → chunk-KVSDRB35.js.map} +1 -1
  89. package/dist/{chunk-HJ5MCB3L.js → chunk-MF54ZHWT.js} +20 -3
  90. package/dist/chunk-MF54ZHWT.js.map +1 -0
  91. package/dist/{chunk-NRDSGLCQ.js → chunk-N5M2EISA.js} +240 -52
  92. package/dist/chunk-N5M2EISA.js.map +1 -0
  93. package/dist/{chunk-OBVJ7RPW.js → chunk-NDF24LYP.js} +3 -3
  94. package/dist/{chunk-OBVJ7RPW.js.map → chunk-NDF24LYP.js.map} +1 -1
  95. package/dist/{chunk-ZGQTS3Y4.js → chunk-NGJIVDDD.js} +3 -3
  96. package/dist/{chunk-ZGQTS3Y4.js.map → chunk-NGJIVDDD.js.map} +1 -1
  97. package/dist/{chunk-PE36IF2D.js → chunk-NJV5SLEK.js} +159 -44
  98. package/dist/chunk-NJV5SLEK.js.map +1 -0
  99. package/dist/{chunk-OOWHXOK6.js → chunk-NJXR5OAM.js} +4 -4
  100. package/dist/{chunk-OOWHXOK6.js.map → chunk-NJXR5OAM.js.map} +1 -1
  101. package/dist/{chunk-CGYEHQOX.js → chunk-NOA6EJXE.js} +11 -2
  102. package/dist/chunk-NOA6EJXE.js.map +1 -0
  103. package/dist/{chunk-V7PQZOBR.js → chunk-NR7H7ZJP.js} +14 -6
  104. package/dist/chunk-NR7H7ZJP.js.map +1 -0
  105. package/dist/{chunk-OBYKJB4U.js → chunk-O2SYFJWP.js} +79 -5
  106. package/dist/chunk-O2SYFJWP.js.map +1 -0
  107. package/dist/{chunk-5LKOAX7I.js → chunk-OQ5MKDS3.js} +21 -5
  108. package/dist/chunk-OQ5MKDS3.js.map +1 -0
  109. package/dist/{chunk-4P3KTUZH.js → chunk-P23WJCH2.js} +51 -14
  110. package/dist/chunk-P23WJCH2.js.map +1 -0
  111. package/dist/{chunk-NWDSLXOX.js → chunk-PHRNUTSG.js} +118 -118
  112. package/dist/chunk-PHRNUTSG.js.map +1 -0
  113. package/dist/{chunk-JZCSNEHK.js → chunk-PNOGX36L.js} +141 -39
  114. package/dist/chunk-PNOGX36L.js.map +1 -0
  115. package/dist/{chunk-4OV5DSBB.js → chunk-PNTXHTBC.js} +5 -5
  116. package/dist/chunk-PNTXHTBC.js.map +1 -0
  117. package/dist/{chunk-TRSQP3JD.js → chunk-POQ6PGNE.js} +20 -4
  118. package/dist/chunk-POQ6PGNE.js.map +1 -0
  119. package/dist/{chunk-Y63UQZ7P.js → chunk-QCEKGYHF.js} +3 -3
  120. package/dist/{chunk-Y63UQZ7P.js.map → chunk-QCEKGYHF.js.map} +1 -1
  121. package/dist/{chunk-GFCXNFTW.js → chunk-SHQMPGOL.js} +4 -4
  122. package/dist/{chunk-GFCXNFTW.js.map → chunk-SHQMPGOL.js.map} +1 -1
  123. package/dist/{chunk-WBYDAL6X.js → chunk-SMT3LNAG.js} +4 -4
  124. package/dist/{chunk-WBYDAL6X.js.map → chunk-SMT3LNAG.js.map} +1 -1
  125. package/dist/{chunk-2FMEGSBN.js → chunk-T5TSLSRO.js} +135 -47
  126. package/dist/chunk-T5TSLSRO.js.map +1 -0
  127. package/dist/{chunk-2JMLDZLP.js → chunk-TVAR4JHA.js} +903 -138
  128. package/dist/chunk-TVAR4JHA.js.map +1 -0
  129. package/dist/{chunk-MVQ3Q2HM.js → chunk-UTKENBB6.js} +5 -5
  130. package/dist/{chunk-MVQ3Q2HM.js.map → chunk-UTKENBB6.js.map} +1 -1
  131. package/dist/{chunk-TH4CTXHX.js → chunk-VRLKHL74.js} +6 -6
  132. package/dist/{chunk-TH4CTXHX.js.map → chunk-VRLKHL74.js.map} +1 -1
  133. package/dist/{chunk-Z2K6BGUR.js → chunk-WI4T3KIS.js} +51 -16
  134. package/dist/chunk-WI4T3KIS.js.map +1 -0
  135. package/dist/{chunk-MGGFBZWA.js → chunk-X3RMRJNR.js} +6 -4
  136. package/dist/chunk-X3RMRJNR.js.map +1 -0
  137. package/dist/{chunk-FRPBSUFH.js → chunk-X6LKA3TJ.js} +5 -5
  138. package/dist/{chunk-FRPBSUFH.js.map → chunk-X6LKA3TJ.js.map} +1 -1
  139. package/dist/{chunk-3KHYGOEY.js → chunk-XMAJJPA4.js} +3 -3
  140. package/dist/{chunk-3KHYGOEY.js.map → chunk-XMAJJPA4.js.map} +1 -1
  141. package/dist/{chunk-Y34MSWEZ.js → chunk-XTVSNWLL.js} +19 -7
  142. package/dist/chunk-XTVSNWLL.js.map +1 -0
  143. package/dist/{chunk-VLXDXRRX.js → chunk-XZY6SBGU.js} +3 -3
  144. package/dist/{chunk-VLXDXRRX.js.map → chunk-XZY6SBGU.js.map} +1 -1
  145. package/dist/{chunk-5QI2JYJC.js → chunk-YQU2BPQD.js} +7 -5
  146. package/dist/chunk-YQU2BPQD.js.map +1 -0
  147. package/dist/cli/index.js +1207 -580
  148. package/dist/cli/index.js.map +1 -1
  149. package/dist/cli/src/agent-checks.d.ts +40 -0
  150. package/dist/cli/src/agent-checks.d.ts.map +1 -0
  151. package/dist/cli/src/asset-feeds.d.ts.map +1 -1
  152. package/dist/cli/src/commands/asset-cmd.d.ts.map +1 -1
  153. package/dist/cli/src/commands/catalog.d.ts +24 -0
  154. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  155. package/dist/cli/src/commands/config.d.ts.map +1 -1
  156. package/dist/cli/src/commands/diff.d.ts +1 -1
  157. package/dist/cli/src/commands/diff.d.ts.map +1 -1
  158. package/dist/cli/src/commands/integration.d.ts.map +1 -1
  159. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  160. package/dist/cli/src/commands/library-publish.d.ts.map +1 -1
  161. package/dist/cli/src/commands/manage.d.ts +6 -0
  162. package/dist/cli/src/commands/manage.d.ts.map +1 -1
  163. package/dist/cli/src/commands/mcp-server.d.ts +1 -1
  164. package/dist/cli/src/commands/migrate-assets.d.ts +2 -1
  165. package/dist/cli/src/commands/migrate-assets.d.ts.map +1 -1
  166. package/dist/cli/src/commands/migrate.d.ts.map +1 -1
  167. package/dist/cli/src/commands/outdated.d.ts.map +1 -1
  168. package/dist/cli/src/commands/plugin/claude-code.d.ts +1 -1
  169. package/dist/cli/src/commands/plugin/claude-code.d.ts.map +1 -1
  170. package/dist/cli/src/commands/plugin/types.d.ts +1 -1
  171. package/dist/cli/src/commands/plugin-registry-cmd.d.ts.map +1 -1
  172. package/dist/cli/src/commands/project.d.ts +3 -1
  173. package/dist/cli/src/commands/project.d.ts.map +1 -1
  174. package/dist/cli/src/commands/publish-core.d.ts +5 -0
  175. package/dist/cli/src/commands/publish-core.d.ts.map +1 -1
  176. package/dist/cli/src/commands/run.d.ts +2 -1
  177. package/dist/cli/src/commands/run.d.ts.map +1 -1
  178. package/dist/cli/src/commands/serve-preflight.d.ts +22 -0
  179. package/dist/cli/src/commands/serve-preflight.d.ts.map +1 -0
  180. package/dist/cli/src/commands/serve.d.ts.map +1 -1
  181. package/dist/cli/src/commands/source-manifest-publish.d.ts.map +1 -1
  182. package/dist/cli/src/commands/source-manifest.d.ts.map +1 -1
  183. package/dist/cli/src/commands/source.d.ts.map +1 -1
  184. package/dist/cli/src/commands/store.d.ts.map +1 -1
  185. package/dist/cli/src/commands/tree.d.ts.map +1 -1
  186. package/dist/cli/src/commands/update.d.ts.map +1 -1
  187. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  188. package/dist/cli/src/commands/why.d.ts +1 -0
  189. package/dist/cli/src/commands/why.d.ts.map +1 -1
  190. package/dist/cli/src/curated-manifests.d.ts +21 -0
  191. package/dist/cli/src/curated-manifests.d.ts.map +1 -0
  192. package/dist/cli/src/helpers.d.ts +12 -0
  193. package/dist/cli/src/helpers.d.ts.map +1 -1
  194. package/dist/cli/src/index-busy.d.ts +8 -0
  195. package/dist/cli/src/index-busy.d.ts.map +1 -0
  196. package/dist/cli/src/plugin-store/paths.d.ts +7 -3
  197. package/dist/cli/src/plugin-store/paths.d.ts.map +1 -1
  198. package/dist/cli/src/skill-checks.d.ts +29 -8
  199. package/dist/cli/src/skill-checks.d.ts.map +1 -1
  200. package/dist/cli/src/store-addable.d.ts +18 -0
  201. package/dist/cli/src/store-addable.d.ts.map +1 -0
  202. package/dist/connectors/config.js +5 -5
  203. package/dist/connectors/index.js +16 -16
  204. package/dist/{connectors-OXP2JBGI.js → connectors-UNKUKEPG.js} +19 -19
  205. package/dist/{connectors-OXP2JBGI.js.map → connectors-UNKUKEPG.js.map} +1 -1
  206. package/dist/core/index.js +7 -7
  207. package/dist/core/logging.js +1 -1
  208. package/dist/core/manifest.js +2 -2
  209. package/dist/core/models.js +1 -1
  210. package/dist/core/publish-manifest.js +2 -2
  211. package/dist/core/ref.js +1 -1
  212. package/dist/core/runtime-assets.js +5 -5
  213. package/dist/core/src/index.d.ts +5 -4
  214. package/dist/core/src/index.d.ts.map +1 -1
  215. package/dist/core/src/lock.d.ts +19 -8
  216. package/dist/core/src/lock.d.ts.map +1 -1
  217. package/dist/core/src/logging/sinks/sqlite-sink.d.ts.map +1 -1
  218. package/dist/core/src/manifest.d.ts +5 -0
  219. package/dist/core/src/manifest.d.ts.map +1 -1
  220. package/dist/core/src/models.d.ts +4 -4
  221. package/dist/core/src/models.d.ts.map +1 -1
  222. package/dist/core/src/package-manager.d.ts +19 -0
  223. package/dist/core/src/package-manager.d.ts.map +1 -0
  224. package/dist/core/src/publish-manifest.d.ts +11 -1
  225. package/dist/core/src/publish-manifest.d.ts.map +1 -1
  226. package/dist/core/src/repo-manager.d.ts +11 -0
  227. package/dist/core/src/repo-manager.d.ts.map +1 -1
  228. package/dist/core/src/settings.d.ts +26 -6
  229. package/dist/core/src/settings.d.ts.map +1 -1
  230. package/dist/core/src/walker.d.ts +6 -1
  231. package/dist/core/src/walker.d.ts.map +1 -1
  232. package/dist/core/src/workspace-config.d.ts +4 -1
  233. package/dist/core/src/workspace-config.d.ts.map +1 -1
  234. package/dist/core/workspace-config.js +4 -4
  235. package/dist/deploy/index.js +7 -7
  236. package/dist/discovery/index.js +5 -5
  237. package/dist/discovery/src/source-config.d.ts.map +1 -1
  238. package/dist/{ensure-sources-5ISVIA4T.js → ensure-sources-POMEQSTM.js} +19 -19
  239. package/dist/{ensure-sources-5ISVIA4T.js.map → ensure-sources-POMEQSTM.js.map} +1 -1
  240. package/dist/factory-assets/connectors/box.js +16 -16
  241. package/dist/factory-assets/connectors/deploy.js +16 -16
  242. package/dist/factory-assets/connectors/devserver.js +16 -16
  243. package/dist/factory-assets/connectors/flow/adapter.js +16 -16
  244. package/dist/factory-assets/connectors/flow/contract/normalize.d.ts.map +1 -1
  245. package/dist/factory-assets/connectors/flow/contract.js +1 -1
  246. package/dist/factory-assets/connectors/flow/engine.js +9 -9
  247. package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
  248. package/dist/factory-assets/connectors/flow/run-flow.js +18 -18
  249. package/dist/factory-assets/connectors/flow.js +16 -16
  250. package/dist/factory-assets/connectors/git.js +16 -16
  251. package/dist/factory-assets/connectors/gmail.js +16 -16
  252. package/dist/factory-assets/connectors/googledrive.js +16 -16
  253. package/dist/factory-assets/connectors/local.js +16 -16
  254. package/dist/factory-assets/connectors/mattermost.js +16 -16
  255. package/dist/factory-assets/connectors/memory.js +16 -16
  256. package/dist/factory-assets/connectors/minio.js +16 -16
  257. package/dist/factory-assets/connectors/postgres.js +16 -16
  258. package/dist/factory-assets/connectors/s3.js +16 -16
  259. package/dist/factory-assets/connectors/sharepoint.js +16 -16
  260. package/dist/factory-assets/connectors/sqlite.js +16 -16
  261. package/dist/factory-assets/connectors/static-server.js +16 -16
  262. package/dist/factory-assets/connectors/tunnel.js +16 -16
  263. package/dist/factory-assets/connectors/webdav.js +16 -16
  264. package/dist/factory-assets/connectors/xstate-store.js +16 -16
  265. package/dist/factory-assets/connectors/xstate.js +16 -16
  266. package/dist/factory-assets/skills/authoring-skaile-assets/SKILL.md +10 -8
  267. package/dist/factory-assets/skills/authoring-skaile-manifests/SKILL.md +15 -11
  268. package/dist/factory-assets/skills/authoring-skaile-manifests/references/dependency-standard.md +16 -11
  269. package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +2 -2
  270. package/dist/{flows-HMSE76PO.js → flows-DVKMZKM4.js} +12 -12
  271. package/dist/{flows-HMSE76PO.js.map → flows-DVKMZKM4.js.map} +1 -1
  272. package/dist/{helpers-X6URBGSF.js → helpers-Y4OT7VIS.js} +19 -19
  273. package/dist/{helpers-X6URBGSF.js.map → helpers-Y4OT7VIS.js.map} +1 -1
  274. package/dist/library/index.js +12 -12
  275. package/dist/library/install.js +4 -4
  276. package/dist/library/src/index.d.ts +3 -3
  277. package/dist/library/src/index.d.ts.map +1 -1
  278. package/dist/library/src/install/source-fetch-github.d.ts.map +1 -1
  279. package/dist/library/src/local/db.d.ts +2 -0
  280. package/dist/library/src/local/db.d.ts.map +1 -1
  281. package/dist/library/src/local/library-dir.d.ts +26 -0
  282. package/dist/library/src/local/library-dir.d.ts.map +1 -1
  283. package/dist/library/src/local/library-manager.d.ts +5 -0
  284. package/dist/library/src/local/library-manager.d.ts.map +1 -1
  285. package/dist/library/src/local/library.d.ts +5 -3
  286. package/dist/library/src/local/library.d.ts.map +1 -1
  287. package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
  288. package/dist/library/src/local/store-paths.d.ts +9 -0
  289. package/dist/library/src/local/store-paths.d.ts.map +1 -1
  290. package/dist/library/src/manifest-writer.d.ts.map +1 -1
  291. package/dist/library/src/remote/index.d.ts +1 -1
  292. package/dist/library/src/remote/index.d.ts.map +1 -1
  293. package/dist/library/src/remote/remote-catalog-source.d.ts +17 -0
  294. package/dist/library/src/remote/remote-catalog-source.d.ts.map +1 -1
  295. package/dist/library/src/sync/git-driver.d.ts +2 -0
  296. package/dist/library/src/sync/git-driver.d.ts.map +1 -1
  297. package/dist/{library-publish-OS3JDW4S.js → library-publish-DI4Q5UOG.js} +73 -38
  298. package/dist/library-publish-DI4Q5UOG.js.map +1 -0
  299. package/dist/open-library-N6ESO6IK.js +40 -0
  300. package/dist/{open-library-4QSUORI6.js.map → open-library-N6ESO6IK.js.map} +1 -1
  301. package/dist/open-registry-CBZ5MWTT.js +32 -0
  302. package/dist/{open-registry-VCKS3MP5.js.map → open-registry-CBZ5MWTT.js.map} +1 -1
  303. package/dist/paths-U6W6MQNQ.js +22 -0
  304. package/dist/{paths-BVO56A25.js.map → paths-U6W6MQNQ.js.map} +1 -1
  305. package/dist/{plugin-store-X7GPWGBG.js → plugin-store-XMZSPVBF.js} +48 -12
  306. package/dist/plugin-store-XMZSPVBF.js.map +1 -0
  307. package/dist/project-UP52HEHC.js +79 -0
  308. package/dist/{project-PAXP46RD.js.map → project-UP52HEHC.js.map} +1 -1
  309. package/dist/resolver/index.js +8 -8
  310. package/dist/runner/index.js +33 -33
  311. package/dist/runner/src/agent.d.ts.map +1 -1
  312. package/dist/runner/src/agents/in-process-host.d.ts.map +1 -1
  313. package/dist/runner/src/composition/index.d.ts +1 -0
  314. package/dist/runner/src/composition/index.d.ts.map +1 -1
  315. package/dist/runner/src/composition/prompt-sections.d.ts +7 -4
  316. package/dist/runner/src/composition/prompt-sections.d.ts.map +1 -1
  317. package/dist/runner/src/composition/resolve.d.ts +10 -1
  318. package/dist/runner/src/composition/resolve.d.ts.map +1 -1
  319. package/dist/runner/src/composition/verify.d.ts +29 -0
  320. package/dist/runner/src/composition/verify.d.ts.map +1 -0
  321. package/dist/runner/src/credential-notice.d.ts +14 -0
  322. package/dist/runner/src/credential-notice.d.ts.map +1 -0
  323. package/dist/runner/src/environment.d.ts.map +1 -1
  324. package/dist/runner/src/final-text.d.ts +17 -0
  325. package/dist/runner/src/final-text.d.ts.map +1 -0
  326. package/dist/runner/src/index.d.ts +2 -0
  327. package/dist/runner/src/index.d.ts.map +1 -1
  328. package/dist/runner/src/resource-handler.d.ts.map +1 -1
  329. package/dist/runner/src/serve.d.ts +13 -0
  330. package/dist/runner/src/serve.d.ts.map +1 -1
  331. package/dist/runner/src/usage-limit-turn-hold.d.ts +51 -0
  332. package/dist/runner/src/usage-limit-turn-hold.d.ts.map +1 -0
  333. package/dist/sdk/asset-manager.js +16 -16
  334. package/dist/sdk/bridge.js +12 -12
  335. package/dist/sdk/core.js +7 -7
  336. package/dist/sdk/flow.js +9 -9
  337. package/dist/sdk/index.js +36 -34
  338. package/dist/sdk/index.js.map +1 -1
  339. package/dist/sdk/resolver.js +8 -8
  340. package/dist/sdk/runner.js +33 -33
  341. package/dist/sdk/src/local-runtime.d.ts.map +1 -1
  342. package/dist/sdk/transport/ws/client.js +1 -1
  343. package/dist/sdk/transport/ws/server.js +1 -1
  344. package/dist/sdk/transport/ws.js +2 -2
  345. package/dist/sdk/transport.js +2 -2
  346. package/dist/serve-preflight-6P2JJEJ3.js +47 -0
  347. package/dist/serve-preflight-6P2JJEJ3.js.map +1 -0
  348. package/dist/{setup-BV55VHWC.js → setup-5IRUH5OS.js} +24 -24
  349. package/dist/{setup-BV55VHWC.js.map → setup-5IRUH5OS.js.map} +1 -1
  350. package/dist/{skills-EDFUCJPX.js → skills-HA36RFNM.js} +13 -13
  351. package/dist/{skills-EDFUCJPX.js.map → skills-HA36RFNM.js.map} +1 -1
  352. package/dist/source-DX5SGWEI.js +41 -0
  353. package/dist/{source-E4VAT5RZ.js.map → source-DX5SGWEI.js.map} +1 -1
  354. package/dist/source-fetch-github-PIO6EMMF.js +4 -0
  355. package/dist/{source-fetch-github-QN4LLTL5.js.map → source-fetch-github-PIO6EMMF.js.map} +1 -1
  356. package/dist/{source-manifest-publish-CLEE6TNH.js → source-manifest-publish-NMRLXWVW.js} +27 -26
  357. package/dist/source-manifest-publish-NMRLXWVW.js.map +1 -0
  358. package/dist/transport/index.js +2 -2
  359. package/dist/transport/src/ws/client.d.ts +6 -0
  360. package/dist/transport/src/ws/client.d.ts.map +1 -1
  361. package/dist/transport/src/ws/server.d.ts +4 -0
  362. package/dist/transport/src/ws/server.d.ts.map +1 -1
  363. package/dist/transport/ws/client.js +1 -1
  364. package/dist/transport/ws/server.js +1 -1
  365. package/dist/transport/ws.js +2 -2
  366. package/dist/tui/index.js +89 -52
  367. package/dist/tui/index.js.map +1 -1
  368. package/dist/tui/src/repl-session.d.ts +1 -1
  369. package/dist/tui/src/repl-session.d.ts.map +1 -1
  370. package/dist/tui/src/repl.d.ts +3 -0
  371. package/dist/tui/src/repl.d.ts.map +1 -1
  372. package/dist/types/manifests.js +1 -1
  373. package/dist/types/src/manifests/agent-composition.d.ts +20 -0
  374. package/dist/types/src/manifests/agent-composition.d.ts.map +1 -1
  375. package/dist/types/src/manifests/agent.d.ts.map +1 -1
  376. package/dist/types/src/manifests/cron.d.ts +15 -0
  377. package/dist/types/src/manifests/cron.d.ts.map +1 -0
  378. package/dist/types/src/manifests/index.d.ts +3 -2
  379. package/dist/types/src/manifests/index.d.ts.map +1 -1
  380. package/dist/types/src/manifests/preset.d.ts +1 -1
  381. package/dist/workspace-plugin/adapters/mcp.js +23 -23
  382. package/dist/workspace-plugin/adapters/omp.js +106 -51
  383. package/dist/workspace-plugin/adapters/omp.js.map +1 -1
  384. package/dist/workspace-plugin/index.js +2 -2
  385. package/dist/workspace-plugin/src/adapters/claude-code.d.ts.map +1 -1
  386. package/dist/workspace-plugin/src/adapters/omp.d.ts +11 -2
  387. package/dist/workspace-plugin/src/adapters/omp.d.ts.map +1 -1
  388. package/package.json +1 -1
  389. package/dist/asset-feeds-ELEBTNUS.js.map +0 -1
  390. package/dist/chunk-2FMEGSBN.js.map +0 -1
  391. package/dist/chunk-2JMLDZLP.js.map +0 -1
  392. package/dist/chunk-32XKCNCL.js.map +0 -1
  393. package/dist/chunk-3BEY4WK3.js.map +0 -1
  394. package/dist/chunk-4OV5DSBB.js.map +0 -1
  395. package/dist/chunk-4P3KTUZH.js.map +0 -1
  396. package/dist/chunk-5LKOAX7I.js.map +0 -1
  397. package/dist/chunk-5QI2JYJC.js.map +0 -1
  398. package/dist/chunk-6HHE63CO.js.map +0 -1
  399. package/dist/chunk-CGYEHQOX.js.map +0 -1
  400. package/dist/chunk-GEWAIQZ5.js.map +0 -1
  401. package/dist/chunk-HJ5MCB3L.js.map +0 -1
  402. package/dist/chunk-IJHH4DSW.js.map +0 -1
  403. package/dist/chunk-IXWODEED.js.map +0 -1
  404. package/dist/chunk-JZCSNEHK.js.map +0 -1
  405. package/dist/chunk-MGGFBZWA.js.map +0 -1
  406. package/dist/chunk-NRDSGLCQ.js.map +0 -1
  407. package/dist/chunk-NWDSLXOX.js.map +0 -1
  408. package/dist/chunk-OBYKJB4U.js.map +0 -1
  409. package/dist/chunk-PE36IF2D.js.map +0 -1
  410. package/dist/chunk-R7FOF242.js +0 -59
  411. package/dist/chunk-R7FOF242.js.map +0 -1
  412. package/dist/chunk-TRSQP3JD.js.map +0 -1
  413. package/dist/chunk-V7PQZOBR.js.map +0 -1
  414. package/dist/chunk-Y34MSWEZ.js.map +0 -1
  415. package/dist/chunk-YBQONMH2.js.map +0 -1
  416. package/dist/chunk-Z2K6BGUR.js.map +0 -1
  417. package/dist/library-publish-OS3JDW4S.js.map +0 -1
  418. package/dist/open-library-4QSUORI6.js +0 -40
  419. package/dist/open-registry-VCKS3MP5.js +0 -32
  420. package/dist/paths-BVO56A25.js +0 -22
  421. package/dist/plugin-store-X7GPWGBG.js.map +0 -1
  422. package/dist/project-PAXP46RD.js +0 -41
  423. package/dist/source-E4VAT5RZ.js +0 -41
  424. package/dist/source-fetch-github-QN4LLTL5.js +0 -4
  425. package/dist/source-manifest-publish-CLEE6TNH.js.map +0 -1
@@ -1,18 +1,18 @@
1
- import { unregisteredLibraryNames, scanUnlistedAssets, getStoreManifestsDir, missingLibraryDirMessage } from './chunk-3BEY4WK3.js';
2
- import { readManifestSourceConfig, isManifestMode, discoverAssetsInTree } from './chunk-GEWAIQZ5.js';
3
- import { installTargetOf, canonicalEntryRef, deployAll, removeAsset, diffDeployedAgent, createScaffold, installAgent } from './chunk-PE36IF2D.js';
1
+ import { unregisteredLibraryNames, renamedLibraryName, resolveConfig, RemoteCatalogSource, OfflineError, describeCatalogError, CatalogHttpError, scanUnlistedAssets, getStoreManifestsDir, missingLibraryDirMessage } from './chunk-C3BMXXTN.js';
2
+ import { readManifestSourceConfig, discoverAssetsInTree, isManifestMode } from './chunk-7CR2OZPA.js';
3
+ import { installTargetOf, canonicalEntryRef, deployAll, removeAsset, diffDeployedAgent, removeExcludedLeftovers, deployedAssetDrift, createScaffold, installAgent } from './chunk-NJV5SLEK.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-6HHE63CO.js';
7
- import { resolveConfiguredDriverTarget, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, buildLockFile, writeLock, readLock, verifyLock, mergeLockFile, extractForPatch, generatePatch, savePatch, applyPatch } from './chunk-2FMEGSBN.js';
8
- import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-GFCXNFTW.js';
6
+ import { driverTargetSupportsAgents, CODEX_IDENTITY_DIR } from './chunk-7QWQTA5S.js';
7
+ import { resolveConfiguredDriverTarget, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, computeHash, buildLockFile, writeLock, readLock, verifyLock, mergeLockFile, extractForPatch, generatePatch, savePatch, applyPatch, LOCK_SCHEMA_VERSION } from './chunk-T5TSLSRO.js';
8
+ import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-SHQMPGOL.js';
9
9
  import { deployedBase, deployedDir, isDeployed, deployedPaths } from './chunk-PMECEZK3.js';
10
- import { resolveSkWorkspaceConfig, effectiveAgentInstances, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, buildProvenanceIndex, coerceVersion, sourceShortName, resolveAll, ensureRepo, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, resolveAsset, readLinks, MAX_PRESET_DEPTH, PresetDepthError, DEFAULT_AGENT_INSTANCE_ID, resolveAgentDefinitionDir, storeManifestSlug } from './chunk-JZCSNEHK.js';
10
+ import { resolveSkWorkspaceConfig, effectiveAgentInstances, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, buildProvenanceIndex, coerceVersion, sourceShortName, readLinks, isCommitOnRemote, resolveAll, agentSourceCacheDir, ensureRepo, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, resolveAsset, MAX_PRESET_DEPTH, PresetDepthError, DEFAULT_AGENT_INSTANCE_ID, resolveAgentDefinitionDir, storeManifestSlug } from './chunk-PNOGX36L.js';
11
11
  import { globalSkaileDir, globalCacheRoot, migrateLegacyDir } from './chunk-L5VSIPQD.js';
12
12
  import { DRIVER_TARGETS } from './chunk-CPSIPIX7.js';
13
- import { loadSkPublishManifest } from './chunk-IXWODEED.js';
14
- import { scanDirectory, parseRequires, dedupeRequirements } from './chunk-Y34MSWEZ.js';
15
- import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr } from './chunk-4OV5DSBB.js';
13
+ import { loadSkPublishManifest } from './chunk-CTP3XBMF.js';
14
+ import { scanDirectory, parseRequires, dedupeRequirements } from './chunk-XTVSNWLL.js';
15
+ import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr, parseDep } from './chunk-PNTXHTBC.js';
16
16
  import { spawnSync } from 'child_process';
17
17
  import { existsSync, rmSync, readdirSync, lstatSync, unlinkSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
18
18
  import path, { resolve, dirname, sep, join, relative, posix } from 'path';
@@ -40,13 +40,10 @@ function writeLedger(projectDir, ledger) {
40
40
  function bindInstance(projectDir, id, instance) {
41
41
  const errors = [];
42
42
  let srcDir;
43
- const addHint = instance.ref.startsWith("agent:") ? ` \u2014 add the agent first: run skaile add ${instance.ref}` : "";
44
43
  try {
45
44
  srcDir = resolveAgentDefinitionDir(projectDir, instance.ref);
46
45
  } catch (err) {
47
- errors.push(
48
- `agent instance "${id}": ${err instanceof Error ? err.message : String(err)}${addHint}`
49
- );
46
+ errors.push(`agent instance "${id}": ${err instanceof Error ? err.message : String(err)}`);
50
47
  }
51
48
  if (srcDir !== void 0 && !existsSync(join(srcDir, "agent.yaml"))) {
52
49
  errors.push(
@@ -404,6 +401,19 @@ function filesUnderManifestDir(paths, manifestRel) {
404
401
  const manifestDir = posix.dirname(manifestRel);
405
402
  return paths.map((p) => posix.relative(manifestDir, p)).filter((rel) => rel !== "" && !rel.startsWith(".."));
406
403
  }
404
+ function lockedAssetFiles(kind, files) {
405
+ const manifestRel = matchManifestPath(
406
+ kind,
407
+ files.map((f) => f.path)
408
+ ) ?? files[0]?.path;
409
+ if (!manifestRel) return [];
410
+ const manifestDir = posix.dirname(manifestRel);
411
+ return files.flatMap((f) => {
412
+ const rel = posix.relative(manifestDir, f.path);
413
+ return rel === "" || rel.startsWith("..") ? [] : [{ rel, sha256: f.sha256 }];
414
+ });
415
+ }
416
+ var GITHUB_REPO_URL = /github\.com[/:]/i;
407
417
  function indexLockByKindName(lock) {
408
418
  const lockByKindName = /* @__PURE__ */ new Map();
409
419
  for (const [key, entry] of Object.entries(lock?.assets ?? {})) {
@@ -438,6 +448,28 @@ function commitsBetween(repoDir, from, to, cache) {
438
448
  return count;
439
449
  }
440
450
  var COMMIT_ID = /^[0-9a-f]{7,64}$/;
451
+ function commitsTouching(repoDir, from, to, dir, cache) {
452
+ if (!COMMIT_ID.test(from) || !COMMIT_ID.test(to)) return null;
453
+ if (from === to) return 0;
454
+ const key = `${from}..${to} -- ${dir}`;
455
+ const hit = cache.get(key);
456
+ if (hit !== void 0) return hit;
457
+ const r = spawnSync("git", ["-C", repoDir, "rev-list", "--count", `${from}..${to}`, "--", dir], {
458
+ encoding: "utf8",
459
+ stdio: "pipe"
460
+ });
461
+ const n = r.status === 0 ? Number.parseInt(r.stdout.trim(), 10) : Number.NaN;
462
+ if (!Number.isFinite(n) || n < 0) return null;
463
+ cache.set(key, n);
464
+ return n;
465
+ }
466
+ function lockedAssetDir(kind, files) {
467
+ const manifestRel = matchManifestPath(
468
+ kind,
469
+ files.map((f) => f.path)
470
+ ) ?? files[0]?.path;
471
+ return manifestRel ? posix.dirname(manifestRel) : ".";
472
+ }
441
473
  function libraryDiskHashes(libPath, cache, registry) {
442
474
  let hashes = cache.get(libPath);
443
475
  if (!hashes) {
@@ -509,7 +541,7 @@ function newestLibraryDef(snap, libraryName, id) {
509
541
  }
510
542
  return best;
511
543
  }
512
- function libraryPreference(isDefaultLib) {
544
+ function libraryPreference(isDefaultLib, rank) {
513
545
  return (a, b) => {
514
546
  const va = coerceVersion(a.entry.version ?? "");
515
547
  const vb = coerceVersion(b.entry.version ?? "");
@@ -517,7 +549,9 @@ function libraryPreference(isDefaultLib) {
517
549
  if (gt(vb, va)) return 1;
518
550
  const da = isDefaultLib.get(a.library) ? 0 : 1;
519
551
  const db = isDefaultLib.get(b.library) ? 0 : 1;
520
- return da - db || a.library.localeCompare(b.library);
552
+ if (da !== db) return da - db;
553
+ const byRank = rank ? rank(a) - rank(b) : 0;
554
+ return byRank || a.library.localeCompare(b.library);
521
555
  };
522
556
  }
523
557
  function qualifyDepRef(ref, fallback) {
@@ -652,8 +686,7 @@ function withKeptLockEntries(lock, kept) {
652
686
  for (const s of kept.sources) if (!sources.has(s.url)) sources.set(s.url, s);
653
687
  return {
654
688
  ...lock,
655
- // v4 once a library-origin entry is present (see buildLockFile).
656
- schema_version: lock.schema_version === 4 || Object.values(kept.assets).some((e) => e.library) ? 4 : 3,
689
+ schema_version: LOCK_SCHEMA_VERSION,
657
690
  assets: { ...lock.assets, ...kept.assets },
658
691
  sources: [...sources.values()].sort((a, b) => a.url.localeCompare(b.url))
659
692
  };
@@ -674,10 +707,39 @@ function relockedRefs(prev, next) {
674
707
  }
675
708
  return out;
676
709
  }
710
+ function libraryMoves(prev, next) {
711
+ if (!prev) return [];
712
+ const out = [];
713
+ for (const [key, entry] of Object.entries(next)) {
714
+ const to = entry.library;
715
+ if (!to || to.installed) continue;
716
+ const id = parseCanonicalLockKey(key);
717
+ for (const [prevKey, before] of Object.entries(prev.assets)) {
718
+ const o = parseCanonicalLockKey(prevKey);
719
+ if (o.kind !== id.kind || o.name !== id.name || o.publisher !== id.publisher) continue;
720
+ const from = before.library;
721
+ if (!from || from.installed || from.name === to.name) continue;
722
+ out.push({ ref: `${id.kind}:@${id.publisher}/${id.name}`, from: from.name, to: to.name });
723
+ break;
724
+ }
725
+ }
726
+ return out;
727
+ }
677
728
  function unregisteredLibraryFix(origin, ref) {
678
729
  const register = origin.url ? `\`skaile library init ${origin.name} --git ${origin.url}\` (no registered library has the remote ${origin.url} either)` : `\`skaile library init ${origin.name} --path <dir>\``;
679
730
  return `register library "${origin.name}" again with ${register}, or drop the dependency with \`skaile remove ${ref}\``;
680
731
  }
732
+ function declaredNoHolderReason(ref) {
733
+ return `is declared but no library or source holds it \u2014 register the library that holds it with \`skaile library init <name> --path <dir>\`, or drop it with \`skaile remove ${ref.split("#")[0]}\``;
734
+ }
735
+ var REGISTER_HOLDER = "register the library that holds it with `skaile library init <name> --path <dir>`";
736
+ function requirementNoHolderFix(asset) {
737
+ return `${REGISTER_HOLDER}, or drop ${asset} with \`skaile remove ${asset}\``;
738
+ }
739
+ function sameAsset(ref, want) {
740
+ const p = parseRefTolerant(ref);
741
+ return p !== null && p.kind === want.kind && p.name === want.name && (!want.publisher || !p.publisher || p.publisher === want.publisher);
742
+ }
681
743
  function lockedLibraryProblem(lookup, origin, ref) {
682
744
  return lookup.state === "dir-missing" ? missingLibraryDirMessage({ name: origin.name, path: lookup.path }) : `library "${origin.name}" is not registered \u2014 renamed or removed? ${unregisteredLibraryFix(origin, ref)}`;
683
745
  }
@@ -767,6 +829,17 @@ var AssetManager = class {
767
829
  onLibraryCollision;
768
830
  /** Store URLs whose failed version lookup was already reported (one warning per URL). */
769
831
  warnedStoreVersionLookups = /* @__PURE__ */ new Set();
832
+ /** Stores a lookup could not reach (store URL -> one line why); `add` reports them instead of "not found". */
833
+ storeFailures = /* @__PURE__ */ new Map();
834
+ /** Sources a provenance walk had to skip (source URL → why); one warning per source. */
835
+ skippedSources = /* @__PURE__ */ new Map();
836
+ /** `onSourceError` of every provenance walk: name the source once, leave it out, go on. */
837
+ reportSourceError = (clone, err) => {
838
+ const why = err instanceof Error ? err.message : String(err);
839
+ if (this.skippedSources.has(clone.sourceUrl)) return;
840
+ this.skippedSources.set(clone.sourceUrl, why);
841
+ console.warn(`skaile: source ${clone.sourceUrl} skipped: ${why}`);
842
+ };
770
843
  constructor(opts) {
771
844
  this.projectDir = resolve(opts.projectDir);
772
845
  this.global = opts.global ?? false;
@@ -801,15 +874,27 @@ var AssetManager = class {
801
874
  return this.global ? globalSkaileDir() : this.projectDir;
802
875
  }
803
876
  /**
804
- * The harnesses a global deploy fans out to: the explicit `harnesses` override
805
- * (a `--harness` filter), else `resolveGlobalHarnesses()`. In project mode this
806
- * is exactly `[driverTarget]`, so the shared fan-out loop degenerates to the
877
+ * The harnesses a global deploy fans out to: the explicit `harnesses` override (a `--harness`
878
+ * filter), else the explicit `global_harnesses:` of `~/.skaile/skaile.yaml`, else every harness
879
+ * whose root dir exists under `~` plus the harness the project is configured for (a project on
880
+ * a harness whose dir does not exist yet still gets its user-scope assets). In project mode
881
+ * this is exactly `[driverTarget]`, so the shared fan-out loop degenerates to the
807
882
  * single-target path.
808
883
  */
809
884
  targetHarnesses() {
810
885
  if (!this.global) return [this.driverTarget];
811
886
  if (this.harnessesOverride && this.harnessesOverride.length > 0) return this.harnessesOverride;
812
- return resolveGlobalHarnesses();
887
+ const resolved = resolveGlobalHarnesses();
888
+ if (resolveGlobalSkWorkspaceConfig().global_harnesses?.length) return resolved;
889
+ const configured = resolveConfiguredDriverTarget(this.projectDir);
890
+ return configured && !resolved.includes(configured) ? [...resolved, configured] : resolved;
891
+ }
892
+ /**
893
+ * The harnesses `add` / `install` deploy into: `[driverTarget]` for a project, the global
894
+ * harness set (see {@link AssetManager.targetHarnesses}) for a global manager.
895
+ */
896
+ deployTargets() {
897
+ return this.targetHarnesses();
813
898
  }
814
899
  get storeCacheDir() {
815
900
  if (this.global) return join(globalCacheRoot(), "store");
@@ -841,14 +926,15 @@ var AssetManager = class {
841
926
  }
842
927
  /** Deploy options for one harness. In global mode, `driverTarget` is the
843
928
  * fan-out target (not the constructor's `driverTarget`, which is project-only). */
844
- deployOptsFor(target, link, onSkippedSymlink) {
929
+ deployOptsFor(target, link, onSkippedSymlink, onEnvFile) {
845
930
  return {
846
931
  registry: this.registry,
847
932
  driverTarget: target,
848
933
  global: this.global,
849
934
  cwd: this.projectDir,
850
935
  link: link === true,
851
- ...onSkippedSymlink ? { onSkippedSymlink: (entry, p) => onSkippedSymlink(canonicalEntryRef(entry), p) } : {}
936
+ ...onSkippedSymlink ? { onSkippedSymlink: (entry, p) => onSkippedSymlink(canonicalEntryRef(entry), p) } : {},
937
+ ...onEnvFile ? { onEnvFile: (entry, p) => onEnvFile(canonicalEntryRef(entry), p) } : {}
852
938
  };
853
939
  }
854
940
  /**
@@ -878,13 +964,18 @@ var AssetManager = class {
878
964
  * skills resolve. Project scope only. An instance whose `ref` resolves to no agent is
879
965
  * reported in `agentInstanceErrors`; any other render error is warned and swallowed.
880
966
  */
881
- renderAgentInstances() {
967
+ async renderAgentInstances() {
882
968
  if (this.global) return {};
883
969
  try {
884
970
  const result = installAgentInstances({
885
971
  projectDir: this.projectDir,
886
972
  driverTarget: this.driverTarget
887
973
  });
974
+ for (const u of unboundAgentInstances(this.projectDir, this.driverTarget)) {
975
+ if (!u.ref.startsWith("agent:")) continue;
976
+ const at = result.errors.findIndex((e) => e.startsWith(`agent instance "${u.id}":`));
977
+ if (at >= 0) result.errors[at] += ` \u2014 ${(await this.unboundAgentAdvice(u.ref)).advice}`;
978
+ }
888
979
  for (const w of [...result.warnings, ...result.errors]) console.warn(`skaile: ${w}`);
889
980
  return {
890
981
  agentInstances: result.instances,
@@ -1007,6 +1098,31 @@ var AssetManager = class {
1007
1098
  if (c.homepage != null) entry.homepage = c.homepage;
1008
1099
  return entry;
1009
1100
  }
1101
+ /**
1102
+ * The {@link ProvenanceCandidate} a source- or store-origin lock entry stands for: what a
1103
+ * deploy needs to restore the asset from the commit the lock names (`files[]` is the asset's
1104
+ * whole file set). `null` for a library-origin entry or a key that is not a canonical ref.
1105
+ * The source URL is the declared source's own spelling (the bundled factory tree and a
1106
+ * `sources:` entry match their declaration by it).
1107
+ */
1108
+ lockedCandidate(key, entry, repositories) {
1109
+ if (!entry.source) return null;
1110
+ const { publisher, kind, name, version } = parseCanonicalLockKey(key);
1111
+ if (!publisher || !kind || !name) return null;
1112
+ const url = entry.source.url;
1113
+ const factory = Object.values(repositories).find((d) => d.factory);
1114
+ const declared = factory?.url !== void 0 && normalizeRepoUrl(url) === normalizeRepoUrl(factory.url) ? factory : repositories[sourceCacheKey(url)];
1115
+ return {
1116
+ publisher,
1117
+ kind,
1118
+ name,
1119
+ version: version ?? "",
1120
+ sourceUrl: declared?.url ?? url,
1121
+ commit: entry.source.commit,
1122
+ files: entry.files,
1123
+ sha256: entry.sha256
1124
+ };
1125
+ }
1010
1126
  /**
1011
1127
  * Resolve refs the source/store pass could not against the user's registered
1012
1128
  * **libraries** (local authoring places). Each library dir is scanned on disk
@@ -1024,6 +1140,9 @@ var AssetManager = class {
1024
1140
  * builds an error hint must not warn).
1025
1141
  * @param opts.library - Resolve only from this library (the one a lock entry names), not
1026
1142
  * from every registered one. Never warns: no choice is made between libraries.
1143
+ * @param opts.lockedLibraries - `kind:publisher/name` → the library its lock entry names
1144
+ * ({@link AssetManager.lockedLibraryNames}). A ref listed here resolves from that library
1145
+ * when it holds a match, whatever the default library is.
1027
1146
  * @returns the matched deploy entries, the set of `missing` refs covered, and
1028
1147
  * each entry's originating library name.
1029
1148
  */
@@ -1036,6 +1155,7 @@ var AssetManager = class {
1036
1155
  const { byKey, isDefaultLib, libraryDir } = scan;
1037
1156
  const entries = [];
1038
1157
  const fileSets = /* @__PURE__ */ new Map();
1158
+ const diskHashes = /* @__PURE__ */ new Map();
1039
1159
  let snap;
1040
1160
  const allCandidates = [...byKey.values()].flat();
1041
1161
  for (const ref of missing) {
@@ -1044,12 +1164,53 @@ var AssetManager = class {
1044
1164
  const hash = ref.indexOf("#");
1045
1165
  const pin = parsed.pin ?? (hash >= 0 ? ref.slice(hash + 1) : void 0);
1046
1166
  const key = parsed.publisher ? `${parsed.kind}:@${parsed.publisher}/${parsed.name}` : void 0;
1047
- const pool = (key ? byKey.get(key) ?? [] : allCandidates.filter(
1167
+ const scanned = (key ? byKey.get(key) ?? [] : allCandidates.filter(
1048
1168
  (c) => c.entry.kind === parsed.kind && c.entry.name === parsed.name
1049
1169
  )).filter((c) => !opts.library || c.library === opts.library);
1050
- const candidates = pool.filter((c) => !pin || matchPin(pin, c.entry.version ?? ""));
1170
+ const pool = [];
1171
+ for (const c of scanned) {
1172
+ if (c.entry.version) {
1173
+ pool.push(c);
1174
+ continue;
1175
+ }
1176
+ if (snap === void 0) snap = await this.loadLibraryIndex();
1177
+ const def = newestLibraryDef(snap, c.library, {
1178
+ kind: c.entry.kind,
1179
+ publisher: c.entry.publisher ?? "",
1180
+ name: c.entry.name
1181
+ });
1182
+ pool.push(def?.version ? { ...c, entry: { ...c.entry, version: def.version } } : c);
1183
+ }
1184
+ let candidates = pool.filter((c) => !pin || matchPin(pin, c.entry.version ?? ""));
1185
+ let lockedLibrary;
1186
+ const lockedMap = opts.preferLocked?.libraries ?? opts.lockedLibraries;
1187
+ if (parsed.publisher) {
1188
+ lockedLibrary = lockedMap?.get(
1189
+ `${parsed.kind}:${parsed.publisher.replace(/^@/, "")}/${parsed.name}`
1190
+ );
1191
+ } else {
1192
+ const same = [...lockedMap ?? []].filter(([k]) => {
1193
+ const slash = k.indexOf("/");
1194
+ return k.startsWith(`${parsed.kind}:`) && k.slice(slash + 1) === parsed.name;
1195
+ });
1196
+ if (same.length === 1) lockedLibrary = same[0][1];
1197
+ }
1198
+ if (lockedLibrary && !opts.preferLocked) {
1199
+ const inLocked = candidates.filter((c) => c.library === lockedLibrary);
1200
+ if (inLocked.length > 0) candidates = inLocked;
1201
+ }
1051
1202
  if (candidates.length === 0) continue;
1052
- candidates.sort(libraryPreference(isDefaultLib));
1203
+ const prefer = opts.preferLocked ? (c) => {
1204
+ if (lockedLibrary && c.library === lockedLibrary) return 0;
1205
+ const id = `${c.entry.kind}:${(c.entry.publisher ?? "").replace(/^@/, "")}/${c.entry.name}`;
1206
+ const lockedSha = opts.preferLocked?.shas.get(id);
1207
+ const libPath = libraryDir.get(c.library);
1208
+ const onDisk = lockedSha && libPath ? libraryDiskHashes(libPath, diskHashes, this.registry).get(
1209
+ `${c.entry.kind}:${c.entry.name}`
1210
+ ) : void 0;
1211
+ return onDisk !== void 0 && onDisk === lockedSha ? 1 : 2;
1212
+ } : void 0;
1213
+ candidates.sort(libraryPreference(isDefaultLib, prefer));
1053
1214
  const hit = candidates[0];
1054
1215
  if (!opts.quiet && !opts.library) {
1055
1216
  this.warnLibraryCollision(
@@ -1058,16 +1219,7 @@ var AssetManager = class {
1058
1219
  hit
1059
1220
  );
1060
1221
  }
1061
- let entry = hit.entry;
1062
- if (!entry.version) {
1063
- if (snap === void 0) snap = await this.loadLibraryIndex();
1064
- const def = newestLibraryDef(snap, hit.library, {
1065
- kind: entry.kind,
1066
- publisher: entry.publisher ?? "",
1067
- name: entry.name
1068
- });
1069
- if (def?.version) entry = { ...entry, version: def.version };
1070
- }
1222
+ const entry = hit.entry;
1071
1223
  withLibraryFiles(entry, libraryDir.get(hit.library), fileSets, this.registry);
1072
1224
  entries.push(entry);
1073
1225
  libraryOf.set(entry, hit.library);
@@ -1199,7 +1351,8 @@ var AssetManager = class {
1199
1351
  const cloneDirs = new Set(clones.map((c) => resolve(c.localPath)));
1200
1352
  const index = await buildProvenanceIndex(clones, {
1201
1353
  projectDir: this.projectDir,
1202
- knownKinds: this.knownKinds
1354
+ knownKinds: this.knownKinds,
1355
+ onSourceError: this.reportSourceError
1203
1356
  });
1204
1357
  const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
1205
1358
  const hasFactory = Object.values(repositories).some((d) => d.factory);
@@ -1408,6 +1561,34 @@ var AssetManager = class {
1408
1561
  publisher: ref?.publisher ?? entry.publisher?.replace(/^@/, "")
1409
1562
  };
1410
1563
  }
1564
+ /**
1565
+ * Refuse to lock a source commit another machine cannot check out. A commit made in the clone
1566
+ * itself (or one `source sync` could not fast-forward past) is in no remote branch: the lock
1567
+ * would name bytes nobody else has, and `install --locked` there could not restore them.
1568
+ * Linked (dev-override) and user-owned local sources are never checked: their lock is
1569
+ * machine-local by choice.
1570
+ *
1571
+ * @param resolved - Candidates about to be locked.
1572
+ * @param repositories - Source declarations of the project.
1573
+ * @throws With `<url> clone has unpublished commit <sha>` and the way out.
1574
+ */
1575
+ assertSourceCommitsPublished(resolved, repositories) {
1576
+ const links = readLinks(this.projectDir);
1577
+ const checked = /* @__PURE__ */ new Set();
1578
+ for (const c of resolved) {
1579
+ if (!c.commit || /^0+$/.test(c.commit) || checked.has(`${c.sourceUrl}@${c.commit}`)) continue;
1580
+ checked.add(`${c.sourceUrl}@${c.commit}`);
1581
+ const hit = Object.entries(repositories).find(
1582
+ ([, d]) => d.url !== void 0 && !d.factory && d.url === c.sourceUrl
1583
+ );
1584
+ if (!hit || links[hit[0]]) continue;
1585
+ const dir = this.resolveRepoDir(hit[1], hit[0]);
1586
+ if (!dir || isCommitOnRemote(dir, c.commit)) continue;
1587
+ throw new Error(
1588
+ `${c.sourceUrl} clone has unpublished commit ${c.commit.slice(0, 7)} - push it, or reset the clone (git -C ${dir} reset --hard origin/${hit[1].branch ?? "main"}) - nothing was locked`
1589
+ );
1590
+ }
1591
+ }
1411
1592
  /**
1412
1593
  * Refuse assets of one kind and name from different publishers: they deploy into the
1413
1594
  * same folder, so the later one would silently overwrite the earlier. Throws one line
@@ -1697,6 +1878,11 @@ var AssetManager = class {
1697
1878
  } catch {
1698
1879
  }
1699
1880
  }
1881
+ if (byName.state === "unregistered") {
1882
+ const renamed = renamedLibraryName(name);
1883
+ const now = renamed ? snap?.libraries.find((l) => l.name === renamed) : void 0;
1884
+ if (now && existsSync(now.path)) return { state: "ok", path: now.path, name: now.name };
1885
+ }
1700
1886
  if (byName.state === "ok" || !origin.url) return byName;
1701
1887
  const wanted = normalizeRepoUrl(origin.url);
1702
1888
  const byUrl = snap?.libraries.find((l) => {
@@ -1710,6 +1896,14 @@ var AssetManager = class {
1710
1896
  const found = this.lockedLibrary(origin, snap);
1711
1897
  return found.state === "ok" ? found.path : null;
1712
1898
  }
1899
+ /**
1900
+ * The `kind:@publisher/name` identities the lock holds for one `kind` and `name`, whatever
1901
+ * their publisher. `diff` uses it to accept a ref without a publisher only when it names
1902
+ * exactly one locked asset.
1903
+ */
1904
+ lockedRefsNamed(kind, name) {
1905
+ return Object.keys(this.tryReadLock()?.assets ?? {}).map(parseCanonicalLockKey).filter((id) => id.kind === kind && id.name === name).map((id) => id.publisher ? `${kind}:@${id.publisher}/${name}` : `${kind}:${name}`);
1906
+ }
1713
1907
  /**
1714
1908
  * Where each library-origin lock entry of this project resolves on this machine: identity
1715
1909
  * `kind:publisher/name` (no scope sigil, no version) → the name the locked library is
@@ -1739,6 +1933,19 @@ var AssetManager = class {
1739
1933
  }
1740
1934
  return out;
1741
1935
  }
1936
+ /**
1937
+ * What `update` prefers when libraries tie: the library each lock entry resolves to here
1938
+ * ({@link lockedLibraryNames}, a `library rename` followed) and the sha256 it locked.
1939
+ */
1940
+ async lockedPreference() {
1941
+ const shas = /* @__PURE__ */ new Map();
1942
+ for (const [key, e] of Object.entries(this.tryReadLock()?.assets ?? {})) {
1943
+ if (!e.library?.sha256 || e.library.installed) continue;
1944
+ const { publisher, kind, name } = parseCanonicalLockKey(key);
1945
+ if (publisher && kind && name) shas.set(`${kind}:${publisher}/${name}`, e.library.sha256);
1946
+ }
1947
+ return { libraries: await this.lockedLibraryNames(), shas };
1948
+ }
1742
1949
  // ── Store fetch-then-deploy ────────────────────────────────────────────
1743
1950
  /** A candidate's stable identity key (`<publisher>/<kind>:<name>@<version>`). */
1744
1951
  candidateKey(c) {
@@ -1804,6 +2011,7 @@ var AssetManager = class {
1804
2011
  const resolved = [];
1805
2012
  const overridesApplied = /* @__PURE__ */ new Set();
1806
2013
  const presets = [];
2014
+ const requiredBy = /* @__PURE__ */ new Map();
1807
2015
  const lib = { entries: [], resolved: /* @__PURE__ */ new Set(), libraryOf: /* @__PURE__ */ new Map() };
1808
2016
  const missing = /* @__PURE__ */ new Set();
1809
2017
  const seen = /* @__PURE__ */ new Set();
@@ -1852,6 +2060,13 @@ var AssetManager = class {
1852
2060
  return depId !== null && declaredById.get(depId) || dep;
1853
2061
  })
1854
2062
  );
2063
+ if (e.kind !== "preset") {
2064
+ for (const dep of deps2) {
2065
+ const depId = refIdentity(dep);
2066
+ if (depId !== null && declaredById.has(depId)) continue;
2067
+ requiredBy.set(dep, [...requiredBy.get(dep) ?? [], canonicalEntryRef(e)]);
2068
+ }
2069
+ }
1855
2070
  }
1856
2071
  for (const r of hit.resolved) lib.resolved.add(r);
1857
2072
  return next;
@@ -1875,7 +2090,9 @@ var AssetManager = class {
1875
2090
  })
1876
2091
  );
1877
2092
  const hit = await this.resolveLibraryEntries([...libraryTried], {
1878
- quiet: !opts.warnCollisions
2093
+ quiet: !opts.warnCollisions,
2094
+ lockedLibraries: opts.lockedLibraries,
2095
+ preferLocked: opts.preferLocked
1879
2096
  });
1880
2097
  const next = absorb(hit);
1881
2098
  const rest = fresh.filter((ref) => !hit.resolved.has(ref));
@@ -1892,7 +2109,11 @@ var AssetManager = class {
1892
2109
  presets.push(...r.presets);
1893
2110
  const hit2 = await this.resolveLibraryEntries(
1894
2111
  r.missing.filter((m) => !libraryTried.has(m)),
1895
- { quiet: !opts.warnCollisions }
2112
+ {
2113
+ quiet: !opts.warnCollisions,
2114
+ lockedLibraries: opts.lockedLibraries,
2115
+ preferLocked: opts.preferLocked
2116
+ }
1896
2117
  );
1897
2118
  next.push(...absorb(hit2));
1898
2119
  for (const m of r.missing) if (!hit2.resolved.has(m)) missing.add(m);
@@ -1903,7 +2124,7 @@ var AssetManager = class {
1903
2124
  const id = refIdentity(m);
1904
2125
  return !lib.resolved.has(m) && !(id !== null && covered.has(id));
1905
2126
  });
1906
- return { resolved, missing: stillMissing, overridesApplied, lib, presets };
2127
+ return { resolved, missing: stillMissing, overridesApplied, lib, presets, requiredBy };
1907
2128
  }
1908
2129
  /** The catalog fetcher to use: an injected one, else built from `stores`. */
1909
2130
  storeFetcherFor(stores) {
@@ -1918,8 +2139,8 @@ var AssetManager = class {
1918
2139
  let url = this.catalogUrlOverride;
1919
2140
  if (url === void 0) {
1920
2141
  try {
1921
- const { resolveConfig } = await import('./library/index.js');
1922
- url = resolveConfig({ projectDir: this.projectDir }).catalog.url;
2142
+ const { resolveConfig: resolveConfig2 } = await import('./library/index.js');
2143
+ url = resolveConfig2({ projectDir: this.projectDir }).catalog.url;
1923
2144
  } catch {
1924
2145
  return [];
1925
2146
  }
@@ -1977,13 +2198,31 @@ var AssetManager = class {
1977
2198
  // from source.url@commit and ignores it, so "" is inert.
1978
2199
  tier: ""
1979
2200
  };
1980
- const { installPath } = await installFromManifest({
1981
- manifest,
1982
- libraryRoot: this.storeCacheDir,
1983
- targetDir,
1984
- fetcher: this.injectedAssetFetcher
2201
+ const root = commonDirPrefix(c.files.map((f) => f.path));
2202
+ const intact = c.files.every((f) => {
2203
+ const rel = root && f.path.startsWith(`${root}/`) ? f.path.slice(root.length + 1) : f.path;
2204
+ try {
2205
+ return computeHash(join(targetDir, rel)) === f.sha256;
2206
+ } catch {
2207
+ return false;
2208
+ }
1985
2209
  });
1986
- staged.set(this.candidateKey(c), join(installPath, this.stagedManifestRel(c)));
2210
+ if (intact) {
2211
+ staged.set(this.candidateKey(c), join(targetDir, this.stagedManifestRel(c)));
2212
+ continue;
2213
+ }
2214
+ try {
2215
+ const { installPath } = await installFromManifest({
2216
+ manifest,
2217
+ libraryRoot: this.storeCacheDir,
2218
+ targetDir,
2219
+ fetcher: this.injectedAssetFetcher
2220
+ });
2221
+ staged.set(this.candidateKey(c), join(installPath, this.stagedManifestRel(c)));
2222
+ } catch (err) {
2223
+ const reason = err instanceof Error ? err.message : String(err);
2224
+ throw new Error(`${manifest.ref} (${c.sourceUrl} at ${c.commit.slice(0, 7)}): ${reason}`);
2225
+ }
1987
2226
  }
1988
2227
  return staged;
1989
2228
  }
@@ -2065,6 +2304,9 @@ var AssetManager = class {
2065
2304
  * @param opts.link - When `true`, symlink local-path assets instead of copying them
2066
2305
  * (the author dev-loop). Machine-local / not for committing; a no-op for
2067
2306
  * store/factory sources. Defaults to copy.
2307
+ * @param opts.moveLibraries - When `true` (`skaile update`), a locked library asset is
2308
+ * re-resolved from every library and may move to another one; {@link InstallResult.moved}
2309
+ * lists the moves. Without it a locked asset stays on the library its lock entry names.
2068
2310
  * @returns {@link InstallResult} summarising what was deployed, what was missing, and any collisions.
2069
2311
  */
2070
2312
  async install(opts) {
@@ -2090,14 +2332,43 @@ var AssetManager = class {
2090
2332
  if (cloneError) return cloneError;
2091
2333
  const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
2092
2334
  projectDir: this.projectDir,
2093
- knownKinds: this.knownKinds
2335
+ knownKinds: this.knownKinds,
2336
+ onSourceError: this.reportSourceError
2337
+ });
2338
+ const { resolved, missing, overridesApplied, lib, presets, requiredBy } = await this.resolveLibraryFirst(dependencies, {
2339
+ provenanceIndex,
2340
+ overrides,
2341
+ stores,
2342
+ declaredRefs: dependencies,
2343
+ // `install` restores what the lock recorded: a changed default library or a second
2344
+ // library holding the same ref does not move a locked asset. `update` re-resolves.
2345
+ ...opts?.moveLibraries ? { preferLocked: await this.lockedPreference() } : { lockedLibraries: await this.lockedLibraryNames() }
2094
2346
  });
2095
- const { resolved, missing, overridesApplied, lib, presets } = await this.resolveLibraryFirst(
2096
- dependencies,
2097
- { provenanceIndex, overrides, stores, declaredRefs: dependencies }
2098
- );
2099
2347
  this.assertNoFolderCollision([...resolved, ...lib.entries.map((e) => this.entryIdentity(e))]);
2348
+ this.assertSourceCommitsPublished(resolved, repositories);
2100
2349
  const stillMissing = missing.filter((m) => !lib.resolved.has(m));
2350
+ const lockedKeys = new Set(Object.keys(this.tryReadLock()?.assets ?? {}));
2351
+ const unmetByNew = /* @__PURE__ */ new Map();
2352
+ for (const m of stillMissing) {
2353
+ const newcomers = (requiredBy.get(m) ?? []).filter((parent) => !lockedKeys.has(parent));
2354
+ if (newcomers.length > 0) unmetByNew.set(m, newcomers);
2355
+ }
2356
+ if (unmetByNew.size > 0) {
2357
+ const hints = {};
2358
+ for (const [m, parents] of unmetByNew) {
2359
+ const requirer = parents.map((p) => p.split("#")[0]).join(", ");
2360
+ hints[m] = ` is required by ${requirer} (not found \u2014 ${REGISTER_HOLDER}, or drop ${requirer} with \`skaile remove ${parents[0].split("#")[0]}\`) \u2014 nothing was installed`;
2361
+ }
2362
+ return {
2363
+ deployed: [],
2364
+ removed: [],
2365
+ missing: [...unmetByNew.keys()],
2366
+ missingHints: hints,
2367
+ lockWritten: false,
2368
+ npmInstalled: [],
2369
+ npmFailed: []
2370
+ };
2371
+ }
2101
2372
  const missingHints = await this.publisherChangeHints(stillMissing);
2102
2373
  const prevLock = this.tryReadLock();
2103
2374
  const kept = keptLockEntries(prevLock, stillMissing);
@@ -2122,6 +2393,7 @@ var AssetManager = class {
2122
2393
  /* @__PURE__ */ new Set([...Object.keys(libraryLock), ...Object.keys(kept.assets)])
2123
2394
  );
2124
2395
  const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
2396
+ const copyDrift = await this.lockedCopyDrift();
2125
2397
  const deployed = [
2126
2398
  ...new Set(
2127
2399
  this.targetHarnesses().flatMap(
@@ -2129,25 +2401,32 @@ var AssetManager = class {
2129
2401
  )
2130
2402
  )
2131
2403
  ];
2404
+ let restored = this.restoredCopies(copyDrift, deployed);
2132
2405
  await this.renderDriverMcpConfig();
2133
- const agentRender = this.renderAgentInstances();
2406
+ const agentRender = await this.renderAgentInstances();
2134
2407
  const lockWanted = resolved.length > 0 || lib.entries.length > 0 || existsSync(this.lockPath);
2135
2408
  let lockWritten = false;
2136
2409
  let relocked = [];
2410
+ let moved = [];
2137
2411
  if (lockWanted) {
2138
2412
  const lockData = withKeptLockEntries(freshLock, kept);
2139
2413
  relocked = relockedRefs(prevLock, lockData);
2414
+ restored = [...restored, ...this.removeExcludedLeftovers(relocked)];
2415
+ moved = libraryMoves(prevLock, libraryLock);
2140
2416
  lockWritten = writeLock(this.lockPath, lockData);
2141
2417
  }
2142
2418
  this.recordInstallHistory(deployed, removed);
2143
2419
  const { npmInstalled, npmFailed } = await this.installRuntimeAssetDeps();
2420
+ await this.adviseMissing(stillMissing, missingHints);
2144
2421
  return {
2145
2422
  deployed,
2146
2423
  removed,
2147
2424
  missing: stillMissing,
2148
2425
  ...Object.keys(missingHints).length > 0 ? { missingHints } : {},
2149
2426
  ...relocked.length > 0 ? { relocked } : {},
2427
+ ...moved.length > 0 ? { moved } : {},
2150
2428
  ...replaced.length > 0 ? { replaced } : {},
2429
+ ...restored.length > 0 ? { restored } : {},
2151
2430
  lockWritten,
2152
2431
  npmInstalled,
2153
2432
  npmFailed,
@@ -2193,7 +2472,8 @@ var AssetManager = class {
2193
2472
  const { kind, name, publisher } = id;
2194
2473
  const component = kind ? this.installTarget(kind) : void 0;
2195
2474
  if (!kind || !name || !component) return [];
2196
- const dir = deployedDir(component, name, this.driverTarget, this.global, this.projectDir);
2475
+ const keptAgent = kind === "agent" && !this.global ? join(agentSourceCacheDir(this.projectDir), name) : null;
2476
+ const dir = keptAgent && existsSync(keptAgent) ? keptAgent : deployedDir(component, name, this.driverTarget, this.global, this.projectDir);
2197
2477
  if (!existsSync(dir)) return [];
2198
2478
  try {
2199
2479
  const scanned = await scanDirectory(dir, publisher ?? "", { knownKinds: this.knownKinds });
@@ -2317,13 +2597,33 @@ var AssetManager = class {
2317
2597
  return null;
2318
2598
  }
2319
2599
  }
2600
+ /**
2601
+ * A catalog client for `storeUrl` that honours `catalog.cache_ttl` (`0` = air-gapped: no
2602
+ * network, so every lookup that is not cached throws `OfflineError`).
2603
+ */
2604
+ remoteStore(storeUrl) {
2605
+ let cacheTtlMs;
2606
+ try {
2607
+ cacheTtlMs = resolveConfig({ projectDir: this.projectDir }).catalog.cache_ttl * 1e3;
2608
+ } catch {
2609
+ }
2610
+ return new RemoteCatalogSource({ baseUrl: storeUrl, cacheTtlMs });
2611
+ }
2612
+ /**
2613
+ * Remember why a store could not be asked (store URL → one line without a JS error class
2614
+ * name), so `add` can say `store unreachable` instead of a bare `Asset not found`.
2615
+ */
2616
+ noteStoreFailure(storeUrl, err) {
2617
+ const text = err instanceof OfflineError ? `${storeUrl}: air-gapped (catalog.cache_ttl: 0), the store was not asked` : describeCatalogError(err, storeUrl);
2618
+ this.storeFailures.set(storeUrl, text);
2619
+ return text;
2620
+ }
2320
2621
  /** Build a store fetcher from the configured stores, or undefined when none. */
2321
2622
  buildStoreFetcher(stores) {
2322
2623
  if (stores.length === 0) return void 0;
2323
2624
  return {
2324
2625
  getInstallManifest: async (storeUrl, ref) => {
2325
- const { RemoteCatalogSource, CatalogHttpError } = await import('./library/index.js');
2326
- const src = new RemoteCatalogSource({ baseUrl: storeUrl });
2626
+ const src = this.remoteStore(storeUrl);
2327
2627
  try {
2328
2628
  const m = await src.getInstallManifest(ref);
2329
2629
  let deps;
@@ -2336,7 +2636,7 @@ var AssetManager = class {
2336
2636
  else if (deps === void 0)
2337
2637
  gap = `has a cached catalog row for ${ref} without dependencies`;
2338
2638
  } catch (err) {
2339
- gap = `dependency lookup for ${ref} failed \u2014 ${err}`;
2639
+ gap = `dependency lookup for ${ref} failed \u2014 ${describeCatalogError(err, storeUrl)}`;
2340
2640
  }
2341
2641
  if (gap) {
2342
2642
  console.warn(`skaile: store ${storeUrl} ${gap}; installing without its dependencies`);
@@ -2350,23 +2650,23 @@ var AssetManager = class {
2350
2650
  };
2351
2651
  } catch (err) {
2352
2652
  if (err instanceof CatalogHttpError && err.status === 404) return null;
2653
+ this.noteStoreFailure(storeUrl, err);
2353
2654
  throw err;
2354
2655
  }
2355
2656
  },
2356
2657
  getCanonicalDigest: async (storeUrl, ref) => {
2357
- const { RemoteCatalogSource } = await import('./library/index.js');
2358
- const src = new RemoteCatalogSource({ baseUrl: storeUrl });
2658
+ const src = this.remoteStore(storeUrl);
2359
2659
  return src.getCanonicalDigest(ref);
2360
2660
  },
2361
2661
  listVersions: async (storeUrl, ref) => {
2362
- const { RemoteCatalogSource } = await import('./library/index.js');
2363
- const src = new RemoteCatalogSource({ baseUrl: storeUrl });
2662
+ const src = this.remoteStore(storeUrl);
2364
2663
  try {
2365
2664
  return await src.listVersions(ref);
2366
2665
  } catch (err) {
2666
+ const text = this.noteStoreFailure(storeUrl, err);
2367
2667
  if (!this.warnedStoreVersionLookups.has(storeUrl)) {
2368
2668
  this.warnedStoreVersionLookups.add(storeUrl);
2369
- console.warn(`skaile: store ${storeUrl} version lookup for ${ref} failed \u2014 ${err}`);
2669
+ console.warn(`skaile: store lookup of ${ref} failed: ${text}`);
2370
2670
  }
2371
2671
  return [];
2372
2672
  }
@@ -2404,28 +2704,84 @@ var AssetManager = class {
2404
2704
  }
2405
2705
  return { npmInstalled, npmFailed };
2406
2706
  }
2707
+ /**
2708
+ * Give every ref `install` could not resolve, and that has no hint yet, the fix the shared
2709
+ * {@link AssetManager.projectState} names for it (a declared ref no library or source holds,
2710
+ * a requirement nothing holds). Runs after the deploy, so a requirer is known by then.
2711
+ * Mutates `hints`: ` <reason>` follows the ref in the `Missing:` line.
2712
+ */
2713
+ async adviseMissing(refs, hints) {
2714
+ const open = refs.filter((r) => !hints[r]);
2715
+ if (open.length === 0) return;
2716
+ const { gaps } = await this.projectState();
2717
+ for (const ref of open) {
2718
+ const want = parseRefTolerant(ref.split(" ")[0] ?? ref);
2719
+ if (!want) continue;
2720
+ const gap = gaps.find(
2721
+ (g) => (g.type === "declared" || g.type === "requires") && g.manualFix && sameAsset(g.ref, want)
2722
+ );
2723
+ if (gap) hints[ref] = ` ${gap.reason}`;
2724
+ }
2725
+ }
2407
2726
  async installLocked(repositories, patches, dependencies) {
2408
2727
  const lock = this.tryReadLock();
2409
2728
  if (!lock) {
2729
+ const gaps2 = (await this.projectState()).gaps.filter((g) => g.type !== "agent");
2730
+ if (gaps2.some((g) => g.manualFix)) {
2731
+ return {
2732
+ deployed: [],
2733
+ removed: [],
2734
+ missing: gaps2.map((g) => g.message),
2735
+ lockWritten: false,
2736
+ npmInstalled: [],
2737
+ npmFailed: []
2738
+ };
2739
+ }
2410
2740
  throw new Error("No lock file found. Run `skaile install` first.");
2411
2741
  }
2412
2742
  this.assertNoFolderCollision(Object.keys(lock.assets).map(parseCanonicalLockKey));
2413
2743
  const declBySlug = new Map(Object.entries(repositories));
2414
2744
  const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
2415
- const factoryDir = Object.values(repositories).find((d) => d.factory)?.path ?? null;
2745
+ const factoryDecl = Object.values(repositories).find((d) => d.factory);
2746
+ const factoryDir = factoryDecl?.path ?? null;
2747
+ const isFactoryUrl = (url) => factoryDir !== null && normalizeRepoUrl(url) === factoryUrl;
2748
+ const unavailable = [];
2416
2749
  for (const src of lock.sources) {
2417
- if (factoryDir && normalizeRepoUrl(src.url) === factoryUrl) continue;
2750
+ if (isFactoryUrl(src.url)) continue;
2418
2751
  const slug = sourceCacheKey(src.url);
2419
- const decl = declBySlug.get(slug) ?? { url: src.url };
2420
- ensureRepo(decl, slug, this.reposDir, { projectDir: this.projectDir, pin: src.commit });
2752
+ const declared = declBySlug.get(slug);
2753
+ if (!declared && GITHUB_REPO_URL.test(src.url)) continue;
2754
+ try {
2755
+ const dir = ensureRepo(declared ?? { url: src.url }, slug, this.reposDir, {
2756
+ projectDir: this.projectDir,
2757
+ // A lock restore deploys the pinned commit's bytes or fails: never another commit's.
2758
+ .../^0*$/.test(src.commit) ? {} : { pin: src.commit, strictPin: true }
2759
+ });
2760
+ if (!declared) repositories[slug] = { url: src.url, path: dir };
2761
+ } catch (err) {
2762
+ const reason = err instanceof Error ? err.message : String(err);
2763
+ unavailable.push(`unavailable: ${src.url} at ${src.commit.slice(0, 7)} (${reason})`);
2764
+ }
2421
2765
  }
2422
- const resolveCloneDir = (sourceUrl) => {
2423
- if (factoryDir && normalizeRepoUrl(sourceUrl) === factoryUrl) return factoryDir;
2766
+ if (unavailable.length > 0) {
2767
+ throw new Error(`Lock file verification failed:
2768
+ ${unavailable.join("\n ")}`);
2769
+ }
2770
+ const candidates = [];
2771
+ const cloneOrigin = {};
2772
+ for (const [key, entry] of Object.entries(lock.assets)) {
2773
+ const candidate = this.lockedCandidate(key, entry, repositories);
2774
+ if (!candidate) continue;
2775
+ candidates.push(candidate);
2776
+ if (isFactoryUrl(candidate.sourceUrl) || this.candidateHasLocalBytes(candidate, repositories)) {
2777
+ cloneOrigin[key] = entry;
2778
+ }
2779
+ }
2780
+ const verification = verifyLock({ ...lock, assets: cloneOrigin }, (sourceUrl) => {
2781
+ if (isFactoryUrl(sourceUrl)) return factoryDir;
2424
2782
  const slug = sourceCacheKey(sourceUrl);
2425
- const decl = declBySlug.get(slug) ?? { url: sourceUrl };
2426
- return this.resolveRepoDir(decl, slug);
2427
- };
2428
- const verification = verifyLock(lock, resolveCloneDir);
2783
+ return this.resolveRepoDir(repositories[slug] ?? { url: sourceUrl }, slug);
2784
+ });
2429
2785
  if (!verification.ok) {
2430
2786
  const issues = [
2431
2787
  ...verification.drifted.map((r) => `drifted: ${r}`),
@@ -2444,30 +2800,16 @@ var AssetManager = class {
2444
2800
  ${lines.join("\n ")}`
2445
2801
  );
2446
2802
  }
2447
- const resolved = [];
2448
- for (const [ref, entry] of Object.entries(lock.assets)) {
2449
- if (!entry.source) continue;
2450
- const { publisher, kind, name } = parseCanonicalLockKey(ref);
2451
- if (!publisher || !kind || !name) continue;
2452
- const slug = sourceCacheKey(entry.source.url);
2453
- const cloneDir = resolveCloneDir(entry.source.url) ?? join(this.reposDir, slug);
2454
- const mdRel = entry.files.find((f) => /\.(md|yaml)$/i.test(f.path))?.path ?? entry.files[0]?.path ?? "";
2455
- resolved.push({
2456
- name,
2457
- kind,
2458
- description: "",
2459
- source: mdRel ? join(cloneDir, mdRel) : cloneDir,
2460
- // Keep the publisher label slash-free (repo name), not the full cache key.
2461
- publisher: sourceShortName(entry.source.url),
2462
- version: entry.source.commit,
2463
- // The lock key is the canonical ref; `publisher` above is only a clone label.
2464
- canonicalRef: ref,
2465
- requires: [],
2466
- dependencies: []
2467
- });
2468
- if (!repositories[slug]) repositories[slug] = { url: entry.source.url, path: cloneDir };
2803
+ let resolved;
2804
+ try {
2805
+ resolved = await this.buildCatalogEntries(candidates, repositories);
2806
+ } catch (err) {
2807
+ const reason = err instanceof Error ? err.message : String(err);
2808
+ throw new Error(`Lock file verification failed:
2809
+ ${reason}`);
2469
2810
  }
2470
2811
  const library = await this.restoreLibraryLockEntries(lock);
2812
+ const copyDrift = await this.lockedCopyDrift();
2471
2813
  const deployed = [
2472
2814
  ...new Set(
2473
2815
  this.targetHarnesses().flatMap(
@@ -2480,8 +2822,9 @@ var AssetManager = class {
2480
2822
  )
2481
2823
  )
2482
2824
  ];
2825
+ const restored = this.restoredCopies(copyDrift, deployed);
2483
2826
  await this.renderDriverMcpConfig();
2484
- const agentRender = this.renderAgentInstances();
2827
+ const agentRender = await this.renderAgentInstances();
2485
2828
  const alreadyMissing = new Set(
2486
2829
  library.missing.map((m) => {
2487
2830
  const p = parseRefTolerant(m.split(" ")[0] ?? m);
@@ -2498,6 +2841,7 @@ var AssetManager = class {
2498
2841
  removed: [],
2499
2842
  missing: [...library.missing, ...gaps],
2500
2843
  lockWritten: false,
2844
+ ...restored.length > 0 ? { restored } : {},
2501
2845
  ...agentRender
2502
2846
  };
2503
2847
  }
@@ -2551,7 +2895,8 @@ var AssetManager = class {
2551
2895
  this.ensureSourceClonesQuiet(repositories, sources);
2552
2896
  const index = await buildProvenanceIndex(this.buildClones(repositories), {
2553
2897
  projectDir: this.projectDir,
2554
- knownKinds: this.knownKinds
2898
+ knownKinds: this.knownKinds,
2899
+ onSourceError: this.reportSourceError
2555
2900
  });
2556
2901
  for (const candidates of index.values()) for (const c of candidates) note(c);
2557
2902
  for (const { entry } of await this.libraryAssets()) note(entry);
@@ -2572,6 +2917,8 @@ var AssetManager = class {
2572
2917
  * store/factory sources. Defaults to copy.
2573
2918
  * @param opts.onLibraryOrigin - Called once per deployed library-origin asset with its
2574
2919
  * canonical ref and the name of the library it was resolved from.
2920
+ * @param opts.onLibraryMoved - Called once per asset whose lock entry named another library
2921
+ * than the one this add resolved it from.
2575
2922
  * @param opts.onPresets - Called once with the presets expanded into this add (the
2576
2923
  * added preset and any it nests); a library-held preset names its library.
2577
2924
  * @returns Array of asset refs that were deployed in this call.
@@ -2584,11 +2931,19 @@ var AssetManager = class {
2584
2931
  this.ensureSourceClonesQuiet(repositories, sources);
2585
2932
  const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
2586
2933
  projectDir: this.projectDir,
2587
- knownKinds: this.knownKinds
2934
+ knownKinds: this.knownKinds,
2935
+ onSourceError: this.reportSourceError
2588
2936
  });
2589
2937
  const { resolved, missing, overridesApplied, lib, presets } = await this.resolveLibraryFirst(
2590
2938
  [ref],
2591
- { provenanceIndex, overrides, stores, declaredRefs: dependencies, warnCollisions: true }
2939
+ {
2940
+ provenanceIndex,
2941
+ overrides,
2942
+ stores,
2943
+ declaredRefs: dependencies,
2944
+ warnCollisions: true,
2945
+ ...opts?.preferLockedLibrary ? { preferLocked: await this.lockedPreference() } : {}
2946
+ }
2592
2947
  );
2593
2948
  if (resolved.length === 0 && lib.entries.length === 0 && presets.length === 0) {
2594
2949
  const snap = await this.loadLibraryIndex();
@@ -2606,6 +2961,12 @@ var AssetManager = class {
2606
2961
  );
2607
2962
  }
2608
2963
  }
2964
+ const unreachable = [...this.storeFailures.values()];
2965
+ if (unreachable.length > 0) {
2966
+ throw new Error(
2967
+ `Store unreachable \u2014 ${ref} was not looked up: ${unreachable.join("; ")}. Check the URL with \`skaile catalog test\`; a library or source can still hold the asset.`
2968
+ );
2969
+ }
2609
2970
  throw new Error(
2610
2971
  `Asset not found: ${ref}${(await this.publisherChangeHints([ref]))[ref] ?? ""}${missing.length ? ` (missing: ${missing.join(", ")})` : ""}`
2611
2972
  );
@@ -2627,6 +2988,7 @@ var AssetManager = class {
2627
2988
  }
2628
2989
  }
2629
2990
  }
2991
+ this.assertSourceCommitsPublished(resolved, repositories);
2630
2992
  this.assertNoFolderCollision(
2631
2993
  [...resolved, ...lib.entries.map((e) => this.entryIdentity(e))],
2632
2994
  [
@@ -2635,8 +2997,8 @@ var AssetManager = class {
2635
2997
  ]
2636
2998
  );
2637
2999
  if (missing.length > 0) {
2638
- console.warn(
2639
- `skaile: ${ref} was added without unresolved dependencies: ${missing.join(", ")}`
3000
+ throw new Error(
3001
+ `${ref.split("#")[0]} requires ${missing.join(", ")} (not found \u2014 ${REGISTER_HOLDER}, or remove the requirement from the asset's manifest) \u2014 nothing was added`
2640
3002
  );
2641
3003
  }
2642
3004
  const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
@@ -2647,7 +3009,7 @@ var AssetManager = class {
2647
3009
  entries,
2648
3010
  repositories,
2649
3011
  patches,
2650
- this.deployOptsFor(t, opts?.link, opts?.onSkippedSymlink)
3012
+ this.deployOptsFor(t, opts?.link, opts?.onSkippedSymlink, opts?.onEnvFile)
2651
3013
  )
2652
3014
  )
2653
3015
  )
@@ -2662,13 +3024,18 @@ var AssetManager = class {
2662
3024
  if (presets.length > 0) opts?.onPresets?.(presets);
2663
3025
  this.addDependencyToConfig(ref);
2664
3026
  await this.renderDriverMcpConfig();
2665
- this.renderAgentInstances();
3027
+ await this.renderAgentInstances();
2666
3028
  if (resolved.length > 0 || lib.entries.length > 0) {
3029
+ const libraryLock = await this.libraryLockEntries(lib);
2667
3030
  const fresh = {
2668
- ...buildLockFile(resolved, overridesApplied, await this.libraryLockEntries(lib)),
3031
+ ...buildLockFile(resolved, overridesApplied, libraryLock),
2669
3032
  ...presetsLockSlice(presets)
2670
3033
  };
2671
- writeLock(this.lockPath, mergeLockFile(this.tryReadLock(), fresh));
3034
+ const prevLock = this.tryReadLock();
3035
+ if (opts?.onLibraryMoved) {
3036
+ for (const m of libraryMoves(prevLock, libraryLock)) opts.onLibraryMoved(m);
3037
+ }
3038
+ writeLock(this.lockPath, mergeLockFile(prevLock, fresh));
2672
3039
  }
2673
3040
  for (const d of deployed) {
2674
3041
  appendHistory(this.projectDir, {
@@ -2726,16 +3093,16 @@ var AssetManager = class {
2726
3093
  const target = memberIdentity(ref);
2727
3094
  if (!target || target.kind === "preset") return [];
2728
3095
  try {
2729
- const sameAsset = (id, want) => !!id && id.kind === want.kind && id.name === want.name && (!id.publisher || !want.publisher || id.publisher === want.publisher);
3096
+ const sameAsset2 = (id, want) => !!id && id.kind === want.kind && id.name === want.name && (!id.publisher || !want.publisher || id.publisher === want.publisher);
2730
3097
  const declared = this.loadConfig().dependencies.map(memberIdentity);
2731
3098
  const out = [];
2732
3099
  const seen = /* @__PURE__ */ new Set();
2733
3100
  for (const p of await this.search(void 0, "preset")) {
2734
3101
  const members = [...p.dependencies, ...p.requires.map(depToStr)];
2735
- if (!members.some((m) => sameAsset(memberIdentity(m), target))) continue;
3102
+ if (!members.some((m) => sameAsset2(memberIdentity(m), target))) continue;
2736
3103
  const publisher = p.publisher?.replace(/^@/, "");
2737
3104
  const presetId = { kind: "preset", name: p.name, publisher };
2738
- if (declared.some((d) => sameAsset(d, presetId))) continue;
3105
+ if (declared.some((d) => sameAsset2(d, presetId))) continue;
2739
3106
  const presetRef = publisher ? assetRefToStr({ kind: "preset", name: p.name, publisher }) : `preset:${p.name}`;
2740
3107
  if (seen.has(presetRef)) continue;
2741
3108
  seen.add(presetRef);
@@ -3057,11 +3424,35 @@ var AssetManager = class {
3057
3424
  const requiredBy = [...requires].filter(([node, deps]) => node !== targetKey && deps.includes(targetKey)).map(([node]) => display(node));
3058
3425
  return requiredBy.length > 0 ? { ref: display(targetKey), requiredBy } : null;
3059
3426
  }
3427
+ /**
3428
+ * Publishers under which the lock or `skaile.yaml` track `kind:name`. A deployed dir carries
3429
+ * no publisher, so this is what says whose copy it is. `undefined` stands for a
3430
+ * publisher-less declaration.
3431
+ */
3432
+ trackedPublishers(kind, name) {
3433
+ const found = /* @__PURE__ */ new Set();
3434
+ const note = (id) => {
3435
+ if (id?.kind === kind && id.name === name) found.add(id.publisher?.replace(/^@/, ""));
3436
+ };
3437
+ for (const key of Object.keys(this.tryReadLock()?.assets ?? {})) {
3438
+ note(parseCanonicalLockKey(key));
3439
+ }
3440
+ try {
3441
+ for (const dep of this.loadConfig().dependencies) note(memberIdentity(dep));
3442
+ } catch {
3443
+ }
3444
+ return found;
3445
+ }
3060
3446
  /** Undeploy `parsed` from every target harness and drop its declaration and lock entry. */
3061
3447
  removeResolved(parsed) {
3448
+ const wantPub = parsed.publisher?.replace(/^@/, "");
3449
+ const tracked = this.trackedPublishers(parsed.kind, parsed.name);
3450
+ const someoneElses = wantPub !== void 0 && tracked.size > 0 && !tracked.has(wantPub) && !tracked.has(void 0);
3062
3451
  let undeployed = false;
3063
- for (const t of this.targetHarnesses()) {
3064
- if (removeAsset(parsed.kind, parsed.name, this.deployOptsFor(t))) undeployed = true;
3452
+ if (!someoneElses) {
3453
+ for (const t of this.targetHarnesses()) {
3454
+ if (removeAsset(parsed.kind, parsed.name, this.deployOptsFor(t))) undeployed = true;
3455
+ }
3065
3456
  }
3066
3457
  const undeclared = this.removeDependencyFromConfig(parsed);
3067
3458
  const unbound = this.removeAgentBindings(parsed);
@@ -3398,12 +3789,12 @@ var AssetManager = class {
3398
3789
  return out;
3399
3790
  }
3400
3791
  /**
3401
- * Library-locked assets whose deployed directory lacks files the deploy writes —
3402
- * a deleted `build.py` / `scripts/` helper next to an intact `SKILL.md`. The
3403
- * expected set is the library entry's file set (every file under its manifest
3404
- * `root:`), the same bytes the lock hashes; only presence is checked, so a
3405
- * local edit never counts. An asset with no deployed directory is not listed
3406
- * (`listDeclared` reports it as drift); an agent is rendered, not copied, so it
3792
+ * Locked assets whose deployed directory lacks files the deploy writes — a deleted
3793
+ * `build.py` / `scripts/` helper next to an intact `SKILL.md`. The expected set is the file
3794
+ * set the lock covers: a library entry's files (every file under its manifest `root:`), a
3795
+ * source or store entry's `files[]` (the asset's whole file set at the locked commit). Only
3796
+ * presence is checked, so a local edit never counts. An asset with no deployed directory is
3797
+ * not listed (`listDeclared` reports it as drift); an agent is rendered, not copied, so it
3407
3798
  * has no file mirror to compare. `skaile install` restores what is listed.
3408
3799
  *
3409
3800
  * @returns One {@link IncompleteDeploy} per affected asset; empty when healthy
@@ -3412,10 +3803,25 @@ var AssetManager = class {
3412
3803
  async incompleteDeploys() {
3413
3804
  const lock = this.tryReadLock();
3414
3805
  if (!lock) return [];
3806
+ const out = [];
3807
+ const { patches } = this.loadConfig();
3808
+ for (const [key, entry] of Object.entries(lock.assets)) {
3809
+ if (!entry.source || entry.files.length === 0) continue;
3810
+ const { publisher, kind, name } = parseCanonicalLockKey(key);
3811
+ const component = kind ? this.installTarget(kind) : void 0;
3812
+ if (!publisher || !kind || !name || !component || component === "agent") continue;
3813
+ if (patches[`${kind}:${name}`]) continue;
3814
+ const dest = join(
3815
+ deployedBase(component, this.driverTarget, this.global, this.projectDir),
3816
+ name
3817
+ );
3818
+ if (!existsSync(dest) || lstatSync(dest).isSymbolicLink()) continue;
3819
+ const missing = lockedAssetFiles(kind, entry.files).map((f) => f.rel).filter((rel) => !existsSync(join(dest, rel)));
3820
+ if (missing.length > 0) out.push({ ref: `${kind}:@${publisher}/${name}`, missing });
3821
+ }
3415
3822
  const locked = Object.entries(lock.assets).filter(([, e]) => e.library !== void 0 && !e.library.installed).map(([key]) => key);
3416
- if (locked.length === 0) return [];
3823
+ if (locked.length === 0) return out;
3417
3824
  const lib = await this.resolveLibraryEntries(locked, { quiet: true });
3418
- const out = [];
3419
3825
  for (const entry of lib.entries) {
3420
3826
  const component = this.installTarget(entry.kind);
3421
3827
  if (!entry.files || !component || component === "agent") continue;
@@ -3647,7 +4053,7 @@ var AssetManager = class {
3647
4053
  return locked.kind === asked.kind && locked.name === asked.name;
3648
4054
  };
3649
4055
  const key = lock.assets[ref] ? ref : Object.keys(lock.assets).find(matchesIdentity);
3650
- if (!key) return [];
4056
+ if (!key) return asked?.kind === "preset" ? this.whyPreset(lock, asked) : [];
3651
4057
  const { requires, roots } = await this.lockedGraph(lock);
3652
4058
  if (roots.includes(key)) return [key, "direct (skaile.yaml)"];
3653
4059
  const requirers = /* @__PURE__ */ new Map();
@@ -3672,6 +4078,52 @@ var AssetManager = class {
3672
4078
  }
3673
4079
  return [key, "not required by a declared dependency"];
3674
4080
  }
4081
+ /**
4082
+ * The {@link AssetManager.why} chain of a preset. A preset is never a lock asset: its record
4083
+ * is the lock's `presets` slice. Declared in `skaile.yaml` it is `direct`; one only a declared
4084
+ * preset nests is traced through the presets that list it.
4085
+ *
4086
+ * @returns `[]` when the lock does not record the preset.
4087
+ */
4088
+ async whyPreset(lock, asked) {
4089
+ const wanted = memberIdentity(assetRefToStr(asked));
4090
+ const same = (ref) => {
4091
+ const id = memberIdentity(ref);
4092
+ return id !== null && wanted !== null && id.kind === "preset" && id.name === wanted.name && (!id.publisher || !wanted.publisher || id.publisher === wanted.publisher);
4093
+ };
4094
+ const recorded = (lock.presets ?? []).find(same);
4095
+ if (!recorded) return [];
4096
+ let declared = [];
4097
+ try {
4098
+ declared = this.loadConfig().dependencies;
4099
+ } catch {
4100
+ }
4101
+ const roots = declared.filter((d) => memberIdentity(d)?.kind === "preset");
4102
+ if (roots.some(same)) return [recorded, "direct (skaile.yaml)"];
4103
+ const parent = /* @__PURE__ */ new Map();
4104
+ const seen = new Set(roots);
4105
+ const queue = [...roots];
4106
+ for (let node = queue.shift(); node !== void 0; node = queue.shift()) {
4107
+ let items = [];
4108
+ try {
4109
+ const entry = await this.info(node);
4110
+ items = entry ? libraryEntryDepRefs(entry) : [];
4111
+ } catch {
4112
+ }
4113
+ for (const item of items) {
4114
+ if (memberIdentity(item)?.kind !== "preset" || seen.has(item)) continue;
4115
+ seen.add(item);
4116
+ parent.set(item, node);
4117
+ if (same(item)) {
4118
+ const chain = [];
4119
+ for (let c = item; c !== void 0; c = parent.get(c)) chain.push(c);
4120
+ return [...chain, "direct (skaile.yaml)"];
4121
+ }
4122
+ queue.push(item);
4123
+ }
4124
+ }
4125
+ return [recorded, "not required by a declared dependency"];
4126
+ }
3675
4127
  /**
3676
4128
  * List installed assets that have a newer upstream available.
3677
4129
  *
@@ -3710,7 +4162,17 @@ var AssetManager = class {
3710
4162
  if (!kind || !name) continue;
3711
4163
  const lockedCommit = entry.source.commit || src.commit;
3712
4164
  const moved = lockedCommit && !/^0+$/.test(lockedCommit) && lockedCommit !== status.localHead ? commitsBetween(clone, lockedCommit, status.localHead, commitCounts) : 0;
3713
- const behind = status.behind + moved;
4165
+ let behind = status.behind + moved;
4166
+ if (behind > 0) {
4167
+ const touching = commitsTouching(
4168
+ clone,
4169
+ COMMIT_ID.test(lockedCommit) && !/^0+$/.test(lockedCommit) ? lockedCommit : status.localHead,
4170
+ status.behind > 0 && status.remoteHead ? status.remoteHead : status.localHead,
4171
+ lockedAssetDir(kind, entry.files),
4172
+ commitCounts
4173
+ );
4174
+ if (touching !== null) behind = touching;
4175
+ }
3714
4176
  if (behind <= 0) continue;
3715
4177
  result.push({
3716
4178
  kind,
@@ -3828,6 +4290,37 @@ var AssetManager = class {
3828
4290
  }
3829
4291
  return null;
3830
4292
  }
4293
+ /**
4294
+ * The deploy entry of a source- or store-origin asset, carrying the file set its lock entry
4295
+ * lists (so a missing or edited helper shows in `diff`, not only the manifest). A source asset
4296
+ * reads from its clone; a store asset from the bytes `add`/`install` staged in the project
4297
+ * store cache. `null` when the lock has no such entry or its bytes are not on disk.
4298
+ *
4299
+ * @param want - Kind, name and optional publisher of the asset.
4300
+ */
4301
+ lockedSourceEntry(want) {
4302
+ const { repositories } = this.loadConfig();
4303
+ for (const [key, e] of Object.entries(this.tryReadLock()?.assets ?? {})) {
4304
+ const id = parseCanonicalLockKey(key);
4305
+ if (!e.source || id.kind !== want.kind || id.name !== want.name) continue;
4306
+ if (want.publisher && id.publisher !== want.publisher) continue;
4307
+ const c = this.lockedCandidate(key, e, repositories);
4308
+ if (!c) continue;
4309
+ if (this.candidateHasLocalBytes(c, repositories)) {
4310
+ return this.candidateToCatalogEntry(c, repositories);
4311
+ }
4312
+ const staged = join(
4313
+ this.storeCacheDir,
4314
+ c.publisher,
4315
+ c.kind,
4316
+ c.name,
4317
+ c.version,
4318
+ this.stagedManifestRel(c)
4319
+ );
4320
+ if (existsSync(staged)) return this.stagedCandidateToEntry(c, staged);
4321
+ }
4322
+ return null;
4323
+ }
3831
4324
  /**
3832
4325
  * Produce a unified diff from the deployed asset to its catalog source: `-` lines
3833
4326
  * are in the deployed copy only (local edits `update` would overwrite), `+` lines
@@ -3845,9 +4338,17 @@ var AssetManager = class {
3845
4338
  async diff(ref) {
3846
4339
  const parsed = parseRefTolerant(ref);
3847
4340
  if (!parsed) return { status: "not-found" };
3848
- const entry = await this.lockedLibraryEntry(parsed) ?? await this.info(ref);
4341
+ const entry = await this.lockedLibraryEntry(parsed) ?? this.lockedSourceEntry(parsed) ?? await this.info(ref);
3849
4342
  if (!entry || !existsSync(entry.source)) return { status: "not-found" };
4343
+ const wantPublisher = parsed.publisher?.replace(/^@/, "");
4344
+ if (wantPublisher) {
4345
+ const lockedHere = Object.keys(this.tryReadLock()?.assets ?? {}).map(parseCanonicalLockKey).filter((id) => id.kind === parsed.kind && id.name === parsed.name);
4346
+ if (lockedHere.length > 0 && !lockedHere.some((id) => id.publisher === wantPublisher)) {
4347
+ return { status: "not-deployed" };
4348
+ }
4349
+ }
3850
4350
  const component = this.installTarget(parsed.kind);
4351
+ if (parsed.kind === "preset" && this.presetRecorded(parsed)) return { status: "no-files" };
3851
4352
  if (!component) return { status: "not-deployed" };
3852
4353
  if (component === "agent") {
3853
4354
  const diff = diffDeployedAgent(entry, this.deployOptsFor(this.driverTarget));
@@ -3888,7 +4389,8 @@ var AssetManager = class {
3888
4389
  this.ensureSourceClonesQuiet(repositories, sources);
3889
4390
  const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
3890
4391
  projectDir: this.projectDir,
3891
- knownKinds: this.knownKinds
4392
+ knownKinds: this.knownKinds,
4393
+ onSourceError: this.reportSourceError
3892
4394
  });
3893
4395
  const { resolved, overridesApplied, lib } = await this.resolveLibraryFirst(dependencies, {
3894
4396
  provenanceIndex,
@@ -4015,7 +4517,11 @@ var AssetManager = class {
4015
4517
  const full = await this.info(
4016
4518
  publisher ? `${entry.kind}:@${publisher}/${entry.name}` : `${entry.kind}:${entry.name}`
4017
4519
  );
4018
- if (!full) {
4520
+ const restorable = !full && publisher !== void 0 && Object.entries(lock?.assets ?? {}).some(([key, e]) => {
4521
+ const id = parseCanonicalLockKey(key);
4522
+ return e.source !== void 0 && id.kind === entry.kind && id.name === entry.name && id.publisher === publisher;
4523
+ });
4524
+ if (!full && !restorable) {
4019
4525
  if (publisher) {
4020
4526
  const canonical = `${entry.kind}:@${publisher}/${entry.name}`;
4021
4527
  const fix = (await this.unresolvedFixes([canonical])).get(canonical);
@@ -4031,8 +4537,9 @@ var AssetManager = class {
4031
4537
  }
4032
4538
  continue;
4033
4539
  }
4540
+ const declaredRequires = full ? full.requires : (await this.deployedRequirementsOf({ ...entry, publisher })).map(parseDep);
4034
4541
  const gaps = [];
4035
- const requirements = dedupeRequirements(full.requires, publisher).map(
4542
+ const requirements = dedupeRequirements(declaredRequires, publisher).map(
4036
4543
  (r) => r.publisher || !publisher ? r : { ...r, publisher }
4037
4544
  );
4038
4545
  for (const req of requirements) {
@@ -4113,7 +4620,11 @@ var AssetManager = class {
4113
4620
  const lock = this.tryReadLock();
4114
4621
  const lockedIds = Object.keys(lock?.assets ?? {}).map(parseCanonicalLockKey);
4115
4622
  const requirements = await this.doctor();
4116
- const gaps = [];
4623
+ let gaps = [];
4624
+ const lockIdentity = (kind, name) => {
4625
+ const publisher = lockedIds.find((id) => id.kind === kind && id.name === name)?.publisher;
4626
+ return publisher ? `${kind}:@${publisher}/${name}` : `${kind}:${name}`;
4627
+ };
4117
4628
  for (const i of requirements) {
4118
4629
  if (i.unresolved) {
4119
4630
  const publisher = lockedIds.find(
@@ -4130,18 +4641,18 @@ var AssetManager = class {
4130
4641
  });
4131
4642
  continue;
4132
4643
  }
4133
- const asset = `${i.assetKind}:${i.assetName}`;
4644
+ const asset = lockIdentity(i.assetKind, i.assetName);
4134
4645
  const dep = `${i.depKind}:${i.depName}`;
4135
4646
  if (i.inRepos) {
4136
4647
  gaps.push({
4137
4648
  type: "requires",
4138
4649
  ref: dep,
4139
4650
  asset,
4140
- reason: `is required by ${asset} but neither deployed nor locked (fixable)`,
4141
- message: `${asset} requires ${dep} (fixable)`
4651
+ reason: `is required by ${asset} but neither deployed nor locked (run skaile install)`,
4652
+ message: `${asset} requires ${dep} (run skaile install)`
4142
4653
  });
4143
4654
  } else {
4144
- const fix = `register the library that holds it with \`skaile library init <name> --path <dir>\`, or drop ${asset} with \`skaile remove ${asset}\``;
4655
+ const fix = requirementNoHolderFix(asset);
4145
4656
  gaps.push({
4146
4657
  type: "requires",
4147
4658
  ref: dep,
@@ -4159,17 +4670,51 @@ var AssetManager = class {
4159
4670
  gaps.push(gap);
4160
4671
  named.add(gapIdentity(gap.ref));
4161
4672
  }
4673
+ const libraryHeld = (dep) => {
4674
+ const want = parseRefTolerant(dep);
4675
+ return want !== null && gaps.some((g) => g.type === "library" && sameAsset(g.ref, want));
4676
+ };
4677
+ gaps = gaps.filter((g) => !(g.type === "requires" && libraryHeld(g.ref)));
4162
4678
  for (const gap of this.undeployedLockGaps(lock, named)) {
4163
4679
  gaps.push(gap);
4164
4680
  named.add(gapIdentity(gap.ref));
4165
4681
  }
4682
+ for (const d of await this.lockedCopyDrift()) {
4683
+ if (named.has(gapIdentity(d.ref))) continue;
4684
+ const parts = [
4685
+ ...d.modified.map((f) => `${f} was edited`),
4686
+ ...d.extra.map((f) => `${f} is not part of the asset`)
4687
+ ];
4688
+ const reason = `is locked, but its deployed copy differs from the lock (${parts.join("; ")}) \u2014 run \`skaile install --locked\` to restore it`;
4689
+ gaps.push({ type: "modified", ref: d.ref, reason, message: `${d.ref} ${reason}` });
4690
+ named.add(gapIdentity(d.ref));
4691
+ }
4166
4692
  gaps.push(...await this.declaredGaps(requirements, named));
4167
4693
  for (const d of await this.incompleteDeploys()) {
4168
4694
  const reason = `is deployed without ${d.missing.join(", ")} \u2014 run \`skaile install\` to restore it`;
4169
4695
  gaps.push({ type: "incomplete", ref: d.ref, reason, message: `${d.ref} ${reason}` });
4170
4696
  }
4697
+ const skipped = [];
4698
+ await buildProvenanceIndex(this.buildClones(this.loadConfig().repositories), {
4699
+ projectDir: this.projectDir,
4700
+ knownKinds: this.knownKinds,
4701
+ onSourceError: (clone, err) => skipped.push({
4702
+ url: clone.sourceUrl,
4703
+ why: err instanceof Error ? err.message : String(err)
4704
+ })
4705
+ });
4706
+ for (const s of skipped) {
4707
+ const reason = `is skipped: ${s.why.replace(`${s.url}: `, "")} \u2014 fix it or run \`skaile source remove\``;
4708
+ gaps.push({
4709
+ type: "source",
4710
+ ref: s.url,
4711
+ reason,
4712
+ message: `source ${s.url} ${reason}`,
4713
+ manualFix: true
4714
+ });
4715
+ }
4171
4716
  for (const u of unboundAgentInstances(this.projectDir, this.driverTarget)) {
4172
- const reason = u.ref.startsWith("agent:") ? `is bound by agent instance "${u.id}" but no agent was added for it \u2014 run \`skaile add ${u.ref}\` first, or drop the binding from skaile.yaml` : `is bound by agent instance "${u.id}" but resolves to no agent.yaml \u2014 fix the path, or drop the binding from skaile.yaml`;
4717
+ const reason = u.ref.startsWith("agent:") ? await this.unboundAgentReason(u.id, u.ref) : `is bound by agent instance "${u.id}" but resolves to no agent.yaml \u2014 fix the path, or drop the binding from skaile.yaml`;
4173
4718
  gaps.push({
4174
4719
  type: "agent",
4175
4720
  ref: u.ref,
@@ -4178,7 +4723,65 @@ var AssetManager = class {
4178
4723
  manualFix: true
4179
4724
  });
4180
4725
  }
4181
- return { gaps, requirements };
4726
+ return {
4727
+ gaps,
4728
+ requirements: requirements.filter(
4729
+ (i) => i.unresolved || !libraryHeld(`${i.depKind}:${i.depName}`)
4730
+ )
4731
+ };
4732
+ }
4733
+ /**
4734
+ * Why the `agent:` ref of agent instance `id` binds to nothing, as the phrase that follows
4735
+ * the ref in a {@link LockGap}; see {@link AssetManager.unboundAgentAdvice}.
4736
+ */
4737
+ async unboundAgentReason(id, ref) {
4738
+ const { lead, advice } = await this.unboundAgentAdvice(ref);
4739
+ return `is bound by agent instance "${id}" but ${lead} \u2014 ${advice}`;
4740
+ }
4741
+ /**
4742
+ * What is wrong with an `agent:` ref bound by an agent instance and what to do (`lead`, then
4743
+ * `advice`). `skaile add` is named only when a registered library or source holds the agent
4744
+ * and `add` takes the ref (it needs a publisher); otherwise the user registers the library
4745
+ * that holds it, writes the publisher, or drops the binding.
4746
+ */
4747
+ async unboundAgentAdvice(ref) {
4748
+ const drop = "or drop the binding from skaile.yaml";
4749
+ const parsedRef = parseRefTolerant(ref);
4750
+ const publisher = parsedRef?.publisher;
4751
+ const lockedAgent = Object.keys(this.tryReadLock()?.assets ?? {}).some((key) => {
4752
+ const k = parseCanonicalLockKey(key);
4753
+ return k.kind === "agent" && k.name === parsedRef?.name && (!publisher || !k.publisher || k.publisher === publisher);
4754
+ });
4755
+ if (lockedAgent) {
4756
+ return {
4757
+ lead: "the agent is locked but not restored here",
4758
+ advice: `run \`skaile install --locked\` first, ${drop}`
4759
+ };
4760
+ }
4761
+ if (publisher && await this.info(ref) !== null) {
4762
+ return {
4763
+ lead: "no agent was added for it",
4764
+ advice: `run \`skaile add ${ref}\` first, ${drop}`
4765
+ };
4766
+ }
4767
+ if (!publisher) {
4768
+ return {
4769
+ lead: "names no publisher",
4770
+ advice: `write it as agent:@<publisher>/<name> (\`skaile add\` needs the publisher), ${drop}`
4771
+ };
4772
+ }
4773
+ const fix = (await this.unresolvedFixes([ref])).get(ref);
4774
+ if (fix) {
4775
+ const register = fix.replace(/, or drop the dependency with `skaile remove [^`]*`$/, "");
4776
+ return {
4777
+ lead: "no agent was added for it, and `skaile add` cannot resolve it",
4778
+ advice: `${register}, ${drop}`
4779
+ };
4780
+ }
4781
+ return {
4782
+ lead: "no registered library or source holds it",
4783
+ advice: `register the library that holds it with \`skaile library init <name> --path <dir>\`, ${drop}`
4784
+ };
4182
4785
  }
4183
4786
  /** @deprecated Use `(await projectState()).gaps`; removed in the next major. */
4184
4787
  async lockGaps() {
@@ -4201,6 +4804,52 @@ var AssetManager = class {
4201
4804
  (g) => names(g.ref) || g.asset && names(g.asset)
4202
4805
  );
4203
4806
  }
4807
+ /**
4808
+ * The {@link ProjectState} gaps of everything `ref` requires, transitively: what `check`
4809
+ * says about the requirements of `ref`, so a command that answers for one asset (`diff`)
4810
+ * does not call it fine while a requirement of it is broken. The requirements are read from
4811
+ * the deployed copies (the lock records no edges), the way `remove` and `install` read them
4812
+ * when no library holds the asset any more. The gaps of `ref` itself are not included
4813
+ * ({@link gapsOf}).
4814
+ *
4815
+ * @param ref - Asset ref in `kind:name` or `kind:@publisher/name` form.
4816
+ */
4817
+ async requirementGapsOf(ref) {
4818
+ const want = parseRefTolerant(ref);
4819
+ if (!want) return [];
4820
+ const gaps = (await this.projectState()).gaps;
4821
+ const lockedIds = Object.keys(this.tryReadLock()?.assets ?? {}).map(parseCanonicalLockKey);
4822
+ const idKey = (id) => `${id.publisher ?? ""}/${id.kind}:${id.name}`;
4823
+ const seen = /* @__PURE__ */ new Set();
4824
+ const queue = [
4825
+ {
4826
+ kind: want.kind,
4827
+ name: want.name,
4828
+ publisher: want.publisher?.replace(/^@/, "") ?? lockedIds.find((l) => l.kind === want.kind && l.name === want.name)?.publisher
4829
+ }
4830
+ ];
4831
+ seen.add(idKey(queue[0]));
4832
+ const out = [];
4833
+ for (const asset of queue) {
4834
+ for (const dep of await this.deployedRequirementsOf(asset)) {
4835
+ const id = memberIdentity(dep);
4836
+ if (!id) continue;
4837
+ const publisher = id.publisher ?? lockedIds.find((l) => l.kind === id.kind && l.name === id.name)?.publisher;
4838
+ const next = { kind: id.kind, name: id.name, publisher };
4839
+ if (seen.has(idKey(next))) continue;
4840
+ seen.add(idKey(next));
4841
+ queue.push(next);
4842
+ for (const g of gaps) {
4843
+ if (out.includes(g)) continue;
4844
+ const p = parseRefTolerant(g.ref);
4845
+ if (p !== null && p.kind === next.kind && p.name === next.name && (!publisher || !p.publisher || p.publisher.replace(/^@/, "") === publisher)) {
4846
+ out.push(g);
4847
+ }
4848
+ }
4849
+ }
4850
+ }
4851
+ return out;
4852
+ }
4204
4853
  /**
4205
4854
  * Locked library assets whose library cannot supply them: the library is not registered
4206
4855
  * (renamed or removed) or no longer holds the asset. Another library holding it makes the
@@ -4266,6 +4915,121 @@ var AssetManager = class {
4266
4915
  }
4267
4916
  return gaps;
4268
4917
  }
4918
+ /**
4919
+ * Remove the files a deploy never writes (`.env`, `.venv/`, `.pytest_cache/`, ...) from the
4920
+ * deployed folders of re-locked assets, as {@link RestoredAsset} rows. The lock covers what
4921
+ * the exclusion list leaves; the extras an older release copied are the owner's to drop, and
4922
+ * this is the one point where they are: the lock was just rewritten.
4923
+ *
4924
+ * @param refs - Lock keys the install re-locked (hash or commit changed).
4925
+ */
4926
+ removeExcludedLeftovers(refs) {
4927
+ const out = [];
4928
+ for (const key of refs) {
4929
+ const { kind, name } = parseCanonicalLockKey(key);
4930
+ const component = kind ? this.installTarget(kind) : void 0;
4931
+ if (!kind || !name || !component || component === "agent") continue;
4932
+ const dest = deployedDir(component, name, this.driverTarget, this.global, this.projectDir);
4933
+ const removed = removeExcludedLeftovers(dest);
4934
+ if (removed.length === 0) continue;
4935
+ const dir = relative(this.projectDir, dest).split(sep).join("/");
4936
+ out.push({ ref: key, restored: [], removed: removed.map((f) => `${dir}/${f}`) });
4937
+ }
4938
+ return out;
4939
+ }
4940
+ /**
4941
+ * The {@link RestoredAsset} rows for the drifted copies (see {@link lockedCopyDrift}) a
4942
+ * deploy just wrote again: those whose ref is among the `deployed` canonical refs.
4943
+ */
4944
+ restoredCopies(drifts, deployed) {
4945
+ const rewritten = new Set(deployed.map((d) => d.split("#")[0]));
4946
+ const out = [];
4947
+ for (const d of drifts) {
4948
+ if (!rewritten.has(d.ref)) continue;
4949
+ const { kind, name } = parseCanonicalLockKey(d.key);
4950
+ const component = kind ? this.installTarget(kind) : void 0;
4951
+ if (!kind || !name || !component) continue;
4952
+ const dir = relative(
4953
+ this.projectDir,
4954
+ deployedDir(component, name, this.driverTarget, this.global, this.projectDir)
4955
+ ).split(sep).join("/");
4956
+ out.push({
4957
+ ref: d.key,
4958
+ restored: d.modified.map((f) => `${dir}/${f}`),
4959
+ removed: d.extra.map((f) => `${dir}/${f}`)
4960
+ });
4961
+ }
4962
+ return out;
4963
+ }
4964
+ /**
4965
+ * Locked assets whose deployed copy differs from the bytes the lock covers: a file edited by
4966
+ * hand, or a file the asset does not ship. One row per asset, `ref` being `kind:@publisher/name`
4967
+ * and `key` its lock key. The one comparison behind the `modified` gap and the `Restored` lines
4968
+ * of `install`.
4969
+ *
4970
+ * - A library-origin asset is compared with its library's files, but only while those equal the
4971
+ * locked hash (changed library content is the `content` gap's business), both modified and
4972
+ * extra files.
4973
+ * - A source asset is compared with the per-file hashes the lock records (modified files only:
4974
+ * the lock lists what the source covers, not what the deploy copies).
4975
+ *
4976
+ * An agent (rendered, not copied), a `--link` deploy, an asset with a configured patch, and a
4977
+ * copy that is missing are not compared: other gaps cover them. Project mode only.
4978
+ */
4979
+ async lockedCopyDrift() {
4980
+ if (this.global) return [];
4981
+ const lock = this.tryReadLock();
4982
+ if (!lock) return [];
4983
+ const { patches } = this.loadConfig();
4984
+ const out = [];
4985
+ const deployOpts = {
4986
+ registry: this.registry,
4987
+ driverTarget: this.driverTarget,
4988
+ global: this.global,
4989
+ cwd: this.projectDir
4990
+ };
4991
+ const refOf = (key) => {
4992
+ const { kind, publisher, name } = parseCanonicalLockKey(key);
4993
+ return publisher ? `${kind}:@${publisher}/${name}` : `${kind}:${name}`;
4994
+ };
4995
+ const keyOf = /* @__PURE__ */ new Map();
4996
+ for (const key of Object.keys(lock.assets)) keyOf.set(refOf(key), key);
4997
+ const { entries } = await this.restoreLibraryLockEntries(lock);
4998
+ for (const e of entries) {
4999
+ if (patches[`${e.kind}:${e.name}`]) continue;
5000
+ const drift = deployedAssetDrift(e, deployOpts);
5001
+ if (!drift || drift.modified.length === 0 && drift.extra.length === 0) continue;
5002
+ const ref = `${e.kind}:@${(e.publisher ?? "").replace(/^@/, "")}/${e.name}`;
5003
+ out.push({ ref, key: keyOf.get(ref) ?? ref, ...drift, library: true });
5004
+ }
5005
+ for (const [key, entry] of Object.entries(lock.assets)) {
5006
+ if (!entry.source || entry.files.length === 0) continue;
5007
+ const { kind, name } = parseCanonicalLockKey(key);
5008
+ const component = kind ? this.installTarget(kind) : void 0;
5009
+ if (!kind || !name || !component || component === "agent") continue;
5010
+ if (patches[`${kind}:${name}`]) continue;
5011
+ const dest = deployedDir(component, name, this.driverTarget, this.global, this.projectDir);
5012
+ try {
5013
+ const deployed = lstatSync(dest);
5014
+ if (deployed.isSymbolicLink() || !deployed.isDirectory()) continue;
5015
+ } catch {
5016
+ continue;
5017
+ }
5018
+ const modified = [];
5019
+ for (const f of lockedAssetFiles(kind, entry.files)) {
5020
+ const deployedFile = join(dest, f.rel);
5021
+ if (!existsSync(deployedFile)) continue;
5022
+ try {
5023
+ if (computeHash(deployedFile) !== f.sha256) modified.push(f.rel);
5024
+ } catch {
5025
+ modified.push(f.rel);
5026
+ }
5027
+ }
5028
+ if (modified.length > 0)
5029
+ out.push({ ref: refOf(key), key, modified, extra: [], library: false });
5030
+ }
5031
+ return out;
5032
+ }
4269
5033
  /**
4270
5034
  * Locked assets (declared or required by one) whose deployed copy is gone: `skaile install`
4271
5035
  * restores them. Same deployability rule as {@link AssetManager.listDeclared}'s `drift`.
@@ -4336,7 +5100,7 @@ var AssetManager = class {
4336
5100
  } else {
4337
5101
  gap = {
4338
5102
  type: "declared",
4339
- reason: `is declared but no library or source holds it \u2014 register the library that holds it with \`skaile library init <name> --path <dir>\`, or drop it with \`skaile remove ${d.ref.split("#")[0]}\``,
5103
+ reason: declaredNoHolderReason(d.ref),
4340
5104
  manualFix: true
4341
5105
  };
4342
5106
  }
@@ -4346,7 +5110,7 @@ var AssetManager = class {
4346
5110
  if (this.presetRecorded(want) || await this.info(d.ref) !== null) continue;
4347
5111
  gap = await this.declaredRefResolvable(d.ref) ? { type: "declared", reason: "is declared but not locked \u2014 run `skaile install`" } : {
4348
5112
  type: "declared",
4349
- reason: `is declared but no library or source holds it \u2014 register the library that holds it with \`skaile library init <name> --path <dir>\`, or drop it with \`skaile remove ${d.ref.split("#")[0]}\``,
5113
+ reason: declaredNoHolderReason(d.ref),
4350
5114
  manualFix: true
4351
5115
  };
4352
5116
  gaps.push({ ...gap, ref: d.ref, message: `${d.ref} ${gap.reason}` });
@@ -4407,7 +5171,8 @@ var AssetManager = class {
4407
5171
  const { repositories } = this.loadConfig();
4408
5172
  const provenanceIndex = await buildProvenanceIndex(this.buildClones(repositories), {
4409
5173
  projectDir: this.projectDir,
4410
- knownKinds: this.knownKinds
5174
+ knownKinds: this.knownKinds,
5175
+ onSourceError: this.reportSourceError
4411
5176
  });
4412
5177
  const lockKeys = Object.keys(lock.assets).map(parseCanonicalLockKey);
4413
5178
  const sourceCandidate = (ref) => {
@@ -4715,5 +5480,5 @@ var AssetManager = class {
4715
5480
  };
4716
5481
 
4717
5482
  export { AssetManager, FACTORY_SOURCE_URL, MissingManifestError, installAgentInstances, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, softDepName };
4718
- //# sourceMappingURL=chunk-2JMLDZLP.js.map
4719
- //# sourceMappingURL=chunk-2JMLDZLP.js.map
5483
+ //# sourceMappingURL=chunk-TVAR4JHA.js.map
5484
+ //# sourceMappingURL=chunk-TVAR4JHA.js.map