@skaile/workspaces 1.3.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 (185) hide show
  1. package/CHANGELOG.md +122 -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-NEZKG6YS.js → asset-feeds-NG5TKB3V.js} +8 -8
  19. package/dist/{asset-feeds-NEZKG6YS.js.map → asset-feeds-NG5TKB3V.js.map} +1 -1
  20. package/dist/asset-manager/index.js +7 -7
  21. package/dist/asset-manager/installer.js +6 -6
  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 +2 -2
  26. package/dist/bridge/drivers/codex.js +1 -1
  27. package/dist/bridge/drivers/continue.js +2 -2
  28. package/dist/bridge/drivers/echo.js +1 -1
  29. package/dist/bridge/drivers/gemini.js +3 -3
  30. package/dist/bridge/drivers/goose.js +2 -2
  31. package/dist/bridge/drivers/omp.js +1 -1
  32. package/dist/bridge/drivers/opencode.js +2 -2
  33. package/dist/bridge/drivers/qwen.js +3 -3
  34. package/dist/bridge/index.js +3 -3
  35. package/dist/{chunk-AZAS3FKN.js → chunk-26YCXWCU.js} +48 -2
  36. package/dist/chunk-26YCXWCU.js.map +1 -0
  37. package/dist/{chunk-SKADPHAM.js → chunk-4N5XR5DQ.js} +2 -2
  38. package/dist/chunk-4N5XR5DQ.js.map +1 -0
  39. package/dist/{chunk-RHMQPLIK.js → chunk-77OXU44H.js} +6 -6
  40. package/dist/{chunk-RHMQPLIK.js.map → chunk-77OXU44H.js.map} +1 -1
  41. package/dist/{chunk-7CNIRF42.js → chunk-AAGD2J4H.js} +20 -11
  42. package/dist/chunk-AAGD2J4H.js.map +1 -0
  43. package/dist/{chunk-BZMTR5WW.js → chunk-B6SSXVF2.js} +4 -4
  44. package/dist/chunk-B6SSXVF2.js.map +1 -0
  45. package/dist/{chunk-YOPSHV7V.js → chunk-BE5KTS2L.js} +3 -3
  46. package/dist/{chunk-YOPSHV7V.js.map → chunk-BE5KTS2L.js.map} +1 -1
  47. package/dist/{chunk-JQMIG7D3.js → chunk-BJHZHNKR.js} +3 -3
  48. package/dist/{chunk-JQMIG7D3.js.map → chunk-BJHZHNKR.js.map} +1 -1
  49. package/dist/{chunk-5XS3BFJS.js → chunk-DNFIKRO6.js} +3 -3
  50. package/dist/{chunk-5XS3BFJS.js.map → chunk-DNFIKRO6.js.map} +1 -1
  51. package/dist/{chunk-PSMW5HLX.js → chunk-GWDZW44L.js} +4 -4
  52. package/dist/{chunk-PSMW5HLX.js.map → chunk-GWDZW44L.js.map} +1 -1
  53. package/dist/{chunk-K5SD7FSF.js → chunk-HKX5VSWQ.js} +3 -3
  54. package/dist/{chunk-K5SD7FSF.js.map → chunk-HKX5VSWQ.js.map} +1 -1
  55. package/dist/{chunk-P3MJYPZE.js → chunk-HWTQ6HPO.js} +4 -4
  56. package/dist/{chunk-P3MJYPZE.js.map → chunk-HWTQ6HPO.js.map} +1 -1
  57. package/dist/{chunk-XKARS6EE.js → chunk-IEX5YV4R.js} +3 -3
  58. package/dist/{chunk-XKARS6EE.js.map → chunk-IEX5YV4R.js.map} +1 -1
  59. package/dist/{chunk-GB4SEEK4.js → chunk-IRORTHGA.js} +10 -4
  60. package/dist/chunk-IRORTHGA.js.map +1 -0
  61. package/dist/{chunk-YXEPZ7LP.js → chunk-JZPEEMIM.js} +5 -5
  62. package/dist/{chunk-YXEPZ7LP.js.map → chunk-JZPEEMIM.js.map} +1 -1
  63. package/dist/{chunk-WFEPI5LA.js → chunk-LCTRCWBK.js} +5 -5
  64. package/dist/{chunk-WFEPI5LA.js.map → chunk-LCTRCWBK.js.map} +1 -1
  65. package/dist/{chunk-QK5JDAFF.js → chunk-NKTFEV7Q.js} +4 -4
  66. package/dist/{chunk-QK5JDAFF.js.map → chunk-NKTFEV7Q.js.map} +1 -1
  67. package/dist/{chunk-ETYDLXI5.js → chunk-OSCJRUKM.js} +2 -2
  68. package/dist/{chunk-ETYDLXI5.js.map → chunk-OSCJRUKM.js.map} +1 -1
  69. package/dist/{chunk-EVZKWY5J.js → chunk-PK7Q7MC7.js} +5 -4
  70. package/dist/chunk-PK7Q7MC7.js.map +1 -0
  71. package/dist/{chunk-RYRJEVQ4.js → chunk-QM2TT2PJ.js} +19 -11
  72. package/dist/chunk-QM2TT2PJ.js.map +1 -0
  73. package/dist/{chunk-SUOGVHGJ.js → chunk-TNSCHS4I.js} +2 -2
  74. package/dist/{chunk-SUOGVHGJ.js.map → chunk-TNSCHS4I.js.map} +1 -1
  75. package/dist/{chunk-Z5Z3MYDL.js → chunk-UPZGQZEJ.js} +3 -3
  76. package/dist/{chunk-Z5Z3MYDL.js.map → chunk-UPZGQZEJ.js.map} +1 -1
  77. package/dist/{chunk-RJ3HUSBC.js → chunk-UQ5FK2AB.js} +3 -3
  78. package/dist/{chunk-RJ3HUSBC.js.map → chunk-UQ5FK2AB.js.map} +1 -1
  79. package/dist/{chunk-OBSSORCE.js → chunk-X57AMTAH.js} +72 -9
  80. package/dist/chunk-X57AMTAH.js.map +1 -0
  81. package/dist/cli/index.js +766 -185
  82. package/dist/cli/index.js.map +1 -1
  83. package/dist/cli/src/commands/preset-apply.d.ts +59 -0
  84. package/dist/cli/src/commands/preset-apply.d.ts.map +1 -0
  85. package/dist/cli/src/commands/preset-cmd.d.ts +2 -2
  86. package/dist/cli/src/commands/preset-cmd.d.ts.map +1 -1
  87. package/dist/cli/src/commands/preset-export.d.ts +93 -0
  88. package/dist/cli/src/commands/preset-export.d.ts.map +1 -0
  89. package/dist/cli/src/commands/source.d.ts.map +1 -1
  90. package/dist/cli/src/commands/why.d.ts +18 -0
  91. package/dist/cli/src/commands/why.d.ts.map +1 -1
  92. package/dist/connectors/config.js +2 -2
  93. package/dist/connectors/index.js +4 -4
  94. package/dist/core/index.js +2 -2
  95. package/dist/core/src/workspace-yaml-editor.d.ts +14 -1
  96. package/dist/core/src/workspace-yaml-editor.d.ts.map +1 -1
  97. package/dist/deploy/index.js +2 -2
  98. package/dist/discovery/index.js +2 -2
  99. package/dist/{ensure-sources-4BAJGJRK.js → ensure-sources-CHC7X7AJ.js} +10 -10
  100. package/dist/{ensure-sources-4BAJGJRK.js.map → ensure-sources-CHC7X7AJ.js.map} +1 -1
  101. package/dist/factory-assets/connectors/box.js +4 -4
  102. package/dist/factory-assets/connectors/deploy.js +4 -4
  103. package/dist/factory-assets/connectors/devserver.js +4 -4
  104. package/dist/factory-assets/connectors/flow/adapter.js +4 -4
  105. package/dist/factory-assets/connectors/flow/engine/engine.d.ts +4 -1
  106. package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
  107. package/dist/factory-assets/connectors/flow/engine/types.d.ts +41 -6
  108. package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
  109. package/dist/factory-assets/connectors/flow/engine.js +3 -3
  110. package/dist/factory-assets/connectors/flow/run-flow.js +5 -5
  111. package/dist/factory-assets/connectors/flow.js +4 -4
  112. package/dist/factory-assets/connectors/git.js +4 -4
  113. package/dist/factory-assets/connectors/gmail.js +4 -4
  114. package/dist/factory-assets/connectors/googledrive.js +4 -4
  115. package/dist/factory-assets/connectors/local.js +4 -4
  116. package/dist/factory-assets/connectors/mattermost.js +4 -4
  117. package/dist/factory-assets/connectors/memory.js +4 -4
  118. package/dist/factory-assets/connectors/minio.js +4 -4
  119. package/dist/factory-assets/connectors/postgres.js +4 -4
  120. package/dist/factory-assets/connectors/s3.js +4 -4
  121. package/dist/factory-assets/connectors/sharepoint.js +4 -4
  122. package/dist/factory-assets/connectors/sqlite.js +4 -4
  123. package/dist/factory-assets/connectors/static-server.js +4 -4
  124. package/dist/factory-assets/connectors/tunnel.js +4 -4
  125. package/dist/factory-assets/connectors/webdav.js +4 -4
  126. package/dist/factory-assets/connectors/xstate-store.js +4 -4
  127. package/dist/factory-assets/connectors/xstate.js +4 -4
  128. package/dist/factory-assets/skills/skaile-author-asset/SKILL.md +1 -1
  129. package/dist/factory-assets/skills/skaile-manage-workspace/SKILL.md +1 -1
  130. package/dist/{flows-UFCOGAAM.js → flows-CGOCUNO6.js} +4 -4
  131. package/dist/{flows-UFCOGAAM.js.map → flows-CGOCUNO6.js.map} +1 -1
  132. package/dist/{helpers-2OL4FRFB.js → helpers-GWG4CYYC.js} +10 -10
  133. package/dist/{helpers-2OL4FRFB.js.map → helpers-GWG4CYYC.js.map} +1 -1
  134. package/dist/library/index.js +6 -6
  135. package/dist/library/install.js +2 -2
  136. package/dist/{open-library-RPV2LIKL.js → open-library-37PXN6MK.js} +15 -15
  137. package/dist/{open-library-RPV2LIKL.js.map → open-library-37PXN6MK.js.map} +1 -1
  138. package/dist/{paths-ML33B5SB.js → paths-UVOHWUPY.js} +4 -4
  139. package/dist/{paths-ML33B5SB.js.map → paths-UVOHWUPY.js.map} +1 -1
  140. package/dist/{plugin-store-G4HKWCTU.js → plugin-store-EIMVEH3O.js} +4 -4
  141. package/dist/{plugin-store-G4HKWCTU.js.map → plugin-store-EIMVEH3O.js.map} +1 -1
  142. package/dist/runner/index.js +10 -10
  143. package/dist/runner/src/capability-registry.d.ts +15 -4
  144. package/dist/runner/src/capability-registry.d.ts.map +1 -1
  145. package/dist/sdk/asset-manager.js +7 -7
  146. package/dist/sdk/bridge.js +3 -3
  147. package/dist/sdk/core.js +2 -2
  148. package/dist/sdk/flow.js +3 -3
  149. package/dist/sdk/index.js +10 -10
  150. package/dist/sdk/runner.js +10 -10
  151. package/dist/sdk/session.js +2 -2
  152. package/dist/sdk/types.js +1 -1
  153. package/dist/session/index.js +2 -2
  154. package/dist/{setup-WS6PF6JQ.js → setup-DINVR6ZQ.js} +7 -7
  155. package/dist/{setup-WS6PF6JQ.js.map → setup-DINVR6ZQ.js.map} +1 -1
  156. package/dist/{source-22U5NOY4.js → source-RXAHIGMD.js} +16 -16
  157. package/dist/{source-22U5NOY4.js.map → source-RXAHIGMD.js.map} +1 -1
  158. package/dist/{store-client-YLMBH5MF.js → store-client-T3F663YG.js} +5 -5
  159. package/dist/{store-client-YLMBH5MF.js.map → store-client-T3F663YG.js.map} +1 -1
  160. package/dist/tui/index.js +10 -10
  161. package/dist/types/index.js +1 -1
  162. package/dist/types/manifests.js +1 -1
  163. package/dist/types/src/capabilities.d.ts +8 -2
  164. package/dist/types/src/capabilities.d.ts.map +1 -1
  165. package/dist/types/src/logging.d.ts +3 -2
  166. package/dist/types/src/logging.d.ts.map +1 -1
  167. package/dist/types/src/manifests/_shared.d.ts +4 -4
  168. package/dist/types/src/manifests/index.d.ts +2 -2
  169. package/dist/types/src/manifests/index.d.ts.map +1 -1
  170. package/dist/types/src/manifests/preset.d.ts +10 -0
  171. package/dist/types/src/manifests/preset.d.ts.map +1 -1
  172. package/dist/types/src/version.d.ts +11 -1
  173. package/dist/types/src/version.d.ts.map +1 -1
  174. package/dist/workspace-plugin/adapters/mcp.js +2 -2
  175. package/dist/workspace-plugin/adapters/omp.js +3 -3
  176. package/dist/workspace-plugin/index.js +1 -1
  177. package/package.json +7 -1
  178. package/dist/chunk-7CNIRF42.js.map +0 -1
  179. package/dist/chunk-AZAS3FKN.js.map +0 -1
  180. package/dist/chunk-BZMTR5WW.js.map +0 -1
  181. package/dist/chunk-EVZKWY5J.js.map +0 -1
  182. package/dist/chunk-GB4SEEK4.js.map +0 -1
  183. package/dist/chunk-OBSSORCE.js.map +0 -1
  184. package/dist/chunk-RYRJEVQ4.js.map +0 -1
  185. package/dist/chunk-SKADPHAM.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-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
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-P3MJYPZE.js';
9
- import '../chunk-BZMTR5WW.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-YXEPZ7LP.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-7CNIRF42.js';
17
- import { buildClaudePluginFiles } from '../chunk-ETYDLXI5.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,38 +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-K5SD7FSF.js';
25
+ import '../chunk-HKX5VSWQ.js';
27
26
  import '../chunk-ISQWMBKD.js';
