@skaile/workspaces 6.3.0 → 6.5.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 (399) hide show
  1. package/CHANGELOG.md +72 -1
  2. package/README.md +4 -0
  3. package/dist/ai-sdk.js +74 -32
  4. package/dist/ai-sdk.js.map +1 -1
  5. package/dist/app-sdk/index.js +1 -1
  6. package/dist/{asset-feeds-3YO2352Y.js → asset-feeds-4I6Z4OI3.js} +21 -21
  7. package/dist/{asset-feeds-3YO2352Y.js.map → asset-feeds-4I6Z4OI3.js.map} +1 -1
  8. package/dist/asset-manager/catalog-deployer.js +7 -7
  9. package/dist/asset-manager/index.js +13 -13
  10. package/dist/asset-manager/installer.js +9 -9
  11. package/dist/asset-manager/renderers.js +8 -8
  12. package/dist/asset-manager/src/index.d.ts +156 -27
  13. package/dist/asset-manager/src/index.d.ts.map +1 -1
  14. package/dist/asset-manager/src/install-instances.d.ts +2 -0
  15. package/dist/asset-manager/src/install-instances.d.ts.map +1 -1
  16. package/dist/asset-manager/src/installer.d.ts +6 -0
  17. package/dist/asset-manager/src/installer.d.ts.map +1 -1
  18. package/dist/bridge/harnesses/claude-sdk.js +23 -18
  19. package/dist/bridge/harnesses/claude-sdk.js.map +1 -1
  20. package/dist/bridge/harnesses/codex.js +36 -34
  21. package/dist/bridge/harnesses/codex.js.map +1 -1
  22. package/dist/bridge/harnesses/continue.js +11 -12
  23. package/dist/bridge/harnesses/continue.js.map +1 -1
  24. package/dist/bridge/harnesses/echo.js +4 -5
  25. package/dist/bridge/harnesses/echo.js.map +1 -1
  26. package/dist/bridge/harnesses/gemini.js +8 -9
  27. package/dist/bridge/harnesses/goose.js +11 -12
  28. package/dist/bridge/harnesses/goose.js.map +1 -1
  29. package/dist/bridge/harnesses/omp.js +16 -11
  30. package/dist/bridge/harnesses/omp.js.map +1 -1
  31. package/dist/bridge/harnesses/opencode.js +11 -12
  32. package/dist/bridge/harnesses/opencode.js.map +1 -1
  33. package/dist/bridge/harnesses/qwen.js +8 -9
  34. package/dist/bridge/harnesses/qwen.js.map +1 -1
  35. package/dist/bridge/index.js +19 -20
  36. package/dist/bridge/src/ai-sdk.d.ts +5 -2
  37. package/dist/bridge/src/ai-sdk.d.ts.map +1 -1
  38. package/dist/bridge/src/codex/login-fallback.d.ts +39 -0
  39. package/dist/bridge/src/codex/login-fallback.d.ts.map +1 -0
  40. package/dist/bridge/src/error-classifier.d.ts +6 -0
  41. package/dist/bridge/src/error-classifier.d.ts.map +1 -1
  42. package/dist/bridge/src/harness-model-check.d.ts +11 -7
  43. package/dist/bridge/src/harness-model-check.d.ts.map +1 -1
  44. package/dist/bridge/src/harnesses/claude-sdk.d.ts.map +1 -1
  45. package/dist/bridge/src/harnesses/codex.d.ts +0 -6
  46. package/dist/bridge/src/harnesses/codex.d.ts.map +1 -1
  47. package/dist/bridge/src/harnesses/omp.d.ts.map +1 -1
  48. package/dist/bridge/src/index.d.ts +1 -0
  49. package/dist/bridge/src/index.d.ts.map +1 -1
  50. package/dist/{chunk-XEVFSDIF.js → chunk-22ZX33H6.js} +6 -6
  51. package/dist/{chunk-XEVFSDIF.js.map → chunk-22ZX33H6.js.map} +1 -1
  52. package/dist/{chunk-KPKGWXUJ.js → chunk-23SG4JRV.js} +4 -4
  53. package/dist/{chunk-KPKGWXUJ.js.map → chunk-23SG4JRV.js.map} +1 -1
  54. package/dist/{chunk-MYXFR27L.js → chunk-2HS357RA.js} +77 -37
  55. package/dist/chunk-2HS357RA.js.map +1 -0
  56. package/dist/{chunk-7TSNR5H5.js → chunk-2IAWVUW3.js} +4 -4
  57. package/dist/{chunk-7TSNR5H5.js.map → chunk-2IAWVUW3.js.map} +1 -1
  58. package/dist/{chunk-6GWSDPBP.js → chunk-2M3VLKT2.js} +3 -3
  59. package/dist/{chunk-6GWSDPBP.js.map → chunk-2M3VLKT2.js.map} +1 -1
  60. package/dist/{chunk-PNUM7GUY.js → chunk-3DA74PLW.js} +3 -3
  61. package/dist/{chunk-PNUM7GUY.js.map → chunk-3DA74PLW.js.map} +1 -1
  62. package/dist/{chunk-7RH6E332.js → chunk-3KSLUVKB.js} +7 -7
  63. package/dist/{chunk-7RH6E332.js.map → chunk-3KSLUVKB.js.map} +1 -1
  64. package/dist/{chunk-6OHZUUR7.js → chunk-5DE3DGVR.js} +11 -5
  65. package/dist/chunk-5DE3DGVR.js.map +1 -0
  66. package/dist/{chunk-32FYVSSW.js → chunk-6EKQENAP.js} +6 -6
  67. package/dist/{chunk-32FYVSSW.js.map → chunk-6EKQENAP.js.map} +1 -1
  68. package/dist/{chunk-QK7AG5AV.js → chunk-6Y3SC7O4.js} +4 -4
  69. package/dist/{chunk-QK7AG5AV.js.map → chunk-6Y3SC7O4.js.map} +1 -1
  70. package/dist/{chunk-ROVZCGTN.js → chunk-7EMS2GZO.js} +40 -37
  71. package/dist/chunk-7EMS2GZO.js.map +1 -0
  72. package/dist/{chunk-COUJOTAJ.js → chunk-7H4GTP7V.js} +3 -3
  73. package/dist/{chunk-COUJOTAJ.js.map → chunk-7H4GTP7V.js.map} +1 -1
  74. package/dist/{chunk-VUWRGMT6.js → chunk-AFTZNR2W.js} +3 -3
  75. package/dist/{chunk-VUWRGMT6.js.map → chunk-AFTZNR2W.js.map} +1 -1
  76. package/dist/{chunk-JMIKGAUA.js → chunk-AVFRFGM2.js} +16 -6
  77. package/dist/chunk-AVFRFGM2.js.map +1 -0
  78. package/dist/{chunk-7MV3NHPT.js → chunk-B436SG5P.js} +42 -25
  79. package/dist/chunk-B436SG5P.js.map +1 -0
  80. package/dist/{chunk-ZEH4WFEG.js → chunk-B4BLJUYN.js} +69 -26
  81. package/dist/chunk-B4BLJUYN.js.map +1 -0
  82. package/dist/{chunk-LEJQAX7G.js → chunk-DNXPXJ3X.js} +82 -50
  83. package/dist/chunk-DNXPXJ3X.js.map +1 -0
  84. package/dist/{chunk-HDVLBEEX.js → chunk-E6UWI3TP.js} +3 -3
  85. package/dist/{chunk-HDVLBEEX.js.map → chunk-E6UWI3TP.js.map} +1 -1
  86. package/dist/{chunk-2TFKXSB5.js → chunk-EN5REMWV.js} +4 -4
  87. package/dist/{chunk-2TFKXSB5.js.map → chunk-EN5REMWV.js.map} +1 -1
  88. package/dist/{chunk-QFFMFZIR.js → chunk-EO7KGX6P.js} +4 -4
  89. package/dist/{chunk-QFFMFZIR.js.map → chunk-EO7KGX6P.js.map} +1 -1
  90. package/dist/{chunk-KIPCBIFJ.js → chunk-ETF6Q3R3.js} +23 -80
  91. package/dist/chunk-ETF6Q3R3.js.map +1 -0
  92. package/dist/{chunk-6WH2WE4B.js → chunk-G4NCSYWR.js} +9 -8
  93. package/dist/chunk-G4NCSYWR.js.map +1 -0
  94. package/dist/{chunk-JCPD4QRM.js → chunk-G5SYFFMD.js} +2 -2
  95. package/dist/{chunk-JCPD4QRM.js.map → chunk-G5SYFFMD.js.map} +1 -1
  96. package/dist/{chunk-Q7CU5GQO.js → chunk-HPA3RP5E.js} +4 -4
  97. package/dist/{chunk-Q7CU5GQO.js.map → chunk-HPA3RP5E.js.map} +1 -1
  98. package/dist/{chunk-KGHYXXXV.js → chunk-IS3NIZBX.js} +588 -133
  99. package/dist/chunk-IS3NIZBX.js.map +1 -0
  100. package/dist/{chunk-XBQTXGY4.js → chunk-IWKKTCXY.js} +98 -17
  101. package/dist/chunk-IWKKTCXY.js.map +1 -0
  102. package/dist/{chunk-HH2H2ZV7.js → chunk-J5JWLKLB.js} +35 -20
  103. package/dist/chunk-J5JWLKLB.js.map +1 -0
  104. package/dist/chunk-J6YPKMMY.js +83 -0
  105. package/dist/chunk-J6YPKMMY.js.map +1 -0
  106. package/dist/{chunk-TGV2EWGL.js → chunk-K4DHYTUM.js} +4 -4
  107. package/dist/{chunk-TGV2EWGL.js.map → chunk-K4DHYTUM.js.map} +1 -1
  108. package/dist/{chunk-72NT2TJU.js → chunk-KA2GLAPS.js} +7 -8
  109. package/dist/chunk-KA2GLAPS.js.map +1 -0
  110. package/dist/{chunk-VUJXZDCY.js → chunk-KSH5CFQS.js} +3 -3
  111. package/dist/{chunk-VUJXZDCY.js.map → chunk-KSH5CFQS.js.map} +1 -1
  112. package/dist/{chunk-BIRW3HOG.js → chunk-KSUT5KAE.js} +3 -3
  113. package/dist/{chunk-BIRW3HOG.js.map → chunk-KSUT5KAE.js.map} +1 -1
  114. package/dist/{chunk-AMJ6KEV7.js → chunk-KUADKMNX.js} +4 -4
  115. package/dist/{chunk-AMJ6KEV7.js.map → chunk-KUADKMNX.js.map} +1 -1
  116. package/dist/{chunk-TSTRH4SM.js → chunk-KYNCY3F3.js} +3 -3
  117. package/dist/{chunk-TSTRH4SM.js.map → chunk-KYNCY3F3.js.map} +1 -1
  118. package/dist/{chunk-V5BPDB4J.js → chunk-LE6QS4PG.js} +3 -3
  119. package/dist/{chunk-V5BPDB4J.js.map → chunk-LE6QS4PG.js.map} +1 -1
  120. package/dist/{chunk-6UP2EDFN.js → chunk-NSIOBKDX.js} +313 -4
  121. package/dist/chunk-NSIOBKDX.js.map +1 -0
  122. package/dist/{chunk-MD3FUZS3.js → chunk-OBZ5WQ32.js} +3 -3
  123. package/dist/{chunk-MD3FUZS3.js.map → chunk-OBZ5WQ32.js.map} +1 -1
  124. package/dist/{chunk-ELSGFI3I.js → chunk-OLHHN3NO.js} +31 -7
  125. package/dist/chunk-OLHHN3NO.js.map +1 -0
  126. package/dist/{chunk-JGH7NB47.js → chunk-S7DVT2BV.js} +4 -4
  127. package/dist/{chunk-JGH7NB47.js.map → chunk-S7DVT2BV.js.map} +1 -1
  128. package/dist/{chunk-PHAMYCZH.js → chunk-SN663FCA.js} +5 -5
  129. package/dist/{chunk-PHAMYCZH.js.map → chunk-SN663FCA.js.map} +1 -1
  130. package/dist/{serve-preflight-H2AYMGJ6.js → chunk-SRTWKZP2.js} +30 -61
  131. package/dist/chunk-SRTWKZP2.js.map +1 -0
  132. package/dist/{chunk-H622YULS.js → chunk-SZTHSPRC.js} +3 -4
  133. package/dist/chunk-SZTHSPRC.js.map +1 -0
  134. package/dist/{chunk-LD6O6QKU.js → chunk-T7HGEMNB.js} +4 -4
  135. package/dist/{chunk-LD6O6QKU.js.map → chunk-T7HGEMNB.js.map} +1 -1
  136. package/dist/{chunk-4QRJBY2P.js → chunk-TDPQOR3C.js} +2 -2
  137. package/dist/{chunk-4QRJBY2P.js.map → chunk-TDPQOR3C.js.map} +1 -1
  138. package/dist/{chunk-Y3I4KGLX.js → chunk-TGUMN5G7.js} +310 -175
  139. package/dist/chunk-TGUMN5G7.js.map +1 -0
  140. package/dist/{chunk-TO5SSMRI.js → chunk-THX5EE3K.js} +67 -19
  141. package/dist/chunk-THX5EE3K.js.map +1 -0
  142. package/dist/{chunk-QTLUZUF2.js → chunk-TNQLDEM6.js} +86 -34
  143. package/dist/chunk-TNQLDEM6.js.map +1 -0
  144. package/dist/{chunk-EQNIBBB6.js → chunk-TROEC2Z2.js} +34 -20
  145. package/dist/chunk-TROEC2Z2.js.map +1 -0
  146. package/dist/{chunk-E5NQRHEB.js → chunk-U6KRTCNA.js} +4 -4
  147. package/dist/{chunk-E5NQRHEB.js.map → chunk-U6KRTCNA.js.map} +1 -1
  148. package/dist/{chunk-J5JRGBKK.js → chunk-UMLCKKIA.js} +64 -32
  149. package/dist/chunk-UMLCKKIA.js.map +1 -0
  150. package/dist/{chunk-DU26AVK2.js → chunk-USVUS4DJ.js} +8 -8
  151. package/dist/{chunk-DU26AVK2.js.map → chunk-USVUS4DJ.js.map} +1 -1
  152. package/dist/{chunk-HGOJEVHY.js → chunk-UZET67SO.js} +4 -4
  153. package/dist/{chunk-HGOJEVHY.js.map → chunk-UZET67SO.js.map} +1 -1
  154. package/dist/{chunk-IZQOO6MN.js → chunk-V5KG6CCV.js} +2 -2
  155. package/dist/{chunk-IZQOO6MN.js.map → chunk-V5KG6CCV.js.map} +1 -1
  156. package/dist/{chunk-76FLBODK.js → chunk-VT6G5AJV.js} +10 -7
  157. package/dist/chunk-VT6G5AJV.js.map +1 -0
  158. package/dist/{chunk-URCJIV7T.js → chunk-W5KCAWGN.js} +3 -3
  159. package/dist/{chunk-URCJIV7T.js.map → chunk-W5KCAWGN.js.map} +1 -1
  160. package/dist/{chunk-YW4ASEUP.js → chunk-WFLQMAR5.js} +5 -5
  161. package/dist/{chunk-YW4ASEUP.js.map → chunk-WFLQMAR5.js.map} +1 -1
  162. package/dist/{chunk-RFQONXET.js → chunk-X44XKBZX.js} +3 -3
  163. package/dist/{chunk-RFQONXET.js.map → chunk-X44XKBZX.js.map} +1 -1
  164. package/dist/{chunk-BPTWWKGF.js → chunk-YBEJCQE5.js} +3 -3
  165. package/dist/{chunk-BPTWWKGF.js.map → chunk-YBEJCQE5.js.map} +1 -1
  166. package/dist/{chunk-PL52CI4U.js → chunk-YRC2EFOU.js} +4 -4
  167. package/dist/{chunk-PL52CI4U.js.map → chunk-YRC2EFOU.js.map} +1 -1
  168. package/dist/{chunk-7QG2WRSN.js → chunk-Z2X7UZEZ.js} +25 -8
  169. package/dist/chunk-Z2X7UZEZ.js.map +1 -0
  170. package/dist/{chunk-U3SXCVR7.js → chunk-ZUU2PLA3.js} +4 -4
  171. package/dist/{chunk-U3SXCVR7.js.map → chunk-ZUU2PLA3.js.map} +1 -1
  172. package/dist/{claude-code-EUKDGOOO.js → claude-code-JY6CYEG7.js} +4 -4
  173. package/dist/{claude-code-EUKDGOOO.js.map → claude-code-JY6CYEG7.js.map} +1 -1
  174. package/dist/cli/index.js +322 -162
  175. package/dist/cli/index.js.map +1 -1
  176. package/dist/cli/src/commands/cache.d.ts.map +1 -1
  177. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  178. package/dist/cli/src/commands/config.d.ts.map +1 -1
  179. package/dist/cli/src/commands/connector.d.ts.map +1 -1
  180. package/dist/cli/src/commands/deploy.d.ts.map +1 -1
  181. package/dist/cli/src/commands/harness.d.ts +5 -4
  182. package/dist/cli/src/commands/harness.d.ts.map +1 -1
  183. package/dist/cli/src/commands/integration.d.ts.map +1 -1
  184. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  185. package/dist/cli/src/commands/library-index.d.ts +6 -0
  186. package/dist/cli/src/commands/library-index.d.ts.map +1 -1
  187. package/dist/cli/src/commands/migrate-files.d.ts +3 -6
  188. package/dist/cli/src/commands/migrate-files.d.ts.map +1 -1
  189. package/dist/cli/src/commands/migrate-harness.d.ts +4 -3
  190. package/dist/cli/src/commands/migrate-harness.d.ts.map +1 -1
  191. package/dist/cli/src/commands/migrate.d.ts +2 -2
  192. package/dist/cli/src/commands/outdated.d.ts.map +1 -1
  193. package/dist/cli/src/commands/plugin-registry-cmd.d.ts.map +1 -1
  194. package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
  195. package/dist/cli/src/commands/project.d.ts +7 -1
  196. package/dist/cli/src/commands/project.d.ts.map +1 -1
  197. package/dist/cli/src/commands/run.d.ts.map +1 -1
  198. package/dist/cli/src/commands/serve-preflight.d.ts +21 -0
  199. package/dist/cli/src/commands/serve-preflight.d.ts.map +1 -1
  200. package/dist/cli/src/commands/serve.d.ts.map +1 -1
  201. package/dist/cli/src/commands/source-manifest.d.ts.map +1 -1
  202. package/dist/cli/src/commands/source.d.ts +8 -0
  203. package/dist/cli/src/commands/source.d.ts.map +1 -1
  204. package/dist/cli/src/commands/update.d.ts.map +1 -1
  205. package/dist/cli/src/commands/validate-instances.d.ts +6 -0
  206. package/dist/cli/src/commands/validate-instances.d.ts.map +1 -1
  207. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  208. package/dist/cli/src/commands/verify.d.ts.map +1 -1
  209. package/dist/cli/src/config-checks.d.ts +8 -0
  210. package/dist/cli/src/config-checks.d.ts.map +1 -1
  211. package/dist/cli/src/ensure-sources.d.ts.map +1 -1
  212. package/dist/cli/src/helpers.d.ts +13 -4
  213. package/dist/cli/src/helpers.d.ts.map +1 -1
  214. package/dist/cli/src/plugin-store/index.d.ts +1 -1
  215. package/dist/cli/src/plugin-store/index.d.ts.map +1 -1
  216. package/dist/cli/src/plugin-store/reconcile.d.ts.map +1 -1
  217. package/dist/cli/src/plugin-store/spec.d.ts +20 -14
  218. package/dist/cli/src/plugin-store/spec.d.ts.map +1 -1
  219. package/dist/cli/src/setup.d.ts.map +1 -1
  220. package/dist/cli/src/skill-checks.d.ts +2 -0
  221. package/dist/cli/src/skill-checks.d.ts.map +1 -1
  222. package/dist/connectors/config.js +5 -5
  223. package/dist/connectors/index.js +23 -24
  224. package/dist/connectors/src/index.d.ts +2 -2
  225. package/dist/connectors/src/index.d.ts.map +1 -1
  226. package/dist/connectors/src/managed-gitconfig.d.ts +22 -0
  227. package/dist/connectors/src/managed-gitconfig.d.ts.map +1 -1
  228. package/dist/connectors/src/npm-installer.d.ts +11 -3
  229. package/dist/connectors/src/npm-installer.d.ts.map +1 -1
  230. package/dist/connectors-VLFOQNS6.js +49 -0
  231. package/dist/{connectors-XYPE2DCQ.js.map → connectors-VLFOQNS6.js.map} +1 -1
  232. package/dist/connectors-shared/index.js +2 -2
  233. package/dist/core/index.js +6 -6
  234. package/dist/core/manifest.js +2 -2
  235. package/dist/core/models.js +1 -1
  236. package/dist/core/publish-manifest.js +3 -3
  237. package/dist/core/ref.js +1 -1
  238. package/dist/core/runtime-assets.js +5 -5
  239. package/dist/core/src/agent-instances.d.ts +6 -0
  240. package/dist/core/src/agent-instances.d.ts.map +1 -1
  241. package/dist/core/src/config-key-diagnostics.d.ts +2 -1
  242. package/dist/core/src/config-key-diagnostics.d.ts.map +1 -1
  243. package/dist/core/src/index.d.ts +5 -5
  244. package/dist/core/src/index.d.ts.map +1 -1
  245. package/dist/core/src/lock.d.ts +20 -1
  246. package/dist/core/src/lock.d.ts.map +1 -1
  247. package/dist/core/src/manifest-yaml-editor.d.ts +4 -1
  248. package/dist/core/src/manifest-yaml-editor.d.ts.map +1 -1
  249. package/dist/core/src/manifest.d.ts +9 -3
  250. package/dist/core/src/manifest.d.ts.map +1 -1
  251. package/dist/core/src/models.d.ts +10 -0
  252. package/dist/core/src/models.d.ts.map +1 -1
  253. package/dist/core/src/repo-manager.d.ts +11 -0
  254. package/dist/core/src/repo-manager.d.ts.map +1 -1
  255. package/dist/core/src/runtime-assets.d.ts +12 -1
  256. package/dist/core/src/runtime-assets.d.ts.map +1 -1
  257. package/dist/core/src/walker.d.ts +3 -0
  258. package/dist/core/src/walker.d.ts.map +1 -1
  259. package/dist/core/src/workspace-config.d.ts +19 -2
  260. package/dist/core/src/workspace-config.d.ts.map +1 -1
  261. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  262. package/dist/core/workspace-config.js +4 -4
  263. package/dist/deploy/index.js +6 -6
  264. package/dist/discovery/index.js +5 -5
  265. package/dist/{ensure-sources-IN3VKHUA.js → ensure-sources-6O54DUQB.js} +31 -31
  266. package/dist/ensure-sources-6O54DUQB.js.map +1 -0
  267. package/dist/factory-assets/connectors/box.js +23 -24
  268. package/dist/factory-assets/connectors/deploy.js +23 -24
  269. package/dist/factory-assets/connectors/devserver.js +23 -24
  270. package/dist/factory-assets/connectors/flow/adapter.js +23 -24
  271. package/dist/factory-assets/connectors/flow/engine.js +7 -7
  272. package/dist/factory-assets/connectors/flow/run-flow.js +25 -26
  273. package/dist/factory-assets/connectors/flow.js +23 -24
  274. package/dist/factory-assets/connectors/git.js +23 -24
  275. package/dist/factory-assets/connectors/gmail.js +23 -24
  276. package/dist/factory-assets/connectors/googledrive.js +23 -24
  277. package/dist/factory-assets/connectors/local.js +23 -24
  278. package/dist/factory-assets/connectors/mattermost.js +23 -24
  279. package/dist/factory-assets/connectors/memory.js +23 -24
  280. package/dist/factory-assets/connectors/minio.js +23 -24
  281. package/dist/factory-assets/connectors/postgres.js +23 -24
  282. package/dist/factory-assets/connectors/s3.js +23 -24
  283. package/dist/factory-assets/connectors/sharepoint.js +23 -24
  284. package/dist/factory-assets/connectors/sqlite.js +23 -24
  285. package/dist/factory-assets/connectors/static-server.js +23 -24
  286. package/dist/factory-assets/connectors/tunnel.js +23 -24
  287. package/dist/factory-assets/connectors/webdav.js +23 -24
  288. package/dist/factory-assets/connectors/xstate-store.js +23 -24
  289. package/dist/factory-assets/connectors/xstate.js +23 -24
  290. package/dist/factory-assets/skills/authoring-skaile-assets/SKILL.md +2 -1
  291. package/dist/{flows-QAO63TDV.js → flows-CIUPK7SB.js} +11 -11
  292. package/dist/{flows-QAO63TDV.js.map → flows-CIUPK7SB.js.map} +1 -1
  293. package/dist/{helpers-HDVGCF3M.js → helpers-YHDQHQEQ.js} +28 -29
  294. package/dist/{helpers-HDVGCF3M.js.map → helpers-YHDQHQEQ.js.map} +1 -1
  295. package/dist/library/index.js +10 -10
  296. package/dist/library/install.js +4 -4
  297. package/dist/library/src/manifest-writer.d.ts +9 -0
  298. package/dist/library/src/manifest-writer.d.ts.map +1 -1
  299. package/dist/library/src/remote/remote-catalog-source.d.ts +9 -0
  300. package/dist/library/src/remote/remote-catalog-source.d.ts.map +1 -1
  301. package/dist/library/src/remote/rest-catalog-source.d.ts +1 -0
  302. package/dist/library/src/remote/rest-catalog-source.d.ts.map +1 -1
  303. package/dist/{library-publish-62OWCNB5.js → library-publish-MUGUST43.js} +53 -53
  304. package/dist/{library-publish-62OWCNB5.js.map → library-publish-MUGUST43.js.map} +1 -1
  305. package/dist/model-endpoint/index.js +9 -10
  306. package/dist/open-library-BVQCB37E.js +57 -0
  307. package/dist/{open-library-TZ7OSDEJ.js.map → open-library-BVQCB37E.js.map} +1 -1
  308. package/dist/{open-registry-RLZKTJG3.js → open-registry-JCSIAKEC.js} +14 -14
  309. package/dist/{open-registry-RLZKTJG3.js.map → open-registry-JCSIAKEC.js.map} +1 -1
  310. package/dist/{paths-D6LBQETQ.js → paths-XLKCAFRZ.js} +9 -9
  311. package/dist/{paths-D6LBQETQ.js.map → paths-XLKCAFRZ.js.map} +1 -1
  312. package/dist/{plugin-store-A5J4TLCH.js → plugin-store-NGRUTCJI.js} +9 -9
  313. package/dist/{plugin-store-A5J4TLCH.js.map → plugin-store-NGRUTCJI.js.map} +1 -1
  314. package/dist/project-BGYMLAS6.js +88 -0
  315. package/dist/{project-4AVRES3M.js.map → project-BGYMLAS6.js.map} +1 -1
  316. package/dist/resolver/index.js +7 -7
  317. package/dist/runner/index.js +38 -39
  318. package/dist/runner/src/capability-roundtrip.d.ts +4 -1
  319. package/dist/runner/src/capability-roundtrip.d.ts.map +1 -1
  320. package/dist/runner/src/composition/resolve-instance.d.ts.map +1 -1
  321. package/dist/runner/src/composition/resolve.d.ts +6 -0
  322. package/dist/runner/src/composition/resolve.d.ts.map +1 -1
  323. package/dist/runner/src/credential-notice.d.ts +3 -2
  324. package/dist/runner/src/credential-notice.d.ts.map +1 -1
  325. package/dist/runner/src/resources.d.ts.map +1 -1
  326. package/dist/runner/src/serve.d.ts.map +1 -1
  327. package/dist/sdk/asset-manager.js +13 -13
  328. package/dist/sdk/bridge.js +19 -20
  329. package/dist/sdk/core.js +6 -6
  330. package/dist/sdk/flow.js +7 -7
  331. package/dist/sdk/index.js +38 -39
  332. package/dist/sdk/index.js.map +1 -1
  333. package/dist/sdk/resolver.js +7 -7
  334. package/dist/sdk/runner.js +38 -39
  335. package/dist/sdk/session.js +2 -2
  336. package/dist/sdk/transport/ws/server.js +7 -7
  337. package/dist/sdk/transport/ws.js +7 -7
  338. package/dist/sdk/transport.js +7 -7
  339. package/dist/sdk/types.js +1 -1
  340. package/dist/serve-preflight-GQY5DZX6.js +56 -0
  341. package/dist/serve-preflight-GQY5DZX6.js.map +1 -0
  342. package/dist/session/index.js +2 -2
  343. package/dist/{setup-NSPQD4V4.js → setup-2NWSKSCO.js} +34 -32
  344. package/dist/setup-2NWSKSCO.js.map +1 -0
  345. package/dist/{skills-B533SRVZ.js → skills-PXZOL2FZ.js} +12 -12
  346. package/dist/{skills-B533SRVZ.js.map → skills-PXZOL2FZ.js.map} +1 -1
  347. package/dist/source-XA3L4H6I.js +86 -0
  348. package/dist/{source-VTECABUO.js.map → source-XA3L4H6I.js.map} +1 -1
  349. package/dist/{source-manifest-publish-YORI42MN.js → source-manifest-publish-QEGMTS33.js} +50 -51
  350. package/dist/source-manifest-publish-QEGMTS33.js.map +1 -0
  351. package/dist/transport/index.js +7 -7
  352. package/dist/transport/ws/server.js +7 -7
  353. package/dist/transport/ws.js +7 -7
  354. package/dist/tui/index.js +38 -39
  355. package/dist/tui/index.js.map +1 -1
  356. package/dist/types/index.js +1 -1
  357. package/dist/types/manifests.js +1 -1
  358. package/dist/types/src/capabilities.d.ts +9 -1
  359. package/dist/types/src/capabilities.d.ts.map +1 -1
  360. package/dist/types/src/manifests/agent.d.ts +2 -2
  361. package/dist/types/src/manifests/agent.d.ts.map +1 -1
  362. package/dist/types/src/version.d.ts +8 -8
  363. package/dist/workspace-plugin/adapters/mcp.js +30 -31
  364. package/dist/workspace-plugin/adapters/omp.js +14 -14
  365. package/dist/workspace-plugin/index.js +1 -1
  366. package/package.json +1 -1
  367. package/dist/chunk-6OHZUUR7.js.map +0 -1
  368. package/dist/chunk-6UP2EDFN.js.map +0 -1
  369. package/dist/chunk-6WH2WE4B.js.map +0 -1
  370. package/dist/chunk-72NT2TJU.js.map +0 -1
  371. package/dist/chunk-76FLBODK.js.map +0 -1
  372. package/dist/chunk-7MV3NHPT.js.map +0 -1
  373. package/dist/chunk-7QG2WRSN.js.map +0 -1
  374. package/dist/chunk-ELSGFI3I.js.map +0 -1
  375. package/dist/chunk-EQNIBBB6.js.map +0 -1
  376. package/dist/chunk-H622YULS.js.map +0 -1
  377. package/dist/chunk-HH2H2ZV7.js.map +0 -1
  378. package/dist/chunk-J5JRGBKK.js.map +0 -1
  379. package/dist/chunk-JMIKGAUA.js.map +0 -1
  380. package/dist/chunk-KGHYXXXV.js.map +0 -1
  381. package/dist/chunk-KIPCBIFJ.js.map +0 -1
  382. package/dist/chunk-LEJQAX7G.js.map +0 -1
  383. package/dist/chunk-MYXFR27L.js.map +0 -1
  384. package/dist/chunk-QTLUZUF2.js.map +0 -1
  385. package/dist/chunk-ROVZCGTN.js.map +0 -1
  386. package/dist/chunk-S6KZHPR4.js +0 -315
  387. package/dist/chunk-S6KZHPR4.js.map +0 -1
  388. package/dist/chunk-TO5SSMRI.js.map +0 -1
  389. package/dist/chunk-XBQTXGY4.js.map +0 -1
  390. package/dist/chunk-Y3I4KGLX.js.map +0 -1
  391. package/dist/chunk-ZEH4WFEG.js.map +0 -1
  392. package/dist/connectors-XYPE2DCQ.js +0 -50
  393. package/dist/ensure-sources-IN3VKHUA.js.map +0 -1
  394. package/dist/open-library-TZ7OSDEJ.js +0 -58
  395. package/dist/project-4AVRES3M.js +0 -88
  396. package/dist/serve-preflight-H2AYMGJ6.js.map +0 -1
  397. package/dist/setup-NSPQD4V4.js.map +0 -1
  398. package/dist/source-VTECABUO.js +0 -87
  399. package/dist/source-manifest-publish-YORI42MN.js.map +0 -1
