@skaile/workspaces 1.3.0 → 1.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 (269) hide show
  1. package/CHANGELOG.md +395 -0
  2. package/README.md +1 -1
  3. package/dist/app-sdk/index.d.ts +2 -0
  4. package/dist/app-sdk/index.js +481 -0
  5. package/dist/app-sdk/index.js.map +1 -0
  6. package/dist/app-sdk/src/app.d.ts +152 -0
  7. package/dist/app-sdk/src/app.d.ts.map +1 -0
  8. package/dist/app-sdk/src/connection-params.d.ts +117 -0
  9. package/dist/app-sdk/src/connection-params.d.ts.map +1 -0
  10. package/dist/app-sdk/src/define-app-capability.d.ts +139 -0
  11. package/dist/app-sdk/src/define-app-capability.d.ts.map +1 -0
  12. package/dist/app-sdk/src/index.d.ts +17 -0
  13. package/dist/app-sdk/src/index.d.ts.map +1 -0
  14. package/dist/app-sdk/src/logger.d.ts +26 -0
  15. package/dist/app-sdk/src/logger.d.ts.map +1 -0
  16. package/dist/app-sdk/src/transport.d.ts +93 -0
  17. package/dist/app-sdk/src/transport.d.ts.map +1 -0
  18. package/dist/app-sdk/src/ws-transport.d.ts +79 -0
  19. package/dist/app-sdk/src/ws-transport.d.ts.map +1 -0
  20. package/dist/{asset-feeds-NEZKG6YS.js → asset-feeds-T35ELX2O.js} +11 -11
  21. package/dist/{asset-feeds-NEZKG6YS.js.map → asset-feeds-T35ELX2O.js.map} +1 -1
  22. package/dist/asset-manager/index.js +15 -10
  23. package/dist/asset-manager/installer.js +9 -9
  24. package/dist/asset-manager/renderers.js +2 -2
  25. package/dist/asset-manager/src/index.d.ts +42 -6
  26. package/dist/asset-manager/src/index.d.ts.map +1 -1
  27. package/dist/bridge/drivers/claude-sdk.js +2 -2
  28. package/dist/bridge/drivers/codex.js +1 -1
  29. package/dist/bridge/drivers/continue.js +2 -2
  30. package/dist/bridge/drivers/echo.js +1 -1
  31. package/dist/bridge/drivers/gemini.js +3 -3
  32. package/dist/bridge/drivers/goose.js +2 -2
  33. package/dist/bridge/drivers/omp.js +1 -1
  34. package/dist/bridge/drivers/opencode.js +2 -2
  35. package/dist/bridge/drivers/qwen.js +3 -3
  36. package/dist/bridge/index.js +3 -3
  37. package/dist/{chunk-YOPSHV7V.js → chunk-2DWXGUZA.js} +3 -3
  38. package/dist/{chunk-YOPSHV7V.js.map → chunk-2DWXGUZA.js.map} +1 -1
  39. package/dist/{chunk-XBG2YDQQ.js → chunk-2ZMO4CFG.js} +3 -3
  40. package/dist/{chunk-XBG2YDQQ.js.map → chunk-2ZMO4CFG.js.map} +1 -1
  41. package/dist/{chunk-OBSSORCE.js → chunk-3CHARYD4.js} +84 -12
  42. package/dist/chunk-3CHARYD4.js.map +1 -0
  43. package/dist/{chunk-SKADPHAM.js → chunk-4N5XR5DQ.js} +2 -2
  44. package/dist/chunk-4N5XR5DQ.js.map +1 -0
  45. package/dist/{chunk-WFEPI5LA.js → chunk-6NR2BEIY.js} +6 -6
  46. package/dist/{chunk-WFEPI5LA.js.map → chunk-6NR2BEIY.js.map} +1 -1
  47. package/dist/{chunk-7CNIRF42.js → chunk-BGIC25VK.js} +26 -15
  48. package/dist/chunk-BGIC25VK.js.map +1 -0
  49. package/dist/{chunk-YXEPZ7LP.js → chunk-CNSNN7N6.js} +6 -6
  50. package/dist/{chunk-YXEPZ7LP.js.map → chunk-CNSNN7N6.js.map} +1 -1
  51. package/dist/{chunk-PSMW5HLX.js → chunk-DMG6NJH7.js} +5 -5
  52. package/dist/{chunk-PSMW5HLX.js.map → chunk-DMG6NJH7.js.map} +1 -1
  53. package/dist/{chunk-P3MJYPZE.js → chunk-FDE7XRQA.js} +57 -9
  54. package/dist/chunk-FDE7XRQA.js.map +1 -0
  55. package/dist/{chunk-K5SD7FSF.js → chunk-HKX5VSWQ.js} +3 -3
  56. package/dist/{chunk-K5SD7FSF.js.map → chunk-HKX5VSWQ.js.map} +1 -1
  57. package/dist/{chunk-AZAS3FKN.js → chunk-HY33RMEA.js} +49 -3
  58. package/dist/chunk-HY33RMEA.js.map +1 -0
  59. package/dist/{chunk-XKARS6EE.js → chunk-IEX5YV4R.js} +3 -3
  60. package/dist/{chunk-XKARS6EE.js.map → chunk-IEX5YV4R.js.map} +1 -1
  61. package/dist/{chunk-GB4SEEK4.js → chunk-IRORTHGA.js} +10 -4
  62. package/dist/chunk-IRORTHGA.js.map +1 -0
  63. package/dist/chunk-J5TI55E7.js +203 -0
  64. package/dist/chunk-J5TI55E7.js.map +1 -0
  65. package/dist/chunk-JIDKX7GC.js +122 -0
  66. package/dist/chunk-JIDKX7GC.js.map +1 -0
  67. package/dist/{chunk-BDUANIAR.js → chunk-KEQ4GLVM.js} +3 -3
  68. package/dist/{chunk-BDUANIAR.js.map → chunk-KEQ4GLVM.js.map} +1 -1
  69. package/dist/{chunk-5XS3BFJS.js → chunk-KMY7HVQ6.js} +3 -3
  70. package/dist/{chunk-5XS3BFJS.js.map → chunk-KMY7HVQ6.js.map} +1 -1
  71. package/dist/{chunk-BZMTR5WW.js → chunk-MZV6WCF3.js} +83 -51
  72. package/dist/chunk-MZV6WCF3.js.map +1 -0
  73. package/dist/{chunk-QK5JDAFF.js → chunk-NKTFEV7Q.js} +4 -4
  74. package/dist/{chunk-QK5JDAFF.js.map → chunk-NKTFEV7Q.js.map} +1 -1
  75. package/dist/{chunk-DZCK7GRW.js → chunk-P4VHQA4N.js} +22 -6
  76. package/dist/chunk-P4VHQA4N.js.map +1 -0
  77. package/dist/{chunk-EVZKWY5J.js → chunk-PK7Q7MC7.js} +5 -4
  78. package/dist/chunk-PK7Q7MC7.js.map +1 -0
  79. package/dist/{chunk-ETYDLXI5.js → chunk-RDYOU6N6.js} +2 -2
  80. package/dist/{chunk-ETYDLXI5.js.map → chunk-RDYOU6N6.js.map} +1 -1
  81. package/dist/{chunk-RHMQPLIK.js → chunk-RSYV7XEN.js} +14 -8
  82. package/dist/chunk-RSYV7XEN.js.map +1 -0
  83. package/dist/{chunk-SUOGVHGJ.js → chunk-TNSCHS4I.js} +2 -2
  84. package/dist/{chunk-SUOGVHGJ.js.map → chunk-TNSCHS4I.js.map} +1 -1
  85. package/dist/{chunk-Z5Z3MYDL.js → chunk-UPZGQZEJ.js} +3 -3
  86. package/dist/{chunk-Z5Z3MYDL.js.map → chunk-UPZGQZEJ.js.map} +1 -1
  87. package/dist/{chunk-RJ3HUSBC.js → chunk-UQ5FK2AB.js} +3 -3
  88. package/dist/{chunk-RJ3HUSBC.js.map → chunk-UQ5FK2AB.js.map} +1 -1
  89. package/dist/{chunk-OUWNTFDG.js → chunk-UQKYT4V6.js} +38 -6
  90. package/dist/chunk-UQKYT4V6.js.map +1 -0
  91. package/dist/chunk-WYGXYGJH.js +688 -0
  92. package/dist/chunk-WYGXYGJH.js.map +1 -0
  93. package/dist/{chunk-TJRWGMD2.js → chunk-ZBZQC5R7.js} +3 -3
  94. package/dist/{chunk-TJRWGMD2.js.map → chunk-ZBZQC5R7.js.map} +1 -1
  95. package/dist/cli/index.js +1022 -374
  96. package/dist/cli/index.js.map +1 -1
  97. package/dist/cli/src/commands/catalog.d.ts +18 -0
  98. package/dist/cli/src/commands/catalog.d.ts.map +1 -1
  99. package/dist/cli/src/commands/library-cmd.d.ts +2 -2
  100. package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
  101. package/dist/cli/src/commands/library-publish.d.ts +53 -0
  102. package/dist/cli/src/commands/library-publish.d.ts.map +1 -0
  103. package/dist/cli/src/commands/preset-apply.d.ts +59 -0
  104. package/dist/cli/src/commands/preset-apply.d.ts.map +1 -0
  105. package/dist/cli/src/commands/preset-cmd.d.ts +8 -2
  106. package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
  107. package/dist/cli/src/commands/preset-export.d.ts +102 -0
  108. package/dist/cli/src/commands/preset-export.d.ts.map +1 -0
  109. package/dist/cli/src/commands/project.d.ts.map +1 -1
  110. package/dist/cli/src/commands/publish-core.d.ts +75 -0
  111. package/dist/cli/src/commands/publish-core.d.ts.map +1 -0
  112. package/dist/cli/src/commands/run.d.ts.map +1 -1
  113. package/dist/cli/src/commands/source-manifest-publish.d.ts +82 -0
  114. package/dist/cli/src/commands/source-manifest-publish.d.ts.map +1 -0
  115. package/dist/cli/src/commands/source-manifest.d.ts +65 -6
  116. package/dist/cli/src/commands/source-manifest.d.ts.map +1 -1
  117. package/dist/cli/src/commands/source.d.ts +13 -0
  118. package/dist/cli/src/commands/source.d.ts.map +1 -1
  119. package/dist/cli/src/commands/store.d.ts.map +1 -1
  120. package/dist/cli/src/commands/why.d.ts +18 -0
  121. package/dist/cli/src/commands/why.d.ts.map +1 -1
  122. package/dist/cli/src/driver-error.d.ts +12 -0
  123. package/dist/cli/src/driver-error.d.ts.map +1 -0
  124. package/dist/cli/src/open-library.d.ts +4 -0
  125. package/dist/cli/src/open-library.d.ts.map +1 -1
  126. package/dist/cli/src/store-auth.d.ts +42 -0
  127. package/dist/cli/src/store-auth.d.ts.map +1 -0
  128. package/dist/cli/src/store-client.d.ts +20 -22
  129. package/dist/cli/src/store-client.d.ts.map +1 -1
  130. package/dist/cli/src/store-publisher.d.ts +91 -0
  131. package/dist/cli/src/store-publisher.d.ts.map +1 -0
  132. package/dist/connectors/config.js +6 -6
  133. package/dist/connectors/index.js +8 -8
  134. package/dist/core/index.js +5 -5
  135. package/dist/core/publish-manifest.js +1 -1
  136. package/dist/core/runtime-assets.js +3 -3
  137. package/dist/core/src/cache-paths.d.ts +17 -0
  138. package/dist/core/src/cache-paths.d.ts.map +1 -1
  139. package/dist/core/src/index.d.ts +2 -2
  140. package/dist/core/src/index.d.ts.map +1 -1
  141. package/dist/core/src/publish-manifest.d.ts.map +1 -1
  142. package/dist/core/src/repo-manager.d.ts +13 -2
  143. package/dist/core/src/repo-manager.d.ts.map +1 -1
  144. package/dist/core/src/walker.d.ts +10 -0
  145. package/dist/core/src/walker.d.ts.map +1 -1
  146. package/dist/core/src/workspace-yaml-editor.d.ts +14 -1
  147. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  148. package/dist/core/workspace-config.js +2 -2
  149. package/dist/deploy/index.js +5 -5
  150. package/dist/discovery/index.js +3 -3
  151. package/dist/discovery/src/discover-manifest.d.ts.map +1 -1
  152. package/dist/discovery/src/discover.d.ts +17 -4
  153. package/dist/discovery/src/discover.d.ts.map +1 -1
  154. package/dist/discovery/src/index.d.ts +1 -1
  155. package/dist/discovery/src/index.d.ts.map +1 -1
  156. package/dist/discovery/src/reference-lint.d.ts +7 -0
  157. package/dist/discovery/src/reference-lint.d.ts.map +1 -1
  158. package/dist/discovery/src/source-config.d.ts +28 -0
  159. package/dist/discovery/src/source-config.d.ts.map +1 -1
  160. package/dist/discovery/src/tree-entries.d.ts.map +1 -1
  161. package/dist/{ensure-sources-4BAJGJRK.js → ensure-sources-QDVFUIUP.js} +18 -13
  162. package/dist/ensure-sources-QDVFUIUP.js.map +1 -0
  163. package/dist/factory-assets/connectors/box.js +8 -8
  164. package/dist/factory-assets/connectors/deploy.js +8 -8
  165. package/dist/factory-assets/connectors/devserver.js +8 -8
  166. package/dist/factory-assets/connectors/flow/adapter.js +8 -8
  167. package/dist/factory-assets/connectors/flow/engine/engine.d.ts +4 -1
  168. package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
  169. package/dist/factory-assets/connectors/flow/engine/types.d.ts +41 -6
  170. package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
  171. package/dist/factory-assets/connectors/flow/engine.js +6 -6
  172. package/dist/factory-assets/connectors/flow/run-flow.js +9 -9
  173. package/dist/factory-assets/connectors/flow.js +8 -8
  174. package/dist/factory-assets/connectors/git.js +8 -8
  175. package/dist/factory-assets/connectors/gmail.js +8 -8
  176. package/dist/factory-assets/connectors/googledrive.js +8 -8
  177. package/dist/factory-assets/connectors/local.js +8 -8
  178. package/dist/factory-assets/connectors/mattermost.js +8 -8
  179. package/dist/factory-assets/connectors/memory.js +8 -8
  180. package/dist/factory-assets/connectors/minio.js +8 -8
  181. package/dist/factory-assets/connectors/postgres.js +8 -8
  182. package/dist/factory-assets/connectors/s3.js +8 -8
  183. package/dist/factory-assets/connectors/sharepoint.js +8 -8
  184. package/dist/factory-assets/connectors/sqlite.js +8 -8
  185. package/dist/factory-assets/connectors/static-server.js +8 -8
  186. package/dist/factory-assets/connectors/tunnel.js +8 -8
  187. package/dist/factory-assets/connectors/webdav.js +8 -8
  188. package/dist/factory-assets/connectors/xstate-store.js +8 -8
  189. package/dist/factory-assets/connectors/xstate.js +8 -8
  190. package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +1 -1
  191. package/dist/factory-assets/skills/skaile-manage-workspace/SKILL.md +1 -1
  192. package/dist/{flows-UFCOGAAM.js → flows-MRVXTYWC.js} +4 -4
  193. package/dist/{flows-UFCOGAAM.js.map → flows-MRVXTYWC.js.map} +1 -1
  194. package/dist/helpers-TF7GCBRQ.js +27 -0
  195. package/dist/{helpers-2OL4FRFB.js.map → helpers-TF7GCBRQ.js.map} +1 -1
  196. package/dist/library/index.js +9 -9
  197. package/dist/library/install.js +5 -5
  198. package/dist/library/src/index.d.ts +2 -2
  199. package/dist/library/src/index.d.ts.map +1 -1
  200. package/dist/library/src/local/local-catalog-source.d.ts +12 -1
  201. package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
  202. package/dist/library/src/sync/manifest-writeback.d.ts +36 -0
  203. package/dist/library/src/sync/manifest-writeback.d.ts.map +1 -1
  204. package/dist/library-publish-K5IBE44D.js +249 -0
  205. package/dist/library-publish-K5IBE44D.js.map +1 -0
  206. package/dist/open-library-4HMIDK3N.js +29 -0
  207. package/dist/{open-library-RPV2LIKL.js.map → open-library-4HMIDK3N.js.map} +1 -1
  208. package/dist/{paths-ML33B5SB.js → paths-P3DWFUPS.js} +8 -8
  209. package/dist/{paths-ML33B5SB.js.map → paths-P3DWFUPS.js.map} +1 -1
  210. package/dist/{plugin-store-G4HKWCTU.js → plugin-store-S7OMGI7N.js} +7 -7
  211. package/dist/{plugin-store-G4HKWCTU.js.map → plugin-store-S7OMGI7N.js.map} +1 -1
  212. package/dist/runner/index.js +14 -14
  213. package/dist/runner/src/agent.d.ts.map +1 -1
  214. package/dist/runner/src/capability-registry.d.ts +15 -4
  215. package/dist/runner/src/capability-registry.d.ts.map +1 -1
  216. package/dist/sdk/asset-manager.js +15 -10
  217. package/dist/sdk/bridge.js +3 -3
  218. package/dist/sdk/core.js +5 -5
  219. package/dist/sdk/flow.js +6 -6
  220. package/dist/sdk/index.js +14 -14
  221. package/dist/sdk/runner.js +14 -14
  222. package/dist/sdk/session.js +2 -2
  223. package/dist/sdk/types.js +1 -1
  224. package/dist/session/index.js +2 -2
  225. package/dist/{setup-WS6PF6JQ.js → setup-2CCS63L6.js} +10 -10
  226. package/dist/{setup-WS6PF6JQ.js.map → setup-2CCS63L6.js.map} +1 -1
  227. package/dist/source-LOAM7IIQ.js +30 -0
  228. package/dist/{source-22U5NOY4.js.map → source-LOAM7IIQ.js.map} +1 -1
  229. package/dist/source-manifest-publish-TGVZ6KMC.js +228 -0
  230. package/dist/source-manifest-publish-TGVZ6KMC.js.map +1 -0
  231. package/dist/tui/index.js +14 -14
  232. package/dist/types/index.js +1 -1
  233. package/dist/types/manifests.js +1 -1
  234. package/dist/types/src/capabilities.d.ts +8 -2
  235. package/dist/types/src/capabilities.d.ts.map +1 -1
  236. package/dist/types/src/logging.d.ts +3 -2
  237. package/dist/types/src/logging.d.ts.map +1 -1
  238. package/dist/types/src/manifests/_shared.d.ts +4 -4
  239. package/dist/types/src/manifests/index.d.ts +2 -2
  240. package/dist/types/src/manifests/index.d.ts.map +1 -1
  241. package/dist/types/src/manifests/preset.d.ts +10 -0
  242. package/dist/types/src/manifests/preset.d.ts.map +1 -1
  243. package/dist/types/src/version.d.ts +11 -1
  244. package/dist/types/src/version.d.ts.map +1 -1
  245. package/dist/workspace-plugin/adapters/mcp.js +2 -2
  246. package/dist/workspace-plugin/adapters/omp.js +3 -3
  247. package/dist/workspace-plugin/index.js +1 -1
  248. package/package.json +7 -1
  249. package/dist/chunk-7CNIRF42.js.map +0 -1
  250. package/dist/chunk-AZAS3FKN.js.map +0 -1
  251. package/dist/chunk-BZMTR5WW.js.map +0 -1
  252. package/dist/chunk-DZCK7GRW.js.map +0 -1
  253. package/dist/chunk-EVZKWY5J.js.map +0 -1
  254. package/dist/chunk-GB4SEEK4.js.map +0 -1
  255. package/dist/chunk-JQMIG7D3.js +0 -93
  256. package/dist/chunk-JQMIG7D3.js.map +0 -1
  257. package/dist/chunk-OBSSORCE.js.map +0 -1
  258. package/dist/chunk-OUWNTFDG.js.map +0 -1
  259. package/dist/chunk-P3MJYPZE.js.map +0 -1
  260. package/dist/chunk-RHMQPLIK.js.map +0 -1
  261. package/dist/chunk-RYRJEVQ4.js +0 -377
  262. package/dist/chunk-RYRJEVQ4.js.map +0 -1
  263. package/dist/chunk-SKADPHAM.js.map +0 -1
  264. package/dist/ensure-sources-4BAJGJRK.js.map +0 -1
  265. package/dist/helpers-2OL4FRFB.js +0 -22
  266. package/dist/open-library-RPV2LIKL.js +0 -29
  267. package/dist/source-22U5NOY4.js +0 -30
  268. package/dist/store-client-YLMBH5MF.js +0 -15
  269. package/dist/store-client-YLMBH5MF.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1,20 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { makeSourceCommand, syncProjectSources } from '../chunk-RYRJEVQ4.js';