27
+ import '../chunk-RRVQAE5D.js';
28
28
  import '../chunk-BMQKMOQY.js';
29
29
  import '../chunk-GAH4YRCI.js';
30
- import '../chunk-RRVQAE5D.js';
31
- import '../chunk-SUOGVHGJ.js';
30
+ import '../chunk-TNSCHS4I.js';
32
31
  import '../chunk-LV2HPH3C.js';
33
- import '../chunk-PSMW5HLX.js';
32
+ import '../chunk-GWDZW44L.js';
34
33
  import '../chunk-BDUANIAR.js';
35
34
  import '../chunk-6E6PKKAD.js';
36
- import { loadFlow, loadAllFlows } from '../chunk-SKADPHAM.js';
35
+ import { loadFlow, loadAllFlows } from '../chunk-4N5XR5DQ.js';
37
36
  import '../chunk-DEYYRFQU.js';
38
37
  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';
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';
42
44
  import '../chunk-TJRWGMD2.js';
43
- import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, resolveSkWorkspaceConfig } from '../chunk-OUWNTFDG.js';
45
+ import { globalCacheRoot, sourceCacheKey, getGlobalCacheDir, resolveAgentDir, loadSkWorkspaceConfig, resolveSkWorkspaceConfig } from '../chunk-OUWNTFDG.js';
44
46
  import { SKAILE_MANIFEST_FILENAME, decodeSkaileManifest, loadSkPublishManifest, saveSkPublishManifest } from '../chunk-DZCK7GRW.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,6 +71,7 @@ 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
 
