@skaile/workspaces 1.2.0 → 1.4.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 (224) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +1 -1
  3. package/dist/app-sdk/index.d.ts +2 -0
  4. package/dist/app-sdk/index.js +295 -0
  5. package/dist/app-sdk/index.js.map +1 -0
  6. package/dist/app-sdk/src/app.d.ts +101 -0
  7. package/dist/app-sdk/src/app.d.ts.map +1 -0
  8. package/dist/app-sdk/src/define-app-capability.d.ts +139 -0
  9. package/dist/app-sdk/src/define-app-capability.d.ts.map +1 -0
  10. package/dist/app-sdk/src/index.d.ts +16 -0
  11. package/dist/app-sdk/src/index.d.ts.map +1 -0
  12. package/dist/app-sdk/src/logger.d.ts +26 -0
  13. package/dist/app-sdk/src/logger.d.ts.map +1 -0
  14. package/dist/app-sdk/src/transport.d.ts +84 -0
  15. package/dist/app-sdk/src/transport.d.ts.map +1 -0
  16. package/dist/app-sdk/src/ws-transport.d.ts +63 -0
  17. package/dist/app-sdk/src/ws-transport.d.ts.map +1 -0
  18. package/dist/{asset-feeds-OBPWVUDL.js → asset-feeds-NG5TKB3V.js} +10 -10
  19. package/dist/{asset-feeds-OBPWVUDL.js.map → asset-feeds-NG5TKB3V.js.map} +1 -1
  20. package/dist/asset-manager/index.js +9 -9
  21. package/dist/asset-manager/installer.js +8 -8
  22. package/dist/asset-manager/renderers.js +2 -2
  23. package/dist/asset-manager/src/index.d.ts +42 -6
  24. package/dist/asset-manager/src/index.d.ts.map +1 -1
  25. package/dist/bridge/drivers/claude-sdk.js +41 -6
  26. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  27. package/dist/bridge/drivers/codex.js +1 -1
  28. package/dist/bridge/drivers/continue.js +2 -2
  29. package/dist/bridge/drivers/echo.js +1 -1
  30. package/dist/bridge/drivers/gemini.js +3 -3
  31. package/dist/bridge/drivers/goose.js +2 -2
  32. package/dist/bridge/drivers/omp.js +1 -1
  33. package/dist/bridge/drivers/opencode.js +2 -2
  34. package/dist/bridge/drivers/qwen.js +3 -3
  35. package/dist/bridge/index.js +4 -3
  36. package/dist/bridge/src/cloud.d.ts +89 -0
  37. package/dist/bridge/src/cloud.d.ts.map +1 -0
  38. package/dist/bridge/src/drivers/claude-sdk.d.ts +12 -1
  39. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  40. package/dist/bridge/src/index.d.ts +1 -0
  41. package/dist/bridge/src/index.d.ts.map +1 -1
  42. package/dist/bridge/src/types.d.ts +31 -0
  43. package/dist/bridge/src/types.d.ts.map +1 -1
  44. package/dist/{chunk-MG6UIS7G.js → chunk-26YCXWCU.js} +52 -3
  45. package/dist/chunk-26YCXWCU.js.map +1 -0
  46. package/dist/{chunk-SKADPHAM.js → chunk-4N5XR5DQ.js} +2 -2
  47. package/dist/chunk-4N5XR5DQ.js.map +1 -0
  48. package/dist/{chunk-CLUJLV7Z.js → chunk-77OXU44H.js} +6 -6
  49. package/dist/{chunk-CLUJLV7Z.js.map → chunk-77OXU44H.js.map} +1 -1
  50. package/dist/{chunk-TTTLCSEQ.js → chunk-AAGD2J4H.js} +121 -41
  51. package/dist/chunk-AAGD2J4H.js.map +1 -0
  52. package/dist/{chunk-KVZ6XKVN.js → chunk-B6SSXVF2.js} +4 -4
  53. package/dist/chunk-B6SSXVF2.js.map +1 -0
  54. package/dist/{chunk-BCJL2UNH.js → chunk-BDUANIAR.js} +3 -3
  55. package/dist/{chunk-BCJL2UNH.js.map → chunk-BDUANIAR.js.map} +1 -1
  56. package/dist/{chunk-YI5OZ7QO.js → chunk-BE5KTS2L.js} +3 -3
  57. package/dist/{chunk-YI5OZ7QO.js.map → chunk-BE5KTS2L.js.map} +1 -1
  58. package/dist/{chunk-ZUE2PIJ5.js → chunk-BJHZHNKR.js} +3 -3
  59. package/dist/{chunk-ZUE2PIJ5.js.map → chunk-BJHZHNKR.js.map} +1 -1
  60. package/dist/{chunk-MQEZE6TU.js → chunk-DNFIKRO6.js} +3 -3
  61. package/dist/{chunk-MQEZE6TU.js.map → chunk-DNFIKRO6.js.map} +1 -1
  62. package/dist/{chunk-VCBLXF7D.js → chunk-GWDZW44L.js} +5 -5
  63. package/dist/{chunk-VCBLXF7D.js.map → chunk-GWDZW44L.js.map} +1 -1
  64. package/dist/{chunk-UY5Q2NZH.js → chunk-HKX5VSWQ.js} +3 -3
  65. package/dist/{chunk-UY5Q2NZH.js.map → chunk-HKX5VSWQ.js.map} +1 -1
  66. package/dist/{chunk-NUS5L5ZJ.js → chunk-HWTQ6HPO.js} +5 -5
  67. package/dist/{chunk-NUS5L5ZJ.js.map → chunk-HWTQ6HPO.js.map} +1 -1
  68. package/dist/{chunk-WRBF34YE.js → chunk-IEX5YV4R.js} +3 -3
  69. package/dist/{chunk-WRBF34YE.js.map → chunk-IEX5YV4R.js.map} +1 -1
  70. package/dist/{chunk-CU5Z7GH3.js → chunk-IRORTHGA.js} +20 -6
  71. package/dist/chunk-IRORTHGA.js.map +1 -0
  72. package/dist/chunk-ISQWMBKD.js +74 -0
  73. package/dist/chunk-ISQWMBKD.js.map +1 -0
  74. package/dist/{chunk-W65IK4MH.js → chunk-JZPEEMIM.js} +6 -6
  75. package/dist/{chunk-W65IK4MH.js.map → chunk-JZPEEMIM.js.map} +1 -1
  76. package/dist/{chunk-35TLBMHX.js → chunk-LCTRCWBK.js} +6 -6
  77. package/dist/{chunk-35TLBMHX.js.map → chunk-LCTRCWBK.js.map} +1 -1
  78. package/dist/{chunk-U5J77IIZ.js → chunk-NKTFEV7Q.js} +4 -4
  79. package/dist/{chunk-U5J77IIZ.js.map → chunk-NKTFEV7Q.js.map} +1 -1
  80. package/dist/{chunk-EPLYVB3L.js → chunk-OSCJRUKM.js} +2 -2
  81. package/dist/{chunk-EPLYVB3L.js.map → chunk-OSCJRUKM.js.map} +1 -1
  82. package/dist/{chunk-5AGARSQT.js → chunk-OUWNTFDG.js} +31 -3
  83. package/dist/chunk-OUWNTFDG.js.map +1 -0
  84. package/dist/{chunk-EVZKWY5J.js → chunk-PK7Q7MC7.js} +5 -4
  85. package/dist/chunk-PK7Q7MC7.js.map +1 -0
  86. package/dist/{chunk-XWLVIEWD.js → chunk-QM2TT2PJ.js} +20 -12
  87. package/dist/chunk-QM2TT2PJ.js.map +1 -0
  88. package/dist/{chunk-BM37QD6U.js → chunk-TJRWGMD2.js} +3 -3
  89. package/dist/{chunk-BM37QD6U.js.map → chunk-TJRWGMD2.js.map} +1 -1
  90. package/dist/{chunk-QVE3ILCY.js → chunk-TNSCHS4I.js} +2 -2
  91. package/dist/chunk-TNSCHS4I.js.map +1 -0
  92. package/dist/{chunk-Z5Z3MYDL.js → chunk-UPZGQZEJ.js} +3 -3
  93. package/dist/{chunk-Z5Z3MYDL.js.map → chunk-UPZGQZEJ.js.map} +1 -1
  94. package/dist/{chunk-52XHXSBN.js → chunk-UQ5FK2AB.js} +3 -3
  95. package/dist/{chunk-52XHXSBN.js.map → chunk-UQ5FK2AB.js.map} +1 -1
  96. package/dist/{chunk-KAWKKMU4.js → chunk-X57AMTAH.js} +74 -11
  97. package/dist/chunk-X57AMTAH.js.map +1 -0
  98. package/dist/{chunk-OIGYDVCM.js → chunk-XBG2YDQQ.js} +3 -3
  99. package/dist/{chunk-OIGYDVCM.js.map → chunk-XBG2YDQQ.js.map} +1 -1
  100. package/dist/cli/index.js +770 -188
  101. package/dist/cli/index.js.map +1 -1
  102. package/dist/cli/src/commands/preset-apply.d.ts +59 -0
  103. package/dist/cli/src/commands/preset-apply.d.ts.map +1 -0
  104. package/dist/cli/src/commands/preset-cmd.d.ts +2 -2
  105. package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
  106. package/dist/cli/src/commands/preset-export.d.ts +93 -0
  107. package/dist/cli/src/commands/preset-export.d.ts.map +1 -0
  108. package/dist/cli/src/commands/source.d.ts.map +1 -1
  109. package/dist/cli/src/commands/why.d.ts +18 -0
  110. package/dist/cli/src/commands/why.d.ts.map +1 -1
  111. package/dist/connectors/config.js +5 -5
  112. package/dist/connectors/index.js +7 -7
  113. package/dist/core/index.js +4 -4
  114. package/dist/core/runtime-assets.js +2 -2
  115. package/dist/core/src/index.d.ts +2 -2
  116. package/dist/core/src/index.d.ts.map +1 -1
  117. package/dist/core/src/settings.d.ts +18 -0
  118. package/dist/core/src/settings.d.ts.map +1 -1
  119. package/dist/core/src/workspace-config.d.ts +45 -0
  120. package/dist/core/src/workspace-config.d.ts.map +1 -1
  121. package/dist/core/src/workspace-yaml-editor.d.ts +14 -1
  122. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  123. package/dist/core/workspace-config.js +1 -1
  124. package/dist/deploy/index.js +4 -4
  125. package/dist/discovery/index.js +2 -2
  126. package/dist/{ensure-sources-BQI2TOZZ.js → ensure-sources-CHC7X7AJ.js} +12 -12
  127. package/dist/{ensure-sources-BQI2TOZZ.js.map → ensure-sources-CHC7X7AJ.js.map} +1 -1
  128. package/dist/factory-assets/connectors/box.js +7 -7
  129. package/dist/factory-assets/connectors/deploy.js +7 -7
  130. package/dist/factory-assets/connectors/devserver.js +7 -7
  131. package/dist/factory-assets/connectors/flow/adapter.js +7 -7
  132. package/dist/factory-assets/connectors/flow/engine/engine.d.ts +4 -1
  133. package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
  134. package/dist/factory-assets/connectors/flow/engine/types.d.ts +41 -6
  135. package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
  136. package/dist/factory-assets/connectors/flow/engine.js +5 -5
  137. package/dist/factory-assets/connectors/flow/run-flow.js +8 -8
  138. package/dist/factory-assets/connectors/flow.js +7 -7
  139. package/dist/factory-assets/connectors/git.js +7 -7
  140. package/dist/factory-assets/connectors/gmail.js +7 -7
  141. package/dist/factory-assets/connectors/googledrive.js +7 -7
  142. package/dist/factory-assets/connectors/local.js +7 -7
  143. package/dist/factory-assets/connectors/mattermost.js +7 -7
  144. package/dist/factory-assets/connectors/memory.js +7 -7
  145. package/dist/factory-assets/connectors/minio.js +7 -7
  146. package/dist/factory-assets/connectors/postgres.js +7 -7
  147. package/dist/factory-assets/connectors/s3.js +7 -7
  148. package/dist/factory-assets/connectors/sharepoint.js +7 -7
  149. package/dist/factory-assets/connectors/sqlite.js +7 -7
  150. package/dist/factory-assets/connectors/static-server.js +7 -7
  151. package/dist/factory-assets/connectors/tunnel.js +7 -7
  152. package/dist/factory-assets/connectors/webdav.js +7 -7
  153. package/dist/factory-assets/connectors/xstate-store.js +7 -7
  154. package/dist/factory-assets/connectors/xstate.js +7 -7
  155. package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +1 -1
  156. package/dist/factory-assets/skills/skaile-manage-workspace/SKILL.md +1 -1
  157. package/dist/{flows-4Z74KY2H.js → flows-CGOCUNO6.js} +4 -4
  158. package/dist/{flows-4Z74KY2H.js.map → flows-CGOCUNO6.js.map} +1 -1
  159. package/dist/{helpers-SC4PWN2F.js → helpers-GWG4CYYC.js} +12 -12
  160. package/dist/{helpers-SC4PWN2F.js.map → helpers-GWG4CYYC.js.map} +1 -1
  161. package/dist/library/index.js +8 -8
  162. package/dist/library/install.js +4 -4
  163. package/dist/{open-library-ZNYIND7N.js → open-library-37PXN6MK.js} +17 -17
  164. package/dist/{open-library-ZNYIND7N.js.map → open-library-37PXN6MK.js.map} +1 -1
  165. package/dist/{paths-S7QNXYMB.js → paths-UVOHWUPY.js} +7 -7
  166. package/dist/{paths-S7QNXYMB.js.map → paths-UVOHWUPY.js.map} +1 -1
  167. package/dist/{plugin-store-3DAVGKHN.js → plugin-store-EIMVEH3O.js} +6 -6
  168. package/dist/{plugin-store-3DAVGKHN.js.map → plugin-store-EIMVEH3O.js.map} +1 -1
  169. package/dist/runner/index.js +14 -13
  170. package/dist/runner/src/capability-registry.d.ts +15 -4
  171. package/dist/runner/src/capability-registry.d.ts.map +1 -1
  172. package/dist/runner/src/cloud-bundle.d.ts +36 -0
  173. package/dist/runner/src/cloud-bundle.d.ts.map +1 -0
  174. package/dist/runner/src/serve.d.ts.map +1 -1
  175. package/dist/runner/src/session-builder.d.ts +32 -0
  176. package/dist/runner/src/session-builder.d.ts.map +1 -1
  177. package/dist/sdk/asset-manager.js +9 -9
  178. package/dist/sdk/bridge.js +4 -3
  179. package/dist/sdk/core.js +4 -4
  180. package/dist/sdk/flow.js +5 -5
  181. package/dist/sdk/index.js +14 -13
  182. package/dist/sdk/index.js.map +1 -1
  183. package/dist/sdk/runner.js +14 -13
  184. package/dist/sdk/session.js +2 -2
  185. package/dist/sdk/types.js +1 -1
  186. package/dist/session/index.js +2 -2
  187. package/dist/{setup-WBGDT2WY.js → setup-DINVR6ZQ.js} +10 -9
  188. package/dist/setup-DINVR6ZQ.js.map +1 -0
  189. package/dist/{source-SBFVULTG.js → source-RXAHIGMD.js} +18 -18
  190. package/dist/{source-SBFVULTG.js.map → source-RXAHIGMD.js.map} +1 -1
  191. package/dist/{store-client-YGDN4L2G.js → store-client-T3F663YG.js} +7 -7
  192. package/dist/{store-client-YGDN4L2G.js.map → store-client-T3F663YG.js.map} +1 -1
  193. package/dist/tui/index.js +14 -13
  194. package/dist/tui/index.js.map +1 -1
  195. package/dist/types/index.js +1 -1
  196. package/dist/types/manifests.js +1 -1
  197. package/dist/types/src/capabilities.d.ts +8 -2
  198. package/dist/types/src/capabilities.d.ts.map +1 -1
  199. package/dist/types/src/logging.d.ts +3 -2
  200. package/dist/types/src/logging.d.ts.map +1 -1
  201. package/dist/types/src/manifests/_shared.d.ts +4 -4
  202. package/dist/types/src/manifests/agent.d.ts +47 -8
  203. package/dist/types/src/manifests/agent.d.ts.map +1 -1
  204. package/dist/types/src/manifests/index.d.ts +3 -3
  205. package/dist/types/src/manifests/index.d.ts.map +1 -1
  206. package/dist/types/src/manifests/preset.d.ts +10 -0
  207. package/dist/types/src/manifests/preset.d.ts.map +1 -1
  208. package/dist/types/src/version.d.ts +11 -1
  209. package/dist/types/src/version.d.ts.map +1 -1
  210. package/dist/workspace-plugin/adapters/mcp.js +2 -2
  211. package/dist/workspace-plugin/adapters/omp.js +3 -3
  212. package/dist/workspace-plugin/index.js +1 -1
  213. package/package.json +7 -1
  214. package/dist/chunk-5AGARSQT.js.map +0 -1
  215. package/dist/chunk-CU5Z7GH3.js.map +0 -1
  216. package/dist/chunk-EVZKWY5J.js.map +0 -1
  217. package/dist/chunk-KAWKKMU4.js.map +0 -1
  218. package/dist/chunk-KVZ6XKVN.js.map +0 -1
  219. package/dist/chunk-MG6UIS7G.js.map +0 -1
  220. package/dist/chunk-QVE3ILCY.js.map +0 -1
  221. package/dist/chunk-SKADPHAM.js.map +0 -1
  222. package/dist/chunk-TTTLCSEQ.js.map +0 -1
  223. package/dist/chunk-XWLVIEWD.js.map +0 -1
  224. package/dist/setup-WBGDT2WY.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { makeSourceCommand, syncProjectSources } from '../chunk-XWLVIEWD.js';