3
- import { openCatalogSource, openLibraryManager, createFullRegistry, openLibrary } from '../chunk-RHMQPLIK.js';
4
- import { logErr, S, logOk, logInfo, colorRef, logWarn, kindColorPad, kindColor, formatSoftDepSection, bareSoftDepName, formatSoftDepHint, formatRelativeTime, fitWidth, formatSoftDepRemovePreview, formatSoftDepAddPreview } from '../chunk-5XS3BFJS.js';
5
- import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-JQMIG7D3.js';
6
- import { AI_RESOURCES, aiResourceRoots } from '../chunk-XBG2YDQQ.js';
2
+ import { AI_RESOURCES, aiResourceRoots } from '../chunk-2ZMO4CFG.js';
3
+ import { getStoreConfig, storeFetch, deviceLogin, loadStoreAuthConfig, logout, hasStoredAuth, isStoreAuthenticated, getStoreToken } from '../chunk-J5TI55E7.js';
4
+ import { makeSourceCommand, syncProjectSources } from '../chunk-WYGXYGJH.js';
5
+ import { openCatalogSource, openLibraryManager, createFullRegistry, openLibrary } from '../chunk-RSYV7XEN.js';
6
+ import { logErr, S, logOk, logInfo, colorRef, logWarn, kindColorPad, kindColor, formatSoftDepSection, bareSoftDepName, formatSoftDepHint, formatRelativeTime, fitWidth, formatSoftDepRemovePreview, formatSoftDepAddPreview } from '../chunk-KMY7HVQ6.js';
7
7
  import { LocalSecretsProvider } from '../chunk-6BEGKO2Q.js';
8
- import { resolveLibraryDir } from '../chunk-P3MJYPZE.js';
9
- import '../chunk-BZMTR5WW.js';
10
- import '../chunk-UKJMQZV6.js';
11
- import '../chunk-R7FOF242.js';
12
- import '../chunk-JXT46QFC.js';
13
- import { runFlow, resumeFlow } from '../chunk-YXEPZ7LP.js';
8
+ import { runFlow, resumeFlow } from '../chunk-CNSNN7N6.js';
14
9
  import '../chunk-4R6DROGN.js';
15
10
  import { validateFlowVersions, parseSkillFrontmatter } from '../chunk-GXC3TYMQ.js';
16
- import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-7CNIRF42.js';
17
- import { buildClaudePluginFiles } from '../chunk-ETYDLXI5.js';
11
+ import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-BGIC25VK.js';
12
+ import { buildClaudePluginFiles } from '../chunk-RDYOU6N6.js';
18
13
  import '../chunk-X5YPJV4N.js';
19
14
  import '../chunk-O7SG5PC2.js';
20
15
  import '../chunk-7QBNJTTQ.js';
@@ -22,38 +17,43 @@ import '../chunk-GTS2FODO.js';
22
17
  import '../chunk-WQ7DE5UC.js';
23
18
  import '../chunk-32NA4TVC.js';
24
19
  import '../chunk-W3UDISS2.js';
25
- import '../chunk-EVZKWY5J.js';
26
- import '../chunk-K5SD7FSF.js';
20
+ import '../chunk-HKX5VSWQ.js';
27
21
  import '../chunk-ISQWMBKD.js';
22
+ import '../chunk-RRVQAE5D.js';
28
23
  import '../chunk-BMQKMOQY.js';
29
24
  import '../chunk-GAH4YRCI.js';
30
- import '../chunk-RRVQAE5D.js';
31
- import '../chunk-SUOGVHGJ.js';
25
+ import '../chunk-TNSCHS4I.js';
32
26
  import '../chunk-LV2HPH3C.js';
33
- import '../chunk-PSMW5HLX.js';
34
- import '../chunk-BDUANIAR.js';
27
+ import '../chunk-DMG6NJH7.js';
28
+ import '../chunk-KEQ4GLVM.js';
35
29
  import '../chunk-6E6PKKAD.js';
36
- import { loadFlow, loadAllFlows } from '../chunk-SKADPHAM.js';
30
+ import { loadFlow, loadAllFlows } from '../chunk-4N5XR5DQ.js';
37
31
  import '../chunk-DEYYRFQU.js';
38
32
  import '../chunk-IFGTRL2I.js';
39
- import { AssetManager, NON_DEPLOYABLE_KINDS, MissingManifestError } from '../chunk-OBSSORCE.js';
40
- import '../chunk-WFEPI5LA.js';
41
- import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync, WorkspaceYamlEditor, checkAgentSkillConformance } from '../chunk-AZAS3FKN.js';
42
- import '../chunk-TJRWGMD2.js';
43
- import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, resolveSkWorkspaceConfig } from '../chunk-OUWNTFDG.js';
44
- import { SKAILE_MANIFEST_FILENAME, decodeSkaileManifest, loadSkPublishManifest, saveSkPublishManifest } from '../chunk-DZCK7GRW.js';
33
+ import '../chunk-PK7Q7MC7.js';
34
+ import { AssetManager, NON_DEPLOYABLE_KINDS, MissingManifestError, normalizeRepoUrl, FACTORY_SOURCE_URL } from '../chunk-3CHARYD4.js';
35
+ import { resolveLibraryDir, getStoreManifestsDir, getDefaultValue, isSecretPlaceholder } from '../chunk-FDE7XRQA.js';
36
+ import '../chunk-MZV6WCF3.js';
37
+ import '../chunk-JXT46QFC.js';
38
+ import '../chunk-UKJMQZV6.js';
39
+ import '../chunk-R7FOF242.js';
40
+ import '../chunk-6NR2BEIY.js';
41
+ import '../chunk-CGYEHQOX.js';
42
+ import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync, WorkspaceYamlEditor, checkAgentSkillConformance } from '../chunk-HY33RMEA.js';
43
+ import { assetSearchDirs, detectDomains } from '../chunk-ZAP2BDHG.js';
44
+ import { factoryAssetRoots } from '../chunk-ZBZQC5R7.js';
45
+ import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, loadSkWorkspaceConfig, resolveSkWorkspaceConfig, storeManifestSlug } from '../chunk-UQKYT4V6.js';
46
+ import { SKAILE_MANIFEST_FILENAME, decodeSkaileManifest, loadSkPublishManifest, saveSkPublishManifest } from '../chunk-P4VHQA4N.js';
45
47
  import { parseFrontmatter } from '../chunk-BQCGFPHW.js';
46
48
  import { parseAssetRef, ASSET_KINDS, isValidAssetName, slugifyAssetName, ASSET_NAME_HINT, assetRefToStr } from '../chunk-GEEW6SY2.js';
47
49
  import '../chunk-F3HNW354.js';
48
50
  import { SUPPORTED_DRIVER_TARGETS } from '../chunk-MENCWIHW.js';
49
51
  import { DRIVER_DEFAULTS } from '../chunk-K5GBV4SA.js';
50
- import { assetSearchDirs, detectDomains } from '../chunk-ZAP2BDHG.js';
51
52
  import { openSqlite } from '../chunk-JB35MVBN.js';
52
53
  import '../chunk-KTBKW2FI.js';
53
54
  import '../chunk-UQ6LFBPZ.js';
54
- import '../chunk-XKARS6EE.js';
55
- import '../chunk-GB4SEEK4.js';
56
- import '../chunk-CGYEHQOX.js';
55
+ import '../chunk-IEX5YV4R.js';
56
+ import { validatePreset } from '../chunk-IRORTHGA.js';
57
57
  import '../chunk-NSBPE2FW.js';
58
58
  import * as fs12 from 'fs';
59
59
  import fs12__default, { readFileSync, existsSync, mkdirSync, writeFileSync, lstatSync, rmSync, readdirSync, statSync } from 'fs';
@@ -71,8 +71,22 @@ import { createInterface } from 'readline';
71
71
  import fs6 from 'fs/promises';
72
72
  import { parse as parse$1, stringify as stringify$1 } from 'smol-toml';
73
73
  import { execSync } from 'child_process';
74
+ import * as readline2 from 'readline/promises';
74
75
  import { createTRPCUntypedClient, httpLink, httpSubscriptionLink } from '@trpc/client';
75
76
 