@@ -1,24 +1,24 @@
1
- import { unregisteredLibraryNames, renamedLibraryName, resolveConfig, RemoteCatalogSource, OfflineError, describeCatalogError, CatalogHttpError, scanUnlistedAssets, missingRootAssets, storeManifestFile, missingLibraryDirMessage, lastLibraryName, filesEntryPaths } from './chunk-Y3I4KGLX.js';
2
- import { SEMVER_RE, invalidSemverMessage, flowFileNameError, computeDeterministicHash, readManifestSourceConfig, discoverAssetsInTree, isManifestMode } from './chunk-XEVFSDIF.js';
1
+ import { unregisteredLibraryNames, renamedLibraryName, resolveConfig, RemoteCatalogSource, OfflineError, describeCatalogError, CatalogHttpError, scanUnlistedAssets, missingRootAssets, storeManifestFile, missingLibraryDirMessage, lastLibraryName, filesEntryPaths } from './chunk-TGUMN5G7.js';
2
+ import { SEMVER_RE, invalidSemverMessage, flowFileNameError, computeDeterministicHash, readManifestSourceConfig, discoverAssetsInTree, isManifestMode } from './chunk-22ZX33H6.js';
3
3
  import { parseFlowDefinition } from './chunk-JRN5QF4C.js';