3
- import { openCatalogSource, openLibraryManager, createFullRegistry, openLibrary } from '../chunk-CLUJLV7Z.js';
4
- import { logErr, S, logOk, logInfo, colorRef, logWarn, kindColorPad, kindColor, formatSoftDepSection, bareSoftDepName, formatSoftDepHint, formatRelativeTime, fitWidth, formatSoftDepRemovePreview, formatSoftDepAddPreview } from '../chunk-MQEZE6TU.js';
5
- import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-ZUE2PIJ5.js';
6
- import { AI_RESOURCES, aiResourceRoots } from '../chunk-OIGYDVCM.js';
2
+ import { AI_RESOURCES, aiResourceRoots } from '../chunk-XBG2YDQQ.js';
3
+ import { makeSourceCommand, syncProjectSources } from '../chunk-QM2TT2PJ.js';
4
+ import { openCatalogSource, openLibraryManager, createFullRegistry, openLibrary } from '../chunk-77OXU44H.js';
5
+ import { logErr, S, logOk, logInfo, colorRef, logWarn, kindColorPad, kindColor, formatSoftDepSection, bareSoftDepName, formatSoftDepHint, formatRelativeTime, fitWidth, formatSoftDepRemovePreview, formatSoftDepAddPreview } from '../chunk-DNFIKRO6.js';
6
+ import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-BJHZHNKR.js';
7
7
  import { LocalSecretsProvider } from '../chunk-6BEGKO2Q.js';
8
- import { resolveLibraryDir } from '../chunk-NUS5L5ZJ.js';
9
- import '../chunk-KVZ6XKVN.js';
8
+ import { resolveLibraryDir, getDefaultValue, isSecretPlaceholder } from '../chunk-HWTQ6HPO.js';
9
+ import '../chunk-B6SSXVF2.js';
10
+ import '../chunk-JXT46QFC.js';
10
11
  import '../chunk-UKJMQZV6.js';
11
12
  import '../chunk-R7FOF242.js';
12
- import '../chunk-JXT46QFC.js';
13
- import { runFlow, resumeFlow } from '../chunk-W65IK4MH.js';
13
+ import { runFlow, resumeFlow } from '../chunk-JZPEEMIM.js';
14
14
  import '../chunk-4R6DROGN.js';
15
15
  import { validateFlowVersions, parseSkillFrontmatter } from '../chunk-GXC3TYMQ.js';
16
- import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-TTTLCSEQ.js';
17
- import { buildClaudePluginFiles } from '../chunk-EPLYVB3L.js';
16
+ import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-AAGD2J4H.js';
17
+ import { buildClaudePluginFiles } from '../chunk-OSCJRUKM.js';
18
18
  import '../chunk-X5YPJV4N.js';
19
19
  import '../chunk-O7SG5PC2.js';
20
20
  import '../chunk-7QBNJTTQ.js';