77
+ function catalogAssetRef(a) {
78
+ return `${a.kind}:@${a.publisher}/${a.name}#${a.version}`;
79
+ }
80
+ function localEntryRef(e) {
81
+ return e.publisher ? `${e.kind}:@${e.publisher}/${e.name}` : e.name;
82
+ }
83
+ function filterCatalogAssets(assets, query) {
84
+ if (!query) return assets;
85
+ const q = query.toLowerCase();
86
+ return assets.filter(
87
+ (a) => a.name.toLowerCase().includes(q) || (a.description?.toLowerCase().includes(q) ?? false) || a.publisher.toLowerCase().includes(q)
88
+ );
89
+ }
76
90
  async function runSearch(kind, query, opts) {
77
91
  if (kind?.includes(":")) {
78
92
  query = kind;
@@ -89,19 +103,68 @@ async function runSearch(kind, query, opts) {
89
103
  await searchStore(query, kind, opts, airGapped);
90
104
  }
91
105
  }
106
+ async function searchIndex(query, kind) {
107
+ const { openLibrary: openLibrary2 } = await import('../open-library-4HMIDK3N.js');
108
+ const library = await openLibrary2();
109
+ try {
110
+ const defs = await library.listAssetDefs(kind ? { kind } : {});
111
+ let entries = defs.map(
112
+ (d) => ({
113
+ name: d.name,
114
+ kind: d.kind,
115
+ description: d.description ?? "",
116
+ source: d.id,
117
+ // Stored publishers may carry the `@` scope sigil (manifest-mode rows);
118
+ // `localEntryRef` re-adds it, so strip here to avoid `@@`.
119
+ publisher: d.publisher ? d.publisher.replace(/^@/, "") : void 0,
120
+ version: d.version,
121
+ requires: [],
122
+ dependencies: [],
123
+ license: d.license,
124
+ category: d.category
125
+ })
126
+ );
127
+ if (query) {
128
+ const q = query.toLowerCase();
129
+ entries = entries.filter(
130
+ (e) => e.name.toLowerCase().includes(q) || e.description.toLowerCase().includes(q)
131
+ );
132
+ }
133
+ return entries;
134
+ } finally {
135
+ library.close();
136
+ }
137
+ }
138
+ function searchFactory(projectDir, query, kind) {
139
+ const prefixes = factoryAssetRoots().map((r) => r.endsWith(path14__default.sep) ? r : r + path14__default.sep);
140
+ const isFactoryPath = (p8) => prefixes.some((fp) => p8 === fp.slice(0, -1) || p8.startsWith(fp));
141
+ try {
142
+ return new AssetManager({ projectDir }).search(query, kind).filter((e) => isFactoryPath(e.source));
143
+ } catch {
144
+ return [];
145
+ }
146
+ }
147
+ function entryDedupKey(e) {
148
+ return `${e.kind}:${e.publisher ? `@${e.publisher}/` : ""}${e.name}`;
149
+ }
150
+ function unionEntries(indexed, factory) {
151
+ const seen = new Set(indexed.map(entryDedupKey));
152
+ return [...indexed, ...factory.filter((e) => !seen.has(entryDedupKey(e)))];
153
+ }
92
154
  async function searchLocal(query, kind, opts, showStore, airGapped) {
93
155
  const projectDir = path14__default.resolve(opts.projectDir);
94
- let entries = new AssetManager({ projectDir }).search(query, kind);
156
+ const factory = searchFactory(projectDir, query, kind);
157
+ let entries = unionEntries(await searchIndex(query, kind), factory);
95
158
  if (entries.length === 0 && !query && !kind && !airGapped) {
96
- const { autoSyncForSearch } = await import('../source-22U5NOY4.js');
159
+ const { autoSyncForSearch } = await import('../source-LOAM7IIQ.js');
97
160
  if (await autoSyncForSearch(projectDir)) {
98
- entries = new AssetManager({ projectDir }).search(query, kind);
161
+ entries = unionEntries(await searchIndex(query, kind), factory);
99
162
  }
100
163
  }
101
164
  if (entries.length === 0) {
102
165
  if (!showStore) {
103
166
  logInfo(
104
- query ? `No entries matching "${query}".` : "No assets found. Run `skaile source sync` first."
167
+ query ? `No entries matching "${query}".` : kind ? `No "${kind}" assets found in this project's sources.` : "No assets found. Run `skaile source sync` first."
105
168
  );
106
169
  }
107
170
  return;
@@ -119,7 +182,7 @@ function printLocalEntries(entries) {
119
182
  byKind.get(e.kind).push(e);
120
183
  }
121
184
  for (const [k, items] of byKind) {
122
- const nameW = Math.max(4, ...items.map((e) => e.name.length));
185
+ const nameW = Math.max(4, ...items.map((e) => localEntryRef(e).length));
123
186
  console.log();
124
187
  console.log(
125
188
  ` ${kindColor(k)} ${S.heading(`${k.toUpperCase()}S`)} ${S.dim(`(${items.length})`)}`
@@ -127,7 +190,9 @@ function printLocalEntries(entries) {
127
190
  console.log(` ${S.rule(nameW + 62)}`);
128
191
  for (const e of items) {
129
192
  const ver = e.version ? S.dim(` v${e.version}`) : "";
130
- console.log(` ${S.cmd(e.name.padEnd(nameW))} ${e.description.slice(0, 60)}${ver}`);
193
+ console.log(
194
+ ` ${S.cmd(localEntryRef(e).padEnd(nameW))} ${e.description.slice(0, 60)}${ver}`
195
+ );
131
196
  }
132
197
  }
133
198
  console.log(`
@@ -145,30 +210,33 @@ async function searchStore(query, kind, opts, airGapped) {
145
210
  logInfo("air-gapped (cache_ttl: 0) \u2014 local sources only");
146
211
  return;
147
212
  }
213
+ const { openCatalogSource: openCatalogSource2, LocalCatalogDisabledError } = await import('../open-library-4HMIDK3N.js');
148
214
  try {
149
- const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-YLMBH5MF.js');
150
- const config = getStoreConfig2();
151
- const params = {};
152
- if (query) params.q = query;
153
- if (kind) params.kind = kind;
154
- const storeRes = await storeFetch2(config, "/api/assets", { params });
155
- if (storeRes.results.length > 0) {
215
+ const remote = await openCatalogSource2({ projectDir: path14__default.resolve(opts.projectDir) });
216
+ const assets = await remote.listAssets(kind ? { kind } : {});
217
+ const results = filterCatalogAssets(assets, query);
218
+ if (results.length > 0) {
156
219
  console.log(`
157
220
  ${S.heading("Store:")}`);
158
- for (const asset of storeRes.results) {
159
- const ref = `${asset.kind}:@${asset.publisher}/${asset.slug}`;
221
+ for (const asset of results) {
222
+ const ref = catalogAssetRef(asset);
160
223
  console.log(
161
- ` \u2B21 ${kindColorPad(asset.kind)} ${colorRef(ref).padEnd(40)} ${S.dim(asset.description?.slice(0, 50) || "")} ${S.dim(`\u2193${asset.downloads}`)}`
224
+ ` \u2B21 ${kindColorPad(asset.kind)} ${colorRef(ref).padEnd(48)} ${S.dim(asset.description?.slice(0, 50) || "")}`
162
225
  );
163
226
  }
164
227
  } else if (opts.store) {
165
228
  logInfo("No results in store");
166
229
  }
167
230
  } catch (e) {
231
+ const local = e instanceof LocalCatalogDisabledError;
168
232
  if (opts.store) {
169
- logErr(`Store search failed: ${e.message}`);
233
+ logErr(
234
+ local ? "remote catalog disabled (catalog.url: local) \u2014 set catalog.url: https://skaile.store to search the store" : `Store search failed: ${e.message}`
235
+ );
170
236
  } else {
171
- logInfo("store unavailable \u2014 showing local results only (run with --store to see the error)");
237
+ logInfo(
238
+ local ? "remote catalog disabled (catalog.url: local) \u2014 showing local results only" : "store unavailable \u2014 showing local results only (run with --store to see the error)"
239
+ );
172
240
  }
173
241
  }
174
242
  }
@@ -1006,7 +1074,7 @@ function makeCatalogCommand() {
1006
1074
  return catalog;
1007
1075
  }
1008
1076
  async function runLocalCatalogTest(projectDir) {
1009
- const { resolveCatalogSource } = await import('../open-library-RPV2LIKL.js');
1077
+ const { resolveCatalogSource } = await import('../open-library-4HMIDK3N.js');
1010
1078
  let resolved;
1011
1079
  try {
1012
1080
  resolved = await resolveCatalogSource({ projectDir });
@@ -1625,8 +1693,8 @@ async function promptField(field) {
1625
1693
  function makeConnectorCommand() {
1626
1694
  const cmd = new Command("connector").description("Operate on connectors in a workspace");
1627
1695
  cmd.command("catalog").description("List all available connector drivers and their dependencies").action(async () => {
1628
- const { scanDirectory, factoryAssetRoots } = await import('../core/index.js');
1629
- const entries = scanFactoryConnectors(scanDirectory, factoryAssetRoots());
1696
+ const { scanDirectory, factoryAssetRoots: factoryAssetRoots2 } = await import('../core/index.js');
1697
+ const entries = scanFactoryConnectors(scanDirectory, factoryAssetRoots2());
1630
1698
  if (entries.length === 0) {
1631
1699
  console.log("No connector descriptors found.");
1632
1700
  return;
@@ -1650,14 +1718,14 @@ function makeConnectorCommand() {
1650
1718
  cmd.command("add <driver>").option("--id <id>", "Connector instance ID (defaults to driver name)").option("--access <level>", "Access level: read-only | read-write", "read-only").option("--no-install", "Skip npm dependency installation").option("--project-dir <path>", "Workspace directory", process.cwd()).description("Add a connector to skaile.yaml and install its npm deps").action(
1651
1719
  async (driver, opts) => {
1652
1720
  const { installNpmPackages } = await import('../connectors/index.js');
1653
- const { scanDirectory, factoryAssetRoots, WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
1721
+ const { scanDirectory, factoryAssetRoots: factoryAssetRoots2, WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
1654
1722
  const projectDir = path14__default.resolve(opts.projectDir);
1655
1723
  const yamlPath = path14__default.join(projectDir, "skaile.yaml");
1656
1724
  if (!existsSync(yamlPath)) {
1657
1725
  logErr("No skaile.yaml found in current directory. Run 'skaile init' first.");
1658
1726
  process.exit(1);
1659
1727
  }
1660
- const entries = scanFactoryConnectors(scanDirectory, factoryAssetRoots());
1728
+ const entries = scanFactoryConnectors(scanDirectory, factoryAssetRoots2());
1661
1729
  const entry = entries.find((e) => e.name === driver);
1662
1730
  if (!entry) {
1663
1731
  logErr(
@@ -1996,7 +2064,7 @@ async function ensureTargetsRegistered(projectDir) {
1996
2064
  const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
1997
2065
  const config = resolveSkWorkspaceConfig2(projectDir);
1998
2066
  if (config.plugins && config.plugins.length > 0) {
1999
- const { ensurePluginsLoaded } = await import('../plugin-store-G4HKWCTU.js');
2067
+ const { ensurePluginsLoaded } = await import('../plugin-store-S7OMGI7N.js');
2000
2068
  const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
2001
2069
  for (const f of result.failed) logWarn(f.error);
2002
2070
  }
@@ -2801,11 +2869,6 @@ function makeIntegrationCommand() {
2801
2869
  cmd.action(() => cmd.help());
2802
2870
  return cmd;
2803
2871
  }
2804
- var NOT_WIRED_MSG_PUBLISH = "`skaile library publish` is scaffolded but not yet wired. Server endpoint `POST /sources/<id>/manifest` lands in a follow-up PR.";
2805
- function notWired(verb, msg) {
2806
- logErr(`${msg} (verb: library ${verb})`);
2807
- process.exit(2);
2808
- }
2809
2872
  function makeLibraryCommand() {
2810
2873
  const cmd = new Command("library").description("Manage user authoring libraries (local / git)");
2811
2874
  cmd.command("list").description("List all registered libraries").option("--json", "Output as JSON").action(async (opts) => {
@@ -3181,9 +3244,20 @@ function makeLibraryCommand() {
3181
3244
  close();
3182
3245
  }
3183
3246
  });
3184
- cmd.command("publish <ref>").description("Push + register manifest atomically (needs git backend)").action((_ref) => notWired("publish", NOT_WIRED_MSG_PUBLISH));
3185
- cmd.command("register <ref>").description("Register manifest in store without pushing (bytes already on github)").action((_ref) => notWired("register", NOT_WIRED_MSG_PUBLISH));
3186
- cmd.command("yank <ref>").description("Retract a catalog entry").option("--version <v>", "Version to yank").action((_ref) => notWired("yank", NOT_WIRED_MSG_PUBLISH));
3247
+ cmd.command("publish <lib>").description("Push + register the library's manifest atomically (needs git backend)").option("--asset <ref>", "Publish only the single asset matching this ref/name").option("--create-repo", "Provision + link a public github repo if the library has no remote").option("--ref <gitref>", "Git ref to ingest (default: the library branch or main)").option("--dry-run", "Print the plan; push nothing and call no store endpoint").action(
3248
+ async (lib, opts) => {
3249
+ const { runLibraryPublish } = await import('../library-publish-K5IBE44D.js');
3250
+ await runLibraryPublish(lib, opts);
3251
+ }
3252
+ );
3253
+ cmd.command("register <lib>").description("Register the library's manifest without pushing (bytes already on github)").option("--asset <ref>", "Publish only the single asset matching this ref/name").option("--ref <gitref>", "Git ref to ingest (default: the library branch or main)").option("--dry-run", "Print the plan; call no store endpoint").action(async (lib, opts) => {
3254
+ const { runLibraryRegister } = await import('../library-publish-K5IBE44D.js');
3255
+ await runLibraryRegister(lib, opts);
3256
+ });
3257
+ cmd.command("yank <lib>").description("Retract the library's published assets from the store").option("--asset <ref>", "Yank only the single asset matching this ref/name").action(async (lib, opts) => {
3258
+ const { runLibraryYank } = await import('../library-publish-K5IBE44D.js');
3259
+ await runLibraryYank(lib, opts);
3260
+ });
3187
3261
  return cmd;
3188
3262
  }
3189
3263
  async function printPerLibraryStatus(name, asJson) {
@@ -3712,7 +3786,7 @@ async function previewSoftDepsUnderCursor() {
3712
3786
  }
3713
3787
  async function loadAssets() {
3714
3788
  softDepPreviewCache.clear();
3715
- const { gatherAssetFeeds } = await import('../asset-feeds-NEZKG6YS.js');
3789
+ const { gatherAssetFeeds } = await import('../asset-feeds-T35ELX2O.js');
3716
3790
  const { entries, domains, notes } = await gatherAssetFeeds(am, am.projectDir);
3717
3791
  if (notes.length > 0) {
3718
3792
  state.message = notes.map((n) => `[${n.feed}] ${n.message}`).join(" \u2022 ");
@@ -3879,7 +3953,7 @@ function reconcileCollapsedSets(rows) {
3879
3953
  async function loadSourcesAndLibraries() {
3880
3954
  try {
3881
3955
  const [{ openLibraryManager: openLibraryManager2 }, { skaileHomeDir }, { getGlobalCacheDir: getGlobalCacheDir2 }] = await Promise.all([
3882
- import('../open-library-RPV2LIKL.js'),
3956
+ import('../open-library-4HMIDK3N.js'),
3883
3957
  import('../library/index.js'),
3884
3958
  import('../core/index.js')
3885
3959
  ]);
@@ -4748,8 +4822,8 @@ function makeManageCommand() {
4748
4822
  }
4749
4823
  function makeMcpServerCommand() {
4750
4824
  return new Command("mcp-server").description("Start skaile workspace MCP server (stdio transport)").option("--project-dir <path>", "Project directory (default: cwd)", process.cwd()).action(async (opts) => {
4751
- const path36 = await import('path');
4752
- const projectDir = path36.default.resolve(opts.projectDir);
4825
+ const path37 = await import('path');
4826
+ const projectDir = path37.default.resolve(opts.projectDir);
4753
4827
  const { LogStore, StdoutSink, registerLogStore } = await import('../core/logging.js');
4754
4828
  registerLogStore(
4755
4829
  new LogStore({
@@ -4965,7 +5039,7 @@ function action2(fn) {
4965
5039
  };
4966
5040
  }
4967
5041
  async function reconcileAndReport(projectDir, plugins) {
4968
- const { reconcilePlugins } = await import('../plugin-store-G4HKWCTU.js');
5042
+ const { reconcilePlugins } = await import('../plugin-store-S7OMGI7N.js');
4969
5043
  if (plugins.length === 0) {
4970
5044
  logInfo("No plugins declared \u2014 nothing to reconcile.");
4971
5045
  return;
@@ -4981,7 +5055,7 @@ async function reconcileAndReport(projectDir, plugins) {
4981
5055
  async function runInstall2(spec, opts) {
4982
5056
  const projectDir = path14__default.resolve(opts.projectDir);
4983
5057
  const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
4984
- const { specName } = await import('../plugin-store-G4HKWCTU.js');
5058
+ const { specName } = await import('../plugin-store-S7OMGI7N.js');
4985
5059
  const yamlPath = path14__default.join(projectDir, "skaile.yaml");
4986
5060
  const editor = WorkspaceYamlEditor2.load(yamlPath);
4987
5061
  const replaced = editor.addPlugin(spec, specName);
@@ -4993,7 +5067,7 @@ async function runInstall2(spec, opts) {
4993
5067
  async function runRemove(name, opts) {
4994
5068
  const projectDir = path14__default.resolve(opts.projectDir);
4995
5069
  const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
4996
- const { specName } = await import('../plugin-store-G4HKWCTU.js');
5070
+ const { specName } = await import('../plugin-store-S7OMGI7N.js');
4997
5071
  const yamlPath = path14__default.join(projectDir, "skaile.yaml");
4998
5072
  const editor = WorkspaceYamlEditor2.load(yamlPath);
4999
5073
  const removed = editor.removePlugin(name, specName);
@@ -5021,7 +5095,7 @@ async function runList(opts) {
5021
5095
  const registry = createPluginRegistry();
5022
5096
  if (declared.length > 0) {
5023
5097
  try {
5024
- const { loadPlugins } = await import('../plugin-store-G4HKWCTU.js');
5098
+ const { loadPlugins } = await import('../plugin-store-S7OMGI7N.js');
5025
5099
  const result = await loadPlugins(projectDir, declared, registry);
5026
5100
  for (const f of result.failed) logWarn(f.error);
5027
5101
  } catch (err) {
@@ -5055,150 +5129,93 @@ function makePluginCommand() {
5055
5129
  cmd.action(() => cmd.help());
5056
5130
  return cmd;
5057
5131
  }
5058
- function makePresetCommand() {
5059
- const cmd = new Command("preset").description("Scaffold and validate preset YAML files");
5060
- cmd.command("init [name]").description("Scaffold a new .preset.yaml file").option("--dir <path>", "Output directory", process.cwd()).action(async (name, opts) => {
5061
- const presetName = name ?? "my-preset";
5062
- const filename = `${presetName}.preset.yaml`;
5063
- const outPath = path14__default.join(path14__default.resolve(opts.dir), filename);
5064
- if (existsSync(outPath)) {
5065
- logErr(`File already exists: ${outPath}`);
5066
- logInfo("Choose a different name or remove the existing file.");
5067
- process.exit(1);
5068
- }
5069
- const template = generatePresetTemplate(presetName);
5070
- writeFileSync(outPath, template, "utf-8");
5071
- logOk(`Created ${S.brand(filename)}`);
5072
- logInfo(`Path: ${outPath}`);
5073
- logInfo("Edit the file to define your preset items, then validate with:");
5074
- logInfo(` skaile preset validate ${filename}`);
5075
- });
5076
- cmd.command("validate <path>").description("Validate a preset YAML file (schema + cycles + nesting depth)").action(async (filePath) => {
5077
- await runPresetValidate(filePath);
5078
- });
5079
- return cmd;
5080
- }
5081
- async function loadPresetForValidation(resolved) {
5082
- if (!existsSync(resolved)) {
5083
- logErr(`File not found: ${resolved}`);
5084
- process.exit(1);
5085
- }
5086
- const content = readFileSync(resolved, "utf-8");
5087
- const { parsePresetYaml } = await import('../library/index.js');
5088
- const parseResult = parsePresetYaml(content);
5089
- if (!parseResult.ok) {
5090
- logErr("Preset validation failed (parse errors):");
5091
- for (const err of parseResult.errors) {
5092
- console.log(` ${S.err("\u2717")} ${err}`);
5093
- }
5094
- process.exit(1);
5095
- }
5096
- if (!parseResult.preset) {
5097
- logErr("Preset validation failed: parser returned no manifest");
5098
- process.exit(1);
5099
- }
5100
- return parseResult.preset;
5101
- }
5102
- async function reportNestedPresetRefs(preset) {
5103
- const { detectNestedPresetRefs } = await import('../library/index.js');
5104
- const nestedRefs = detectNestedPresetRefs(preset);
5105
- if (nestedRefs.length > 0) {
5106
- logInfo(`Nested preset refs: ${nestedRefs.length} (depth validated at apply-time)`);
5107
- }
5108
- }
5109
- async function checkPresetCycles(preset, resolved) {
5110
- const { detectCycles } = await import('../discovery/index.js');
5111
- const presetRef = path14__default.basename(resolved, ".preset.yaml");
5112
- const edges = [];
5132
+
5133
+ // cli/src/commands/preset-apply.ts
5134
+ var TOKEN_RE = /\$\{(INPUT|SECRET):([^}]+)\}/g;
5135
+ var NESTED_PRESET_UNSUPPORTED = "nested presets are not supported by `preset apply` yet";
5136
+ function resolvePlaceholderValue(placeholder, providedValue) {
5137
+ if (isSecretPlaceholder(placeholder)) {
5138
+ return { envRef: `env:${placeholder.key}` };
5139
+ }
5140
+ const fallback = getDefaultValue(placeholder);
5141
+ const resolved = providedValue ?? (fallback !== void 0 ? String(fallback) : void 0);
5142
+ if (resolved === void 0) {
5143
+ if (placeholder.required !== false) {
5144
+ throw new Error(`Placeholder "${placeholder.key}" is required but no value was provided`);
5145
+ }
5146
+ return { value: "" };
5147
+ }
5148
+ return { value: resolved };
5149
+ }
5150
+ function presetItemsToWorkspace(preset, values) {
5151
+ const dependencies = [];
5152
+ const connectors = [];
5153
+ const mcpServers = [];
5154
+ const envVarsToSet = /* @__PURE__ */ new Set();
5113
5155
  for (const item of preset.items) {
5114
- if (item.ref) {
5115
- edges.push({ from: presetRef, to: item.ref, field: "items[].ref" });
5116
- }
5117
- }
5118
- if (edges.length === 0) return;
5119
- const cycleResult = detectCycles(edges);
5120
- if (!cycleResult.acyclic) {
5121
- logErr("Cycle detected in preset references:");
5122
- if (cycleResult.cyclePath) {
5123
- console.log(` ${S.err("\u2717")} ${cycleResult.cyclePath.join(" \u2192 ")}`);
5156
+ const ref = item.ref;
5157
+ if (ref.startsWith("preset:")) throw new Error(NESTED_PRESET_UNSUPPORTED);
5158
+ if (item.id && ref.startsWith("connector:")) {
5159
+ const config = resolveItemConfig(item, preset, values, envVarsToSet);
5160
+ connectors.push({
5161
+ id: item.id,
5162
+ driver: ref.slice("connector:".length),
5163
+ ...config
5164
+ });
5165
+ } else if (item.id && ref.startsWith("mcp:")) {
5166
+ const config = resolveItemConfig(item, preset, values, envVarsToSet);
5167
+ mcpServers.push({ id: item.id, ...config });
5168
+ } else {
5169
+ dependencies.push(ref);
5124
5170
  }
5125
- process.exit(1);
5126
5171
  }
5172
+ const sources = (preset.sources ?? []).map(
5173
+ (s) => s.pin ? { url: s.url, pin: s.pin } : { url: s.url }
5174
+ );
5175
+ return { dependencies, connectors, mcpServers, sources, envVarsToSet: [...envVarsToSet] };
5127
5176
  }
5128
- async function checkPresetPlaceholders(allPlaceholders) {
5129
- if (allPlaceholders.length === 0) return false;
5130
- const { validatePlaceholder } = await import('../library/index.js');
5131
- const invalidEntries = [];
5132
- for (const ph of allPlaceholders) {
5133
- const result = validatePlaceholder(ph, ph.default);
5134
- if (!result.valid) {
5135
- invalidEntries.push({ key: ph.key, errors: result.errors });
5177
+ function resolveItemConfig(item, preset, values, envVarsToSet) {
5178
+ const byKey = placeholderIndex(item, preset);
5179
+ const resolve5 = (key) => {
5180
+ const ph = byKey.get(key) ?? { key, type: "text", required: true };
5181
+ return resolvePlaceholderValue(ph, values[key]);
5182
+ };
5183
+ return substituteNode(item.config ?? {}, resolve5, envVarsToSet);
5184
+ }
5185
+ function placeholderIndex(item, preset) {
5186
+ const map = /* @__PURE__ */ new Map();
5187
+ for (const input of preset.inputs ?? []) map.set(input.key, input);
5188
+ for (const ph of item.placeholders ?? []) map.set(ph.key, ph);
5189
+ return map;
5190
+ }
5191
+ function substituteNode(node, resolve5, envVarsToSet) {
5192
+ if (typeof node === "string") return substituteString(node, resolve5, envVarsToSet);
5193
+ if (Array.isArray(node)) return node.map((el) => substituteNode(el, resolve5, envVarsToSet));
5194
+ if (node && typeof node === "object") {
5195
+ const out = {};
5196
+ for (const [k, v] of Object.entries(node)) out[k] = substituteNode(v, resolve5, envVarsToSet);
5197
+ return out;
5198
+ }
5199
+ return node;
5200
+ }
5201
+ function substituteString(value, resolve5, envVarsToSet) {
5202
+ const tokens = [...value.matchAll(TOKEN_RE)];
5203
+ if (tokens.length === 0) return value;
5204
+ const secret = tokens.find(([, ns]) => ns === "SECRET") ?? tokens.find(([, , key]) => "envRef" in resolve5(key));
5205
+ if (secret) {
5206
+ const [full, , key] = secret;
5207
+ if (tokens.length > 1 || full !== value) {
5208
+ throw new Error(
5209
+ `Secret "${key}" must be the only token in its value \u2014 remove the surrounding tokens/text`
5210
+ );
5136
5211
  }
5212
+ envVarsToSet.add(key);
5213
+ return `env:${key}`;
5137
5214
  }
5138
- if (invalidEntries.length === 0) return false;
5139
- logWarn("Placeholder issues:");
5140
- for (const r of invalidEntries) {
5141
- console.log(` ${S.warn("!")} ${r.key}: ${r.errors.join(", ")}`);
5142
- }
5143
- return true;
5144
- }
5145
- function printPresetSummary(preset, placeholderCount, hasWarnings) {
5146
- console.log();
5147
- console.log(S.heading(" Preset Validation"));
5148
- console.log(` ${S.rule(40)}`);
5149
- console.log(` ${S.label("Name")} ${preset.name}`);
5150
- console.log(` ${S.label("Version")} ${preset.version ?? "unversioned"}`);
5151
- console.log(` ${S.label("Items")} ${preset.items.length}`);
5152
- console.log(` ${S.label("Inputs")} ${preset.inputs?.length ?? 0}`);
5153
- console.log(` ${S.label("Placeholders")} ${placeholderCount}`);
5154
- console.log();
5155
- if (hasWarnings) {
5156
- logWarn("Validation passed with warnings");
5157
- } else {
5158
- logOk("Preset is valid");
5159
- }
5160
- }
5161
- async function runPresetValidate(filePath) {
5162
- const resolved = path14__default.resolve(filePath);
5163
- const preset = await loadPresetForValidation(resolved);
5164
- await reportNestedPresetRefs(preset);
5165
- await checkPresetCycles(preset, resolved);
5166
- const allPlaceholders = preset.items.flatMap((item) => item.placeholders ?? []);
5167
- const hasWarnings = await checkPresetPlaceholders(allPlaceholders);
5168
- printPresetSummary(preset, allPlaceholders.length, hasWarnings);
5169
- }
5170
- function generatePresetTemplate(name) {
5171
- return `# ${name}.preset.yaml
5172
- # Polymorphic Preset \u2014 composition + configuration primitive for the asset store.
5173
- # See: _devlog/specs/2026-05-03-ai-asset-store-v2-design.md \xA7 Presets
5174
-
5175
- name: "${name}"
5176
- version: "1.0.0"
5177
- description: "TODO: describe what this preset configures"
5178
-
5179
- # Placeholders \u2014 values the user provides at apply-time.
5180
- # Types: text, multiline, secret, number, boolean, enum, oauth, instance-picker
5181
- placeholders:
5182
- - key: EXAMPLE_KEY
5183
- type: text
5184
- label: "Example placeholder"
5185
- required: true
5186
- # default: "some-value"
5187
- # hint: "Help text shown to the user"
5188
-
5189
- # Items \u2014 each becomes a Library Instance when the preset is applied.
5190
- # Ref types:
5191
- # - Asset ref (kind:publisher/name@pin) \u2192 creates new Instance
5192
- # - Library Instance ref (#uuid) \u2192 reuses existing Instance
5193
- # - Nested Preset ref (preset:path.preset.yaml) \u2192 recursive apply (max depth 3)
5194
- items:
5195
- - id: example-item
5196
- ref: "skill:@skaile/example-skill@^1.0"
5197
- config:
5198
- # Use \${INPUT:EXAMPLE_KEY} for placeholder interpolation
5199
- setting: "\${INPUT:EXAMPLE_KEY}"
5200
- # credential_ref: "\${SECRET:EXAMPLE_KEY}" # for secret placeholders
5201
- `;
5215
+ return value.replace(TOKEN_RE, (_m, _ns, key) => {
5216
+ const resolved = resolve5(key);
5217
+ return "envRef" in resolved ? resolved.envRef : resolved.value;
5218
+ });
5202
5219
  }
5203
5220
  var REF_HINT = "kind:@<publisher>/<name>#<version> (e.g. skill:@skaile/use-exa#1.0.0)";
5204
5221
  function isValidAssetRef(ref) {
@@ -5275,7 +5292,7 @@ async function runRefInstall(ref, opts) {
5275
5292
  const spinner5 = p6.spinner();
5276
5293
  spinner5.start(`Installing ${ref}`);
5277
5294
  try {
5278
- const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-RPV2LIKL.js');
5295
+ const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-4HMIDK3N.js');
5279
5296
  const catalog = await openCatalogSource2({ projectDir: path14__default.resolve(opts.projectDir) });
5280
5297
  if (!supportsInstallManifest(catalog)) {
5281
5298
  throw new Error(
@@ -5290,6 +5307,9 @@ async function runRefInstall(ref, opts) {
5290
5307
  logInfo(`Path: ${S.dim(result.installPath)}`);
5291
5308
  logInfo(`Commit: ${S.dim(result.commitSha)}`);
5292
5309
  logInfo(`Files: ${S.dim(String(result.fileCount))}`);
5310
+ logInfo(
5311
+ S.dim(`Installed globally (see Path above) \u2014 use \`skaile add ${ref}\` to deploy into this project.`)
5312
+ );
5293
5313
  } finally {
5294
5314
  library.close();
5295
5315
  }
@@ -5312,7 +5332,7 @@ async function runYamlInstall(opts) {
5312
5332
  return;
5313
5333
  }
5314
5334
  try {
5315
- const { ensureSourcesCloned } = await import('../ensure-sources-4BAJGJRK.js');
5335
+ const { ensureSourcesCloned } = await import('../ensure-sources-QDVFUIUP.js');
5316
5336
  const hydrate = ensureSourcesCloned(projectDir, { quiet: true });
5317
5337
  if (hydrate.cloned.length > 0) {
5318
5338
  logOk(`Cloned source(s): ${hydrate.cloned.join(", ")}`);
@@ -5468,48 +5488,669 @@ function makeCleanCommand() {
5468
5488
  }
5469
5489
  });
5470
5490
  }
5471
- function makeRebuildCommand() {
5472
- return new Command("rebuild").description("Re-snapshot inline-snapshot composition items and bump prompt version").argument("[agent]", "Agent name or path (defaults to current project agent)").option("--project-dir <dir>", "Project directory", process.cwd()).action(async (agentArg, opts) => {
5473
- const projectDir = path14__default.resolve(opts.projectDir);
5474
- let agentDir;
5475
- if (agentArg) {
5476
- if (agentArg.includes("/") || agentArg.includes("\\")) {
5477
- agentDir = path14__default.resolve(projectDir, agentArg);
5478
- } else {
5479
- const resolved = resolveAgentDir(projectDir);
5480
- agentDir = resolved ?? path14__default.resolve(projectDir, agentArg);
5481
- }
5482
- } else {
5483
- const resolved = resolveAgentDir(projectDir);
5484
- if (!resolved) {
5485
- logErr("No agent found in current project. Specify an agent name or path.");
5486
- process.exit(1);
5487
- }
5488
- agentDir = resolved;
5489
- }
5490
- const manifest = await loadAgentManifest(agentDir);
5491
- if (!manifest) {
5492
- logErr(`No agent.yaml found in ${agentDir}`);
5493
- process.exit(1);
5494
- }
5495
- if (!manifest.composes || manifest.composes.length === 0) {
5496
- logInfo("Agent has no composes: items \u2014 nothing to rebuild.");
5497
- return;
5491
+ function buildPresetFromWorkspace(config, name, sources = []) {
5492
+ const items = [];
5493
+ for (const ref of config.dependencies ?? []) {
5494
+ items.push({ ref });
5495
+ }
5496
+ for (const connector of config.connectors ?? []) {
5497
+ const config_ = compact({
5498
+ access: connector.access,
5499
+ auth: connector.auth,
5500
+ options: connector.options,
5501
+ version: connector.version,
5502
+ tags: connector.tags
5503
+ });
5504
+ items.push({
5505
+ id: connector.id,
5506
+ ref: `connector:${connector.driver}`,
5507
+ ...config_ ? { config: config_ } : {}
5508
+ });
5509
+ }
5510
+ for (const mcp of config.mcp_servers ?? []) {
5511
+ const config_ = compact({
5512
+ transport: mcp.transport,
5513
+ command: mcp.command,
5514
+ args: mcp.args,
5515
+ env: mcp.env,
5516
+ url: mcp.url,
5517
+ headers: mcp.headers
5518
+ });
5519
+ items.push({
5520
+ id: mcp.id,
5521
+ ref: `mcp:${mcp.id}`,
5522
+ ...config_ ? { config: config_ } : {}
5523
+ });
5524
+ }
5525
+ const project = config.name ?? name;
5526
+ return {
5527
+ name,
5528
+ version: "1.0.0",
5529
+ description: `Exported from ${project} workspace`,
5530
+ items,
5531
+ // Never let a credential-bearing source URL reach a shareable preset.
5532
+ ...sources.length > 0 ? { sources: sources.map((s) => ({ ...s, url: stripUrlCredentials(s.url) })) } : {}
5533
+ };
5534
+ }
5535
+ function neededSources(config, lock) {
5536
+ const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
5537
+ const nonFactory = (config.sources ?? []).filter((s) => normalizeRepoUrl(s.url) !== factoryUrl);
5538
+ if (!lock) return nonFactory;
5539
+ const inClosure = new Set(lock.sources.map((s) => normalizeRepoUrl(s.url)));
5540
+ return nonFactory.filter((s) => inClosure.has(normalizeRepoUrl(s.url)));
5541
+ }
5542
+ function sourcesWithSidecar(sources) {
5543
+ const dir = getStoreManifestsDir();
5544
+ return sources.filter((s) => existsSync(path14.join(dir, `${storeManifestSlug(s.url)}.yaml`))).map((s) => s.url);
5545
+ }
5546
+ function compact(obj) {
5547
+ const out = {};
5548
+ for (const [k, v] of Object.entries(obj)) {
5549
+ if (v !== void 0 && v !== null) out[k] = v;
5550
+ }
5551
+ return Object.keys(out).length > 0 ? out : void 0;
5552
+ }
5553
+ var SECRET_REFERENCE_PREFIXES = ["env", "forge", "secret", "op", "kp", "oauth", "pat"];
5554
+ var SECRET_REFERENCE_RE = new RegExp(`^(${SECRET_REFERENCE_PREFIXES.join("|")}):`);
5555
+ function isSecretReference(value) {
5556
+ return SECRET_REFERENCE_RE.test(value);
5557
+ }
5558
+ var PLACEHOLDER_RE = /^\$\{(INPUT|SECRET):[^}]+\}$/;
5559
+ var SUSPICIOUS_KEY_RE = /(?:^|[_-])(?:token|password|passwd|secret|apikey|key|dsn|credential|auth|authorization|bearer)(?:[_-]|$)/i;
5560
+ var PROTO_CREDENTIAL_URL_RE = /\w+:\/\/[^/@\s]+:[^/@\s]+@/;
5561
+ function urlHasCredentials(url) {
5562
+ return PROTO_CREDENTIAL_URL_RE.test(url);
5563
+ }
5564
+ function stripUrlCredentials(url) {
5565
+ return url.replace(/^(\w+:\/\/)[^/@]*:[^/@]*@/, "$1");
5566
+ }
5567
+ function isLongEntropic(value) {
5568
+ if (value.length < 20) return false;
5569
+ if (/^\w+:\/\//.test(value) || value.startsWith("/")) return false;
5570
+ const classes = [/[a-z]/, /[A-Z]/, /[0-9]/].filter((re) => re.test(value)).length;
5571
+ return classes >= 2;
5572
+ }
5573
+ function scanInlineSecrets(preset) {
5574
+ const findings = [];
5575
+ for (const item of preset.items) {
5576
+ const itemId = typeof item.id === "string" ? item.id : "";
5577
+ if (item.config) scanNode(item.config, [], false, itemId, findings);
5578
+ }
5579
+ return findings;
5580
+ }
5581
+ function scanNode(node, segments, inCredentialField, itemId, findings) {
5582
+ if (typeof node === "string") {
5583
+ if (isInlineSecret(node, segments.at(-1) ?? "", inCredentialField)) {
5584
+ findings.push({ itemId, path: segments.join("."), maskedValue: maskSecret(node) });
5498
5585
  }
5499
- logInfo(
5500
- `Rebuilding ${manifest.name ?? "agent"} (${manifest.composes.length} composition items)...`
5586
+ return;
5587
+ }
5588
+ if (Array.isArray(node)) {
5589
+ node.forEach(
5590
+ (el, i) => scanNode(el, [...segments, String(i)], inCredentialField, itemId, findings)
5501
5591
  );
5502
- const loadContent = async (ref, kind) => {
5503
- try {
5504
- const repos = {};
5505
- const mixinKind = kind;
5506
- const resolved = await resolveMixin(ref, mixinKind, agentDir, repos);
5507
- return resolved?.content ?? null;
5508
- } catch {
5509
- return null;
5510
- }
5592
+ return;
5593
+ }
5594
+ if (node && typeof node === "object") {
5595
+ for (const [k, v] of Object.entries(node)) {
5596
+ const credField = inCredentialField || k === "auth";
5597
+ scanNode(v, [...segments, k], credField, itemId, findings);
5598
+ }
5599
+ }
5600
+ }
5601
+ function isInlineSecret(value, key, inCredentialField) {
5602
+ if (isSecretReference(value) || PLACEHOLDER_RE.test(value)) return false;
5603
+ if (inCredentialField) return true;
5604
+ if (SUSPICIOUS_KEY_RE.test(key)) return true;
5605
+ if (PROTO_CREDENTIAL_URL_RE.test(value)) return true;
5606
+ return isLongEntropic(value);
5607
+ }
5608
+ function maskSecret(value) {
5609
+ const proto = value.match(/^(\w+):\/\/[^/@\s]+:[^/@\s]+@/);
5610
+ if (proto) return `${proto[1]}://\u2026:****@\u2026`;
5611
+ return "****";
5612
+ }
5613
+ function redactSecrets(preset, findings) {
5614
+ const next = structuredClone(preset);
5615
+ const keys = [];
5616
+ const used = /* @__PURE__ */ new Set();
5617
+ for (const finding of findings) {
5618
+ const item = next.items.find((it) => it.id === finding.itemId);
5619
+ if (!item?.config) continue;
5620
+ const base = placeholderKey(finding.itemId, finding.path);
5621
+ let key = base;
5622
+ for (let n = 2; used.has(key); n++) key = `${base}_${n}`;
5623
+ if (!setAtPath(item.config, finding.path.split("."), `\${INPUT:${key}}`)) continue;
5624
+ used.add(key);
5625
+ const placeholder = {
5626
+ key,
5627
+ type: "secret",
5628
+ required: true,
5629
+ prompt: `Secret value for ${finding.itemId}.${finding.path}`
5511
5630
  };
5512
- const result = await compileComposition({
5631
+ item.placeholders = [...item.placeholders ?? [], placeholder];
5632
+ keys.push(key);
5633
+ }
5634
+ return { preset: next, keys };
5635
+ }
5636
+ function placeholderKey(itemId, path37) {
5637
+ return `${itemId}_${path37}`.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
5638
+ }
5639
+ function setAtPath(root, segments, value) {
5640
+ let node = root;
5641
+ for (let i = 0; i < segments.length - 1; i++) {
5642
+ if (!node || typeof node !== "object") return false;
5643
+ node = node[segments[i]];
5644
+ }
5645
+ if (!node || typeof node !== "object") return false;
5646
+ node[segments.at(-1)] = value;
5647
+ return true;
5648
+ }
5649
+ var INLINE_SECRETS_HEADER = "# \u26A0\uFE0F CONTAINS INLINE SECRETS \u2014 replace with placeholders before sharing or publishing";
5650
+ function secretHeaderComment(findings) {
5651
+ return findings.length > 0 ? INLINE_SECRETS_HEADER : null;
5652
+ }
5653
+
5654
+ // cli/src/commands/preset-cmd.ts
5655
+ function makePresetCommand() {
5656
+ const cmd = new Command("preset").description("Scaffold and validate preset YAML files");
5657
+ cmd.command("init [name]").description("Scaffold a new .preset.yaml file").option("--dir <path>", "Output directory", process.cwd()).action(async (name, opts) => {
5658
+ const presetName = name ?? "my-preset";
5659
+ const filename = `${presetName}.preset.yaml`;
5660
+ const outPath = path14__default.join(path14__default.resolve(opts.dir), filename);
5661
+ if (existsSync(outPath)) {
5662
+ logErr(`File already exists: ${outPath}`);
5663
+ logInfo("Choose a different name or remove the existing file.");
5664
+ process.exit(1);
5665
+ }
5666
+ const template = generatePresetTemplate(presetName);
5667
+ writeFileSync(outPath, template, "utf-8");
5668
+ logOk(`Created ${S.brand(filename)}`);
5669
+ logInfo(`Path: ${outPath}`);
5670
+ logInfo("Edit the file to define your preset items, then validate with:");
5671
+ logInfo(` skaile preset validate ${filename}`);
5672
+ });
5673
+ cmd.command("validate <path>").description("Validate a preset YAML file (schema + cycles + nesting depth)").action(async (filePath) => {
5674
+ await runPresetValidate(filePath);
5675
+ });
5676
+ cmd.command("export [name]").description("Snapshot the workspace's skaile.yaml into a shareable .preset.yaml").option("--project-dir <path>", "Project directory to read skaile.yaml from", process.cwd()).option("--out <file>", "Explicit output file path").option("--dir <path>", "Output directory (default: cwd)", process.cwd()).option("--redact", "Replace every detected inline secret with a placeholder").option(
5677
+ "--allow-inline-secrets",
5678
+ "Exit 0 even when inline secrets remain unredacted (opt-in; unsafe for CI)"
5679
+ ).option("--force", "Overwrite the output file if it already exists").action(async (name, opts) => {
5680
+ await runPresetExport(name, opts);
5681
+ });
5682
+ cmd.command("apply <path>").description("Apply a .preset.yaml additively into the workspace's skaile.yaml").option("--project-dir <path>", "Project directory holding skaile.yaml", process.cwd()).option(
5683
+ "--input <KEY=VAL>",
5684
+ "Provide a placeholder value (repeatable)",
5685
+ collectInput,
5686
+ []
5687
+ ).option("--yes", "Skip interactive prompts; use defaults where available").option("--no-deploy", "Skip the final `skaile install` step").action(async (filePath, opts) => {
5688
+ await runPresetApply(filePath, opts);
5689
+ });
5690
+ return cmd;
5691
+ }
5692
+ async function runPresetExport(name, opts) {
5693
+ const projectDir = path14__default.resolve(opts.projectDir);
5694
+ const file = loadSkWorkspaceConfig(projectDir);
5695
+ if (!file) {
5696
+ logErr(`No skaile.yaml found in ${projectDir}`);
5697
+ logInfo("Run this from a project directory or pass --project-dir <path>.");
5698
+ process.exit(1);
5699
+ }
5700
+ const presetName = name ?? file.config.name ?? "my-preset";
5701
+ let lock = null;
5702
+ try {
5703
+ lock = readLock(path14__default.join(projectDir, "skaile.lock.yaml"));
5704
+ } catch {
5705
+ lock = null;
5706
+ }
5707
+ const sources = neededSources(file.config, lock);
5708
+ if (lock === null && sources.length > 0) {
5709
+ logInfo("No lock file \u2014 exporting all non-factory sources conservatively.");
5710
+ }
5711
+ const strippedCreds = sources.filter((s) => urlHasCredentials(s.url)).length;
5712
+ if (strippedCreds > 0) {
5713
+ logWarn(
5714
+ `Stripped credentials from ${strippedCreds} source URL(s) \u2014 resolved via git auth on apply.`
5715
+ );
5716
+ }
5717
+ const sidecarSources = sourcesWithSidecar(sources);
5718
+ if (sidecarSources.length > 0) {
5719
+ logWarn(
5720
+ `${sidecarSources.length} captured source(s) rely on a MACHINE-LOCAL sidecar overlay (\`~/.skaile/store/manifests/*\`) \u2014 assets may not resolve on other machines or when this preset is shared, until the sidecar manifest is published: ${sidecarSources.join(", ")}`
5721
+ );
5722
+ }
5723
+ let preset = buildPresetFromWorkspace(file.config, presetName, sources);
5724
+ if (preset.items.length === 0) {
5725
+ logErr("Nothing to export \u2014 skaile.yaml has no dependencies, connectors, or mcp_servers.");
5726
+ process.exit(1);
5727
+ }
5728
+ const result = validatePreset(preset);
5729
+ if (!result.ok) {
5730
+ logErr("Internal error: generated preset failed validation:");
5731
+ for (const err of result.errors) console.error(` ${S.err("\u2717")} ${err.path}: ${err.message}`);
5732
+ process.exit(1);
5733
+ }
5734
+ const findings = scanInlineSecrets(preset);
5735
+ reportFindings(findings);
5736
+ const toRedact = await resolveRedactions(findings, opts.redact === true);
5737
+ if (toRedact.length > 0) preset = redactSecrets(preset, toRedact).preset;
5738
+ const unresolved = scanInlineSecrets(preset);
5739
+ const header = secretHeaderComment(unresolved);
5740
+ const body = stringify(preset);
5741
+ const contents = header ? `${header}
5742
+ ${body}` : body;
5743
+ if (opts.out && opts.dir !== process.cwd()) logWarn("--dir is ignored when --out is set.");
5744
+ const outPath = opts.out ? path14__default.resolve(opts.out) : path14__default.join(path14__default.resolve(opts.dir), `${presetName}.preset.yaml`);
5745
+ if (existsSync(outPath) && !opts.force) {
5746
+ logErr(`File already exists: ${outPath}`);
5747
+ logInfo("Pass --force to overwrite, or choose a different --out/--dir.");
5748
+ process.exit(1);
5749
+ }
5750
+ try {
5751
+ writeFileSync(outPath, contents, "utf-8");
5752
+ } catch (e) {
5753
+ logErr(`Failed to write ${outPath}: ${e.message}`);
5754
+ process.exit(1);
5755
+ }
5756
+ logOk(`Exported ${S.brand(path14__default.basename(outPath))}`);
5757
+ logInfo(`Path: ${outPath}`);
5758
+ if (unresolved.length > 0) {
5759
+ console.error(S.err("This file is NOT safe to share."));
5760
+ if (!opts.allowInlineSecrets) {
5761
+ logErr(`${unresolved.length} inline secret(s) left unredacted.`);
5762
+ logInfo(
5763
+ "Re-run with --redact to placeholder them, or --allow-inline-secrets to accept the risk."
5764
+ );
5765
+ process.exit(1);
5766
+ }
5767
+ }
5768
+ }
5769
+ function reportFindings(findings) {
5770
+ if (findings.length === 0) return;
5771
+ console.error(S.err(`\u26A0\uFE0F ${findings.length} potential INLINE secret(s) found`));
5772
+ for (const f of findings) {
5773
+ console.error(` ${S.err("\u2022")} ${f.itemId} ${S.dim(f.path)} ${f.maskedValue}`);
5774
+ }
5775
+ }
5776
+ async function resolveRedactions(findings, redactAll) {
5777
+ if (findings.length === 0) return [];
5778
+ if (redactAll) return findings;
5779
+ if (!process.stdin.isTTY) return [];
5780
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
5781
+ try {
5782
+ const chosen = [];
5783
+ for (const f of findings) {
5784
+ const answer = await rl.question(`Redact ${f.itemId}.${f.path}? [y/N] `);
5785
+ if (/^y(es)?$/i.test(answer.trim())) chosen.push(f);
5786
+ }
5787
+ return chosen;
5788
+ } finally {
5789
+ rl.close();
5790
+ }
5791
+ }
5792
+ function collectInput(value, acc) {
5793
+ acc.push(value);
5794
+ return acc;
5795
+ }
5796
+ async function runPresetApply(filePath, opts) {
5797
+ const projectDir = path14__default.resolve(opts.projectDir);
5798
+ const file = loadSkWorkspaceConfig(projectDir);
5799
+ if (!file) {
5800
+ logErr("No skaile.yaml \u2014 run `skaile init` first");
5801
+ process.exit(1);
5802
+ }
5803
+ const resolved = path14__default.resolve(filePath);
5804
+ if (!existsSync(resolved)) {
5805
+ logErr(`File not found: ${resolved}`);
5806
+ process.exit(1);
5807
+ }
5808
+ const { parsePresetYaml } = await import('../library/index.js');
5809
+ const parseResult = parsePresetYaml(readFileSync(resolved, "utf-8"));
5810
+ if (!parseResult.ok || !parseResult.preset) {
5811
+ logErr("Preset validation failed:");
5812
+ for (const err of parseResult.errors) console.error(` ${S.err("\u2717")} ${err}`);
5813
+ process.exit(1);
5814
+ }
5815
+ const preset = parseResult.preset;
5816
+ if (preset.items.some((it) => it.ref.startsWith("preset:"))) {
5817
+ logErr(NESTED_PRESET_UNSUPPORTED);
5818
+ logInfo("Remove the nested `preset:` item(s) and re-run.");
5819
+ process.exit(1);
5820
+ }
5821
+ const values = parseInputs(opts.input);
5822
+ await promptMissingInputs(preset.items, preset.inputs, values, opts.yes === true);
5823
+ let items;
5824
+ try {
5825
+ items = presetItemsToWorkspace(preset, values);
5826
+ } catch (err) {
5827
+ logErr(err.message);
5828
+ process.exit(1);
5829
+ }
5830
+ const editor = WorkspaceYamlEditor.load(file.path);
5831
+ const summary = applyItems(editor, file.config, items);
5832
+ const changed = summary.addedSources.length + summary.addedDeps.length + summary.addedConnectors.length + summary.addedMcp.length;
5833
+ if (changed > 0) editor.save();
5834
+ printApplySummary(summary, items.envVarsToSet);
5835
+ if (opts.deploy) {
5836
+ await runPresetInstall(projectDir);
5837
+ } else {
5838
+ logInfo("Skipped deploy (--no-deploy). Run `skaile install` to deploy the new dependencies.");
5839
+ }
5840
+ }
5841
+ function parseInputs(pairs) {
5842
+ const out = {};
5843
+ for (const pair of pairs) {
5844
+ const eq = pair.indexOf("=");
5845
+ if (eq <= 0) {
5846
+ logErr(`Invalid --input "${pair}" \u2014 expected KEY=VALUE.`);
5847
+ process.exit(1);
5848
+ }
5849
+ out[pair.slice(0, eq)] = pair.slice(eq + 1);
5850
+ }
5851
+ return out;
5852
+ }
5853
+ async function promptMissingInputs(presetItems, presetInputs, values, yes) {
5854
+ const seen = /* @__PURE__ */ new Set();
5855
+ const toPrompt = [];
5856
+ const collect = (ph) => {
5857
+ if (seen.has(ph.key) || isSecretPlaceholder(ph)) return;
5858
+ seen.add(ph.key);
5859
+ if (values[ph.key] === void 0) toPrompt.push(ph);
5860
+ };
5861
+ for (const input of presetInputs ?? []) collect(input);
5862
+ for (const item of presetItems) for (const ph of item.placeholders ?? []) collect(ph);
5863
+ if (toPrompt.length === 0 || yes || !process.stdin.isTTY) return;
5864
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
5865
+ try {
5866
+ for (const ph of toPrompt) {
5867
+ const def = getDefaultValue(ph);
5868
+ const suffix = def !== void 0 ? ` [${String(def)}]` : "";
5869
+ const answer = (await rl.question(`${ph.prompt ?? ph.key}${suffix}: `)).trim();
5870
+ if (answer) values[ph.key] = answer;
5871
+ else if (def !== void 0) values[ph.key] = String(def);
5872
+ }
5873
+ } finally {
5874
+ rl.close();
5875
+ }
5876
+ }
5877
+ function applyItems(editor, config, items) {
5878
+ const s = {
5879
+ addedDeps: [],
5880
+ skippedDeps: [],
5881
+ addedConnectors: [],
5882
+ skippedConnectors: [],
5883
+ unchangedConnectors: [],
5884
+ addedMcp: [],
5885
+ skippedMcp: [],
5886
+ unchangedMcp: [],
5887
+ addedSources: [],
5888
+ skippedSources: [],
5889
+ unchangedSources: []
5890
+ };
5891
+ const existingSourceUrls = new Map(
5892
+ editor.getSources().map((src) => [normalizeRepoUrl(src.url), src])
5893
+ );
5894
+ for (const src of items.sources) {
5895
+ const key = normalizeRepoUrl(src.url);
5896
+ const existing = existingSourceUrls.get(key);
5897
+ if (!existing) {
5898
+ editor.setSource(src);
5899
+ existingSourceUrls.set(key, src);
5900
+ s.addedSources.push(src.url);
5901
+ } else if ((existing.pin ?? void 0) !== (src.pin ?? void 0)) {
5902
+ s.skippedSources.push({ url: src.url, keptPin: existing.pin, presetPin: src.pin });
5903
+ } else {
5904
+ s.unchangedSources.push(src.url);
5905
+ }
5906
+ }
5907
+ for (const ref of items.dependencies) {
5908
+ if (editor.addDependency(ref)) s.addedDeps.push(ref);
5909
+ else s.skippedDeps.push(ref);
5910
+ }
5911
+ const existingConnectors = new Map((config.connectors ?? []).map((c) => [c.id, c]));
5912
+ for (const conn of items.connectors) {
5913
+ const existing = existingConnectors.get(conn.id);
5914
+ if (existing) {
5915
+ (stableEqual(existing, conn) ? s.unchangedConnectors : s.skippedConnectors).push(conn.id);
5916
+ continue;
5917
+ }
5918
+ editor.setConnector(conn);
5919
+ s.addedConnectors.push(conn.id);
5920
+ }
5921
+ const existingMcp = new Map((config.mcp_servers ?? []).map((m) => [m.id, m]));
5922
+ for (const mcp of items.mcpServers) {
5923
+ const existing = existingMcp.get(mcp.id);
5924
+ if (existing) {
5925
+ (stableEqual(existing, mcp) ? s.unchangedMcp : s.skippedMcp).push(mcp.id);
5926
+ continue;
5927
+ }
5928
+ editor.setMcpServer(mcp);
5929
+ s.addedMcp.push(mcp.id);
5930
+ }
5931
+ return s;
5932
+ }
5933
+ function stableEqual(a, b) {
5934
+ return canonicalJson(a) === canonicalJson(b);
5935
+ }
5936
+ function canonicalJson(value) {
5937
+ return JSON.stringify(
5938
+ value,
5939
+ (_k, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(
5940
+ Object.entries(v).sort(([x], [y]) => x.localeCompare(y))
5941
+ ) : v
5942
+ );
5943
+ }
5944
+ function printApplySummary(summary, envVarsToSet) {
5945
+ const added = summary.addedSources.length + summary.addedDeps.length + summary.addedConnectors.length + summary.addedMcp.length;
5946
+ if (added === 0) {
5947
+ logInfo("Nothing to apply \u2014 everything was already present.");
5948
+ } else {
5949
+ if (summary.addedSources.length > 0) {
5950
+ logOk(`Registered ${summary.addedSources.length} source(s).`);
5951
+ }
5952
+ logOk(
5953
+ `Applied ${summary.addedDeps.length} dependency(ies), ${summary.addedConnectors.length} connector(s), ${summary.addedMcp.length} mcp server(s).`
5954
+ );
5955
+ }
5956
+ const skipped = [
5957
+ ...summary.skippedSources.map(
5958
+ (s) => `source ${s.url} (kept pin '${s.keptPin ?? "none"}', preset had '${s.presetPin ?? "none"}')`
5959
+ ),
5960
+ ...summary.skippedDeps.map((d) => `dependency ${d} (already present)`),
5961
+ ...summary.skippedConnectors.map((c) => `connector ${c} (id exists, differs)`),
5962
+ ...summary.skippedMcp.map((m) => `mcp ${m} (id exists, differs)`)
5963
+ ];
5964
+ for (const line of skipped) logWarn(`Skipped ${line}`);
5965
+ const unchanged = summary.unchangedConnectors.length + summary.unchangedMcp.length + summary.unchangedSources.length;
5966
+ if (unchanged > 0)
5967
+ logInfo(`${unchanged} source/connector/mcp entry(ies) already matched \u2014 left as-is.`);
5968
+ if (envVarsToSet.length > 0) {
5969
+ logWarn(
5970
+ `This preset references secrets by env var \u2014 provide them before running: ${envVarsToSet.join(", ")}`
5971
+ );
5972
+ logInfo("Add them to .env (gitignored) or export in your shell, e.g. export <VAR>=\u2026");
5973
+ }
5974
+ }
5975
+ async function runPresetInstall(projectDir) {
5976
+ try {
5977
+ const { AssetManager: AssetManager2 } = await import('../asset-manager/index.js');
5978
+ const am2 = new AssetManager2({ projectDir });
5979
+ logInfo("Installing new dependencies\u2026");
5980
+ const result = await am2.install({});
5981
+ for (const d of result.deployed) logOk(d);
5982
+ for (const m of result.missing) logErr(`Missing: ${m}`);
5983
+ await reportInstalledSoftDeps(am2);
5984
+ } catch (err) {
5985
+ logErr(`Install failed: ${err instanceof Error ? err.message : String(err)}`);
5986
+ logInfo("Run `skaile install` to deploy the new dependencies.");
5987
+ }
5988
+ }
5989
+ async function loadPresetForValidation(resolved) {
5990
+ if (!existsSync(resolved)) {
5991
+ logErr(`File not found: ${resolved}`);
5992
+ process.exit(1);
5993
+ }
5994
+ const content = readFileSync(resolved, "utf-8");
5995
+ const { parsePresetYaml } = await import('../library/index.js');
5996
+ const parseResult = parsePresetYaml(content);
5997
+ if (!parseResult.ok) {
5998
+ logErr("Preset validation failed (parse errors):");
5999
+ for (const err of parseResult.errors) {
6000
+ console.log(` ${S.err("\u2717")} ${err}`);
6001
+ }
6002
+ process.exit(1);
6003
+ }
6004
+ if (!parseResult.preset) {
6005
+ logErr("Preset validation failed: parser returned no manifest");
6006
+ process.exit(1);
6007
+ }
6008
+ return parseResult.preset;
6009
+ }
6010
+ async function reportNestedPresetRefs(preset) {
6011
+ const { detectNestedPresetRefs } = await import('../library/index.js');
6012
+ const nestedRefs = detectNestedPresetRefs(preset);
6013
+ if (nestedRefs.length > 0) {
6014
+ logInfo(`Nested preset refs: ${nestedRefs.length} (depth validated at apply-time)`);
6015
+ }
6016
+ }
6017
+ async function checkPresetCycles(preset, resolved) {
6018
+ const { detectCycles } = await import('../discovery/index.js');
6019
+ const presetRef = path14__default.basename(resolved, ".preset.yaml");
6020
+ const edges = [];
6021
+ for (const item of preset.items) {
6022
+ if (item.ref) {
6023
+ edges.push({ from: presetRef, to: item.ref, field: "items[].ref" });
6024
+ }
6025
+ }
6026
+ if (edges.length === 0) return;
6027
+ const cycleResult = detectCycles(edges);
6028
+ if (!cycleResult.acyclic) {
6029
+ logErr("Cycle detected in preset references:");
6030
+ if (cycleResult.cyclePath) {
6031
+ console.log(` ${S.err("\u2717")} ${cycleResult.cyclePath.join(" \u2192 ")}`);
6032
+ }
6033
+ process.exit(1);
6034
+ }
6035
+ }
6036
+ async function checkPresetPlaceholders(allPlaceholders) {
6037
+ if (allPlaceholders.length === 0) return false;
6038
+ const { validatePlaceholder } = await import('../library/index.js');
6039
+ const invalidEntries = [];
6040
+ for (const ph of allPlaceholders) {
6041
+ if (ph.default === void 0) continue;
6042
+ const result = validatePlaceholder(ph, ph.default);
6043
+ if (!result.valid) {
6044
+ invalidEntries.push({ key: ph.key, errors: result.errors });
6045
+ }
6046
+ }
6047
+ if (invalidEntries.length === 0) return false;
6048
+ logWarn("Placeholder issues:");
6049
+ for (const r of invalidEntries) {
6050
+ console.log(` ${S.warn("!")} ${r.key}: ${r.errors.join(", ")}`);
6051
+ }
6052
+ return true;
6053
+ }
6054
+ function printPresetSummary(preset, placeholderCount, hasWarnings) {
6055
+ console.log();
6056
+ console.log(S.heading(" Preset Validation"));
6057
+ console.log(` ${S.rule(40)}`);
6058
+ console.log(` ${S.label("Name")} ${preset.name}`);
6059
+ console.log(` ${S.label("Version")} ${preset.version ?? "unversioned"}`);
6060
+ console.log(` ${S.label("Items")} ${preset.items.length}`);
6061
+ console.log(` ${S.label("Inputs")} ${preset.inputs?.length ?? 0}`);
6062
+ console.log(` ${S.label("Placeholders")} ${placeholderCount}`);
6063
+ console.log();
6064
+ if (hasWarnings) {
6065
+ logWarn("Validation passed with warnings");
6066
+ } else {
6067
+ logOk("Preset is valid");
6068
+ }
6069
+ }
6070
+ async function runPresetValidate(filePath) {
6071
+ const resolved = path14__default.resolve(filePath);
6072
+ const preset = await loadPresetForValidation(resolved);
6073
+ await reportNestedPresetRefs(preset);
6074
+ await checkPresetCycles(preset, resolved);
6075
+ const allPlaceholders = preset.items.flatMap((item) => item.placeholders ?? []);
6076
+ const hasWarnings = await checkPresetPlaceholders(allPlaceholders);
6077
+ printPresetSummary(preset, allPlaceholders.length, hasWarnings);
6078
+ }
6079
+ function generatePresetTemplate(name) {
6080
+ return `# ${name}.preset.yaml
6081
+ # Polymorphic Preset \u2014 composition + configuration primitive for the asset store.
6082
+ # See: _devlog/specs/2026-05-03-ai-asset-store-v2-design.md \xA7 Presets
6083
+
6084
+ name: "${name}"
6085
+ version: "1.0.0"
6086
+ description: "TODO: describe what this preset configures"
6087
+
6088
+ # Placeholders \u2014 values the user provides at apply-time.
6089
+ # Types: text, multiline, secret, number, boolean, enum, oauth, instance-picker
6090
+ placeholders:
6091
+ - key: EXAMPLE_KEY
6092
+ type: text
6093
+ label: "Example placeholder"
6094
+ required: true
6095
+ # default: "some-value"
6096
+ # hint: "Help text shown to the user"
6097
+
6098
+ # Items \u2014 each becomes a Library Instance when the preset is applied.
6099
+ # Ref types:
6100
+ # - Asset ref (kind:publisher/name@pin) \u2192 creates new Instance
6101
+ # - Library Instance ref (#uuid) \u2192 reuses existing Instance
6102
+ # - Nested Preset ref (preset:path.preset.yaml) \u2192 recursive apply (max depth 3)
6103
+ items:
6104
+ - id: example-item
6105
+ ref: "skill:@skaile/example-skill@^1.0"
6106
+ config:
6107
+ # Use \${INPUT:EXAMPLE_KEY} for placeholder interpolation
6108
+ setting: "\${INPUT:EXAMPLE_KEY}"
6109
+ # credential_ref: "\${SECRET:EXAMPLE_KEY}" # for secret placeholders
6110
+ `;
6111
+ }
6112
+ function makeRebuildCommand() {
6113
+ return new Command("rebuild").description("Re-snapshot inline-snapshot composition items and bump prompt version").argument("[agent]", "Agent name or path (defaults to current project agent)").option("--project-dir <dir>", "Project directory", process.cwd()).action(async (agentArg, opts) => {
6114
+ const projectDir = path14__default.resolve(opts.projectDir);
6115
+ let agentDir;
6116
+ if (agentArg) {
6117
+ if (agentArg.includes("/") || agentArg.includes("\\")) {
6118
+ agentDir = path14__default.resolve(projectDir, agentArg);
6119
+ } else {
6120
+ const resolved = resolveAgentDir(projectDir);
6121
+ agentDir = resolved ?? path14__default.resolve(projectDir, agentArg);
6122
+ }
6123
+ } else {
6124
+ const resolved = resolveAgentDir(projectDir);
6125
+ if (!resolved) {
6126
+ logErr("No agent found in current project. Specify an agent name or path.");
6127
+ process.exit(1);
6128
+ }
6129
+ agentDir = resolved;
6130
+ }
6131
+ const manifest = await loadAgentManifest(agentDir);
6132
+ if (!manifest) {
6133
+ logErr(`No agent.yaml found in ${agentDir}`);
6134
+ process.exit(1);
6135
+ }
6136
+ if (!manifest.composes || manifest.composes.length === 0) {
6137
+ logInfo("Agent has no composes: items \u2014 nothing to rebuild.");
6138
+ return;
6139
+ }
6140
+ logInfo(
6141
+ `Rebuilding ${manifest.name ?? "agent"} (${manifest.composes.length} composition items)...`
6142
+ );
6143
+ const loadContent = async (ref, kind) => {
6144
+ try {
6145
+ const repos = {};
6146
+ const mixinKind = kind;
6147
+ const resolved = await resolveMixin(ref, mixinKind, agentDir, repos);
6148
+ return resolved?.content ?? null;
6149
+ } catch {
6150
+ return null;
6151
+ }
6152
+ };
6153
+ const result = await compileComposition({
5513
6154
  agentDir,
5514
6155
  items: manifest.composes,
5515
6156
  loadContent
@@ -5523,6 +6164,21 @@ function makeRebuildCommand() {
5523
6164
  logInfo(`Written: ${result.manifestPath}`);
5524
6165
  });
5525
6166
  }
6167
+
6168
+ // cli/src/driver-error.ts
6169
+ function friendlyDriverError(err) {
6170
+ const e = err;
6171
+ if (!e) return null;
6172
+ const isSpawnEnoent = e.code === "ENOENT" && (e.syscall?.startsWith("spawn") === true || /^spawn .*ENOENT/.test(e.message ?? ""));
6173
+ const isConfig = e.category === "config" || e.detail?.category === "config";
6174
+ if (!isSpawnEnoent && !isConfig) return null;
6175
+ const hint = e.hint ?? e.detail?.hint;
6176
+ const base = hint ?? "The configured agent binary is not installed or not on your PATH.";
6177
+ return `${base}
6178
+ Try a different backend with \`--driver claude-sdk\`.`;
6179
+ }
6180
+
6181
+ // cli/src/commands/run.ts
5526
6182
  function makeFlowEventHandler() {
5527
6183
  const md = new MarkdownStreamer();
5528
6184
  const displayed = /* @__PURE__ */ new Set();
@@ -5608,14 +6264,24 @@ function makeRunCommand() {
5608
6264
  }
5609
6265
  process.exit(1);
5610
6266
  }
5611
- await runAgentChat({
5612
- projectDir,
5613
- driver: config.driver,
5614
- model: config.model,
5615
- provider: config.provider,
5616
- message: textPrompt,
5617
- skill: opts.skill
5618
- });
6267
+ try {
6268
+ await runAgentChat({
6269
+ projectDir,
6270
+ driver: config.driver,
6271
+ model: config.model,
6272
+ provider: config.provider,
6273
+ message: textPrompt,
6274
+ skill: opts.skill
6275
+ });
6276
+ } catch (err) {
6277
+ const friendly = friendlyDriverError(err);
6278
+ if (friendly) {
6279
+ logErr(friendly);
6280
+ } else {
6281
+ logErr(err instanceof Error ? err.message : String(err));
6282
+ }
6283
+ process.exit(1);
6284
+ }
5619
6285
  return;
5620
6286
  }
5621
6287
  let flow;
@@ -5634,17 +6300,23 @@ function makeRunCommand() {
5634
6300
  if (opts.label) console.log(` ${S.label("Label:")} ${opts.label}`);
5635
6301
  if (dryRun) console.log(` ${S.warn("(dry run \u2014 no agent will be started)")}`);
5636
6302
  console.log();
5637
- await runFlow({
5638
- projectDir,
5639
- flowPath,
5640
- driver,
5641
- model,
5642
- provider,
5643
- sessionLabel: opts.label,
5644
- dryRun,
5645
- onEvent: makeFlowEventHandler(),
5646
- onLog: (line) => console.log(line)
5647
- });
6303
+ try {
6304
+ await runFlow({
6305
+ projectDir,
6306
+ flowPath,
6307
+ driver,
6308
+ model,
6309
+ provider,
6310
+ sessionLabel: opts.label,
6311
+ dryRun,
6312
+ onEvent: makeFlowEventHandler(),
6313
+ onLog: (line) => console.log(line)
6314
+ });
6315
+ } catch (err) {
6316
+ const friendly = friendlyDriverError(err);
6317
+ logErr(friendly ?? (err instanceof Error ? err.message : String(err)));
6318
+ process.exit(1);
6319
+ }
5648
6320
  });
5649
6321
  return cmd;
5650
6322
  }
@@ -5786,12 +6458,12 @@ function makeServeCommand() {
5786
6458
  );
5787
6459
  }
5788
6460
  async function ensurePluginsLoadedForServe(projectDir) {
5789
- const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-2OL4FRFB.js');
6461
+ const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-TF7GCBRQ.js');
5790
6462
  try {
5791
6463
  const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
5792
6464
  const config = resolveSkWorkspaceConfig2(projectDir);
5793
6465
  if (!config.plugins || config.plugins.length === 0) return;
5794
- const { ensurePluginsLoaded } = await import('../plugin-store-G4HKWCTU.js');
6466
+ const { ensurePluginsLoaded } = await import('../plugin-store-S7OMGI7N.js');
5795
6467
  const { pluginRegistry } = await import('../plugin-registry/index.js');
5796
6468
  const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
5797
6469
  if (result.loaded.length > 0) {
@@ -5805,21 +6477,21 @@ async function ensurePluginsLoadedForServe(projectDir) {
5805
6477
  }
5806
6478
  }
5807
6479
  async function runCompileTest(opts) {
5808
- const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-2OL4FRFB.js');
6480
+ const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-TF7GCBRQ.js');
5809
6481
  const { tmpdir } = await import('os');
5810
- const { dirname: dirname4, join: join5, resolve: resolve5 } = await import('path');
6482
+ const { dirname: dirname4, join: join6, resolve: resolve5 } = await import('path');
5811
6483
  const { fileURLToPath: fileURLToPath2 } = await import('url');
5812
- const { unlinkSync, existsSync: existsSync13 } = await import('fs');
6484
+ const { unlinkSync, existsSync: existsSync14 } = await import('fs');
5813
6485
  const { spawn } = await import('child_process');
5814
6486
  const { portableSpawnSync: portableSpawnSync2 } = await import('../core/index.js');
5815
- const { MONOREPO_ROOT } = await import('../paths-ML33B5SB.js');
6487
+ const { MONOREPO_ROOT } = await import('../paths-P3DWFUPS.js');
5816
6488
  if (MONOREPO_ROOT === null) {
5817
6489
  logErr2("--compile-test is only available inside the skaile-dev workspace.");
5818
6490
  logInfo2("Run it from a checkout of skaile-ai/skaile-dev (the monorepo shell).");
5819
6491
  process.exitCode = 2;
5820
6492
  return;
5821
6493
  }
5822
- const outfile = join5(tmpdir(), `skaile-compile-test-${Date.now()}`);
6494
+ const outfile = join6(tmpdir(), `skaile-compile-test-${Date.now()}`);
5823
6495
  const __thisFile = fileURLToPath2(import.meta.url);
5824
6496
  const entryPoint = resolve5(dirname4(__thisFile), "..", "index.ts");
5825
6497
  logInfo2(`Building compiled binary...`);
@@ -5871,7 +6543,7 @@ async function runCompileTest(opts) {
5871
6543
  } catch {
5872
6544
  }
5873
6545
  try {
5874
- if (existsSync13(outfile)) unlinkSync(outfile);
6546
+ if (existsSync14(outfile)) unlinkSync(outfile);
5875
6547
  } catch {
5876
6548
  }
5877
6549
  }
@@ -6625,96 +7297,37 @@ function makeShowCommand() {
6625
7297
  }
6626
7298
  });
6627
7299
  }
7300
+ async function fetchIdentity() {
7301
+ const jwt = await getStoreToken();
7302
+ return storeFetch({ ...getStoreConfig(), jwt }, "/api/v1/me");
7303
+ }
6628
7304
  function makeStoreCommand() {
6629
7305
  const store = new Command("store").description("Manage the AI Asset Store");
6630
- store.command("login").description("Authenticate with the store").option("--email <email>", "Login with email instead of GitHub").action(async (opts) => {
6631
- const config = getStoreConfig();
6632
- if (opts.email) {
6633
- const readline2 = await import('readline');
6634
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
6635
- const password = await new Promise((resolve5) => {
6636
- rl.question("Password: ", (answer) => {
6637
- rl.close();
6638
- resolve5(answer);
6639
- });
6640
- });
6641
- try {
6642
- const res = await storeFetch(
6643
- config,
6644
- "/api/auth/login",
6645
- { method: "POST", body: { email: opts.email, password } }
6646
- );
6647
- saveStoreTokens(res.jwt, res.refreshToken);
6648
- logOk(`Logged in as ${S.brand(`@${res.publisher.slug}`)}`);
6649
- } catch (e) {
6650
- logErr(e.message);
6651
- process.exit(1);
6652
- }
6653
- } else {
6654
- const loginUrl = `${config.url}/api/auth/login/github`;
6655
- console.log(`
6656
- Opening browser to authenticate...
6657
- ${S.dim(loginUrl)}
6658
- `);
6659
- const http = await import('http');
6660
- const { URL: URL2 } = await import('url');
6661
- const server = http.createServer((req, res) => {
6662
- const url = new URL2(req.url ?? "/", "http://localhost");
6663
- const jwt = url.searchParams.get("token");
6664
- const refresh = url.searchParams.get("refresh");
6665
- if (jwt && refresh) {
6666
- saveStoreTokens(jwt, refresh);
6667
- res.writeHead(200, { "Content-Type": "text/html" });
6668
- res.end("<h2>Authenticated! You can close this tab.</h2>");
6669
- logOk("Authenticated with the store");
6670
- server.close();
6671
- } else {
6672
- res.writeHead(400);
6673
- res.end("Missing tokens");
6674
- }
6675
- });
6676
- server.listen(0, () => {
6677
- const port = server.address()?.port;
6678
- const open = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
6679
- import('child_process').then(({ exec }) => {
6680
- exec(`${open} "${loginUrl}"`);
6681
- });
6682
- console.log(` ${S.dim(`Listening on http://localhost:${port} for callback...`)}`);
6683
- });
6684
- setTimeout(() => {
6685
- logErr("Login timed out");
6686
- server.close();
6687
- process.exit(1);
6688
- }, 12e4);
7306
+ store.command("login").description("Sign in to the store (Keycloak device flow \u2014 approve in your browser)").action(async () => {
7307
+ try {
7308
+ await deviceLogin(loadStoreAuthConfig());
7309
+ const me = await fetchIdentity().catch(() => ({}));
7310
+ logOk(`Signed in${me.username ? ` as ${S.brand(`@${me.username}`)}` : "."}`);
7311
+ } catch (e) {
7312
+ logErr(e.message);
7313
+ process.exit(1);
6689
7314
  }
6690
7315
  });
6691
- store.command("logout").description("Sign out of the store").action(async () => {
6692
- const config = getStoreConfig();
6693
- if (config.refreshToken) {
6694
- try {
6695
- await storeFetch(config, "/api/auth/logout", {
6696
- method: "POST",
6697
- body: { refreshToken: config.refreshToken }
6698
- });
6699
- } catch {
6700
- }
6701
- }
6702
- clearStoreTokens();
6703
- logOk("Logged out");
7316
+ store.command("logout").description("Sign out of the store").action(() => {
7317
+ logout(loadStoreAuthConfig());
7318
+ logOk("Signed out.");
6704
7319
  });
6705
7320
  store.command("whoami").description("Show current store identity").action(async () => {
6706
- const config = getStoreConfig();
6707
- if (!isStoreAuthenticated(config)) {
6708
- logErr("Not authenticated. Run: skaile store login");
7321
+ if (!hasStoredAuth(loadStoreAuthConfig())) {
7322
+ logErr("Not signed in. Run: skaile store login");
6709
7323
  process.exit(1);
6710
7324
  }
6711
7325
  try {
6712
- const me = await storeFetch(config, "/api/auth/me");
7326
+ const me = await fetchIdentity();
6713
7327
  console.log();
6714
- console.log(` ${S.heading("Publisher")} ${S.brand(`@${me.slug}`)}`);
6715
- console.log(` ${S.label("Name")} ${me.displayName}`);
6716
- console.log(` ${S.label("Email")} ${me.email}`);
6717
- console.log(` ${S.label("Verified")} ${me.verified ? S.ok("yes") : S.dim("no")}`);
7328
+ console.log(` ${S.heading("Publisher")} ${S.brand(`@${me.username ?? "?"}`)}`);
7329
+ if (me.githubLogin) console.log(` ${S.label("GitHub")} ${me.githubLogin}`);
7330
+ if (me.roles?.length) console.log(` ${S.label("Roles")} ${me.roles.join(", ")}`);
6718
7331
  console.log();
6719
7332
  } catch (e) {
6720
7333
  logErr(e.message);
@@ -6752,9 +7365,8 @@ function makeStoreCommand() {
6752
7365
  process.exit(2);
6753
7366
  });
6754
7367
  store.command("yank <version>").description("Yank (soft-delete) a published version").option("--asset <ref>", "Asset reference (@publisher/slug)").action(async (version, opts) => {
6755
- const config = getStoreConfig();
6756
- if (!isStoreAuthenticated(config)) {
6757
- logErr("Not authenticated. Run: skaile store login");
7368
+ if (!isStoreAuthenticated()) {
7369
+ logErr("Not signed in. Run: skaile store login");
6758
7370
  process.exit(1);
6759
7371
  }
6760
7372
  if (!opts.asset) {
@@ -6768,7 +7380,8 @@ function makeStoreCommand() {
6768
7380
  process.exit(1);
6769
7381
  }
6770
7382
  try {
6771
- await storeFetch(config, `/api/assets/${publisher}/${slug}/${version}/yank`, {
7383
+ const jwt = await getStoreToken();
7384
+ await storeFetch({ ...getStoreConfig(), jwt }, `/api/assets/${publisher}/${slug}/${version}/yank`, {
6772
7385
  method: "PATCH"
6773
7386
  });
6774
7387
  logOk(`Yanked v${version} of @${publisher}/${slug}`);
@@ -7448,12 +8061,43 @@ function makeVerifyCommand() {
7448
8061
  }
7449
8062
  });
7450
8063
  }
8064
+ function refKindName(ref) {
8065
+ const trimmed = ref.trim();
8066
+ const colon = trimmed.indexOf(":");
8067
+ let kind = colon === -1 ? void 0 : trimmed.slice(0, colon);
8068
+ if (kind === "mcp") kind = "mcp-server";
8069
+ let rest = colon === -1 ? trimmed : trimmed.slice(colon + 1);
8070
+ const hash = rest.indexOf("#");
8071
+ if (hash !== -1) rest = rest.slice(0, hash);
8072
+ if (rest.startsWith("@")) {
8073
+ const slash = rest.indexOf("/");
8074
+ if (slash !== -1) rest = rest.slice(slash + 1);
8075
+ }
8076
+ return { kind, name: rest };
8077
+ }
8078
+ function whyEmptyMessage(ref, declared) {
8079
+ const { kind, name } = refKindName(ref);
8080
+ const isDeclared = declared.some(
8081
+ (d) => d.name === name && (kind === void 0 || d.kind === kind)
8082
+ );
8083
+ if (isDeclared) {
8084
+ return `${ref} not found in lock file. Run \`skaile install\` first.`;
8085
+ }
8086
+ return `${ref} is not a dependency of this project \u2014 nothing in skaile.yaml requires it. Run \`skaile list\` to see declared dependencies.`;
8087
+ }
7451
8088
  function makeWhyCommand() {
7452
8089
  return new Command("why").description("Show why an asset is installed (dependency chain)").argument("<ref>", "Asset reference (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).action(async (ref, opts) => {
7453
8090
  const am2 = new AssetManager({ projectDir: path14__default.resolve(opts.projectDir) });
7454
8091
  const chain = am2.why(ref);
7455
8092
  if (chain.length === 0) {
7456
- logErr(`${ref} not found in lock file. Run \`skaile install\` first.`);
8093
+ let declared;
8094
+ try {
8095
+ declared = am2.listDeclared();
8096
+ } catch {
8097
+ logErr(`${ref} not found in lock file. Run \`skaile install\` first.`);
8098
+ return;
8099
+ }
8100
+ logErr(whyEmptyMessage(ref, declared));
7457
8101
  return;
7458
8102
  }
7459
8103
  console.log();
@@ -7599,7 +8243,7 @@ program.command("init [project-dir]").description("Initialize a project director
7599
8243
  );
7600
8244
  async function runInit(projectDir, opts) {
7601
8245
  const { mkdirSync: mkdirSync5, writeFileSync: writeFileSync4 } = await import('fs');
7602
- const { stringify } = await import('yaml');
8246
+ const { stringify: stringify2 } = await import('yaml');
7603
8247
  const { DRIVER_TARGETS, SUPPORTED_DRIVER_TARGETS: SUPPORTED_DRIVER_TARGETS2 } = await import('../core/index.js');
7604
8248
  const backend = opts.backend;
7605
8249
  if (!SUPPORTED_DRIVER_TARGETS2.includes(backend)) {
@@ -7610,29 +8254,29 @@ async function runInit(projectDir, opts) {
7610
8254
  const resolved = path14__default.resolve(projectDir ?? ".");
7611
8255
  const projectName = path14__default.basename(resolved);
7612
8256
  const created = [];
7613
- const { existsSync: existsSync13 } = await import('fs');
7614
- if (!existsSync13(resolved)) {
8257
+ const { existsSync: existsSync14 } = await import('fs');
8258
+ if (!existsSync14(resolved)) {
7615
8259
  mkdirSync5(resolved, { recursive: true });
7616
8260
  created.push(".");
7617
8261
  }
7618
8262
  const skaileDir = path14__default.join(resolved, ".skaile");
7619
- if (!existsSync13(skaileDir)) {
8263
+ if (!existsSync14(skaileDir)) {
7620
8264
  mkdirSync5(path14__default.join(skaileDir, "sessions"), { recursive: true });
7621
8265
  created.push(".skaile/");
7622
8266
  }
7623
8267
  const settingsPath = path14__default.join(skaileDir, "settings.json");
7624
- if (!existsSync13(settingsPath)) {
8268
+ if (!existsSync14(settingsPath)) {
7625
8269
  writeFileSync4(settingsPath, "{}\n");
7626
8270
  created.push(".skaile/settings.json");
7627
8271
  }
7628
8272
  const wsConfigPath = path14__default.join(resolved, "skaile.yaml");
7629
- if (!existsSync13(wsConfigPath)) {
7630
- writeFileSync4(wsConfigPath, stringify(buildInitWorkspaceConfig(projectName, backend)));
8273
+ if (!existsSync14(wsConfigPath)) {
8274
+ writeFileSync4(wsConfigPath, stringify2(buildInitWorkspaceConfig(projectName, backend)));
7631
8275
  created.push("skaile.yaml");
7632
8276
  }
7633
8277
  for (const dir of new Set(Object.values(DRIVER_TARGETS[backend].local))) {
7634
8278
  const full = path14__default.join(resolved, dir);
7635
- if (!existsSync13(full)) {
8279
+ if (!existsSync14(full)) {
7636
8280
  mkdirSync5(full, { recursive: true });
7637
8281
  created.push(`${dir}/`);
7638
8282
  }
@@ -7663,7 +8307,7 @@ function buildInitWorkspaceConfig(projectName, backend) {
7663
8307
  }
7664
8308
  async function initGitScaffold(resolved, created) {
7665
8309
  const { execSync: execSync3 } = await import('child_process');
7666
- const { existsSync: existsSync13, readFileSync: readFileSync8, writeFileSync: writeFileSync4 } = await import('fs');
8310
+ const { existsSync: existsSync14, readFileSync: readFileSync8, writeFileSync: writeFileSync4 } = await import('fs');
7667
8311
  const gitignorePath = path14__default.join(resolved, ".gitignore");
7668
8312
  const entries = [
7669
8313
  "node_modules/",
@@ -7679,7 +8323,7 @@ async function initGitScaffold(resolved, created) {
7679
8323
  ".env",
7680
8324
  ".env.local"
7681
8325
  ];
7682
- const existing = existsSync13(gitignorePath) ? readFileSync8(gitignorePath, "utf-8") : "";
8326
+ const existing = existsSync14(gitignorePath) ? readFileSync8(gitignorePath, "utf-8") : "";
7683
8327
  const have = new Set(existing.split("\n").map((l) => l.trim()));
7684
8328
  const append = entries.filter((e) => !have.has(e));
7685
8329
  if (append.length > 0) {
@@ -7688,7 +8332,7 @@ async function initGitScaffold(resolved, created) {
7688
8332
  `);
7689
8333
  if (!existing) created.push(".gitignore");
7690
8334
  }
7691
- if (!existsSync13(path14__default.join(resolved, ".git"))) {
8335
+ if (!existsSync14(path14__default.join(resolved, ".git"))) {
7692
8336
  try {
7693
8337
  execSync3("git init", { cwd: resolved, stdio: "pipe" });
7694
8338
  created.push(".git/");
@@ -7718,7 +8362,7 @@ async function bootstrapUserConfig() {
7718
8362
  }
7719
8363
  async function hydrateInitSources(resolved, created) {
7720
8364
  try {
7721
- const { ensureSourcesCloned } = await import('../ensure-sources-4BAJGJRK.js');
8365
+ const { ensureSourcesCloned } = await import('../ensure-sources-QDVFUIUP.js');
7722
8366
  const hydrate = ensureSourcesCloned(resolved, { quiet: true });
7723
8367
  for (const n of hydrate.cloned) created.push(`~/.skaile/cache/sources/${n}/`);
7724
8368
  if (hydrate.failed.length > 0) {
@@ -7751,10 +8395,14 @@ function printInitSummary(resolved, projectDir, created) {
7751
8395
  console.log(
7752
8396
  ` ${S.dim(`${step}.`)} ${S.cmd("skaile search <query>")} ${S.dim("\u2014 find assets to add")}`
7753
8397
  );
8398
+ step++;
8399
+ console.log(
8400
+ ` ${S.dim(`${step}.`)} ${S.cmd("skaile preset apply <file>")} ${S.dim("\u2014 apply a saved preset (assets + config) at once")}`
8401
+ );
7754
8402
  console.log();
7755
8403
  }
7756
8404
  program.command("setup").description("Interactive provider setup wizard").action(async () => {
7757
- const { cmdSetup } = await import('../setup-WS6PF6JQ.js');
8405
+ const { cmdSetup } = await import('../setup-2CCS63L6.js');
7758
8406
  await cmdSetup([], { projectDir: process.cwd() });
7759
8407
  });
7760
8408
  program.addCommand(makeInstallCommand());