4
- import { installTargetOf, readKeptLegacyCopies, writeKeptLegacyCopies, canonicalEntryRef, diffDeployedAgent, deployedAssetDrift, assertNoLibrarySymlinks, deployAll, removeAsset, composedDriftRefs, sourceComposedFiles, cachedComposedFiles, createScaffold, installAgent } from './chunk-MYXFR27L.js';
4
+ import { installTargetOf, readKeptLegacyCopies, writeKeptLegacyCopies, canonicalEntryRef, diffDeployedAgent, deployedAssetDrift, entriesNeedingDeploy, assertNoLibrarySymlinks, deployAll, removeAsset, composedDriftRefs, sourceComposedFiles, cachedComposedFiles, createScaffold, installAgent } from './chunk-2HS357RA.js';
5
5
  import { appendHistory, getRecentHistory, clearHistory } from './chunk-KTBKW2FI.js';
6
6
  import { commitChanges } from './chunk-UQ6LFBPZ.js';
7
- import { harnessTargetSupportsAgents, CODEX_IDENTITY_DIR, agentRenderFiles } from './chunk-LD6O6QKU.js';
8
- import { isScaffoldBody, isScaffoldDescription } from './chunk-76FLBODK.js';
9
- import { resolveConfiguredHarnessName, FACTORY_SOURCE_URL, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, writeLock, computeHash, buildLockFile, readLock, assertLockHashes, verifyLock, mergeLockFile, resyncLockSources, extractForPatch, generatePatch, savePatch, applyPatch, LOCK_SCHEMA_VERSION } from './chunk-J5JRGBKK.js';
10
- import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-6WH2WE4B.js';
7
+ import { harnessTargetSupportsAgents, CODEX_IDENTITY_DIR, agentRenderFiles } from './chunk-T7HGEMNB.js';
8
+ import { isScaffoldBody, isScaffoldDescription } from './chunk-VT6G5AJV.js';
9
+ import { resolveConfiguredHarnessName, FACTORY_SOURCE_URL, isFactoryMemorySource, GLOBAL_INSTALL_DIR_NAME, resolveLibraryDir, buildLibraryLockEntries, writeLock, computeHash, buildLockFile, readLock, LockParseError, assertLockHashes, verifyLock, mergeLockFile, resyncLockSources, extractForPatch, generatePatch, savePatch, applyPatch, LOCK_SCHEMA_VERSION } from './chunk-UMLCKKIA.js';
10
+ import { resolveRuntimeAssets, factoryAssetRoots } from './chunk-G4NCSYWR.js';
11
11
  import { deployedBase, isDeployed, deployedPaths, deployedDir } from './chunk-5KRMNUL4.js';
12
- import { resolveSkWorkspaceConfig, effectiveAgentInstances, mcpDeclarationFromMdContent, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, buildProvenanceIndex, coerceVersion, sourceShortName, readLinks, isCommitOnRemote, findWorkspaceRoot, resolveAll, agentSourceCacheDir, ensureRepo, resolveAgentDefinitionDir, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, resolveAsset, MAX_PRESET_DEPTH, PresetDepthError, DEFAULT_AGENT_INSTANCE_ID } from './chunk-XBQTXGY4.js';
12
+ import { resolveSkWorkspaceConfig, effectiveAgentInstances, mcpDeclarationFromMdContent, loadMcpServerDeclarations, resolveGlobalHarnesses, resolveGlobalSkWorkspaceConfig, getGlobalCacheDir, sourceCacheKey, getRepoCommit, matchPin, buildProvenanceIndex, coerceVersion, sourceShortName, readLinks, isCommitOnRemote, findWorkspaceRoot, resolveSourcePublisher, resolveAll, agentSourceCacheDir, ensureRepo, resolveAgentDefinitionDir, scanRepo, SKAILE_YAML_DEFAULT, checkRepoStatus, resolveAsset, DEFAULT_AGENT_INSTANCE_ID, MAX_PRESET_DEPTH, PresetDepthError } from './chunk-IWKKTCXY.js';
13
13
  import { globalSkaileDir, globalCacheRoot, migrateLegacyDir } from './chunk-L5VSIPQD.js';
14
14
  import { HARNESS_TARGETS, SUPPORTED_HARNESSES } from './chunk-VTETQCR6.js';
15
- import { listAssetFiles, loadSkPublishManifest, decodeSkaileManifest, isNonAssetDir, isNonAssetFile } from './chunk-KPKGWXUJ.js';
16
- import { scanDirectory, parseFrontmatter, parseRequires, dedupeRequirements, readAssetBody } from './chunk-JMIKGAUA.js';
17
- import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr, parseDep } from './chunk-IZQOO6MN.js';
15
+ import { listAssetFiles, loadSkPublishManifest, decodeSkaileManifest, isNonAssetDir, isNonAssetFile } from './chunk-23SG4JRV.js';
16
+ import { scanDirectory, parseFrontmatter, parseRequires, dedupeRequirements, readAssetBody } from './chunk-AVFRFGM2.js';
17
+ import { parseAssetRef, parseAssetRefLoose, assetRefToStr, slugifyAssetName, depToStr, parseDep } from './chunk-V5KG6CCV.js';
18
18
  import { spawnSync } from 'child_process';
19
- import { existsSync, rmSync, readFileSync, writeFileSync, readdirSync, lstatSync, statSync, unlinkSync, mkdirSync } from 'fs';
19
+ import { existsSync, rmSync, readFileSync, writeFileSync, readdirSync, lstatSync, statSync, realpathSync, unlinkSync, mkdirSync } from 'fs';
20
20
  import { homedir } from 'os';
21
- import path, { resolve, dirname, sep, join, relative, posix } from 'path';
21
+ import path, { resolve, dirname, sep, join, relative, isAbsolute, posix } from 'path';
22
22
  import { gt } from 'semver';
23
23
  import { parse, stringify } from 'yaml';
24
24
  import { createHash } from 'crypto';
@@ -128,10 +128,18 @@ function removeAgentInstanceRenderings(opts) {
128
128
  }
129
129
  return removed;
130
130
  }