76
77
  async function runSearch(kind, query, opts) {
@@ -93,7 +94,7 @@ async function searchLocal(query, kind, opts, showStore, airGapped) {
93
94
  const projectDir = path14__default.resolve(opts.projectDir);
94
95
  let entries = new AssetManager({ projectDir }).search(query, kind);
95
96
  if (entries.length === 0 && !query && !kind && !airGapped) {
96
- const { autoSyncForSearch } = await import('../source-22U5NOY4.js');
97
+ const { autoSyncForSearch } = await import('../source-RXAHIGMD.js');
97
98
  if (await autoSyncForSearch(projectDir)) {
98
99
  entries = new AssetManager({ projectDir }).search(query, kind);
99
100
  }
@@ -146,7 +147,7 @@ async function searchStore(query, kind, opts, airGapped) {
146
147
  return;
147
148
  }
148
149
  try {
149
- const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-YLMBH5MF.js');
150
+ const { getStoreConfig: getStoreConfig2, storeFetch: storeFetch2 } = await import('../store-client-T3F663YG.js');
150
151
  const config = getStoreConfig2();
151
152
  const params = {};
152
153
  if (query) params.q = query;
@@ -1006,7 +1007,7 @@ function makeCatalogCommand() {
1006
1007
  return catalog;
1007
1008
  }
1008
1009
  async function runLocalCatalogTest(projectDir) {
1009
- const { resolveCatalogSource } = await import('../open-library-RPV2LIKL.js');
1010
+ const { resolveCatalogSource } = await import('../open-library-37PXN6MK.js');
1010
1011
  let resolved;
1011
1012
  try {
1012
1013
  resolved = await resolveCatalogSource({ projectDir });
@@ -1996,7 +1997,7 @@ async function ensureTargetsRegistered(projectDir) {
1996
1997
  const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
1997
1998
  const config = resolveSkWorkspaceConfig2(projectDir);
1998
1999
  if (config.plugins && config.plugins.length > 0) {
1999
- const { ensurePluginsLoaded } = await import('../plugin-store-G4HKWCTU.js');
2000
+ const { ensurePluginsLoaded } = await import('../plugin-store-EIMVEH3O.js');
2000
2001
  const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
2001
2002
  for (const f of result.failed) logWarn(f.error);
2002
2003
  }
@@ -3712,7 +3713,7 @@ async function previewSoftDepsUnderCursor() {
3712
3713
  }
3713
3714
  async function loadAssets() {
3714
3715
  softDepPreviewCache.clear();
3715
- const { gatherAssetFeeds } = await import('../asset-feeds-NEZKG6YS.js');
3716
+ const { gatherAssetFeeds } = await import('../asset-feeds-NG5TKB3V.js');
3716
3717
  const { entries, domains, notes } = await gatherAssetFeeds(am, am.projectDir);
3717
3718
  if (notes.length > 0) {
3718
3719
  state.message = notes.map((n) => `[${n.feed}] ${n.message}`).join(" \u2022 ");
@@ -3879,7 +3880,7 @@ function reconcileCollapsedSets(rows) {
3879
3880
  async function loadSourcesAndLibraries() {
3880
3881
  try {
3881
3882
  const [{ openLibraryManager: openLibraryManager2 }, { skaileHomeDir }, { getGlobalCacheDir: getGlobalCacheDir2 }] = await Promise.all([
3882
- import('../open-library-RPV2LIKL.js'),
3883
+ import('../open-library-37PXN6MK.js'),
3883
3884
  import('../library/index.js'),
3884
3885
  import('../core/index.js')
3885
3886
  ]);
@@ -4965,7 +4966,7 @@ function action2(fn) {
4965
4966
  };
4966
4967
  }
4967
4968
  async function reconcileAndReport(projectDir, plugins) {
4968
- const { reconcilePlugins } = await import('../plugin-store-G4HKWCTU.js');
4969
+ const { reconcilePlugins } = await import('../plugin-store-EIMVEH3O.js');
4969
4970
  if (plugins.length === 0) {
4970
4971
  logInfo("No plugins declared \u2014 nothing to reconcile.");
4971
4972
  return;
@@ -4981,7 +4982,7 @@ async function reconcileAndReport(projectDir, plugins) {
4981
4982
  async function runInstall2(spec, opts) {
4982
4983
  const projectDir = path14__default.resolve(opts.projectDir);
4983
4984
  const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
4984
- const { specName } = await import('../plugin-store-G4HKWCTU.js');
4985
+ const { specName } = await import('../plugin-store-EIMVEH3O.js');
4985
4986
  const yamlPath = path14__default.join(projectDir, "skaile.yaml");
4986
4987
  const editor = WorkspaceYamlEditor2.load(yamlPath);
4987
4988
  const replaced = editor.addPlugin(spec, specName);
@@ -4993,7 +4994,7 @@ async function runInstall2(spec, opts) {
4993
4994
  async function runRemove(name, opts) {
4994
4995
  const projectDir = path14__default.resolve(opts.projectDir);
4995
4996
  const { WorkspaceYamlEditor: WorkspaceYamlEditor2 } = await import('../core/index.js');
4996
- const { specName } = await import('../plugin-store-G4HKWCTU.js');
4997
+ const { specName } = await import('../plugin-store-EIMVEH3O.js');
4997
4998
  const yamlPath = path14__default.join(projectDir, "skaile.yaml");
4998
4999
  const editor = WorkspaceYamlEditor2.load(yamlPath);
4999
5000
  const removed = editor.removePlugin(name, specName);
@@ -5021,7 +5022,7 @@ async function runList(opts) {
5021
5022
  const registry = createPluginRegistry();
5022
5023
  if (declared.length > 0) {
5023
5024
  try {
5024
- const { loadPlugins } = await import('../plugin-store-G4HKWCTU.js');
5025
+ const { loadPlugins } = await import('../plugin-store-EIMVEH3O.js');
5025
5026
  const result = await loadPlugins(projectDir, declared, registry);
5026
5027
  for (const f of result.failed) logWarn(f.error);
5027
5028
  } catch (err) {
@@ -5055,150 +5056,93 @@ function makePluginCommand() {
5055
5056
  cmd.action(() => cmd.help());
5056
5057
  return cmd;
5057
5058
  }
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 = [];
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();
5113
5082
  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 ")}`);
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);
5124
5097
  }
5125
- process.exit(1);
5126
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] };
5127
5103
  }
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 });
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
+ );
5136
5138
  }
5139
+ envVarsToSet.add(key);
5140
+ return `env:${key}`;
5137
5141
  }
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
- `;
5142
+ return value.replace(TOKEN_RE, (_m, _ns, key) => {
5143
+ const resolved = resolve5(key);
5144
+ return "envRef" in resolved ? resolved.envRef : resolved.value;
5145
+ });
5202
5146
  }
5203
5147
  var REF_HINT = "kind:@<publisher>/<name>#<version> (e.g. skill:@skaile/use-exa#1.0.0)";
5204
5148
  function isValidAssetRef(ref) {
@@ -5275,7 +5219,7 @@ async function runRefInstall(ref, opts) {
5275
5219
  const spinner5 = p6.spinner();
5276
5220
  spinner5.start(`Installing ${ref}`);
5277
5221
  try {
5278
- const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-RPV2LIKL.js');
5222
+ const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-37PXN6MK.js');
5279
5223
  const catalog = await openCatalogSource2({ projectDir: path14__default.resolve(opts.projectDir) });
5280
5224
  if (!supportsInstallManifest(catalog)) {
5281
5225
  throw new Error(
@@ -5312,7 +5256,7 @@ async function runYamlInstall(opts) {
5312
5256
  return;
5313
5257
  }
5314
5258
  try {
5315
- const { ensureSourcesCloned } = await import('../ensure-sources-4BAJGJRK.js');
5259
+ const { ensureSourcesCloned } = await import('../ensure-sources-CHC7X7AJ.js');
5316
5260
  const hydrate = ensureSourcesCloned(projectDir, { quiet: true });
5317
5261
  if (hydrate.cloned.length > 0) {
5318
5262
  logOk(`Cloned source(s): ${hydrate.cloned.join(", ")}`);
@@ -5468,6 +5412,612 @@ function makeCleanCommand() {
5468
5412
  }
5469
5413
  });
5470
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
+ }
5471
6021
  function makeRebuildCommand() {
5472
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) => {
5473
6023
  const projectDir = path14__default.resolve(opts.projectDir);
@@ -5786,12 +6336,12 @@ function makeServeCommand() {
5786
6336
  );
5787
6337
  }
5788
6338
  async function ensurePluginsLoadedForServe(projectDir) {
5789
- const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-2OL4FRFB.js');
6339
+ const { logInfo: logInfo2, logWarn: logWarn2 } = await import('../helpers-GWG4CYYC.js');
5790
6340
  try {
5791
6341
  const { resolveSkWorkspaceConfig: resolveSkWorkspaceConfig2 } = await import('../core/index.js');
5792
6342
  const config = resolveSkWorkspaceConfig2(projectDir);
5793
6343
  if (!config.plugins || config.plugins.length === 0) return;
5794
- const { ensurePluginsLoaded } = await import('../plugin-store-G4HKWCTU.js');
6344
+ const { ensurePluginsLoaded } = await import('../plugin-store-EIMVEH3O.js');
5795
6345
  const { pluginRegistry } = await import('../plugin-registry/index.js');
5796
6346
  const result = await ensurePluginsLoaded(projectDir, config.plugins, pluginRegistry);
5797
6347
  if (result.loaded.length > 0) {
@@ -5805,14 +6355,14 @@ async function ensurePluginsLoadedForServe(projectDir) {
5805
6355
  }
5806
6356
  }
5807
6357
  async function runCompileTest(opts) {
5808
- const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-2OL4FRFB.js');
6358
+ const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-GWG4CYYC.js');
5809
6359
  const { tmpdir } = await import('os');
5810
6360
  const { dirname: dirname4, join: join5, resolve: resolve5 } = await import('path');
5811
6361
  const { fileURLToPath: fileURLToPath2 } = await import('url');
5812
6362
  const { unlinkSync, existsSync: existsSync13 } = await import('fs');
5813
6363
  const { spawn } = await import('child_process');
5814
6364
  const { portableSpawnSync: portableSpawnSync2 } = await import('../core/index.js');
5815
- const { MONOREPO_ROOT } = await import('../paths-ML33B5SB.js');
6365
+ const { MONOREPO_ROOT } = await import('../paths-UVOHWUPY.js');
5816
6366
  if (MONOREPO_ROOT === null) {
5817
6367
  logErr2("--compile-test is only available inside the skaile-dev workspace.");
5818
6368
  logInfo2("Run it from a checkout of skaile-ai/skaile-dev (the monorepo shell).");
@@ -6630,8 +7180,8 @@ function makeStoreCommand() {
6630
7180
  store.command("login").description("Authenticate with the store").option("--email <email>", "Login with email instead of GitHub").action(async (opts) => {
6631
7181
  const config = getStoreConfig();
6632
7182
  if (opts.email) {
6633
- const readline2 = await import('readline');
6634
- 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 });
6635
7185
  const password = await new Promise((resolve5) => {
6636
7186
  rl.question("Password: ", (answer) => {
6637
7187
  rl.close();
@@ -7448,12 +7998,43 @@ function makeVerifyCommand() {
7448
7998
  }
7449
7999
  });
7450
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
+ }
7451
8025
  function makeWhyCommand() {
7452
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) => {
7453
8027
  const am2 = new AssetManager({ projectDir: path14__default.resolve(opts.projectDir) });
7454
8028
  const chain = am2.why(ref);
7455
8029
  if (chain.length === 0) {
7456
- 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));
7457
8038
  return;
7458
8039
  }
7459
8040
  console.log();
@@ -7599,7 +8180,7 @@ program.command("init [project-dir]").description("Initialize a project director
7599
8180
  );
7600
8181
  async function runInit(projectDir, opts) {
7601
8182
  const { mkdirSync: mkdirSync5, writeFileSync: writeFileSync4 } = await import('fs');
7602
- const { stringify } = await import('yaml');
8183
+ const { stringify: stringify2 } = await import('yaml');
7603
8184
  const { DRIVER_TARGETS, SUPPORTED_DRIVER_TARGETS: SUPPORTED_DRIVER_TARGETS2 } = await import('../core/index.js');
7604
8185
  const backend = opts.backend;
7605
8186
  if (!SUPPORTED_DRIVER_TARGETS2.includes(backend)) {
@@ -7627,7 +8208,7 @@ async function runInit(projectDir, opts) {
7627
8208
  }
7628
8209
  const wsConfigPath = path14__default.join(resolved, "skaile.yaml");
7629
8210
  if (!existsSync13(wsConfigPath)) {
7630
- writeFileSync4(wsConfigPath, stringify(buildInitWorkspaceConfig(projectName, backend)));
8211
+ writeFileSync4(wsConfigPath, stringify2(buildInitWorkspaceConfig(projectName, backend)));
7631
8212
  created.push("skaile.yaml");
7632
8213
  }
7633
8214
  for (const dir of new Set(Object.values(DRIVER_TARGETS[backend].local))) {
@@ -7718,7 +8299,7 @@ async function bootstrapUserConfig() {
7718
8299
  }
7719
8300
  async function hydrateInitSources(resolved, created) {
7720
8301
  try {
7721
- const { ensureSourcesCloned } = await import('../ensure-sources-4BAJGJRK.js');
8302
+ const { ensureSourcesCloned } = await import('../ensure-sources-CHC7X7AJ.js');
7722
8303
  const hydrate = ensureSourcesCloned(resolved, { quiet: true });
7723
8304
  for (const n of hydrate.cloned) created.push(`~/.skaile/cache/sources/${n}/`);
7724
8305
  if (hydrate.failed.length > 0) {
@@ -7754,7 +8335,7 @@ function printInitSummary(resolved, projectDir, created) {
7754
8335
  console.log();
7755
8336
  }
7756
8337
  program.command("setup").description("Interactive provider setup wizard").action(async () => {
7757
- const { cmdSetup } = await import('../setup-WS6PF6JQ.js');
8338
+ const { cmdSetup } = await import('../setup-DINVR6ZQ.js');
7758
8339
  await cmdSetup([], { projectDir: process.cwd() });
7759
8340
  });
7760
8341
  program.addCommand(makeInstallCommand());