@@ -22,37 +22,38 @@ import '../chunk-GTS2FODO.js';
22
22
  import '../chunk-WQ7DE5UC.js';
23
23
  import '../chunk-32NA4TVC.js';
24
24
  import '../chunk-W3UDISS2.js';
25
- import '../chunk-EVZKWY5J.js';
26
- import '../chunk-UY5Q2NZH.js';
25
+ import '../chunk-HKX5VSWQ.js';
26
+ import '../chunk-ISQWMBKD.js';
27
+ import '../chunk-RRVQAE5D.js';
27
28
  import '../chunk-BMQKMOQY.js';
28
29
  import '../chunk-GAH4YRCI.js';
29
- import '../chunk-RRVQAE5D.js';
30
- import '../chunk-QVE3ILCY.js';
30
+ import '../chunk-TNSCHS4I.js';
31
31
  import '../chunk-LV2HPH3C.js';
32
- import '../chunk-VCBLXF7D.js';
33
- import '../chunk-BCJL2UNH.js';
32
+ import '../chunk-GWDZW44L.js';
33
+ import '../chunk-BDUANIAR.js';
34
34
  import '../chunk-6E6PKKAD.js';
35
- import { loadFlow, loadAllFlows } from '../chunk-SKADPHAM.js';
35
+ import { loadFlow, loadAllFlows } from '../chunk-4N5XR5DQ.js';
36
36
  import '../chunk-DEYYRFQU.js';
37
37
  import '../chunk-IFGTRL2I.js';
38
- import { AssetManager, NON_DEPLOYABLE_KINDS, MissingManifestError } from '../chunk-KAWKKMU4.js';
39
- import '../chunk-35TLBMHX.js';
40
- import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync, WorkspaceYamlEditor, checkAgentSkillConformance } from '../chunk-MG6UIS7G.js';
41
- import '../chunk-BM37QD6U.js';
42
- import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, resolveSkWorkspaceConfig } from '../chunk-5AGARSQT.js';
38
+ import '../chunk-PK7Q7MC7.js';
39
+ import { AssetManager, NON_DEPLOYABLE_KINDS, MissingManifestError, normalizeRepoUrl, FACTORY_SOURCE_URL } from '../chunk-X57AMTAH.js';
40
+ import '../chunk-LCTRCWBK.js';
41
+ import '../chunk-CGYEHQOX.js';
42
+ import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync, WorkspaceYamlEditor, checkAgentSkillConformance } from '../chunk-26YCXWCU.js';
43
+ import { assetSearchDirs, detectDomains } from '../chunk-ZAP2BDHG.js';
44
+ import '../chunk-TJRWGMD2.js';
45
+ import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, loadSkWorkspaceConfig, resolveSkWorkspaceConfig } from '../chunk-OUWNTFDG.js';
43
46
  import { SKAILE_MANIFEST_FILENAME, decodeSkaileManifest, loadSkPublishManifest, saveSkPublishManifest } from '../chunk-DZCK7GRW.js';
44
47
  import { parseFrontmatter } from '../chunk-BQCGFPHW.js';
45
48
  import { parseAssetRef, ASSET_KINDS, isValidAssetName, slugifyAssetName, ASSET_NAME_HINT, assetRefToStr } from '../chunk-GEEW6SY2.js';
46
49
  import '../chunk-F3HNW354.js';
47
50
  import { SUPPORTED_DRIVER_TARGETS } from '../chunk-MENCWIHW.js';
48
51
  import { DRIVER_DEFAULTS } from '../chunk-K5GBV4SA.js';
49
- import { assetSearchDirs, detectDomains } from '../chunk-ZAP2BDHG.js';
50
52
  import { openSqlite } from '../chunk-JB35MVBN.js';
51
53
  import '../chunk-KTBKW2FI.js';
52
54
  import '../chunk-UQ6LFBPZ.js';
53
- import '../chunk-WRBF34YE.js';
54
- import '../chunk-CU5Z7GH3.js';
55
- import '../chunk-CGYEHQOX.js';
55
+ import '../chunk-IEX5YV4R.js';
56
+ import { validatePreset } from '../chunk-IRORTHGA.js';
56
57
  import '../chunk-NSBPE2FW.js';
57
58
  import * as fs12 from 'fs';
58
59
  import fs12__default, { readFileSync, existsSync, mkdirSync, writeFileSync, lstatSync, rmSync, readdirSync, statSync } from 'fs';
@@ -70,6 +71,7 @@ import { createInterface } from 'readline';
70
71
  import fs6 from 'fs/promises';
71
72
  import { parse as parse$1, stringify as stringify$1 } from 'smol-toml';
72
73
  import { execSync } from 'child_process';
74
+ import * as readline2 from 'readline/promises';
73
75
  import { createTRPCUntypedClient, httpLink, httpSubscriptionLink } from '@trpc/client';
74
76
 