131
+ function readInstanceFile(abs) {
132
+ try {
133
+ return statSync(abs).isDirectory() ? "\0dir" : readFileSync(abs, "utf-8");
134
+ } catch {
135
+ return void 0;
136
+ }
137
+ }
131
138
  function installAgentInstances(opts) {
132
139
  const { projectDir, harness, log } = opts;
133
140
  const result = {
134
141
  instances: [],
142
+ changed: [],
135
143
  removed: [],
136
144
  warnings: [],
137
145
  errors: []
@@ -151,6 +159,9 @@ function installAgentInstances(opts) {
151
159
  if (previous[id]) owned[id] = previous[id];
152
160
  continue;
153
161
  }
162
+ const before = /* @__PURE__ */ new Map();
163
+ for (const rel of previous[id] ?? [])
164
+ before.set(rel, readInstanceFile(resolve(projectDir, rel)));
154
165
  const rendered = installAgent(
155
166
  srcDir,
156
167
  agentDeployDir,
@@ -169,6 +180,13 @@ function installAgentInstances(opts) {
169
180
  const files = [...new Set(rendered.created)];
170
181
  owned[id] = files;
171
182
  result.instances.push({ id, files });
183
+ const sameFiles = files.length === before.size && files.every((rel) => before.has(rel));
184
+ if (!sameFiles || files.some((rel) => {
185
+ const old = before.get(rel);
186
+ return old === void 0 || readInstanceFile(resolve(projectDir, rel)) !== old;
187
+ })) {
188
+ result.changed.push(id);
189
+ }
172
190
  }
173
191
  const claimed = new Set(Object.values(owned).flat());
174
192
  const root = resolve(projectDir);
@@ -803,6 +821,10 @@ function lockedAssetFiles(kind, files) {
803
821
  });
804
822
  }
805
823
  var GITHUB_REPO_URL = /github\.com[/:]/i;
824
+ function isBundledFactoryEntry(entry) {
825
+ if (isFactoryMemorySource(entry.source)) return true;
826
+ return entry.origin === void 0 && entry.source !== void 0 && normalizeRepoUrl(entry.source.url) === normalizeRepoUrl(FACTORY_SOURCE_URL);
827
+ }
806
828
  var GIT_URL = /^(?:[a-z][a-z0-9+.-]*:\/\/|[\w.-]+@[\w.-]+:)/i;
807
829
  function indexLockByKindName(lock) {
808
830
  const lockByKindName = /* @__PURE__ */ new Map();
@@ -1403,6 +1425,8 @@ var AssetManager = class {
1403
1425
  warnedStoreVersionLookups = /* @__PURE__ */ new Set();
1404
1426
  /** Stores a lookup could not reach (store URL -> one line why); `add` reports them instead of "not found". */
1405
1427
  storeFailures = /* @__PURE__ */ new Map();
1428
+ /** Source origins `outdated` could not fetch; read once through {@link takeUnreachableSources}. */
1429
+ unreachableSources = [];
1406
1430
  /** Store URLs a lookup failed on because the client is air-gapped (`cache_ttl: 0`), not because the store is down. */
1407
1431
  airGappedStores = /* @__PURE__ */ new Set();
1408
1432
  /** Sources a provenance walk had to skip (source URL → why); one warning per source. */
@@ -1504,13 +1528,38 @@ var AssetManager = class {
1504
1528
  });
1505
1529
  }
1506
1530
  /**
1507
- * A 5.12.0 global record has no `global_asset_harnesses:` key, yet `add --global --harness`
1508
- * wrote one copy only. On the first global `install`, an entry with exactly one existing copy
1509
- * among several default harnesses is that harness's subset (OQ6): record it, so the install
1510
- * does not fan the asset out to harnesses the user never asked for (p1442). A copy under the
1511
- * 5.12.0 omp folder (`~/.omp/skills`) counts as an omp copy. Nothing is inferred once the key
1512
- * exists (an entry without a row then means "every harness"), with one default harness, or
1513
- * with no copy yet. Global mode only; returns the rows written.
1531
+ * The harness a 5.12.0 `add --global --harness omp` left as the asset's only copy: the copy sits
1532
+ * under the 5.12.0 omp folder (`~/.omp/skills`) and no other default harness holds one. That
1533
+ * folder is the only real 5.12.0 evidence: a current `add --global` writes no row either, so a
1534
+ * lone copy elsewhere may be a copy the user deleted or a harness added later (p1442).
1535
+ */
1536
+ legacySingleCopyHarness(component, name, all) {
1537
+ if (!all.includes("omp") || legacyOmpPaths(component, name).length === 0) return [];
1538
+ const others = all.filter((h) => h !== "omp" && isDeployed(component, name, h, true));
1539
+ return others.length === 0 ? ["omp"] : [];
1540
+ }
1541
+ /**
1542
+ * Whether `~/.skaile/skaile.yaml` is a 5.12.0 record: it exists and has no
1543
+ * `global_asset_harnesses:` key yet, so {@link inferLegacyHarnessSubsets} has not run.
1544
+ */
1545
+ isLegacyHarnessRecord() {
1546
+ if (!this.global) return false;
1547
+ const configPath = join(this.stateRoot, "skaile.yaml");
1548
+ if (!existsSync(configPath)) return false;
1549
+ try {
1550
+ const raw = parse(readFileSync(configPath, "utf8"));
1551
+ return !!raw && typeof raw === "object" && !(ENTRY_HARNESSES_KEY in raw);
1552
+ } catch {
1553
+ return false;
1554
+ }
1555
+ }
1556
+ /**
1557
+ * A 5.12.0 global record has no `global_asset_harnesses:` key, yet `add --global --harness omp`
1558
+ * wrote one copy only, under `~/.omp/skills`. On the first global `install`, an entry whose only
1559
+ * copy sits there (see {@link legacySingleCopyHarness}) is omp's subset (OQ6): record it, so the
1560
+ * install does not fan the asset out to harnesses the user never asked for (p1442). Nothing is
1561
+ * inferred without that evidence, once the key exists (an entry without a row then means "every
1562
+ * harness"), or with one default harness. Global mode only; returns the rows written.
1514
1563
  */
1515
1564
  inferLegacyHarnessSubsets(entries) {
1516
1565
  if (!this.global) return [];
@@ -1529,9 +1578,7 @@ var AssetManager = class {
1529
1578
  for (const e of entries) {
1530
1579
  const component = this.installTarget(e.kind);
1531
1580
  if (!component || !e.name) continue;
1532
- const holding = all.filter(
1533
- (h) => isDeployed(component, e.name, h, true) || h === "omp" && legacyOmpPaths(component, e.name).length > 0
1534
- );
1581
+ const holding = this.legacySingleCopyHarness(component, e.name, all);
1535
1582
  if (holding.length !== 1) continue;
1536
1583
  const publisher = e.publisher?.replace(/^@/, "");
1537
1584
  const ref = `${e.kind}:${publisher ? `@${publisher}/` : ""}${e.name}`;
@@ -1702,6 +1749,13 @@ var AssetManager = class {
1702
1749
  takeLegacyCopiesRemoved() {
1703
1750
  return this.legacyCopiesRemoved.splice(0);
1704
1751
  }
1752
+ /**
1753
+ * The source origins the last {@link outdated} call could not fetch (name, URL, why). Their
1754
+ * behind count was not checked, so the CLI must not call the project up to date (p1820).
1755
+ */
1756
+ takeUnreachableSources() {
1757
+ return this.unreachableSources.splice(0);
1758
+ }
1705
1759
  /**
1706
1760
  * The harnesses `add` / `install` deploy into: `[harness]` for a project, the global
1707
1761
  * harness set (see {@link AssetManager.targetHarnesses}) for a global manager.
@@ -1920,6 +1974,7 @@ var AssetManager = class {
1920
1974
  for (const w of [...result.warnings, ...result.errors]) console.warn(`skaile: ${w}`);
1921
1975
  return {
1922
1976
  agentInstances: result.instances,
1977
+ agentInstancesChanged: result.changed,
1923
1978
  agentFilesRemoved: result.removed,
1924
1979
  ...result.errors.length > 0 ? { agentInstanceErrors: result.errors } : {},
1925
1980
  ...details.length > 0 ? { agentInstanceDetails: details } : {}
@@ -1950,11 +2005,7 @@ var AssetManager = class {
1950
2005
  };
1951
2006
  }
1952
2007
  const factory = resolveFactorySource();
1953
- const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
1954
- const alreadyDeclared = Object.values(repositories).some(
1955
- (d) => d.url !== void 0 && normalizeRepoUrl(d.url) === factoryUrl
1956
- );
1957
- if (factory && !alreadyDeclared && !repositories[factory.publisher]) {
2008
+ if (factory && !repositories[factory.publisher]) {
1958
2009
  repositories[factory.publisher] = {
1959
2010
  path: factory.dir,
1960
2011
  url: FACTORY_SOURCE_URL,
@@ -1969,7 +2020,7 @@ var AssetManager = class {
1969
2020
  for (const [slug, decl] of Object.entries(repositories)) {
1970
2021
  const localPath = this.resolveRepoDir(decl, slug);
1971
2022
  if (!localPath || !existsSync(localPath)) continue;
1972
- if (decl.url && normalizeRepoUrl(decl.url) !== normalizeRepoUrl(FACTORY_SOURCE_URL) && !existsSync(join(localPath, ".git"))) {
2023
+ if (decl.url && !decl.factory && !existsSync(join(localPath, ".git"))) {
1973
2024
  this.reportSourceError(
1974
2025
  { localPath, sourceUrl: decl.url, commit: "" },
1975
2026
  new Error(
@@ -2066,8 +2117,8 @@ var AssetManager = class {
2066
2117
  const { publisher, kind, name, version } = parseCanonicalLockKey(key);
2067
2118
  if (!publisher || !kind || !name) return null;
2068
2119
  const url = entry.source.url;
2069
- const factory = Object.values(repositories).find((d) => d.factory);
2070
- const declared = factory?.url !== void 0 && normalizeRepoUrl(url) === normalizeRepoUrl(factory.url) ? factory : repositories[sourceCacheKey(url)];
2120
+ const factory = isFactoryMemorySource(entry.source) ? Object.values(repositories).find((d) => d.factory) : void 0;
2121
+ const declared = factory ?? repositories[sourceCacheKey(url)];
2071
2122
  return {
2072
2123
  publisher,
2073
2124
  kind,
@@ -2378,7 +2429,6 @@ var AssetManager = class {
2378
2429
  knownKinds: this.knownKinds,
2379
2430
  onSourceError: this.reportSourceError
2380
2431
  });
2381
- const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
2382
2432
  const hasFactory = Object.values(repositories).some((d) => d.factory);
2383
2433
  const out = /* @__PURE__ */ new Map();
2384
2434
  const originsOf = (key) => {
@@ -2402,7 +2452,7 @@ var AssetManager = class {
2402
2452
  return a.sourceUrl.localeCompare(b.sourceUrl);
2403
2453
  });
2404
2454
  for (const c of held) {
2405
- const factory = hasFactory && normalizeRepoUrl(c.sourceUrl) === factoryUrl;
2455
+ const factory = hasFactory && isFactoryMemorySource({ url: c.sourceUrl, commit: c.commit });
2406
2456
  originsOf(key).push({
2407
2457
  kind: factory ? "factory" : "source",
2408
2458
  label: factory ? "factory" : sourceShortName(c.sourceUrl),
@@ -2826,7 +2876,8 @@ var AssetManager = class {
2826
2876
  });
2827
2877
  }
2828
2878
  /**
2829
- * Open the local index and snapshot its libraries + asset definitions.
2879
+ * Open the local index and snapshot its libraries + asset definitions, scoped to this
2880
+ * project's `sources:` ({@link scopeLibraryIndex}).
2830
2881
  * Best-effort: no index file (never indexed), or any open/guard failure,
2831
2882
  * yields `null` — a lock/outdated read must never create `index.db` or throw.
2832
2883
  */
@@ -2837,14 +2888,7 @@ var AssetManager = class {
2837
2888
  if (!existsSync(indexPath)) return null;
2838
2889
  const index = new LocalIndex({ indexPath });
2839
2890
  try {
2840
- const libraries = await index.listSources();
2841
- const defs = await index.listAssetDefs();
2842
- const hidden = this.undeclaredSourceClones(libraries);
2843
- if (hidden.size === 0) return { libraries, defs };
2844
- return {
2845
- libraries: libraries.filter((l) => !hidden.has(l.id)),
2846
- defs: defs.filter((d) => !d.libraryId || !hidden.has(d.libraryId))
2847
- };
2891
+ return this.scopeLibraryIndex(await index.listSources(), await index.listAssetDefs());
2848
2892
  } finally {
2849
2893
  index.close();
2850
2894
  }
@@ -2852,32 +2896,85 @@ var AssetManager = class {
2852
2896
  return null;
2853
2897
  }
2854
2898
  }
2899
+ /**
2900
+ * The `sources:` this project declares by clone key, or `undefined` outside a project (no
2901
+ * `skaile.yaml` in `projectDir` or a parent) or when the file does not parse. Outside a
2902
+ * project nothing declares sources, so the machine-wide search reads every clone.
2903
+ */
2904
+ declaredSourceEntries() {
2905
+ if (!this.global && !findWorkspaceRoot(this.projectDir)) return void 0;
2906
+ try {
2907
+ return new Map(this.loadConfig().sources.map((s) => [sourceCacheKey(s.url), s]));
2908
+ } catch {
2909
+ return void 0;
2910
+ }
2911
+ }
2912
+ /** The clone-cache key a source-clone reader row stands for, or `undefined` for any other library. */
2913
+ sourceCloneKey(l) {
2914
+ if (l.ownership !== "reader" || !("url" in l.backendConfig)) return void 0;
2915
+ const cloneRoots = [
2916
+ join(globalCacheRoot(), "sources"),
2917
+ join(globalSkaileDir(), "sources"),
2918
+ this.reposDir
2919
+ ].map((d) => resolve(d));
2920
+ const at = resolve(l.path);
2921
+ return cloneRoots.some((root) => at.startsWith(root + sep)) ? sourceCacheKey(l.backendConfig.url) : void 0;
2922
+ }
2855
2923
  /**
2856
2924
  * Ids of the registered libraries that are the clone of a source this project does not
2857
2925
  * declare. `source add` registers the clone as a machine-wide reader library so `search`
2858
2926
  * and the indexer can read it, but a `sources:` clone belongs to the projects that declare
2859
- * it (OQ12, p321): another project neither resolves it, nor ties with it by name, nor has
2927
+ * it (OQ4, p321): another project neither resolves it, nor ties with it by name, nor has
2860
2928
  * `update` re-pin its locked assets to it. A reader library the user registered by hand
2861
2929
  * (`library init --reader --git`) lives outside the sources cache and is never hidden.
2862
2930
  */
2863
2931
  undeclaredSourceClones(libraries) {
2864
2932
  const hidden = /* @__PURE__ */ new Set();
2865
- if (!this.global && !findWorkspaceRoot(this.projectDir)) return hidden;
2866
- let declared;
2867
- try {
2868
- declared = new Set(this.loadConfig().sources.map((s) => sourceCacheKey(s.url)));
2869
- } catch {
2870
- return hidden;
2871
- }
2872
- const cloneRoots = [join(globalCacheRoot(), "sources"), this.reposDir].map((d) => resolve(d));
2933
+ const declared = this.declaredSourceEntries();
2934
+ if (!declared) return hidden;
2873
2935
  for (const l of libraries) {
2874
- if (l.ownership !== "reader" || !("url" in l.backendConfig)) continue;
2875
- const at = resolve(l.path);
2876
- if (!cloneRoots.some((root) => at.startsWith(root + sep))) continue;
2877
- if (!declared.has(sourceCacheKey(l.backendConfig.url))) hidden.add(l.id);
2936
+ const key = this.sourceCloneKey(l);
2937
+ if (key !== void 0 && !declared.has(key)) hidden.add(l.id);
2878
2938
  }
2879
2939
  return hidden;
2880
2940
  }
2941
+ /**
2942
+ * The index as this project sees it: the source clones of other projects are dropped
2943
+ * ({@link undeclaredSourceClones}) and the assets of a declared source carry the publisher
2944
+ * this project's `sources:` entry names. The clone and its index rows are shared by every
2945
+ * project that declares the source, so a project that declares another `--publisher` is
2946
+ * re-labelled here, at read time, and never changes what the others see (p1822).
2947
+ */
2948
+ scopeLibraryIndex(libraries, defs) {
2949
+ const hidden = this.undeclaredSourceClones(libraries);
2950
+ const declared = this.declaredSourceEntries();
2951
+ const relabel = /* @__PURE__ */ new Map();
2952
+ for (const l of libraries) {
2953
+ const key = this.sourceCloneKey(l);
2954
+ const entry = key === void 0 ? void 0 : declared?.get(key);
2955
+ if (!entry?.publisher || hidden.has(l.id) || !("url" in l.backendConfig)) continue;
2956
+ const indexed = resolveSourcePublisher(l.path, l.backendConfig.publisher);
2957
+ const mine = resolveSourcePublisher(l.path, entry.publisher);
2958
+ if (indexed && mine && indexed !== mine) relabel.set(l.id, { from: indexed, to: mine });
2959
+ }
2960
+ if (hidden.size === 0 && relabel.size === 0) return { libraries, defs };
2961
+ const bare = (publisher) => publisher.replace(/^@/, "");
2962
+ return {
2963
+ libraries: libraries.filter((l) => !hidden.has(l.id)).map((l) => {
2964
+ const r = relabel.get(l.id);
2965
+ return r && "url" in l.backendConfig ? { ...l, backendConfig: { ...l.backendConfig, publisher: r.to } } : l;
2966
+ }),
2967
+ defs: defs.filter((d) => !d.libraryId || !hidden.has(d.libraryId)).map((d) => {
2968
+ const r = d.libraryId ? relabel.get(d.libraryId) : void 0;
2969
+ if (!r || bare(d.publisher) !== r.from) return d;
2970
+ return {
2971
+ ...d,
2972
+ publisher: d.publisher.startsWith("@") ? `@${r.to}` : r.to,
2973
+ id: d.id.replace(`@${r.from}/`, `@${r.to}/`)
2974
+ };
2975
+ })
2976
+ };
2977
+ }
2881
2978
  /**
2882
2979
  * Library-origin lock entries for the assets {@link resolveLibraryEntries}
2883
2980
  * matched. The locked version is the one actually deployed: the on-disk
@@ -2952,6 +3049,106 @@ var AssetManager = class {
2952
3049
  })
2953
3050
  );
2954
3051
  }
3052
+ /**
3053
+ * The entries a deploy would write into some target harness: those without a current deployed
3054
+ * copy. An intact copy stays as it is, so a library change that does not reach it (a symlink
3055
+ * added next to the files) stops no `install` or `update` (p1857).
3056
+ */
3057
+ needingDeploy(entries, link = false) {
3058
+ const targets = this.targetHarnesses();
3059
+ return entries.filter(
3060
+ (e) => targets.some((t) => entriesNeedingDeploy([e], this.deployOptsFor(t, link)).length > 0)
3061
+ );
3062
+ }
3063
+ /**
3064
+ * The files of a library asset that git has not committed (edited, new or staged only), as
3065
+ * paths relative to the library root. The asset's own folder, or its one manifest file when it
3066
+ * shares the folder with other assets. Empty when the asset is not in a git work tree.
3067
+ */
3068
+ uncommittedAssetFiles(entry, libPath) {
3069
+ const provider = this.registry.getProvider(entry.kind);
3070
+ if (!provider) return [];
3071
+ let scope;
3072
+ try {
3073
+ const manifest = statSync(entry.source).isDirectory() ? readdirSync(entry.source).find((f) => provider.matchesPath(f)) : entry.source;
3074
+ if (!manifest) return [];
3075
+ const manifestPath = isAbsolute(manifest) ? manifest : join(entry.source, manifest);
3076
+ const filter = provider.defaultFileFilter(manifestPath);
3077
+ const rootName = relative(dirname(filter.assetRoot), filter.assetRoot);
3078
+ scope = filter.files.length > 1 || rootName === entry.name ? filter.assetRoot : manifestPath;
3079
+ } catch {
3080
+ return [];
3081
+ }
3082
+ const cwd = statSync(scope).isDirectory() ? scope : dirname(scope);
3083
+ const top = spawnSync("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
3084
+ encoding: "utf8"
3085
+ });
3086
+ if (top.status !== 0) return [];
3087
+ const status = spawnSync(
3088
+ "git",
3089
+ ["-C", cwd, "status", "--porcelain", "--untracked-files=all", "--", scope],
3090
+ { encoding: "utf8" }
3091
+ );
3092
+ if (status.status !== 0) return [];
3093
+ const root = top.stdout.trim();
3094
+ return status.stdout.split("\n").filter((l) => l.trim() !== "").map(
3095
+ (l) => relative(realpathSync(libPath), join(root, l.slice(3).trim().replace(/^"|"$/g, "")))
3096
+ );
3097
+ }
3098
+ /**
3099
+ * Warn when a library with a git remote has uncommitted changes in the folders of the assets
3100
+ * `add` or `install` lock: the lock records the bytes on this disk, which no clone of the
3101
+ * remote holds, so a teammate (or CI) cannot restore them. One line per library naming the
3102
+ * files; the command goes on (OQ5, p1859).
3103
+ */
3104
+ async warnUncommittedLibraryAssets(lib) {
3105
+ if (lib.entries.length === 0) return;
3106
+ const snap = await this.loadLibraryIndex();
3107
+ const files = /* @__PURE__ */ new Map();
3108
+ for (const e of lib.entries) {
3109
+ const name = lib.libraryOf.get(e);
3110
+ const registered = name ? snap?.libraries.find((l) => l.name === name) : void 0;
3111
+ if (!name || !registered || !libraryRemoteUrl(registered)) continue;
3112
+ const libPath = this.libraryPath({ name }, snap);
3113
+ if (!libPath) continue;
3114
+ const set = files.get(name) ?? /* @__PURE__ */ new Set();
3115
+ for (const f of this.uncommittedAssetFiles(e, libPath)) set.add(f);
3116
+ if (set.size > 0) files.set(name, set);
3117
+ }
3118
+ for (const [name, set] of files) {
3119
+ const list = [...set].sort();
3120
+ const shown = list.slice(0, 5).join(", ");
3121
+ const more = list.length > 5 ? `, +${list.length - 5} more` : "";
3122
+ this.emitLibraryCollision(
3123
+ `library "${name}" has uncommitted changes (${shown}${more}): other machines cannot restore those bytes from its remote. Commit and push them.`,
3124
+ []
3125
+ );
3126
+ }
3127
+ }
3128
+ /**
3129
+ * Whether every declared ref is locked and no declared pin differs from the locked version:
3130
+ * `skaile.yaml` asks for nothing the lock does not hold. A declared preset counts when the
3131
+ * lock records it.
3132
+ */
3133
+ declaredMatchesLock(lock, declared) {
3134
+ const keys = Object.keys(lock.assets).map((key) => parseCanonicalLockKey(key));
3135
+ for (const d of declared) {
3136
+ const want = parseRefTolerant(d.split(" ")[0] ?? d);
3137
+ if (!want) return false;
3138
+ if (want.kind === "preset") {
3139
+ if (!this.presetRecorded(want)) return false;
3140
+ continue;
3141
+ }
3142
+ const held = keys.filter(
3143
+ (id) => id.kind === want.kind && id.name === want.name && (!want.publisher || !id.publisher || id.publisher === want.publisher)
3144
+ );
3145
+ if (held.length === 0) return false;
3146
+ if (want.pin && !held.some((id) => !id.version || matchPin(want.pin, id.version))) {
3147
+ return false;
3148
+ }
3149
+ }
3150
+ return true;
3151
+ }
2955
3152
  /**
2956
3153
  * Lock keys that `skaile.yaml` still wants: the entries a declared ref names, plus every
2957
3154
  * entry such an asset requires, transitively. All keys when a `preset:` is declared.
@@ -2985,7 +3182,12 @@ var AssetManager = class {
2985
3182
  * assets on this machine (not registered, or registered with its directory gone). Plain
2986
3183
  * `install` stops on a non-empty list, the same lines `install --locked` prints: it must
2987
3184
  * not re-resolve a locked asset from another origin. The ONE function that decides "the
2988
- * locked library is not here", so a lock-recorded URL fallback extends it in one place.
3185
+ * locked library is not here", so a store-restore fallback extends it in one place.
3186
+ *
3187
+ * An entry `install --locked` already restored from a store (its library is not registered
3188
+ * here and the store cache holds the locked version, see {@link restoredFromStoreCache}) is no
3189
+ * line: it is returned in `restored`, and plain `install` then restores the lock as
3190
+ * `install --locked` does instead of re-resolving it from another origin (OQ8, p1655).
2989
3191
  *
2990
3192
  * Only entries `skaile.yaml` still declares, or that a declared asset requires
2991
3193
  * (transitively, read from the deployed copy), count. A stale entry nothing declares is
@@ -3000,21 +3202,26 @@ var AssetManager = class {
3000
3202
  const locked = Object.entries(lock.assets).filter(
3001
3203
  ([key, e]) => e.library !== void 0 && !e.library.installed && inScope.has(key)
3002
3204
  );
3003
- if (locked.length === 0) return [];
3004
- const snap = await this.loadLibraryIndex();
3005
3205
  const lines = [];
3206
+ const restored = [];
3207
+ if (locked.length === 0) return { lines, restored };
3208
+ const snap = await this.loadLibraryIndex();
3006
3209
  for (const [ref, e] of locked) {
3007
3210
  const { publisher, kind, name } = parseCanonicalLockKey(ref);
3008
3211
  if (!publisher || !kind || !name) continue;
3009
3212
  const lookup = this.lockedLibrary(e.library, snap, { kind, publisher, name });
3010
3213
  if (lookup.state === "ok") continue;
3214
+ if (await this.restoredFromStoreCache(ref, e, snap)) {
3215
+ restored.push(ref);
3216
+ continue;
3217
+ }
3011
3218
  const component = this.installTarget(kind);
3012
3219
  const deployed = component !== void 0 && isDeployed(component, name, this.harness, this.global, this.projectDir);
3013
3220
  const problem = lockedLibraryProblem(lookup, e.library, `${kind}:@${publisher}/${name}`);
3014
3221
  const kept = deployed ? "the deployed copy and its lock entry are kept" : "its lock entry is kept; no deployed copy exists";
3015
3222
  lines.push(`${ref} (${problem}; ${kept})`);
3016
3223
  }
3017
- return lines;
3224
+ return { lines, restored };
3018
3225
  }
3019
3226
  /**
3020
3227
  * Re-resolve every library-origin lock entry from its named library, for
@@ -3023,17 +3230,21 @@ var AssetManager = class {
3023
3230
  * version; anything else is reported in `missing` with the reason.
3024
3231
  *
3025
3232
  * @param opts.storeFallback - `install --locked` only: an entry whose library is not registered
3026
- * here is restored from a store that serves the locked sha256 (OQ15, p1490). Reads such as
3027
- * the drift check never ask a store.
3233
+ * here is restored from a store that serves the locked sha256 (OQ8, OQ15, p1490, p1655). The
3234
+ * lock records the library's remote and hash but no commit and no file list, so the library
3235
+ * remote itself cannot be fetched at a locked commit; the store pointer names the upstream
3236
+ * repository and commit that hold exactly the hashed bytes. Reads such as the drift check
3237
+ * never ask a store.
3028
3238
  */
3029
3239
  async restoreLibraryLockEntries(lock, opts) {
3030
3240
  const entries = [];
3031
3241
  const missing = [];
3032
3242
  const viaStore = [];
3243
+ const storeOrigins = {};
3033
3244
  const locked = Object.entries(lock.assets).filter(
3034
3245
  ([, e]) => e.library !== void 0 && !e.library.installed
3035
3246
  );
3036
- if (locked.length === 0) return { entries, missing, viaStore };
3247
+ if (locked.length === 0) return { entries, missing, viaStore, storeOrigins };
3037
3248
  const snap = await this.loadLibraryIndex();
3038
3249
  const diskHashes = /* @__PURE__ */ new Map();
3039
3250
  const fileSets = /* @__PURE__ */ new Map();
@@ -3048,8 +3259,9 @@ var AssetManager = class {
3048
3259
  if (lookup.state !== "ok") {
3049
3260
  const fallback = lookup.state === "unregistered" && opts?.storeFallback ? await this.storeEntryForLockedLibraryAsset(publisher, kind, name, origin) : null;
3050
3261
  if (fallback) {
3051
- entries.push(fallback);
3262
+ entries.push(fallback.entry);
3052
3263
  viaStore.push(ref);
3264
+ storeOrigins[ref] = fallback.from;
3053
3265
  continue;
3054
3266
  }
3055
3267
  missing.push(
@@ -3086,7 +3298,7 @@ var AssetManager = class {
3086
3298
  const onDisk = libraryDiskHashes(libPath, diskHashes, this.registry).get(`${kind}:${name}`);
3087
3299
  if (onDisk !== void 0 && onDisk !== origin.sha256) {
3088
3300
  missing.push(
3089
- `${ref} (library "${lookup.name}" content changed since lock \u2014 or the lock predates the one shared asset hash; re-lock with \`skaile install\`)`
3301
+ `${ref} (library "${lookup.name}" content changed since lock \u2014 or the lock predates the one shared asset hash; re-lock with \`skaile update\`)`
3090
3302
  );
3091
3303
  continue;
3092
3304
  }
@@ -3094,17 +3306,21 @@ var AssetManager = class {
3094
3306
  found.version = sameVersion ? origin.version : current;
3095
3307
  entries.push(withLibraryFiles(found, libPath, fileSets, this.registry));
3096
3308
  }
3097
- return { entries, missing, viaStore };
3309
+ return { entries, missing, viaStore, storeOrigins };
3098
3310
  }
3099
3311
  /**
3100
- * Whether a library-origin lock entry was restored from its lock url: its library is not
3312
+ * Whether a library-origin lock entry was restored from a store: its library is not
3101
3313
  * registered here, and the project's store cache holds the locked version (`install --locked`
3102
- * fetched and SHA-verified it there against the sha256 the lock recorded). Such an entry is
3103
- * as restorable as a source- or store-origin one: `check` and `doctor` read its requirements
3104
- * from the deployed copy and do not ask for the library (OQ8, p1655). An unregistered library
3105
- * that never restored anything here stays a gap.
3314
+ * fetched it there and SHA-verified every file against the sha256 the lock recorded). Such an
3315
+ * entry is as restorable as a source- or store-origin one: `check`, `doctor` and plain
3316
+ * `install` read its requirements from the deployed copy and do not ask for the library (OQ8,
3317
+ * p1655). An unregistered library that never restored anything here stays a gap.
3318
+ *
3319
+ * This only checks that the cache directory of the locked version is not empty: the bytes were
3320
+ * verified when they were fetched, and a later edit of the deployed copy is the `modified`
3321
+ * drift check's job, not this one's.
3106
3322
  */
3107
- async restoredFromLockUrl(key, entry, snap) {
3323
+ async restoredFromStoreCache(key, entry, snap) {
3108
3324
  const origin = entry.library;
3109
3325
  if (!origin || origin.installed || !origin.sha256 || !origin.version) return false;
3110
3326
  if (isPseudoVersion(origin.version)) return false;
@@ -3124,7 +3340,8 @@ var AssetManager = class {
3124
3340
  * store (project `stores:` or the global catalog) that serves `kind:@publisher/name#version`
3125
3341
  * under the very sha256 the lock recorded holds the same bytes. The entry is fetched and
3126
3342
  * verified like any store asset; the lock stays as it is. `null` when the lock has no hash,
3127
- * no store answers, or the hash differs (OQ15, p1490).
3343
+ * no store answers, or the hash differs (OQ15, p1490). `from` names the store and the
3344
+ * upstream repository and commit its pointer gives, for the notice.
3128
3345
  */
3129
3346
  async storeEntryForLockedLibraryAsset(publisher, kind, name, origin) {
3130
3347
  if (!origin.sha256 || !origin.version || isPseudoVersion(origin.version)) return null;
@@ -3155,7 +3372,12 @@ var AssetManager = class {
3155
3372
  ],
3156
3373
  repositories
3157
3374
  );
3158
- if (entry) return entry;
3375
+ if (entry) {
3376
+ return {
3377
+ entry,
3378
+ from: `${store.url} (${sourceShortName(m.sourceUrl)}@${m.commit.slice(0, 7)})`
3379
+ };
3380
+ }
3159
3381
  } catch (err) {
3160
3382
  this.noteStoreFailure(store.url, err);
3161
3383
  }
@@ -3198,12 +3420,21 @@ var AssetManager = class {
3198
3420
  async keepLockedLibraryEntries(lock) {
3199
3421
  if (!lock) return void 0;
3200
3422
  const out = /* @__PURE__ */ new Map();
3423
+ const snap = await this.loadLibraryIndex();
3424
+ const diskHashes = /* @__PURE__ */ new Map();
3201
3425
  for (const [key, entry] of Object.entries(lock.assets)) {
3202
3426
  if (!entry.library || entry.library.installed || !entry.library.version) continue;
3203
3427
  const id = parseCanonicalLockKey(key);
3204
3428
  if (!id.publisher || !id.kind || !id.name) continue;
3205
3429
  const requires = entry.requires ?? await this.deployedRequirementsOf(id);
3206
- out.set(`${id.publisher}/${id.kind}:${id.name}`, { key, entry, requires });
3430
+ const lookup = this.lockedLibrary(entry.library, snap, {
3431
+ kind: id.kind,
3432
+ publisher: id.publisher,
3433
+ name: id.name
3434
+ });
3435
+ const onDisk = lookup.state === "ok" && entry.library.sha256 ? libraryDiskHashes(lookup.path, diskHashes, this.registry).get(`${id.kind}:${id.name}`) : void 0;
3436
+ const contentChanged = onDisk !== void 0 && onDisk !== entry.library.sha256;
3437
+ out.set(`${id.publisher}/${id.kind}:${id.name}`, { key, entry, requires, contentChanged });
3207
3438
  }
3208
3439
  return out.size > 0 ? out : void 0;
3209
3440
  }
@@ -3514,6 +3745,7 @@ var AssetManager = class {
3514
3745
  const requiredBy = /* @__PURE__ */ new Map();
3515
3746
  const keptVersions = {};
3516
3747
  const versionBlocked = [];
3748
+ const contentHeld = [];
3517
3749
  const lib = { entries: [], resolved: /* @__PURE__ */ new Set(), libraryOf: /* @__PURE__ */ new Map() };
3518
3750
  const missing = /* @__PURE__ */ new Set();
3519
3751
  const seen = /* @__PURE__ */ new Set();
@@ -3528,6 +3760,9 @@ var AssetManager = class {
3528
3760
  const locked = opts.keepLocked?.get(id);
3529
3761
  const lockedVersion = locked?.entry.library?.version;
3530
3762
  if (!locked || !lockedVersion || !e.version) return void 0;
3763
+ if (locked.contentChanged && coerceVersion(e.version) === coerceVersion(lockedVersion)) {
3764
+ return locked;
3765
+ }
3531
3766
  if (isPseudoVersion(lockedVersion) || isPseudoVersion(e.version)) return void 0;
3532
3767
  if (coerceVersion(e.version) === coerceVersion(lockedVersion)) return void 0;
3533
3768
  for (const r of asked) if (refIdentity(r) === id && r.includes("#")) return void 0;
@@ -3564,10 +3799,18 @@ var AssetManager = class {
3564
3799
  });
3565
3800
  } else if (keep) {
3566
3801
  keptVersions[keep.key] = keep.entry;
3802
+ const sameVersion = coerceVersion(e.version ?? "") === coerceVersion(keep.entry.library?.version ?? "");
3803
+ const libraryName = origin ?? keep.entry.library?.name ?? "";
3567
3804
  if (!this.lockedCopyDeployed(e.kind, e.name)) {
3568
3805
  versionBlocked.push(
3569
- `${keep.key} (library "${origin ?? keep.entry.library?.name}" is at ${e.version}, locked ${keep.entry.library?.version}; the deployed copy is gone and cannot be restored from the ${gt(coerceVersion(e.version ?? ""), coerceVersion(keep.entry.library?.version ?? "")) ? "newer" : "older"} files \u2014 \`skaile update\` moves the lock to it)`
3806
+ sameVersion ? `${keep.key} (library "${libraryName}" content changed since lock; the deployed copy is gone and cannot be restored from the changed files \u2014 \`skaile update\` takes the change)` : `${keep.key} (library "${origin ?? keep.entry.library?.name}" is at ${e.version}, locked ${keep.entry.library?.version}; the deployed copy is gone and cannot be restored from the ${gt(coerceVersion(e.version ?? ""), coerceVersion(keep.entry.library?.version ?? "")) ? "newer" : "older"} files \u2014 \`skaile update\` moves the lock to it)`
3570
3807
  );
3808
+ } else if (sameVersion) {
3809
+ contentHeld.push({
3810
+ ref: keep.key,
3811
+ library: libraryName,
3812
+ version: keep.entry.library?.version ?? ""
3813
+ });
3571
3814
  }
3572
3815
  } else {
3573
3816
  lib.entries.push(e);
@@ -3670,7 +3913,8 @@ var AssetManager = class {
3670
3913
  presets,
3671
3914
  requiredBy,
3672
3915
  keptVersions,
3673
- versionBlocked
3916
+ versionBlocked,
3917
+ contentHeld
3674
3918
  };
3675
3919
  }
3676
3920
  /**
@@ -3942,18 +4186,25 @@ var AssetManager = class {
3942
4186
  return this.installLocked(repositories, patches, dependencies);
3943
4187
  }
3944
4188
  const lockForLibraries = this.tryReadLock();
4189
+ const restoredFromStore = /* @__PURE__ */ new Set();
3945
4190
  if (lockForLibraries) {
3946
4191
  const unavailable = await this.unavailableLockedLibraries(lockForLibraries, dependencies);
3947
- if (unavailable.length > 0) {
4192
+ for (const key of unavailable.restored) restoredFromStore.add(key);
4193
+ if (unavailable.lines.length > 0) {
3948
4194
  return {
3949
4195
  deployed: [],
3950
4196
  removed: [],
3951
- missing: unavailable,
4197
+ missing: unavailable.lines,
3952
4198
  lockWritten: false,
3953
4199
  npmInstalled: [],
3954
4200
  npmFailed: []
3955
4201
  };
3956
4202
  }
4203
+ if (unavailable.restored.length > 0 && this.declaredMatchesLock(lockForLibraries, dependencies) && [...await this.declaredLockKeys(lockForLibraries, dependencies)].every(
4204
+ (key) => restoredFromStore.has(key)
4205
+ )) {
4206
+ return this.installLocked(repositories, patches, dependencies);
4207
+ }
3957
4208
  const orphans = await this.orphanedSourceGaps(
3958
4209
  await this.declaredLockKeys(lockForLibraries, dependencies),
3959
4210
  true
@@ -3989,7 +4240,8 @@ var AssetManager = class {
3989
4240
  presets,
3990
4241
  requiredBy,
3991
4242
  keptVersions,
3992
- versionBlocked
4243
+ versionBlocked,
4244
+ contentHeld
3993
4245
  } = await this.resolveLibraryFirst(dependencies, {
3994
4246
  provenanceIndex,
3995
4247
  overrides,
@@ -4057,6 +4309,7 @@ var AssetManager = class {
4057
4309
  missingHints[ref] = `${fix ? "" : missingHints[ref] ?? ""} (${notes.join("; ")})`;
4058
4310
  }
4059
4311
  const directLock = this.directInstallLockEntries();
4312
+ await this.warnUncommittedLibraryAssets(lib);
4060
4313
  const libraryLock = {
4061
4314
  ...directLock,
4062
4315
  ...await this.libraryLockEntries(lib, opts?.moveLibraries === true),
@@ -4069,7 +4322,33 @@ var AssetManager = class {
4069
4322
  await this.keepRequiredByKept(prevLock, kept, Object.keys(freshLock.assets));
4070
4323
  const entries = [...await this.buildCatalogEntries(resolved, repositories), ...lib.entries];
4071
4324
  assertFlowsLoad(entries, "installed");
4072
- assertNoLibrarySymlinks(entries, this.registry, repositories, opts?.link === true);
4325
+ assertNoLibrarySymlinks(
4326
+ this.needingDeploy(entries, opts?.link === true),
4327
+ this.registry,
4328
+ repositories,
4329
+ opts?.link === true
4330
+ );
4331
+ if (libraryAssetCheck && lib.entries.length > 0) {
4332
+ const lockedIds = new Set([...lockedKeys].map((k) => k.split("#")[0]));
4333
+ const snap = await this.loadLibraryIndex();
4334
+ for (const e of lib.entries) {
4335
+ if (lockedIds.has(canonicalEntryRef(e).split("#")[0])) continue;
4336
+ const origin = lib.libraryOf.get(e);
4337
+ const libPath = origin ? this.libraryPath({ name: origin }, snap) : null;
4338
+ if (!origin || !libPath || !e.source) continue;
4339
+ const lines = libraryAssetCheck(
4340
+ { kind: e.kind, name: e.name, source: e.source },
4341
+ libPath
4342
+ ).filter((l) => /placeholder/i.test(l));
4343
+ if (lines.length > 0) {
4344
+ throw new Error(
4345
+ `${canonicalEntryRef(e)} in library "${origin}" fails the checks of \`skaile validate\` - nothing was installed:
4346
+ ${lines.map((l) => ` ${l}`).join("\n")}
4347
+ Fix the asset, then run \`skaile library index ${origin}\` and install again.`
4348
+ );
4349
+ }
4350
+ }
4351
+ }
4073
4352
  const forceCommand = opts?.moveLibraries === true ? "skaile update --force" : "skaile install --force";
4074
4353
  if (!opts?.force) {
4075
4354
  await this.assertNoHandEditsPruned(
@@ -4128,6 +4407,8 @@ var AssetManager = class {
4128
4407
  ...mcpRestored
4129
4408
  ];
4130
4409
  const rewritten = this.rewrittenAgentFiles(unrecordedAgents);
4410
+ const editedHeld = new Set(copyDrift.filter((d) => d.libraryChanged).map((d) => d.ref));
4411
+ const heldEdits = editedHeld.size > 0 && opts?.moveLibraries !== true ? (await this.projectState()).gaps.filter((g) => g.type === "modified" && editedHeld.has(g.ref)).map((g) => g.message) : [];
4131
4412
  const lockWanted = resolved.length > 0 || lib.entries.length > 0 || existsSync(this.lockPath);
4132
4413
  let lockWritten = false;
4133
4414
  let relocked = [];
@@ -4147,8 +4428,15 @@ var AssetManager = class {
4147
4428
  }
4148
4429
  this.recordInstallHistory(deployed, removed);
4149
4430
  const { npmInstalled, npmFailed } = await this.installRuntimeAssetDeps();
4150
- const reportedMissing = stillMissing.filter((m) => !storeNotChecked.some((n) => n.ref === m));
4431
+ const restoredRefs = stillMissing.filter((m) => {
4432
+ const keys = kept.keysByRef.get(m) ?? [];
4433
+ return keys.length > 0 && keys.every((k) => restoredFromStore.has(k));
4434
+ });
4435
+ const reportedMissing = stillMissing.filter(
4436
+ (m) => !storeNotChecked.some((n) => n.ref === m) && !restoredRefs.includes(m)
4437
+ );
4151
4438
  for (const n of storeNotChecked) delete missingHints[n.ref];
4439
+ for (const m of restoredRefs) delete missingHints[m];
4152
4440
  await this.adviseMissing(reportedMissing, missingHints);
4153
4441
  const placeholderBodies = scaffoldBodyRefs(lib.entries, deployed);
4154
4442
  const placeholderDescriptions = scaffoldDescriptionRefs(lib.entries, deployed);
@@ -4158,13 +4446,15 @@ var AssetManager = class {
4158
4446
  missing: [
4159
4447
  ...reportedMissing,
4160
4448
  ...versionBlocked.filter((b) => !reportedMissing.includes(b)),
4161
- ...composedDrift
4449
+ ...composedDrift,
4450
+ ...heldEdits.filter((h) => !reportedMissing.includes(h))
4162
4451
  ],
4163
4452
  ...placeholderBodies.length > 0 ? { placeholderBodies } : {},
4164
4453
  ...placeholderDescriptions.length > 0 ? { placeholderDescriptions } : {},
4165
4454
  ...storeNotChecked.length > 0 ? { storeNotChecked } : {},
4166
4455
  ...Object.keys(missingHints).length > 0 ? { missingHints } : {},
4167
4456
  ...relocked.length > 0 ? { relocked } : {},
4457
+ ...contentHeld.length > 0 ? { contentHeld } : {},
4168
4458
  ...inferredHarnesses.length > 0 ? { inferredHarnesses } : {},
4169
4459
  ...moved.length > 0 ? { moved } : {},
4170
4460
  ...replaced.length > 0 ? { replaced } : {},
@@ -4564,7 +4854,11 @@ Nothing was changed. Move your edits away, or run \`${forceCommand}\` to overwri
4564
4854
  ).split(sep).join("/");
4565
4855
  files = files.map((f) => `${dir}/${f}`);
4566
4856
  }
4567
- if (files.length > 0) lines.push(`${d.ref} (${files.join(", ")} was edited)`);
4857
+ if (files.length > 0) {
4858
+ lines.push(
4859
+ d.unrecorded ? `${d.ref} (${files.join(", ")} differs from the library's content now; the lock keeps no per-file record, so it may be a library change, not your edit)` : `${d.ref} (${files.join(", ")} was edited)`
4860
+ );
4861
+ }
4568
4862
  }
4569
4863
  if (lines.length === 0) return;
4570
4864
  throw new Error(
@@ -4632,11 +4926,15 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
4632
4926
  });
4633
4927
  }
4634
4928
  }
4635
- /** Read the lock, returning null on a legacy v1 lock instead of throwing. */
4929
+ /**
4930
+ * Read the lock, returning null on a legacy v1 lock instead of throwing. A lock that is not
4931
+ * valid YAML throws {@link LockParseError}: it is not an empty lock (p1892).
4932
+ */
4636
4933
  tryReadLock() {
4637
4934
  try {
4638
4935
  return readLock(this.lockPath);
4639
- } catch {
4936
+ } catch (err) {
4937
+ if (err instanceof LockParseError) throw err;
4640
4938
  return null;
4641
4939
  }
4642
4940
  }
@@ -4806,13 +5104,11 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
4806
5104
  );
4807
5105
  }
4808
5106
  const declBySlug = new Map(Object.entries(repositories));
4809
- const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
4810
5107
  const factoryDecl = Object.values(repositories).find((d) => d.factory);
4811
5108
  const factoryDir = factoryDecl?.path ?? null;
4812
- const isFactoryUrl = (url) => factoryDir !== null && normalizeRepoUrl(url) === factoryUrl;
4813
5109
  const unavailable = [];
4814
5110
  for (const src of lock.sources) {
4815
- if (isFactoryUrl(src.url)) continue;
5111
+ if (factoryDir !== null && isFactoryMemorySource(src)) continue;
4816
5112
  const slug = sourceCacheKey(src.url);
4817
5113
  const declared = declBySlug.get(slug);
4818
5114
  if (!declared && GITHUB_REPO_URL.test(src.url)) continue;
@@ -4842,8 +5138,8 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
4842
5138
  cloneOrigin[key] = entry;
4843
5139
  }
4844
5140
  }
4845
- const verification = verifyLock({ ...lock, assets: cloneOrigin }, (sourceUrl) => {
4846
- if (isFactoryUrl(sourceUrl)) return factoryDir;
5141
+ const verification = verifyLock({ ...lock, assets: cloneOrigin }, (sourceUrl, entry) => {
5142
+ if (factoryDir !== null && isFactoryMemorySource(entry.source)) return factoryDir;
4847
5143
  const slug = sourceCacheKey(sourceUrl);
4848
5144
  return this.resolveRepoDir(repositories[slug] ?? { url: sourceUrl }, slug);
4849
5145
  });
@@ -4875,7 +5171,11 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
4875
5171
  }
4876
5172
  const library = await this.restoreLibraryLockEntries(lock, { storeFallback: true });
4877
5173
  assertFlowsLoad([...resolved, ...library.entries], "installed");
4878
- assertNoLibrarySymlinks([...resolved, ...library.entries], this.registry, repositories);
5174
+ assertNoLibrarySymlinks(
5175
+ this.needingDeploy([...resolved, ...library.entries]),
5176
+ this.registry,
5177
+ repositories
5178
+ );
4879
5179
  const inferredHarnesses = this.inferLegacyHarnessSubsets([...resolved, ...library.entries]);
4880
5180
  const composedPre = /* @__PURE__ */ new Set();
4881
5181
  if (!this.global) {
@@ -4939,7 +5239,12 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
4939
5239
  const p = parseRefTolerant(g.ref);
4940
5240
  return !(p && alreadyMissing.has(`${p.kind}:${p.name}`));
4941
5241
  }).map((g) => g.message);
4942
- const libraryNotices = this.lockUrlRestoreNotices(lock, library.viaStore);
5242
+ const libraryNotices = this.storeRestoreNotices(
5243
+ lock,
5244
+ library.viaStore,
5245
+ library.storeOrigins,
5246
+ deployed.length > 0 || restored.length > 0
5247
+ );
4943
5248
  const placeholderBodies = scaffoldBodyRefs(library.entries, deployed);
4944
5249
  const placeholderDescriptions = scaffoldDescriptionRefs(library.entries, deployed);
4945
5250
  return {
@@ -4958,22 +5263,44 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
4958
5263
  }
4959
5264
  /**
4960
5265
  * One notice line per locked library that is not registered here and whose assets
4961
- * `install --locked` restored from the lock url: names the library, how many assets came from
4962
- * the url, and how to register the library to move the lock later (OQ8, p1655).
5266
+ * `install --locked` restored from a store: names the library, how many assets came from
5267
+ * which store (and the upstream repository and commit its pointer names), and how to register
5268
+ * the library to move the lock later (OQ8, p1655). Every file was SHA-verified against the
5269
+ * hash the lock recorded.
4963
5270
  */
4964
- lockUrlRestoreNotices(lock, viaStore) {
5271
+ storeRestoreNotices(lock, viaStore, storeOrigins, wroteFiles) {
4965
5272
  const byLibrary = /* @__PURE__ */ new Map();
4966
5273
  for (const key of viaStore) {
4967
5274
  const origin = lock.assets[key]?.library;
4968
5275
  if (!origin) continue;
4969
5276
  const name = unregisteredLibraryName(origin);
4970
- const row = byLibrary.get(name) ?? { ...origin.url ? { url: origin.url } : {}, refs: [] };
5277
+ const row = byLibrary.get(name) ?? {
5278
+ ...origin.url ? { url: origin.url } : {},
5279
+ refs: [],
5280
+ from: /* @__PURE__ */ new Set()
5281
+ };
4971
5282
  row.refs.push(key);
5283
+ const from = storeOrigins[key];
5284
+ if (from) row.from.add(from);
4972
5285
  byLibrary.set(name, row);
4973
5286
  }
4974
- return [...byLibrary].map(([name, { url, refs }]) => {
5287
+ return [...byLibrary].map(([name, { url, refs, from }]) => {
4975
5288
  const register = url ? `skaile library init ${name} --git ${url}` : `skaile library init ${name} --path <dir>`;
4976
- return `Library "${name}" is not registered here: restored ${refs.length} asset${refs.length === 1 ? "" : "s"} from the lock url${url ? ` ${url}` : ""}. Register it with \`${register}\` to move the lock later.`;
5289
+ if (!wroteFiles) {
5290
+ return `Library "${name}" is not registered here. Register it with \`${register}\` to move the lock later.`;
5291
+ }
5292
+ const byStore = /* @__PURE__ */ new Map();
5293
+ for (const f of from) {
5294
+ const storeUrl = f.split(" (")[0] ?? f;
5295
+ const pointers = byStore.get(storeUrl) ?? /* @__PURE__ */ new Set();
5296
+ pointers.add(f);
5297
+ byStore.set(storeUrl, pointers);
5298
+ }
5299
+ const stores = [...byStore].map(
5300
+ ([storeUrl, pointers]) => pointers.size === 1 ? [...pointers][0] : storeUrl
5301
+ );
5302
+ const where = stores.length > 0 ? ` from the store ${stores.join(", ")}` : " from a store";
5303
+ return `Library "${name}" is not registered here: restored ${refs.length} asset${refs.length === 1 ? "" : "s"}${where}, every file checked against the lock. Register it with \`${register}\` to move the lock later.`;
4977
5304
  });
4978
5305
  }
4979
5306
  /**
@@ -5057,7 +5384,6 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
5057
5384
  */
5058
5385
  planAddItems(resolved, lib, stores, parsed) {
5059
5386
  const storeUrls = new Set(stores.map((s) => normalizeRepoUrl(s.url)));
5060
- const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
5061
5387
  const shownPath = (dir) => {
5062
5388
  const rel = relative(this.projectDir, dir);
5063
5389
  return this.global || rel.startsWith("..") ? dir.replace(homedir(), "~") : `${rel}${dir.endsWith(sep) ? sep : ""}`;
@@ -5090,7 +5416,7 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
5090
5416
  publisher: c.publisher.replace(/^@/, "")
5091
5417
  }),
5092
5418
  version: c.version,
5093
- origin: storeUrls.has(url) ? `store ${c.sourceUrl}` : url === factoryUrl ? "factory" : `source ${sourceShortName(c.sourceUrl)}`,
5419
+ origin: storeUrls.has(url) ? `store ${c.sourceUrl}` : isFactoryMemorySource({ url: c.sourceUrl, commit: c.commit }) ? "factory" : `source ${sourceShortName(c.sourceUrl)}`,
5094
5420
  requested: asked(c.kind, c.name, c.publisher),
5095
5421
  ...planPaths(
5096
5422
  folders(c.kind, c.name),
@@ -5266,6 +5592,30 @@ Nothing was changed. Copy your edits away, or run \`${forceCommand}\` to replace
5266
5592
  }
5267
5593
  }
5268
5594
  }
5595
+ if (libraryAssetCheck) {
5596
+ for (const l of snap?.libraries ?? []) {
5597
+ const libPath = this.libraryPath({ name: l.name }, snap);
5598
+ const manifest = libPath ? loadSkPublishManifest(libPath) : null;
5599
+ if (!libPath || !manifest || manifest.source) continue;
5600
+ const entry = (manifest.assets ?? []).find(
5601
+ (a) => a.kind === parsed.kind && a.name === parsed.name && !a.source && (!parsed.publisher || bare(a.publisher ?? manifest.publisher) === bare(parsed.publisher))
5602
+ );
5603
+ if (!entry?.root || isAbsolute(entry.root) || entry.root.split(/[\\/]+/).includes("..")) {
5604
+ continue;
5605
+ }
5606
+ const problems = libraryAssetCheck(
5607
+ { kind: entry.kind, name: entry.name, source: join(libPath, entry.root) },
5608
+ libPath
5609
+ );
5610
+ if (problems.length > 0) {
5611
+ throw new Error(
5612
+ `${parsed.kind}:${parsed.name} in library "${l.name}" fails the checks of \`skaile validate\` - nothing was added:
5613
+ ${problems.map((line) => ` ${line}`).join("\n")}
5614
+ Fix the asset, then run \`skaile library index ${l.name}\` and add it again.`
5615
+ );
5616
+ }
5617
+ }
5618
+ }
5269
5619
  const unreachable = [...this.storeFailures.values()];
5270
5620
  if (unreachable.length > 0) {
5271
5621
  throw new Error(
@@ -5458,9 +5808,15 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
5458
5808
  }
5459
5809
  this.addDependencyToConfig(ref, opts?.only === true);
5460
5810
  this.widenDependencyRows(entries, parsed);
5811
+ if (!opts?.preferLockedLibrary) {
5812
+ const bound = this.bindAgentInstance(entries, parsed);
5813
+ if (bound?.keptRef !== void 0) opts?.onAgentBindingKept?.(bound.id, bound.keptRef);
5814
+ else if (bound) opts?.onAgentBound?.(bound.id, bound.ref);
5815
+ }
5461
5816
  await this.renderHarnessMcpConfig();
5462
5817
  await this.renderAgentInstances();
5463
5818
  if (resolved.length > 0 || lib.entries.length > 0) {
5819
+ await this.warnUncommittedLibraryAssets(lib);
5464
5820
  const libraryLock = await this.libraryLockEntries(lib);
5465
5821
  const fresh = {
5466
5822
  ...buildLockFile(resolved, overridesApplied, libraryLock),
@@ -6269,17 +6625,18 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
6269
6625
  listStaleHarnessCopies() {
6270
6626
  if (this.global) return [];
6271
6627
  const tracked = /* @__PURE__ */ new Map();
6272
- const track = (kind, name) => {
6628
+ const track = (kind, name, locked = false) => {
6273
6629
  const component = kind ? this.installTarget(kind) : void 0;
6274
6630
  if (kind && name && component) {
6275
- tracked.set(`${kind}:${name}`, { kind, name, component });
6631
+ const key = `${kind}:${name}`;
6632
+ tracked.set(key, { kind, name, component, locked: locked || !!tracked.get(key)?.locked });
6276
6633
  }
6277
6634
  };
6278
6635
  const lock = this.tryReadLock();
6279
6636
  if (lock) {
6280
6637
  for (const key of Object.keys(lock.assets)) {
6281
6638
  const { kind, name } = parseCanonicalLockKey(key);
6282
- track(kind, name);
6639
+ track(kind, name, true);
6283
6640
  }
6284
6641
  }
6285
6642
  try {
@@ -6295,23 +6652,36 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
6295
6652
  const out = [];
6296
6653
  for (const other of SUPPORTED_HARNESSES) {
6297
6654
  if (other === this.harness) continue;
6298
- for (const { kind, name, component: k } of tracked.values()) {
6655
+ for (const { kind, name, component: k, locked } of tracked.values()) {
6299
6656
  if (deployedDir(k, name, other, false, this.projectDir) === deployedDir(k, name, this.harness, false, this.projectDir)) {
6300
6657
  continue;
6301
6658
  }
6302
- const found = deployedPaths(k, name, other, false, this.projectDir).find((p) => {
6659
+ const footprints = deployedPaths(k, name, other, false, this.projectDir);
6660
+ if (k === "agent" && other === "codex") {
6661
+ footprints.push(
6662
+ join(
6663
+ dirname(deployedBase(k, other, false, this.projectDir)),
6664
+ CODEX_IDENTITY_DIR,
6665
+ `${name}.md`
6666
+ )
6667
+ );
6668
+ }
6669
+ const otherBase = deployedBase(k, other, false, this.projectDir);
6670
+ const activeBase = deployedBase(k, this.harness, false, this.projectDir);
6671
+ for (const p of footprints) {
6303
6672
  try {
6304
6673
  lstatSync(p);
6305
- return true;
6306
6674
  } catch {
6307
- return false;
6675
+ continue;
6308
6676
  }
6309
- });
6310
- if (found) out.push({ harness: other, kind, name, path: found });
6677
+ const twin = join(activeBase, relative(otherBase, p));
6678
+ const skaileWrote = k === "agent" ? locked : !relative(otherBase, p).startsWith("..") && sameBytes(p, twin);
6679
+ out.push({ harness: other, kind, name, path: p, skaileWrote });
6680
+ }
6311
6681
  }
6312
6682
  }
6313
6683
  return out.sort(
6314
- (a, b) => a.harness.localeCompare(b.harness) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name)
6684
+ (a, b) => a.harness.localeCompare(b.harness) || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name) || a.path.localeCompare(b.path)
6315
6685
  );
6316
6686
  }
6317
6687
  /**
@@ -6558,7 +6928,13 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
6558
6928
  const locked = parsed.kind === "preset" ? lock !== null : lockKeys.some(
6559
6929
  (k) => k.kind === parsed.kind && k.name === parsed.name && (!parsed.publisher || k.publisher === parsed.publisher)
6560
6930
  );
6561
- const recorded = this.entryHarnessRecord(parsed.kind, parsed.name, parsed.publisher);
6931
+ let recorded = this.entryHarnessRecord(parsed.kind, parsed.name, parsed.publisher);
6932
+ if (!recorded && this.isLegacyHarnessRecord()) {
6933
+ const all = this.defaultGlobalHarnesses();
6934
+ const component = this.installTarget(parsed.kind);
6935
+ const holding = all.length >= 2 && component ? this.legacySingleCopyHarness(component, parsed.name, all) : [];
6936
+ if (holding.length === 1) recorded = holding;
6937
+ }
6562
6938
  for (const harness of recorded ? harnesses.filter((h) => recorded.includes(h)) : harnesses) {
6563
6939
  let state;
6564
6940
  if (!locked) {
@@ -6843,6 +7219,13 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
6843
7219
  if (!decl) continue;
6844
7220
  const status = checkRepoStatus(decl, slug, this.reposDir);
6845
7221
  if (status.kind !== "remote" || !status.localHead) continue;
7222
+ if (status.error !== void 0) {
7223
+ this.unreachableSources.push({
7224
+ name: sourceShortName(src.url),
7225
+ url: src.url,
7226
+ reason: status.error
7227
+ });
7228
+ }
6846
7229
  const clone = decl.path ?? join(this.reposDir, slug);
6847
7230
  for (const [ref, entry] of Object.entries(lock.assets)) {
6848
7231
  if (entry.source?.url !== src.url) continue;
@@ -7500,7 +7883,7 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
7500
7883
  break;
7501
7884
  }
7502
7885
  librarySnap ??= { snap: await this.loadLibraryIndex() };
7503
- if (await this.restoredFromLockUrl(key, e, librarySnap.snap)) {
7886
+ if (await this.restoredFromStoreCache(key, e, librarySnap.snap)) {
7504
7887
  restorable = true;
7505
7888
  break;
7506
7889
  }
@@ -7702,11 +8085,15 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
7702
8085
  named.add(gapIdentity(ref));
7703
8086
  }
7704
8087
  for (const d of await this.lockedCopyDrift()) {
7705
- if (named.has(gapIdentity(d.ref))) continue;
8088
+ if (named.has(gapIdentity(d.ref))) {
8089
+ const at = d.libraryChanged ? gaps.findIndex((g) => g.type === "content" && gapIdentity(g.ref) === gapIdentity(d.ref)) : -1;
8090
+ if (at < 0) continue;
8091
+ gaps.splice(at, 1);
8092
+ }
7706
8093
  const parts = [
7707
8094
  ...d.modified.map((f) => {
7708
8095
  const instance = d.instanceOf?.get(f);
7709
- return instance ? `${f}, the file of agent instance "${instance}", was edited` : `${f} was edited`;
8096
+ return instance ? `${f}, the file of agent instance "${instance}", was edited` : d.unrecorded ? `${f} differs from the library's content now (the lock keeps no per-file record: it may be a library change, not your edit)` : `${f} was edited`;
7710
8097
  }),
7711
8098
  ...d.extra.map((f) => `${f} is not part of the asset`)
7712
8099
  ];
@@ -7982,7 +8369,7 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
7982
8369
  found.version
7983
8370
  );
7984
8371
  if (!diskVersion || isPseudoVersion(diskVersion) || !gt(coerceVersion(diskVersion), coerceVersion(origin.version)) && (isPseudoVersion(origin.version) || diskVersion === origin.version)) {
7985
- const reason2 = `is locked, but library "${lookup.name}" content changed since lock \u2014 or the lock predates the one shared asset hash; run \`skaile install\` to re-lock it`;
8372
+ const reason2 = `is locked, but library "${lookup.name}" content changed since lock \u2014 or the lock predates the one shared asset hash; run \`skaile update\` to re-lock it`;
7986
8373
  gaps.push({ type: "content", ref, reason: reason2, message: `${ref} ${reason2}` });
7987
8374
  }
7988
8375
  }
@@ -7995,7 +8382,7 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
7995
8382
  continue;
7996
8383
  }
7997
8384
  if (lookup.state === "unregistered") {
7998
- if (await this.restoredFromLockUrl(key, entry, snap)) continue;
8385
+ if (await this.restoredFromStoreCache(key, entry, snap)) continue;
7999
8386
  const reason2 = `is locked, but library "${unregisteredLibraryName(origin)}" is not registered (renamed or removed?) \u2014 ${unregisteredLibraryFix(origin, ref)}`;
8000
8387
  gaps.push({ type: "library", ref, reason: reason2, message: `${ref} ${reason2}`, manualFix: true });
8001
8388
  continue;
@@ -8037,16 +8424,19 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8037
8424
  for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
8038
8425
  const url = entry.source?.url;
8039
8426
  if (!url || entry.library || !GIT_URL.test(url)) continue;
8427
+ if (entry.origin === "store" && !online) continue;
8040
8428
  if (inScope && !inScope.has(key)) continue;
8041
- if (normalizeRepoUrl(url) === normalizeRepoUrl(FACTORY_SOURCE_URL)) continue;
8429
+ if (isBundledFactoryEntry(entry)) continue;
8042
8430
  const { publisher, kind, name, version } = parseCanonicalLockKey(key);
8043
8431
  if (!publisher || !kind || !name) continue;
8044
8432
  if (registered.has(sourceCacheKey(url))) continue;
8045
8433
  const ref = `${kind}:@${publisher}/${name}`;
8046
8434
  const locked = version ? `${ref}#${version}` : void 0;
8047
- if (GITHUB_REPO_URL.test(url) && await this.storeListsRef(stores, ref, online, locked))
8435
+ const askStores = entry.origin === "store" && entry.store && !stores.some((s) => s.url === entry.store) ? [...stores, { url: entry.store }] : stores;
8436
+ if (GITHUB_REPO_URL.test(url) && await this.storeListsRef(askStores, ref, online, locked))
8048
8437
  continue;
8049
- const reason = `is locked, but its source ${url} is not in skaile.yaml sources: any more \u2014 register it again with \`skaile source add ${url}\`, or drop the dependency with \`skaile remove ${ref}\``;
8438
+ const base = `is locked, but its source ${url} is not in skaile.yaml sources: any more \u2014 register it again with \`skaile source add ${url}\`, or drop the dependency with \`skaile remove ${ref}\``;
8439
+ const reason = stores.length > 0 && GITHUB_REPO_URL.test(url) && entry.origin !== "store" ? `${base}. If a store supplied it (store unreachable, or its cache is cold), run \`skaile install\` with the store online to record the store as its origin` : base;
8050
8440
  out.set(key, { type: "source", ref, reason, message: `${ref} ${reason}`, manualFix: true });
8051
8441
  }
8052
8442
  return out;
@@ -8059,6 +8449,7 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8059
8449
  * its cached catalog holds.
8060
8450
  */
8061
8451
  async storeListsRef(stores, ref, online, lockedRef) {
8452
+ if (online && this.catalogUrlOverride === "") return true;
8062
8453
  for (const store of stores) {
8063
8454
  try {
8064
8455
  if (this.injectedStoreFetcher) {
@@ -8095,8 +8486,8 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8095
8486
  const gaps = [];
8096
8487
  for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
8097
8488
  const url = entry.source?.url;
8098
- if (!url || entry.library || !GIT_URL.test(url)) continue;
8099
- if (normalizeRepoUrl(url) === normalizeRepoUrl(FACTORY_SOURCE_URL)) continue;
8489
+ if (!url || entry.library || entry.origin === "store" || !GIT_URL.test(url)) continue;
8490
+ if (isBundledFactoryEntry(entry)) continue;
8100
8491
  const { publisher, kind, name } = parseCanonicalLockKey(key);
8101
8492
  if (!publisher || !kind || !name || named.has(`${kind}:${name}`)) continue;
8102
8493
  const ref = `${kind}:@${publisher}/${name}`;
@@ -8124,7 +8515,8 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8124
8515
  const want = sourceCacheKey(url);
8125
8516
  const out = [];
8126
8517
  for (const [key, entry] of Object.entries(this.tryReadLock()?.assets ?? {})) {
8127
- if (entry.library || !entry.source || sourceCacheKey(entry.source.url) !== want) continue;
8518
+ if (entry.library || !entry.source || isFactoryMemorySource(entry.source)) continue;
8519
+ if (sourceCacheKey(entry.source.url) !== want) continue;
8128
8520
  const { publisher, kind, name } = parseCanonicalLockKey(key);
8129
8521
  if (kind && name) out.push(publisher ? `${kind}:@${publisher}/${name}` : `${kind}:${name}`);
8130
8522
  }
@@ -8439,7 +8831,15 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8439
8831
  })?.modified ?? [];
8440
8832
  }
8441
8833
  if (modified.length > 0) {
8442
- out.push({ ref, key, modified, extra: [], library: true, libraryChanged: true });
8834
+ out.push({
8835
+ ref,
8836
+ key,
8837
+ modified,
8838
+ extra: [],
8839
+ library: true,
8840
+ libraryChanged: true,
8841
+ ...entry.copied && entry.copied.length > 0 ? {} : { unrecorded: true }
8842
+ });
8443
8843
  }
8444
8844
  }
8445
8845
  return out;
@@ -8487,15 +8887,10 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8487
8887
  const ref = `${e.kind}:@${(e.publisher ?? "").replace(/^@/, "")}/${e.name}`;
8488
8888
  out.push({ ref, key: keyOf.get(ref) ?? ref, ...drift, library: true });
8489
8889
  }
8490
- out.push(
8491
- ...await this.changedLibraryCopyEdits(
8492
- lock,
8493
- new Set(
8494
- entries.map((e) => `${e.kind}:@${(e.publisher ?? "").replace(/^@/, "")}/${e.name}`)
8495
- ),
8496
- patches
8497
- )
8890
+ const restoredRefs = new Set(
8891
+ entries.map((e) => `${e.kind}:@${(e.publisher ?? "").replace(/^@/, "")}/${e.name}`)
8498
8892
  );
8893
+ out.push(...await this.changedLibraryCopyEdits(lock, restoredRefs, patches));
8499
8894
  for (const [key, entry] of Object.entries(lock.assets)) {
8500
8895
  if (!entry.source || entry.files.length === 0) continue;
8501
8896
  const { kind, name } = parseCanonicalLockKey(key);
@@ -8529,6 +8924,7 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8529
8924
  if (modified.length > 0 || extra.length > 0)
8530
8925
  out.push({ ref: refOf(key), key, modified, extra, library: false });
8531
8926
  }
8927
+ let snapForAgents;
8532
8928
  for (const [key, entry] of Object.entries(lock.assets)) {
8533
8929
  const recorded = [...entry.rendered ?? [], ...entry.composed ?? []];
8534
8930
  if (recorded.length === 0 || entry.library?.installed) continue;
@@ -8550,12 +8946,24 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
8550
8946
  for (const inst of renderedInstanceFiles(this.projectDir, this.harness)) {
8551
8947
  for (const f of inst.files) if (modified.includes(f)) instanceOf.set(f, inst.id);
8552
8948
  }
8949
+ let libraryChanged = false;
8950
+ const origin = entry.library;
8951
+ const id = parseCanonicalLockKey(key);
8952
+ if (origin && id.publisher && !restoredRefs.has(refOf(key))) {
8953
+ snapForAgents ??= await this.loadLibraryIndex();
8954
+ libraryChanged = this.lockedLibrary(origin, snapForAgents, {
8955
+ kind,
8956
+ publisher: id.publisher,
8957
+ name
8958
+ }).state === "ok";
8959
+ }
8553
8960
  out.push({
8554
8961
  ref: refOf(key),
8555
8962
  key,
8556
8963
  modified,
8557
8964
  extra: [],
8558
8965
  library: false,
8966
+ ...libraryChanged ? { libraryChanged: true } : {},
8559
8967
  rendered: true,
8560
8968
  lockedSha: new Map(recorded.map((f) => [f.path, f.sha256])),
8561
8969
  instanceOf
@@ -9085,6 +9493,53 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
9085
9493
  }
9086
9494
  return out;
9087
9495
  }
9496
+ /**
9497
+ * Bind the agent `skaile add agent:<ref>` just added as an instance `agents.<name>:
9498
+ * {ref: agent:@publisher/name}` of `skaile.yaml`, so `run --instance <name>` and the repl
9499
+ * banner hint work at once. An agent that an instance or `agent:` already binds, or an id
9500
+ * `agents:` already holds, is left as it is. Project mode only.
9501
+ *
9502
+ * @returns the instance id and ref bound; the id with `keptRef` (what that id binds instead)
9503
+ * when the id is taken by another agent; `undefined` when nothing was bound and nothing needs saying.
9504
+ */
9505
+ bindAgentInstance(entries, parsed) {
9506
+ if (this.global || parsed.kind !== "agent") return void 0;
9507
+ const configPath = join(this.stateRoot, "skaile.yaml");
9508
+ if (!existsSync(configPath)) return void 0;
9509
+ const bare = (p) => p?.replace(/^@/, "");
9510
+ const entry = entries.find(
9511
+ (e) => e.kind === "agent" && e.name === parsed.name && (!parsed.publisher || bare(e.publisher) === bare(parsed.publisher))
9512
+ );
9513
+ const publisher = bare(entry?.publisher ?? parsed.publisher);
9514
+ const ref = publisher ? `agent:@${publisher}/${parsed.name}` : `agent:${parsed.name}`;
9515
+ const id = parsed.name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^[^a-z]+/, "");
9516
+ if (!/^[a-z][a-z0-9_-]*$/.test(id)) return void 0;
9517
+ let raw;
9518
+ try {
9519
+ raw = parse(readFileSync(configPath, "utf8"));
9520
+ } catch {
9521
+ return void 0;
9522
+ }
9523
+ if (!raw || typeof raw !== "object") return void 0;
9524
+ const bound = (value) => {
9525
+ const p = typeof value === "string" ? parseRefTolerant(value) : null;
9526
+ return !!p && p.kind === "agent" && p.name === parsed.name && (!publisher || !p.publisher || bare(p.publisher) === publisher);
9527
+ };
9528
+ const instances = raw.agents && typeof raw.agents === "object" && !Array.isArray(raw.agents) ? raw.agents : {};
9529
+ const primary = raw.agent && typeof raw.agent === "object" ? raw.agent.definition : void 0;
9530
+ if (bound(primary) || Object.values(instances).some((i) => bound(i?.ref))) {
9531
+ return void 0;
9532
+ }
9533
+ const taken = instances[id]?.ref;
9534
+ if (id in instances)
9535
+ return { id, ref, keptRef: typeof taken === "string" ? taken : "another agent" };
9536
+ if (id === DEFAULT_AGENT_INSTANCE_ID && typeof primary === "string") {
9537
+ return { id, ref, keptRef: primary };
9538
+ }
9539
+ raw.agents = { ...instances, [id]: { ref } };
9540
+ writeFileSync(configPath, stringify(raw, { lineWidth: 120 }));
9541
+ return { id, ref };
9542
+ }
9088
9543
  /** The refs `skaile.yaml` binds agent instances to: every `agents.<id>.ref` and `agent.definition`. Project mode only. */
9089
9544
  declaredAgentInstanceRefs() {
9090
9545
  if (this.global) return [];
@@ -9167,5 +9622,5 @@ Fix the asset in the source repo, then run \`skaile source sync\` and add it aga
9167
9622
  };
9168
9623
 
9169
9624
  export { AssetManager, LibraryEntryError, MissingManifestError, installAgentInstances, labelDiffHeaders, mcpNotRunnableReason, normalizeRepoUrl, parseCanonicalLockKey, parseRefTolerant, renderMcpServerConfig, setLibraryAssetCheck, setRenamedEntryLookup, softDepName };
9170
- //# sourceMappingURL=chunk-KGHYXXXV.js.map
9171
- //# sourceMappingURL=chunk-KGHYXXXV.js.map
9625
+ //# sourceMappingURL=chunk-IS3NIZBX.js.map
9626
+ //# sourceMappingURL=chunk-IS3NIZBX.js.map