75
77
  async function runSearch(kind, query, opts) {
@@ -92,7 +94,7 @@ async function searchLocal(query, kind, opts, showStore, airGapped) {
92
94
  const projectDir = path14__default.resolve(opts.projectDir);
93
95
  let entries = new AssetManager({ projectDir }).search(query, kind);
94
96
  if (entries.length === 0 && !query && !kind && !airGapped) {
95
- const { autoSyncForSearch } = await import('../source-SBFVULTG.js');
97
+ const { autoSyncForSearch } = await import('../source-RXAHIGMD.js');
96
98
  if (await autoSyncForSearch(projectDir)) {
97
99
  entries = new AssetManager({ projectDir }).search(query, kind);
98
100
  }
@@ -145,7 +147,7 @@ async function searchStore(query, kind, opts, airGapped) {
145
147
  return;
146
148
  }
147
149
  try {
148
- const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-YGDN4L2G.js');
150
+ const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-T3F663YG.js');
149
151
  const config = getStoreConfig2();
150
152
  const params = {};
151
153
  if (query) params.q = query;
@@ -1005,7 +1007,7 @@ function makeCatalogCommand() {
1005
1007
  return catalog;
1006
1008
  }
1007
1009
  async function runLocalCatalogTest(projectDir) {
1008
- const { resolveCatalogSource } = await import('../open-library-ZNYIND7N.js');
1010
+ const { resolveCatalogSource } = await import('../open-library-37PXN6MK.js');
1009
1011
  let resolved;
1010
1012
  try {
1011
1013
  resolved = await resolveCatalogSource({ projectDir });
@@ -1995,7 +1997,7 @@ async function ensureTargetsRegistered(projectDir) {
1995
1997
  const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
1996
1998
  const config = resolveSkWorkspaceConfig2(projectDir);
1997
1999
  if (config.plugins && config.plugins.length > 0) {
1998
- const { ensurePluginsLoaded } = await import('../plugin-store-3DAVGKHN.js');
2000
+ const { ensurePluginsLoaded } = await import('../plugin-store-EIMVEH3O.js');
1999
2001
  const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
2000
2002
  for (const f of result.failed) logWarn(f.error);
2001
2003
  }
@@ -3711,7 +3713,7 @@ async function previewSoftDepsUnderCursor() {
3711
3713
  }
3712
3714
  async function loadAssets() {
3713
3715
  softDepPreviewCache.clear();
3714
- const { gatherAssetFeeds } = await import('../asset-feeds-OBPWVUDL.js');
3716
+ const { gatherAssetFeeds } = await import('../asset-feeds-NG5TKB3V.js');
3715
3717
  const { entries, domains, notes } = await gatherAssetFeeds(am, am.projectDir);
3716
3718
  if (notes.length > 0) {
3717
3719
  state.message = notes.map((n) => `[${n.feed}] ${n.message}`).join(" \u2022 ");
@@ -3878,7 +3880,7 @@ function reconcileCollapsedSets(rows) {
3878
3880
  async function loadSourcesAndLibraries() {
3879
3881
  try {
3880
3882
  const [{ openLibraryManager: openLibraryManager2 }, { skaileHomeDir }, { getGlobalCacheDir: getGlobalCacheDir2 }] = await Promise.all([
3881
- import('../open-library-ZNYIND7N.js'),
3883
+ import('../open-library-37PXN6MK.js'),
3882
3884
  import('../library/index.js'),
3883
3885
  import('../core/index.js')
3884
3886
  ]);
@@ -4964,7 +4966,7 @@ function action2(fn) {
4964
4966
  };
4965
4967
  }
4966
4968
  async function reconcileAndReport(projectDir, plugins) {
4967
- const { reconcilePlugins } = await import('../plugin-store-3DAVGKHN.js');
4969
+ const { reconcilePlugins } = await import('../plugin-store-EIMVEH3O.js');
4968
4970
  if (plugins.length === 0) {
4969
4971
  logInfo("No plugins declared \u2014 nothing to reconcile.");
4970
4972
  return;
@@ -4980,7 +4982,7 @@ async function reconcileAndReport(projectDir, plugins) {
4980
4982
  async function runInstall2(spec, opts) {
4981
4983
  const projectDir = path14__default.resolve(opts.projectDir);
4982
4984
  const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
4983
- const { specName } = await import('../plugin-store-3DAVGKHN.js');
4985
+ const { specName } = await import('../plugin-store-EIMVEH3O.js');
4984
4986
  const yamlPath = path14__default.join(projectDir, "skaile.yaml");
4985
4987
  const editor = WorkspaceYamlEditor2.load(yamlPath);
4986
4988
  const replaced = editor.addPlugin(spec, specName);
@@ -4992,7 +4994,7 @@ async function runInstall2(spec, opts) {
4992
4994
  async function runRemove(name, opts) {
4993
4995
  const projectDir = path14__default.resolve(opts.projectDir);
4994
4996
  const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
4995
- const { specName } = await import('../plugin-store-3DAVGKHN.js');
4997
+ const { specName } = await import('../plugin-store-EIMVEH3O.js');
4996
4998
  const yamlPath = path14__default.join(projectDir, "skaile.yaml");
4997
4999
  const editor = WorkspaceYamlEditor2.load(yamlPath);
4998
5000
  const removed = editor.removePlugin(name, specName);
@@ -5020,7 +5022,7 @@ async function runList(opts) {
5020
5022
  const registry = createPluginRegistry();
5021
5023
  if (declared.length > 0) {
5022
5024
  try {
5023
- const { loadPlugins } = await import('../plugin-store-3DAVGKHN.js');
5025
+ const { loadPlugins } = await import('../plugin-store-EIMVEH3O.js');
5024
5026
  const result = await loadPlugins(projectDir, declared, registry);
5025
5027
  for (const f of result.failed) logWarn(f.error);
5026
5028
  } catch (err) {
@@ -5054,150 +5056,93 @@ function makePluginCommand() {
5054
5056
  cmd.action(() => cmd.help());
5055
5057
  return cmd;
5056
5058
  }
5057
- function makePresetCommand() {
5058
- const cmd = new Command("preset").description("Scaffold and validate preset YAML files");
5059
- cmd.command("init [name]").description("Scaffold a new .preset.yaml file").option("--dir <path>", "Output directory", process.cwd()).action(async (name, opts) => {
5060
- const presetName = name ?? "my-preset";
5061
- const filename = `${presetName}.preset.yaml`;
5062
- const outPath = path14__default.join(path14__default.resolve(opts.dir), filename);
5063
- if (existsSync(outPath)) {
5064
- logErr(`File already exists: ${outPath}`);
5065
- logInfo("Choose a different name or remove the existing file.");
5066
- process.exit(1);
5067
- }
5068
- const template = generatePresetTemplate(presetName);
5069
- writeFileSync(outPath, template, "utf-8");
5070
- logOk(`Created ${S.brand(filename)}`);
5071
- logInfo(`Path: ${outPath}`);
5072
- logInfo("Edit the file to define your preset items, then validate with:");
5073
- logInfo(` skaile preset validate ${filename}`);
5074
- });
5075
- cmd.command("validate <path>").description("Validate a preset YAML file (schema + cycles + nesting depth)").action(async (filePath) => {
5076
- await runPresetValidate(filePath);
5077
- });
5078
- return cmd;
5079
- }
5080
- async function loadPresetForValidation(resolved) {
5081
- if (!existsSync(resolved)) {
5082
- logErr(`File not found: ${resolved}`);
5083
- process.exit(1);
5084
- }
5085
- const content = readFileSync(resolved, "utf-8");
5086
- const { parsePresetYaml } = await import('../library/index.js');
5087
- const parseResult = parsePresetYaml(content);
5088
- if (!parseResult.ok) {
5089
- logErr("Preset validation failed (parse errors):");
5090
- for (const err of parseResult.errors) {
5091
- console.log(` ${S.err("\u2717")} ${err}`);
5092
- }
5093
- process.exit(1);
5094
- }
5095
- if (!parseResult.preset) {
5096
- logErr("Preset validation failed: parser returned no manifest");
5097
- process.exit(1);
5098
- }
5099
- return parseResult.preset;
5100
- }
5101
- async function reportNestedPresetRefs(preset) {
5102
- const { detectNestedPresetRefs } = await import('../library/index.js');
5103
- const nestedRefs = detectNestedPresetRefs(preset);
5104
- if (nestedRefs.length > 0) {
5105
- logInfo(`Nested preset refs: ${nestedRefs.length} (depth validated at apply-time)`);
5106
- }
5107
- }
5108
- async function checkPresetCycles(preset, resolved) {
5109
- const { detectCycles } = await import('../discovery/index.js');
5110
- const presetRef = path14__default.basename(resolved, ".preset.yaml");
5111
- const edges = [];
5059
+
5060
+ // cli/src/commands/preset-apply.ts
5061
+ var TOKEN_RE = /\$\{(INPUT|SECRET):([^}]+)\}/g;
5062
+ var NESTED_PRESET_UNSUPPORTED = "nested presets are not supported by `preset apply` yet";
5063
+ function resolvePlaceholderValue(placeholder, providedValue) {
5064
+ if (isSecretPlaceholder(placeholder)) {
5065
+ return { envRef: `env:${placeholder.key}` };
5066
+ }
5067
+ const fallback = getDefaultValue(placeholder);
5068
+ const resolved = providedValue ?? (fallback !== void 0 ? String(fallback) : void 0);
5069
+ if (resolved === void 0) {
5070
+ if (placeholder.required !== false) {
5071
+ throw new Error(`Placeholder "${placeholder.key}" is required but no value was provided`);
5072
+ }
5073
+ return { value: "" };
5074
+ }
5075
+ return { value: resolved };
5076
+ }
5077
+ function presetItemsToWorkspace(preset, values) {
5078
+ const dependencies = [];
5079
+ const connectors = [];
5080
+ const mcpServers = [];
5081
+ const envVarsToSet = /* @__PURE__ */ new Set();
5112
5082
  for (const item of preset.items) {
5113
- if (item.ref) {
5114
- edges.push({ from: presetRef, to: item.ref, field: "items[].ref" });
5115
- }
5116
- }
5117
- if (edges.length === 0) return;
5118
- const cycleResult = detectCycles(edges);
5119
- if (!cycleResult.acyclic) {
5120
- logErr("Cycle detected in preset references:");
5121
- if (cycleResult.cyclePath) {
5122
- console.log(` ${S.err("\u2717")} ${cycleResult.cyclePath.join(" \u2192 ")}`);
5083
+ const ref = item.ref;
5084
+ if (ref.startsWith("preset:")) throw new Error(NESTED_PRESET_UNSUPPORTED);
5085
+ if (item.id && ref.startsWith("connector:")) {
5086
+ const config = resolveItemConfig(item, preset, values, envVarsToSet);
5087
+ connectors.push({
5088
+ id: item.id,
5089
+ driver: ref.slice("connector:".length),
5090
+ ...config
5091
+ });
5092
+ } else if (item.id && ref.startsWith("mcp:")) {
5093
+ const config = resolveItemConfig(item, preset, values, envVarsToSet);
5094
+ mcpServers.push({ id: item.id, ...config });
5095
+ } else {
5096
+ dependencies.push(ref);
5123
5097
  }
5124
- process.exit(1);
5125
5098
  }
5099
+ const sources = (preset.sources ?? []).map(
5100
+ (s) => s.pin ? { url: s.url, pin: s.pin } : { url: s.url }
5101
+ );
5102
+ return { dependencies, connectors, mcpServers, sources, envVarsToSet: [...envVarsToSet] };
5126
5103
  }
5127
- async function checkPresetPlaceholders(allPlaceholders) {
5128
- if (allPlaceholders.length === 0) return false;
5129
- const { validatePlaceholder } = await import('../library/index.js');
5130
- const invalidEntries = [];
5131
- for (const ph of allPlaceholders) {
5132
- const result = validatePlaceholder(ph, ph.default);
5133
- if (!result.valid) {
5134
- invalidEntries.push({ key: ph.key, errors: result.errors });
5104
+ function resolveItemConfig(item, preset, values, envVarsToSet) {
5105
+ const byKey = placeholderIndex(item, preset);
5106
+ const resolve5 = (key) => {
5107
+ const ph = byKey.get(key) ?? { key, type: "text", required: true };
5108
+ return resolvePlaceholderValue(ph, values[key]);
5109
+ };
5110
+ return substituteNode(item.config ?? {}, resolve5, envVarsToSet);
5111
+ }
5112
+ function placeholderIndex(item, preset) {
5113
+ const map = /* @__PURE__ */ new Map();
5114
+ for (const input of preset.inputs ?? []) map.set(input.key, input);
5115
+ for (const ph of item.placeholders ?? []) map.set(ph.key, ph);
5116
+ return map;
5117
+ }
5118
+ function substituteNode(node, resolve5, envVarsToSet) {
5119
+ if (typeof node === "string") return substituteString(node, resolve5, envVarsToSet);
5120
+ if (Array.isArray(node)) return node.map((el) => substituteNode(el, resolve5, envVarsToSet));
5121
+ if (node && typeof node === "object") {
5122
+ const out = {};
5123
+ for (const [k, v] of Object.entries(node)) out[k] = substituteNode(v, resolve5, envVarsToSet);
5124
+ return out;
5125
+ }
5126
+ return node;
5127
+ }
5128
+ function substituteString(value, resolve5, envVarsToSet) {
5129
+ const tokens = [...value.matchAll(TOKEN_RE)];
5130
+ if (tokens.length === 0) return value;
5131
+ const secret = tokens.find(([, ns]) => ns === "SECRET") ?? tokens.find(([, , key]) => "envRef" in resolve5(key));
5132
+ if (secret) {
5133
+ const [full, , key] = secret;
5134
+ if (tokens.length > 1 || full !== value) {
5135
+ throw new Error(
5136
+ `Secret "${key}" must be the only token in its value \u2014 remove the surrounding tokens/text`
5137
+ );
5135
5138
  }
5139
+ envVarsToSet.add(key);
5140
+ return `env:${key}`;
5136
5141
  }
5137
- if (invalidEntries.length === 0) return false;
5138
- logWarn("Placeholder issues:");
5139
- for (const r of invalidEntries) {
5140
- console.log(` ${S.warn("!")} ${r.key}: ${r.errors.join(", ")}`);
5141
- }
5142
- return true;
5143
- }
5144
- function printPresetSummary(preset, placeholderCount, hasWarnings) {
5145
- console.log();
5146
- console.log(S.heading(" Preset Validation"));
5147
- console.log(` ${S.rule(40)}`);
5148
- console.log(` ${S.label("Name")} ${preset.name}`);
5149
- console.log(` ${S.label("Version")} ${preset.version ?? "unversioned"}`);
5150
- console.log(` ${S.label("Items")} ${preset.items.length}`);
5151
- console.log(` ${S.label("Inputs")} ${preset.inputs?.length ?? 0}`);
5152
- console.log(` ${S.label("Placeholders")} ${placeholderCount}`);
5153
- console.log();
5154
- if (hasWarnings) {
5155
- logWarn("Validation passed with warnings");
5156
- } else {
5157
- logOk("Preset is valid");
5158
- }
5159
- }
5160
- async function runPresetValidate(filePath) {
5161
- const resolved = path14__default.resolve(filePath);
5162
- const preset = await loadPresetForValidation(resolved);
5163
- await reportNestedPresetRefs(preset);
5164
- await checkPresetCycles(preset, resolved);
5165
- const allPlaceholders = preset.items.flatMap((item) => item.placeholders ?? []);
5166
- const hasWarnings = await checkPresetPlaceholders(allPlaceholders);
5167
- printPresetSummary(preset, allPlaceholders.length, hasWarnings);
5168
- }
5169
- function generatePresetTemplate(name) {
5170
- return `# ${name}.preset.yaml
5171
- # Polymorphic Preset \u2014 composition + configuration primitive for the asset store.
5172
- # See: _devlog/specs/2026-05-03-ai-asset-store-v2-design.md \xA7 Presets
5173
-
5174
- name: "${name}"
5175
- version: "1.0.0"
5176
- description: "TODO: describe what this preset configures"
5177
-
5178
- # Placeholders \u2014 values the user provides at apply-time.
5179
- # Types: text, multiline, secret, number, boolean, enum, oauth, instance-picker
5180
- placeholders:
5181
- - key: EXAMPLE_KEY
5182
- type: text
5183
- label: "Example placeholder"
5184
- required: true
5185
- # default: "some-value"
5186
- # hint: "Help text shown to the user"
5187
-
5188
- # Items \u2014 each becomes a Library Instance when the preset is applied.
5189
- # Ref types:
5190
- # - Asset ref (kind:publisher/name@pin) \u2192 creates new Instance
5191
- # - Library Instance ref (#uuid) \u2192 reuses existing Instance
5192
- # - Nested Preset ref (preset:path.preset.yaml) \u2192 recursive apply (max depth 3)
5193
- items:
5194
- - id: example-item
5195
- ref: "skill:@skaile/example-skill@^1.0"
5196
- config:
5197
- # Use \${INPUT:EXAMPLE_KEY} for placeholder interpolation
5198
- setting: "\${INPUT:EXAMPLE_KEY}"
5199
- # credential_ref: "\${SECRET:EXAMPLE_KEY}" # for secret placeholders
5200
- `;
5142
+ return value.replace(TOKEN_RE, (_m, _ns, key) => {
5143
+ const resolved = resolve5(key);
5144
+ return "envRef" in resolved ? resolved.envRef : resolved.value;
5145
+ });
5201
5146
  }
5202
5147
  var REF_HINT = "kind:@<publisher>/<name>#<version> (e.g. skill:@skaile/use-exa#1.0.0)";
5203
5148
  function isValidAssetRef(ref) {
@@ -5274,7 +5219,7 @@ async function runRefInstall(ref, opts) {
5274
5219
  const spinner5 = p6.spinner();
5275
5220
  spinner5.start(`Installing ${ref}`);
5276
5221
  try {
5277
- const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-ZNYIND7N.js');
5222
+ const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-37PXN6MK.js');
5278
5223
  const catalog = await openCatalogSource2({ projectDir: path14__default.resolve(opts.projectDir) });
5279
5224
  if (!supportsInstallManifest(catalog)) {
5280
5225
  throw new Error(
@@ -5311,7 +5256,7 @@ async function runYamlInstall(opts) {
5311
5256
  return;
5312
5257
  }
5313
5258
  try {
5314
- const { ensureSourcesCloned } = await import('../ensure-sources-BQI2TOZZ.js');
5259
+ const { ensureSourcesCloned } = await import('../ensure-sources-CHC7X7AJ.js');
5315
5260
  const hydrate = ensureSourcesCloned(projectDir, { quiet: true });
5316
5261
  if (hydrate.cloned.length > 0) {
5317
5262
  logOk(`Cloned source(s): ${hydrate.cloned.join(", ")}`);
@@ -5467,6 +5412,612 @@ function makeCleanCommand() {
5467
5412
  }
5468
5413
  });
5469
5414
  }
5415
+
5416
+ // cli/src/commands/preset-export.ts
5417
+ function buildPresetFromWorkspace(config, name, sources = []) {
5418
+ const items = [];
5419
+ for (const ref of config.dependencies ?? []) {
5420
+ items.push({ ref });
5421
+ }
5422
+ for (const connector of config.connectors ?? []) {
5423
+ const config_ = compact({
5424
+ access: connector.access,
5425
+ auth: connector.auth,
5426
+ options: connector.options,
5427
+ version: connector.version,
5428
+ tags: connector.tags
5429
+ });
5430
+ items.push({
5431
+ id: connector.id,
5432
+ ref: `connector:${connector.driver}`,
5433
+ ...config_ ? { config: config_ } : {}
5434
+ });
5435
+ }
5436
+ for (const mcp of config.mcp_servers ?? []) {
5437
+ const config_ = compact({
5438
+ transport: mcp.transport,
5439
+ command: mcp.command,
5440
+ args: mcp.args,
5441
+ env: mcp.env,
5442
+ url: mcp.url,
5443
+ headers: mcp.headers
5444
+ });
5445
+ items.push({
5446
+ id: mcp.id,
5447
+ ref: `mcp:${mcp.id}`,
5448
+ ...config_ ? { config: config_ } : {}
5449
+ });
5450
+ }
5451
+ const project = config.name ?? name;
5452
+ return {
5453
+ name,
5454
+ version: "1.0.0",
5455
+ description: `Exported from ${project} workspace`,
5456
+ items,
5457
+ // Never let a credential-bearing source URL reach a shareable preset.
5458
+ ...sources.length > 0 ? { sources: sources.map((s) => ({ ...s, url: stripUrlCredentials(s.url) })) } : {}
5459
+ };
5460
+ }
5461
+ function neededSources(config, lock) {
5462
+ const factoryUrl = normalizeRepoUrl(FACTORY_SOURCE_URL);
5463
+ const nonFactory = (config.sources ?? []).filter((s) => normalizeRepoUrl(s.url) !== factoryUrl);
5464
+ if (!lock) return nonFactory;
5465
+ const inClosure = new Set(lock.sources.map((s) => normalizeRepoUrl(s.url)));
5466
+ return nonFactory.filter((s) => inClosure.has(normalizeRepoUrl(s.url)));
5467
+ }
5468
+ function compact(obj) {
5469
+ const out = {};
5470
+ for (const [k, v] of Object.entries(obj)) {
5471
+ if (v !== void 0 && v !== null) out[k] = v;
5472
+ }
5473
+ return Object.keys(out).length > 0 ? out : void 0;
5474
+ }
5475
+ var SECRET_REFERENCE_PREFIXES = ["env", "forge", "secret", "op", "kp", "oauth", "pat"];
5476
+ var SECRET_REFERENCE_RE = new RegExp(`^(${SECRET_REFERENCE_PREFIXES.join("|")}):`);
5477
+ function isSecretReference(value) {
5478
+ return SECRET_REFERENCE_RE.test(value);
5479
+ }
5480
+ var PLACEHOLDER_RE = /^\$\{(INPUT|SECRET):[^}]+\}$/;
5481
+ var SUSPICIOUS_KEY_RE = /(?:^|[_-])(?:token|password|passwd|secret|apikey|key|dsn|credential|auth|authorization|bearer)(?:[_-]|$)/i;
5482
+ var PROTO_CREDENTIAL_URL_RE = /\w+:\/\/[^/@\s]+:[^/@\s]+@/;
5483
+ function urlHasCredentials(url) {
5484
+ return PROTO_CREDENTIAL_URL_RE.test(url);
5485
+ }
5486
+ function stripUrlCredentials(url) {
5487
+ return url.replace(/^(\w+:\/\/)[^/@]*:[^/@]*@/, "$1");
5488
+ }
5489
+ function isLongEntropic(value) {
5490
+ if (value.length < 20) return false;
5491
+ if (/^\w+:\/\//.test(value) || value.startsWith("/")) return false;
5492
+ const classes = [/[a-z]/, /[A-Z]/, /[0-9]/].filter((re) => re.test(value)).length;
5493
+ return classes >= 2;
5494
+ }
5495
+ function scanInlineSecrets(preset) {
5496
+ const findings = [];
5497
+ for (const item of preset.items) {
5498
+ const itemId = typeof item.id === "string" ? item.id : "";
5499
+ if (item.config) scanNode(item.config, [], false, itemId, findings);
5500
+ }
5501
+ return findings;
5502
+ }
5503
+ function scanNode(node, segments, inCredentialField, itemId, findings) {
5504
+ if (typeof node === "string") {
5505
+ if (isInlineSecret(node, segments.at(-1) ?? "", inCredentialField)) {
5506
+ findings.push({ itemId, path: segments.join("."), maskedValue: maskSecret(node) });
5507
+ }
5508
+ return;
5509
+ }
5510
+ if (Array.isArray(node)) {
5511
+ node.forEach(
5512
+ (el, i) => scanNode(el, [...segments, String(i)], inCredentialField, itemId, findings)
5513
+ );
5514
+ return;
5515
+ }
5516
+ if (node && typeof node === "object") {
5517
+ for (const [k, v] of Object.entries(node)) {
5518
+ const credField = inCredentialField || k === "auth";
5519
+ scanNode(v, [...segments, k], credField, itemId, findings);
5520
+ }
5521
+ }
5522
+ }
5523
+ function isInlineSecret(value, key, inCredentialField) {
5524
+ if (isSecretReference(value) || PLACEHOLDER_RE.test(value)) return false;
5525
+ if (inCredentialField) return true;
5526
+ if (SUSPICIOUS_KEY_RE.test(key)) return true;
5527
+ if (PROTO_CREDENTIAL_URL_RE.test(value)) return true;
5528
+ return isLongEntropic(value);
5529
+ }
5530
+ function maskSecret(value) {
5531
+ const proto = value.match(/^(\w+):\/\/[^/@\s]+:[^/@\s]+@/);
5532
+ if (proto) return `${proto[1]}://\u2026:****@\u2026`;
5533
+ return "****";
5534
+ }
5535
+ function redactSecrets(preset, findings) {
5536
+ const next = structuredClone(preset);
5537
+ const keys = [];
5538
+ const used = /* @__PURE__ */ new Set();
5539
+ for (const finding of findings) {
5540
+ const item = next.items.find((it) => it.id === finding.itemId);
5541
+ if (!item?.config) continue;
5542
+ const base = placeholderKey(finding.itemId, finding.path);
5543
+ let key = base;
5544
+ for (let n = 2; used.has(key); n++) key = `${base}_${n}`;
5545
+ if (!setAtPath(item.config, finding.path.split("."), `\${INPUT:${key}}`)) continue;
5546
+ used.add(key);
5547
+ const placeholder = {
5548
+ key,
5549
+ type: "secret",
5550
+ required: true,
5551
+ prompt: `Secret value for ${finding.itemId}.${finding.path}`
5552
+ };
5553
+ item.placeholders = [...item.placeholders ?? [], placeholder];
5554
+ keys.push(key);
5555
+ }
5556
+ return { preset: next, keys };
5557
+ }
5558
+ function placeholderKey(itemId, path36) {
5559
+ return `${itemId}_${path36}`.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
5560
+ }
5561
+ function setAtPath(root, segments, value) {
5562
+ let node = root;
5563
+ for (let i = 0; i < segments.length - 1; i++) {
5564
+ if (!node || typeof node !== "object") return false;
5565
+ node = node[segments[i]];
5566
+ }
5567
+ if (!node || typeof node !== "object") return false;
5568
+ node[segments.at(-1)] = value;
5569
+ return true;
5570
+ }
5571
+ var INLINE_SECRETS_HEADER = "# \u26A0\uFE0F CONTAINS INLINE SECRETS \u2014 replace with placeholders before sharing or publishing";
5572
+ function secretHeaderComment(findings) {
5573
+ return findings.length > 0 ? INLINE_SECRETS_HEADER : null;
5574
+ }
5575
+
5576
+ // cli/src/commands/preset-cmd.ts
5577
+ function makePresetCommand() {
5578
+ const cmd = new Command("preset").description("Scaffold and validate preset YAML files");
5579
+ cmd.command("init [name]").description("Scaffold a new .preset.yaml file").option("--dir <path>", "Output directory", process.cwd()).action(async (name, opts) => {
5580
+ const presetName = name ?? "my-preset";
5581
+ const filename = `${presetName}.preset.yaml`;
5582
+ const outPath = path14__default.join(path14__default.resolve(opts.dir), filename);
5583
+ if (existsSync(outPath)) {
5584
+ logErr(`File already exists: ${outPath}`);
5585
+ logInfo("Choose a different name or remove the existing file.");
5586
+ process.exit(1);
5587
+ }
5588
+ const template = generatePresetTemplate(presetName);
5589
+ writeFileSync(outPath, template, "utf-8");
5590
+ logOk(`Created ${S.brand(filename)}`);
5591
+ logInfo(`Path: ${outPath}`);
5592
+ logInfo("Edit the file to define your preset items, then validate with:");
5593
+ logInfo(` skaile preset validate ${filename}`);
5594
+ });
5595
+ cmd.command("validate <path>").description("Validate a preset YAML file (schema + cycles + nesting depth)").action(async (filePath) => {
5596
+ await runPresetValidate(filePath);
5597
+ });
5598
+ 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(
5599
+ "--allow-inline-secrets",
5600
+ "Exit 0 even when inline secrets remain unredacted (opt-in; unsafe for CI)"
5601
+ ).option("--force", "Overwrite the output file if it already exists").action(async (name, opts) => {
5602
+ await runPresetExport(name, opts);
5603
+ });
5604
+ 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(
5605
+ "--input <KEY=VAL>",
5606
+ "Provide a placeholder value (repeatable)",
5607
+ collectInput,
5608
+ []
5609
+ ).option("--yes", "Skip interactive prompts; use defaults where available").option("--no-deploy", "Skip the final `skaile install` step").action(async (filePath, opts) => {
5610
+ await runPresetApply(filePath, opts);
5611
+ });
5612
+ return cmd;
5613
+ }
5614
+ async function runPresetExport(name, opts) {
5615
+ const projectDir = path14__default.resolve(opts.projectDir);
5616
+ const file = loadSkWorkspaceConfig(projectDir);
5617
+ if (!file) {
5618
+ logErr(`No skaile.yaml found in ${projectDir}`);
5619
+ logInfo("Run this from a project directory or pass --project-dir <path>.");
5620
+ process.exit(1);
5621
+ }
5622
+ const presetName = name ?? file.config.name ?? "my-preset";
5623
+ let lock = null;
5624
+ try {
5625
+ lock = readLock(path14__default.join(projectDir, "skaile.lock.yaml"));
5626
+ } catch {
5627
+ lock = null;
5628
+ }
5629
+ const sources = neededSources(file.config, lock);
5630
+ if (lock === null && sources.length > 0) {
5631
+ logInfo("No lock file \u2014 exporting all non-factory sources conservatively.");
5632
+ }
5633
+ const strippedCreds = sources.filter((s) => urlHasCredentials(s.url)).length;
5634
+ if (strippedCreds > 0) {
5635
+ logWarn(`Stripped credentials from ${strippedCreds} source URL(s) \u2014 resolved via git auth on apply.`);
5636
+ }
5637
+ let preset = buildPresetFromWorkspace(file.config, presetName, sources);
5638
+ if (preset.items.length === 0) {
5639
+ logErr("Nothing to export \u2014 skaile.yaml has no dependencies, connectors, or mcp_servers.");
5640
+ process.exit(1);
5641
+ }
5642
+ const result = validatePreset(preset);
5643
+ if (!result.ok) {
5644
+ logErr("Internal error: generated preset failed validation:");
5645
+ for (const err of result.errors) console.error(` ${S.err("\u2717")} ${err.path}: ${err.message}`);
5646
+ process.exit(1);
5647
+ }
5648
+ const findings = scanInlineSecrets(preset);
5649
+ reportFindings(findings);
5650
+ const toRedact = await resolveRedactions(findings, opts.redact === true);
5651
+ if (toRedact.length > 0) preset = redactSecrets(preset, toRedact).preset;
5652
+ const unresolved = scanInlineSecrets(preset);
5653
+ const header = secretHeaderComment(unresolved);
5654
+ const body = stringify(preset);
5655
+ const contents = header ? `${header}
5656
+ ${body}` : body;
5657
+ if (opts.out && opts.dir !== process.cwd()) logWarn("--dir is ignored when --out is set.");
5658
+ const outPath = opts.out ? path14__default.resolve(opts.out) : path14__default.join(path14__default.resolve(opts.dir), `${presetName}.preset.yaml`);
5659
+ if (existsSync(outPath) && !opts.force) {
5660
+ logErr(`File already exists: ${outPath}`);
5661
+ logInfo("Pass --force to overwrite, or choose a different --out/--dir.");
5662
+ process.exit(1);
5663
+ }
5664
+ try {
5665
+ writeFileSync(outPath, contents, "utf-8");
5666
+ } catch (e) {
5667
+ logErr(`Failed to write ${outPath}: ${e.message}`);
5668
+ process.exit(1);
5669
+ }
5670
+ logOk(`Exported ${S.brand(path14__default.basename(outPath))}`);
5671
+ logInfo(`Path: ${outPath}`);
5672
+ if (unresolved.length > 0) {
5673
+ console.error(S.err("This file is NOT safe to share."));
5674
+ if (!opts.allowInlineSecrets) {
5675
+ logErr(`${unresolved.length} inline secret(s) left unredacted.`);
5676
+ logInfo(
5677
+ "Re-run with --redact to placeholder them, or --allow-inline-secrets to accept the risk."
5678
+ );
5679
+ process.exit(1);
5680
+ }
5681
+ }
5682
+ }
5683
+ function reportFindings(findings) {
5684
+ if (findings.length === 0) return;
5685
+ console.error(S.err(`\u26A0\uFE0F ${findings.length} potential INLINE secret(s) found`));
5686
+ for (const f of findings) {
5687
+ console.error(` ${S.err("\u2022")} ${f.itemId} ${S.dim(f.path)} ${f.maskedValue}`);
5688
+ }
5689
+ }
5690
+ async function resolveRedactions(findings, redactAll) {
5691
+ if (findings.length === 0) return [];
5692
+ if (redactAll) return findings;
5693
+ if (!process.stdin.isTTY) return [];
5694
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
5695
+ try {
5696
+ const chosen = [];
5697
+ for (const f of findings) {
5698
+ const answer = await rl.question(`Redact ${f.itemId}.${f.path}? [y/N] `);
5699
+ if (/^y(es)?$/i.test(answer.trim())) chosen.push(f);
5700
+ }
5701
+ return chosen;
5702
+ } finally {
5703
+ rl.close();
5704
+ }
5705
+ }
5706
+ function collectInput(value, acc) {
5707
+ acc.push(value);
5708
+ return acc;
5709
+ }
5710
+ async function runPresetApply(filePath, opts) {
5711
+ const projectDir = path14__default.resolve(opts.projectDir);
5712
+ const file = loadSkWorkspaceConfig(projectDir);
5713
+ if (!file) {
5714
+ logErr("No skaile.yaml \u2014 run `skaile init` first");
5715
+ process.exit(1);
5716
+ }
5717
+ const resolved = path14__default.resolve(filePath);
5718
+ if (!existsSync(resolved)) {
5719
+ logErr(`File not found: ${resolved}`);
5720
+ process.exit(1);
5721
+ }
5722
+ const { parsePresetYaml } = await import('../library/index.js');
5723
+ const parseResult = parsePresetYaml(readFileSync(resolved, "utf-8"));
5724
+ if (!parseResult.ok || !parseResult.preset) {
5725
+ logErr("Preset validation failed:");
5726
+ for (const err of parseResult.errors) console.error(` ${S.err("\u2717")} ${err}`);
5727
+ process.exit(1);
5728
+ }
5729
+ const preset = parseResult.preset;
5730
+ if (preset.items.some((it) => it.ref.startsWith("preset:"))) {
5731
+ logErr(NESTED_PRESET_UNSUPPORTED);
5732
+ logInfo("Remove the nested `preset:` item(s) and re-run.");
5733
+ process.exit(1);
5734
+ }
5735
+ const values = parseInputs(opts.input);
5736
+ await promptMissingInputs(preset.items, preset.inputs, values, opts.yes === true);
5737
+ let items;
5738
+ try {
5739
+ items = presetItemsToWorkspace(preset, values);
5740
+ } catch (err) {
5741
+ logErr(err.message);
5742
+ process.exit(1);
5743
+ }
5744
+ const editor = WorkspaceYamlEditor.load(file.path);
5745
+ const summary = applyItems(editor, file.config, items);
5746
+ const changed = summary.addedSources.length + summary.addedDeps.length + summary.addedConnectors.length + summary.addedMcp.length;
5747
+ if (changed > 0) editor.save();
5748
+ printApplySummary(summary, items.envVarsToSet);
5749
+ if (opts.deploy) {
5750
+ await runPresetInstall(projectDir);
5751
+ } else {
5752
+ logInfo("Skipped deploy (--no-deploy). Run `skaile install` to deploy the new dependencies.");
5753
+ }
5754
+ }
5755
+ function parseInputs(pairs) {
5756
+ const out = {};
5757
+ for (const pair of pairs) {
5758
+ const eq = pair.indexOf("=");
5759
+ if (eq <= 0) {
5760
+ logErr(`Invalid --input "${pair}" \u2014 expected KEY=VALUE.`);
5761
+ process.exit(1);
5762
+ }
5763
+ out[pair.slice(0, eq)] = pair.slice(eq + 1);
5764
+ }
5765
+ return out;
5766
+ }
5767
+ async function promptMissingInputs(presetItems, presetInputs, values, yes) {
5768
+ const seen = /* @__PURE__ */ new Set();
5769
+ const toPrompt = [];
5770
+ const collect = (ph) => {
5771
+ if (seen.has(ph.key) || isSecretPlaceholder(ph)) return;
5772
+ seen.add(ph.key);
5773
+ if (values[ph.key] === void 0) toPrompt.push(ph);
5774
+ };
5775
+ for (const input of presetInputs ?? []) collect(input);
5776
+ for (const item of presetItems) for (const ph of item.placeholders ?? []) collect(ph);
5777
+ if (toPrompt.length === 0 || yes || !process.stdin.isTTY) return;
5778
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
5779
+ try {
5780
+ for (const ph of toPrompt) {
5781
+ const def = getDefaultValue(ph);
5782
+ const suffix = def !== void 0 ? ` [${String(def)}]` : "";
5783
+ const answer = (await rl.question(`${ph.prompt ?? ph.key}${suffix}: `)).trim();
5784
+ if (answer) values[ph.key] = answer;
5785
+ else if (def !== void 0) values[ph.key] = String(def);
5786
+ }
5787
+ } finally {
5788
+ rl.close();
5789
+ }
5790
+ }
5791
+ function applyItems(editor, config, items) {
5792
+ const s = {
5793
+ addedDeps: [],
5794
+ skippedDeps: [],
5795
+ addedConnectors: [],
5796
+ skippedConnectors: [],
5797
+ unchangedConnectors: [],
5798
+ addedMcp: [],
5799
+ skippedMcp: [],
5800
+ unchangedMcp: [],
5801
+ addedSources: [],
5802
+ skippedSources: [],
5803
+ unchangedSources: []
5804
+ };
5805
+ const existingSourceUrls = new Map(
5806
+ editor.getSources().map((src) => [normalizeRepoUrl(src.url), src])
5807
+ );
5808
+ for (const src of items.sources) {
5809
+ const key = normalizeRepoUrl(src.url);
5810
+ const existing = existingSourceUrls.get(key);
5811
+ if (!existing) {
5812
+ editor.setSource(src);
5813
+ existingSourceUrls.set(key, src);
5814
+ s.addedSources.push(src.url);
5815
+ } else if ((existing.pin ?? void 0) !== (src.pin ?? void 0)) {
5816
+ s.skippedSources.push({ url: src.url, keptPin: existing.pin, presetPin: src.pin });
5817
+ } else {
5818
+ s.unchangedSources.push(src.url);
5819
+ }
5820
+ }
5821
+ for (const ref of items.dependencies) {
5822
+ if (editor.addDependency(ref)) s.addedDeps.push(ref);
5823
+ else s.skippedDeps.push(ref);
5824
+ }
5825
+ const existingConnectors = new Map((config.connectors ?? []).map((c) => [c.id, c]));
5826
+ for (const conn of items.connectors) {
5827
+ const existing = existingConnectors.get(conn.id);
5828
+ if (existing) {
5829
+ (stableEqual(existing, conn) ? s.unchangedConnectors : s.skippedConnectors).push(conn.id);
5830
+ continue;
5831
+ }
5832
+ editor.setConnector(conn);
5833
+ s.addedConnectors.push(conn.id);
5834
+ }
5835
+ const existingMcp = new Map((config.mcp_servers ?? []).map((m) => [m.id, m]));
5836
+ for (const mcp of items.mcpServers) {
5837
+ const existing = existingMcp.get(mcp.id);
5838
+ if (existing) {
5839
+ (stableEqual(existing, mcp) ? s.unchangedMcp : s.skippedMcp).push(mcp.id);
5840
+ continue;
5841
+ }
5842
+ editor.setMcpServer(mcp);
5843
+ s.addedMcp.push(mcp.id);
5844
+ }
5845
+ return s;
5846
+ }
5847
+ function stableEqual(a, b) {
5848
+ return canonicalJson(a) === canonicalJson(b);
5849
+ }
5850
+ function canonicalJson(value) {
5851
+ return JSON.stringify(
5852
+ value,
5853
+ (_k, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(
5854
+ Object.entries(v).sort(([x], [y]) => x.localeCompare(y))
5855
+ ) : v
5856
+ );
5857
+ }
5858
+ function printApplySummary(summary, envVarsToSet) {
5859
+ const added = summary.addedSources.length + summary.addedDeps.length + summary.addedConnectors.length + summary.addedMcp.length;
5860
+ if (added === 0) {
5861
+ logInfo("Nothing to apply \u2014 everything was already present.");
5862
+ } else {
5863
+ if (summary.addedSources.length > 0) {
5864
+ logOk(`Registered ${summary.addedSources.length} source(s).`);
5865
+ }
5866
+ logOk(
5867
+ `Applied ${summary.addedDeps.length} dependency(ies), ${summary.addedConnectors.length} connector(s), ${summary.addedMcp.length} mcp server(s).`
5868
+ );
5869
+ }
5870
+ const skipped = [
5871
+ ...summary.skippedSources.map(
5872
+ (s) => `source ${s.url} (kept pin '${s.keptPin ?? "none"}', preset had '${s.presetPin ?? "none"}')`
5873
+ ),
5874
+ ...summary.skippedDeps.map((d) => `dependency ${d} (already present)`),
5875
+ ...summary.skippedConnectors.map((c) => `connector ${c} (id exists, differs)`),
5876
+ ...summary.skippedMcp.map((m) => `mcp ${m} (id exists, differs)`)
5877
+ ];
5878
+ for (const line of skipped) logWarn(`Skipped ${line}`);
5879
+ const unchanged = summary.unchangedConnectors.length + summary.unchangedMcp.length + summary.unchangedSources.length;
5880
+ if (unchanged > 0) logInfo(`${unchanged} source/connector/mcp entry(ies) already matched \u2014 left as-is.`);
5881
+ if (envVarsToSet.length > 0) {
5882
+ logWarn(`Set these env vars before running: ${envVarsToSet.join(", ")}`);
5883
+ }
5884
+ }
5885
+ async function runPresetInstall(projectDir) {
5886
+ try {
5887
+ const { AssetManager: AssetManager2 } = await import('../asset-manager/index.js');
5888
+ const am2 = new AssetManager2({ projectDir });
5889
+ logInfo("Installing new dependencies\u2026");
5890
+ const result = await am2.install({});
5891
+ for (const d of result.deployed) logOk(d);
5892
+ for (const m of result.missing) logErr(`Missing: ${m}`);
5893
+ await reportInstalledSoftDeps(am2);
5894
+ } catch (err) {
5895
+ logErr(`Install failed: ${err instanceof Error ? err.message : String(err)}`);
5896
+ logInfo("Run `skaile install` to deploy the new dependencies.");
5897
+ }
5898
+ }
5899
+ async function loadPresetForValidation(resolved) {
5900
+ if (!existsSync(resolved)) {
5901
+ logErr(`File not found: ${resolved}`);
5902
+ process.exit(1);
5903
+ }
5904
+ const content = readFileSync(resolved, "utf-8");
5905
+ const { parsePresetYaml } = await import('../library/index.js');
5906
+ const parseResult = parsePresetYaml(content);
5907
+ if (!parseResult.ok) {
5908
+ logErr("Preset validation failed (parse errors):");
5909
+ for (const err of parseResult.errors) {
5910
+ console.log(` ${S.err("\u2717")} ${err}`);
5911
+ }
5912
+ process.exit(1);
5913
+ }
5914
+ if (!parseResult.preset) {
5915
+ logErr("Preset validation failed: parser returned no manifest");
5916
+ process.exit(1);
5917
+ }
5918
+ return parseResult.preset;
5919
+ }
5920
+ async function reportNestedPresetRefs(preset) {
5921
+ const { detectNestedPresetRefs } = await import('../library/index.js');
5922
+ const nestedRefs = detectNestedPresetRefs(preset);
5923
+ if (nestedRefs.length > 0) {
5924
+ logInfo(`Nested preset refs: ${nestedRefs.length} (depth validated at apply-time)`);
5925
+ }
5926
+ }
5927
+ async function checkPresetCycles(preset, resolved) {
5928
+ const { detectCycles } = await import('../discovery/index.js');
5929
+ const presetRef = path14__default.basename(resolved, ".preset.yaml");
5930
+ const edges = [];
5931
+ for (const item of preset.items) {
5932
+ if (item.ref) {
5933
+ edges.push({ from: presetRef, to: item.ref, field: "items[].ref" });
5934
+ }
5935
+ }
5936
+ if (edges.length === 0) return;
5937
+ const cycleResult = detectCycles(edges);
5938
+ if (!cycleResult.acyclic) {
5939
+ logErr("Cycle detected in preset references:");
5940
+ if (cycleResult.cyclePath) {
5941
+ console.log(` ${S.err("\u2717")} ${cycleResult.cyclePath.join(" \u2192 ")}`);
5942
+ }
5943
+ process.exit(1);
5944
+ }
5945
+ }
5946
+ async function checkPresetPlaceholders(allPlaceholders) {
5947
+ if (allPlaceholders.length === 0) return false;
5948
+ const { validatePlaceholder } = await import('../library/index.js');
5949
+ const invalidEntries = [];
5950
+ for (const ph of allPlaceholders) {
5951
+ const result = validatePlaceholder(ph, ph.default);
5952
+ if (!result.valid) {
5953
+ invalidEntries.push({ key: ph.key, errors: result.errors });
5954
+ }
5955
+ }
5956
+ if (invalidEntries.length === 0) return false;
5957
+ logWarn("Placeholder issues:");
5958
+ for (const r of invalidEntries) {
5959
+ console.log(` ${S.warn("!")} ${r.key}: ${r.errors.join(", ")}`);
5960
+ }
5961
+ return true;
5962
+ }
5963
+ function printPresetSummary(preset, placeholderCount, hasWarnings) {
5964
+ console.log();
5965
+ console.log(S.heading(" Preset Validation"));
5966
+ console.log(` ${S.rule(40)}`);
5967
+ console.log(` ${S.label("Name")} ${preset.name}`);
5968
+ console.log(` ${S.label("Version")} ${preset.version ?? "unversioned"}`);
5969
+ console.log(` ${S.label("Items")} ${preset.items.length}`);
5970
+ console.log(` ${S.label("Inputs")} ${preset.inputs?.length ?? 0}`);
5971
+ console.log(` ${S.label("Placeholders")} ${placeholderCount}`);
5972
+ console.log();
5973
+ if (hasWarnings) {
5974
+ logWarn("Validation passed with warnings");
5975
+ } else {
5976
+ logOk("Preset is valid");
5977
+ }
5978
+ }
5979
+ async function runPresetValidate(filePath) {
5980
+ const resolved = path14__default.resolve(filePath);
5981
+ const preset = await loadPresetForValidation(resolved);
5982
+ await reportNestedPresetRefs(preset);
5983
+ await checkPresetCycles(preset, resolved);
5984
+ const allPlaceholders = preset.items.flatMap((item) => item.placeholders ?? []);
5985
+ const hasWarnings = await checkPresetPlaceholders(allPlaceholders);
5986
+ printPresetSummary(preset, allPlaceholders.length, hasWarnings);
5987
+ }
5988
+ function generatePresetTemplate(name) {
5989
+ return `# ${name}.preset.yaml
5990
+ # Polymorphic Preset \u2014 composition + configuration primitive for the asset store.
5991
+ # See: _devlog/specs/2026-05-03-ai-asset-store-v2-design.md \xA7 Presets
5992
+
5993
+ name: "${name}"
5994
+ version: "1.0.0"
5995
+ description: "TODO: describe what this preset configures"
5996
+
5997
+ # Placeholders \u2014 values the user provides at apply-time.
5998
+ # Types: text, multiline, secret, number, boolean, enum, oauth, instance-picker
5999
+ placeholders:
6000
+ - key: EXAMPLE_KEY
6001
+ type: text
6002
+ label: "Example placeholder"
6003
+ required: true
6004
+ # default: "some-value"
6005
+ # hint: "Help text shown to the user"
6006
+
6007
+ # Items \u2014 each becomes a Library Instance when the preset is applied.
6008
+ # Ref types:
6009
+ # - Asset ref (kind:publisher/name@pin) \u2192 creates new Instance
6010
+ # - Library Instance ref (#uuid) \u2192 reuses existing Instance
6011
+ # - Nested Preset ref (preset:path.preset.yaml) \u2192 recursive apply (max depth 3)
6012
+ items:
6013
+ - id: example-item
6014
+ ref: "skill:@skaile/example-skill@^1.0"
6015
+ config:
6016
+ # Use \${INPUT:EXAMPLE_KEY} for placeholder interpolation
6017
+ setting: "\${INPUT:EXAMPLE_KEY}"
6018
+ # credential_ref: "\${SECRET:EXAMPLE_KEY}" # for secret placeholders
6019
+ `;
6020
+ }
5470
6021
  function makeRebuildCommand() {
5471
6022
  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) => {
5472
6023
  const projectDir = path14__default.resolve(opts.projectDir);
@@ -5785,12 +6336,12 @@ function makeServeCommand() {
5785
6336
  );
5786
6337
  }
5787
6338
  async function ensurePluginsLoadedForServe(projectDir) {
5788
- const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-SC4PWN2F.js');
6339
+ const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-GWG4CYYC.js');
5789
6340
  try {
5790
6341
  const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
5791
6342
  const config = resolveSkWorkspaceConfig2(projectDir);
5792
6343
  if (!config.plugins || config.plugins.length === 0) return;
5793
- const { ensurePluginsLoaded } = await import('../plugin-store-3DAVGKHN.js');
6344
+ const { ensurePluginsLoaded } = await import('../plugin-store-EIMVEH3O.js');
5794
6345
  const { pluginRegistry } = await import('../plugin-registry/index.js');
5795
6346
  const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
5796
6347
  if (result.loaded.length > 0) {
@@ -5804,14 +6355,14 @@ async function ensurePluginsLoadedForServe(projectDir) {
5804
6355
  }
5805
6356
  }
5806
6357
  async function runCompileTest(opts) {
5807
- const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-SC4PWN2F.js');
6358
+ const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-GWG4CYYC.js');
5808
6359
  const { tmpdir } = await import('os');
5809
6360
  const { dirname: dirname4, join: join5, resolve: resolve5 } = await import('path');
5810
6361
  const { fileURLToPath: fileURLToPath2 } = await import('url');
5811
6362
  const { unlinkSync, existsSync: existsSync13 } = await import('fs');
5812
6363
  const { spawn } = await import('child_process');
5813
6364
  const { portableSpawnSync: portableSpawnSync2 } = await import('../core/index.js');
5814
- const { MONOREPO_ROOT } = await import('../paths-S7QNXYMB.js');
6365
+ const { MONOREPO_ROOT } = await import('../paths-UVOHWUPY.js');
5815
6366
  if (MONOREPO_ROOT === null) {
5816
6367
  logErr2("--compile-test is only available inside the skaile-dev workspace.");
5817
6368
  logInfo2("Run it from a checkout of skaile-ai/skaile-dev (the monorepo shell).");
@@ -6629,8 +7180,8 @@ function makeStoreCommand() {
6629
7180
  store.command("login").description("Authenticate with the store").option("--email <email>", "Login with email instead of GitHub").action(async (opts) => {
6630
7181
  const config = getStoreConfig();
6631
7182
  if (opts.email) {
6632
- const readline2 = await import('readline');
6633
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
7183
+ const readline3 = await import('readline');
7184
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
6634
7185
  const password = await new Promise((resolve5) => {
6635
7186
  rl.question("Password: ", (answer) => {
6636
7187
  rl.close();
@@ -7447,12 +7998,43 @@ function makeVerifyCommand() {
7447
7998
  }
7448
7999
  });
7449
8000
  }
8001
+ function refKindName(ref) {
8002
+ const trimmed = ref.trim();
8003
+ const colon = trimmed.indexOf(":");
8004
+ let kind = colon === -1 ? void 0 : trimmed.slice(0, colon);
8005
+ if (kind === "mcp") kind = "mcp-server";
8006
+ let rest = colon === -1 ? trimmed : trimmed.slice(colon + 1);
8007
+ const hash = rest.indexOf("#");
8008
+ if (hash !== -1) rest = rest.slice(0, hash);
8009
+ if (rest.startsWith("@")) {
8010
+ const slash = rest.indexOf("/");
8011
+ if (slash !== -1) rest = rest.slice(slash + 1);
8012
+ }
8013
+ return { kind, name: rest };
8014
+ }
8015
+ function whyEmptyMessage(ref, declared) {
8016
+ const { kind, name } = refKindName(ref);
8017
+ const isDeclared = declared.some(
8018
+ (d) => d.name === name && (kind === void 0 || d.kind === kind)
8019
+ );
8020
+ if (isDeclared) {
8021
+ return `${ref} not found in lock file. Run \`skaile install\` first.`;
8022
+ }
8023
+ return `${ref} is not a dependency of this project \u2014 nothing in skaile.yaml requires it. Run \`skaile list\` to see declared dependencies.`;
8024
+ }
7450
8025
  function makeWhyCommand() {
7451
8026
  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) => {
7452
8027
  const am2 = new AssetManager({ projectDir: path14__default.resolve(opts.projectDir) });
7453
8028
  const chain = am2.why(ref);
7454
8029
  if (chain.length === 0) {
7455
- logErr(`${ref} not found in lock file. Run \`skaile install\` first.`);
8030
+ let declared;
8031
+ try {
8032
+ declared = am2.listDeclared();
8033
+ } catch {
8034
+ logErr(`${ref} not found in lock file. Run \`skaile install\` first.`);
8035
+ return;
8036
+ }
8037
+ logErr(whyEmptyMessage(ref, declared));
7456
8038
  return;
7457
8039
  }
7458
8040
  console.log();
@@ -7598,7 +8180,7 @@ program.command("init [project-dir]").description("Initialize a project director
7598
8180
  );
7599
8181
  async function runInit(projectDir, opts) {
7600
8182
  const { mkdirSync: mkdirSync5, writeFileSync: writeFileSync4 } = await import('fs');
7601
- const { stringify } = await import('yaml');
8183
+ const { stringify: stringify2 } = await import('yaml');
7602
8184
  const { DRIVER_TARGETS, SUPPORTED_DRIVER_TARGETS: SUPPORTED_DRIVER_TARGETS2 } = await import('../core/index.js');
7603
8185
  const backend = opts.backend;
7604
8186
  if (!SUPPORTED_DRIVER_TARGETS2.includes(backend)) {
@@ -7626,7 +8208,7 @@ async function runInit(projectDir, opts) {
7626
8208
  }
7627
8209
  const wsConfigPath = path14__default.join(resolved, "skaile.yaml");
7628
8210
  if (!existsSync13(wsConfigPath)) {
7629
- writeFileSync4(wsConfigPath, stringify(buildInitWorkspaceConfig(projectName, backend)));
8211
+ writeFileSync4(wsConfigPath, stringify2(buildInitWorkspaceConfig(projectName, backend)));
7630
8212
  created.push("skaile.yaml");
7631
8213
  }
7632
8214
  for (const dir of new Set(Object.values(DRIVER_TARGETS[backend].local))) {
@@ -7717,7 +8299,7 @@ async function bootstrapUserConfig() {
7717
8299
  }
7718
8300
  async function hydrateInitSources(resolved, created) {
7719
8301
  try {
7720
- const { ensureSourcesCloned } = await import('../ensure-sources-BQI2TOZZ.js');
8302
+ const { ensureSourcesCloned } = await import('../ensure-sources-CHC7X7AJ.js');
7721
8303
  const hydrate = ensureSourcesCloned(resolved, { quiet: true });
7722
8304
  for (const n of hydrate.cloned) created.push(`~/.skaile/cache/sources/${n}/`);
7723
8305
  if (hydrate.failed.length > 0) {
@@ -7753,7 +8335,7 @@ function printInitSummary(resolved, projectDir, created) {
7753
8335
  console.log();
7754
8336
  }
7755
8337
  program.command("setup").description("Interactive provider setup wizard").action(async () => {
7756
- const { cmdSetup } = await import('../setup-WBGDT2WY.js');
8338
+ const { cmdSetup } = await import('../setup-DINVR6ZQ.js');
7757
8339
  await cmdSetup([], { projectDir: process.cwd() });
7758
8340
  });
7759
8341
  program.addCommand(makeInstallCommand());