@x-code-cli/core 0.2.10 → 0.3.1

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 (258) hide show
  1. package/dist/agent/compression.d.ts +12 -2
  2. package/dist/agent/compression.d.ts.map +1 -1
  3. package/dist/agent/compression.js +51 -2
  4. package/dist/agent/compression.js.map +1 -1
  5. package/dist/agent/file-ingest.js +2 -2
  6. package/dist/agent/file-ingest.js.map +1 -1
  7. package/dist/agent/loop-state.d.ts +3 -2
  8. package/dist/agent/loop-state.d.ts.map +1 -1
  9. package/dist/agent/loop-state.js.map +1 -1
  10. package/dist/agent/loop.d.ts.map +1 -1
  11. package/dist/agent/loop.js +134 -5
  12. package/dist/agent/loop.js.map +1 -1
  13. package/dist/agent/memory-extractor.js +5 -5
  14. package/dist/agent/memory-extractor.js.map +1 -1
  15. package/dist/agent/plan-storage.js +1 -1
  16. package/dist/agent/plan-storage.js.map +1 -1
  17. package/dist/agent/sub-agents/index.d.ts +2 -1
  18. package/dist/agent/sub-agents/index.d.ts.map +1 -1
  19. package/dist/agent/sub-agents/index.js +1 -1
  20. package/dist/agent/sub-agents/index.js.map +1 -1
  21. package/dist/agent/sub-agents/loader.d.ts +13 -3
  22. package/dist/agent/sub-agents/loader.d.ts.map +1 -1
  23. package/dist/agent/sub-agents/loader.js +36 -9
  24. package/dist/agent/sub-agents/loader.js.map +1 -1
  25. package/dist/agent/sub-agents/registry.d.ts +18 -1
  26. package/dist/agent/sub-agents/registry.d.ts.map +1 -1
  27. package/dist/agent/sub-agents/registry.js +38 -5
  28. package/dist/agent/sub-agents/registry.js.map +1 -1
  29. package/dist/agent/sub-agents/runner.d.ts.map +1 -1
  30. package/dist/agent/sub-agents/runner.js +45 -1
  31. package/dist/agent/sub-agents/runner.js.map +1 -1
  32. package/dist/agent/sub-agents/types.d.ts +4 -1
  33. package/dist/agent/sub-agents/types.d.ts.map +1 -1
  34. package/dist/agent/system-prompt.d.ts +21 -0
  35. package/dist/agent/system-prompt.d.ts.map +1 -1
  36. package/dist/agent/system-prompt.js +68 -2
  37. package/dist/agent/system-prompt.js.map +1 -1
  38. package/dist/agent/tool-execution.d.ts.map +1 -1
  39. package/dist/agent/tool-execution.js +220 -1
  40. package/dist/agent/tool-execution.js.map +1 -1
  41. package/dist/commands/index.d.ts +6 -0
  42. package/dist/commands/index.d.ts.map +1 -0
  43. package/dist/commands/index.js +3 -0
  44. package/dist/commands/index.js.map +1 -0
  45. package/dist/commands/loader.d.ts +19 -0
  46. package/dist/commands/loader.d.ts.map +1 -0
  47. package/dist/commands/loader.js +109 -0
  48. package/dist/commands/loader.js.map +1 -0
  49. package/dist/commands/registry.d.ts +44 -0
  50. package/dist/commands/registry.d.ts.map +1 -0
  51. package/dist/commands/registry.js +102 -0
  52. package/dist/commands/registry.js.map +1 -0
  53. package/dist/commands/types.d.ts +23 -0
  54. package/dist/commands/types.d.ts.map +1 -0
  55. package/dist/commands/types.js +26 -0
  56. package/dist/commands/types.js.map +1 -0
  57. package/dist/config/index.d.ts +9 -0
  58. package/dist/config/index.d.ts.map +1 -1
  59. package/dist/config/index.js +12 -10
  60. package/dist/config/index.js.map +1 -1
  61. package/dist/hooks/bus.d.ts +54 -0
  62. package/dist/hooks/bus.d.ts.map +1 -0
  63. package/dist/hooks/bus.js +165 -0
  64. package/dist/hooks/bus.js.map +1 -0
  65. package/dist/hooks/config-schema.d.ts +854 -0
  66. package/dist/hooks/config-schema.d.ts.map +1 -0
  67. package/dist/hooks/config-schema.js +79 -0
  68. package/dist/hooks/config-schema.js.map +1 -0
  69. package/dist/hooks/executor.d.ts +16 -0
  70. package/dist/hooks/executor.d.ts.map +1 -0
  71. package/dist/hooks/executor.js +183 -0
  72. package/dist/hooks/executor.js.map +1 -0
  73. package/dist/hooks/index.d.ts +10 -0
  74. package/dist/hooks/index.d.ts.map +1 -0
  75. package/dist/hooks/index.js +6 -0
  76. package/dist/hooks/index.js.map +1 -0
  77. package/dist/hooks/registry.d.ts +23 -0
  78. package/dist/hooks/registry.d.ts.map +1 -0
  79. package/dist/hooks/registry.js +49 -0
  80. package/dist/hooks/registry.js.map +1 -0
  81. package/dist/hooks/types.d.ts +165 -0
  82. package/dist/hooks/types.d.ts.map +1 -0
  83. package/dist/hooks/types.js +25 -0
  84. package/dist/hooks/types.js.map +1 -0
  85. package/dist/hooks/variables.d.ts +22 -0
  86. package/dist/hooks/variables.d.ts.map +1 -0
  87. package/dist/hooks/variables.js +80 -0
  88. package/dist/hooks/variables.js.map +1 -0
  89. package/dist/index.d.ts +57 -2
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +38 -2
  92. package/dist/index.js.map +1 -1
  93. package/dist/knowledge/auto-memory.d.ts +1 -1
  94. package/dist/knowledge/auto-memory.d.ts.map +1 -1
  95. package/dist/knowledge/auto-memory.js +10 -10
  96. package/dist/knowledge/auto-memory.js.map +1 -1
  97. package/dist/knowledge/loader.js +12 -12
  98. package/dist/knowledge/loader.js.map +1 -1
  99. package/dist/mcp/arg-parser.d.ts +49 -0
  100. package/dist/mcp/arg-parser.d.ts.map +1 -0
  101. package/dist/mcp/arg-parser.js +357 -0
  102. package/dist/mcp/arg-parser.js.map +1 -0
  103. package/dist/mcp/client.d.ts +73 -0
  104. package/dist/mcp/client.d.ts.map +1 -0
  105. package/dist/mcp/client.js +376 -0
  106. package/dist/mcp/client.js.map +1 -0
  107. package/dist/mcp/config-schema.d.ts +64 -0
  108. package/dist/mcp/config-schema.d.ts.map +1 -0
  109. package/dist/mcp/config-schema.js +86 -0
  110. package/dist/mcp/config-schema.js.map +1 -0
  111. package/dist/mcp/config-writer.d.ts +41 -0
  112. package/dist/mcp/config-writer.d.ts.map +1 -0
  113. package/dist/mcp/config-writer.js +138 -0
  114. package/dist/mcp/config-writer.js.map +1 -0
  115. package/dist/mcp/env-safety.d.ts +12 -0
  116. package/dist/mcp/env-safety.d.ts.map +1 -0
  117. package/dist/mcp/env-safety.js +80 -0
  118. package/dist/mcp/env-safety.js.map +1 -0
  119. package/dist/mcp/expand-env.d.ts +14 -0
  120. package/dist/mcp/expand-env.d.ts.map +1 -0
  121. package/dist/mcp/expand-env.js +52 -0
  122. package/dist/mcp/expand-env.js.map +1 -0
  123. package/dist/mcp/loader.d.ts +81 -0
  124. package/dist/mcp/loader.d.ts.map +1 -0
  125. package/dist/mcp/loader.js +223 -0
  126. package/dist/mcp/loader.js.map +1 -0
  127. package/dist/mcp/name-mangling.d.ts +11 -0
  128. package/dist/mcp/name-mangling.d.ts.map +1 -0
  129. package/dist/mcp/name-mangling.js +82 -0
  130. package/dist/mcp/name-mangling.js.map +1 -0
  131. package/dist/mcp/oauth/callback-server.d.ts +25 -0
  132. package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
  133. package/dist/mcp/oauth/callback-server.js +118 -0
  134. package/dist/mcp/oauth/callback-server.js.map +1 -0
  135. package/dist/mcp/oauth/provider.d.ts +80 -0
  136. package/dist/mcp/oauth/provider.d.ts.map +1 -0
  137. package/dist/mcp/oauth/provider.js +292 -0
  138. package/dist/mcp/oauth/provider.js.map +1 -0
  139. package/dist/mcp/oauth/token-storage.d.ts +42 -0
  140. package/dist/mcp/oauth/token-storage.d.ts.map +1 -0
  141. package/dist/mcp/oauth/token-storage.js +121 -0
  142. package/dist/mcp/oauth/token-storage.js.map +1 -0
  143. package/dist/mcp/permissions.d.ts +28 -0
  144. package/dist/mcp/permissions.d.ts.map +1 -0
  145. package/dist/mcp/permissions.js +105 -0
  146. package/dist/mcp/permissions.js.map +1 -0
  147. package/dist/mcp/registry.d.ts +150 -0
  148. package/dist/mcp/registry.d.ts.map +1 -0
  149. package/dist/mcp/registry.js +334 -0
  150. package/dist/mcp/registry.js.map +1 -0
  151. package/dist/mcp/resources.d.ts +7 -0
  152. package/dist/mcp/resources.d.ts.map +1 -0
  153. package/dist/mcp/resources.js +40 -0
  154. package/dist/mcp/resources.js.map +1 -0
  155. package/dist/mcp/tool-bridge.d.ts +16 -0
  156. package/dist/mcp/tool-bridge.d.ts.map +1 -0
  157. package/dist/mcp/tool-bridge.js +56 -0
  158. package/dist/mcp/tool-bridge.js.map +1 -0
  159. package/dist/mcp/trust.d.ts +31 -0
  160. package/dist/mcp/trust.d.ts.map +1 -0
  161. package/dist/mcp/trust.js +103 -0
  162. package/dist/mcp/trust.js.map +1 -0
  163. package/dist/mcp/types.d.ts +73 -0
  164. package/dist/mcp/types.d.ts.map +1 -0
  165. package/dist/mcp/types.js +13 -0
  166. package/dist/mcp/types.js.map +1 -0
  167. package/dist/permissions/index.d.ts +1 -1
  168. package/dist/permissions/index.d.ts.map +1 -1
  169. package/dist/permissions/index.js +10 -4
  170. package/dist/permissions/index.js.map +1 -1
  171. package/dist/permissions/session-store.d.ts +79 -17
  172. package/dist/permissions/session-store.d.ts.map +1 -1
  173. package/dist/permissions/session-store.js +298 -38
  174. package/dist/permissions/session-store.js.map +1 -1
  175. package/dist/plugins/consent.d.ts +87 -0
  176. package/dist/plugins/consent.d.ts.map +1 -0
  177. package/dist/plugins/consent.js +181 -0
  178. package/dist/plugins/consent.js.map +1 -0
  179. package/dist/plugins/enable-state.d.ts +34 -0
  180. package/dist/plugins/enable-state.d.ts.map +1 -0
  181. package/dist/plugins/enable-state.js +159 -0
  182. package/dist/plugins/enable-state.js.map +1 -0
  183. package/dist/plugins/installer.d.ts +64 -0
  184. package/dist/plugins/installer.d.ts.map +1 -0
  185. package/dist/plugins/installer.js +416 -0
  186. package/dist/plugins/installer.js.map +1 -0
  187. package/dist/plugins/integration.d.ts +91 -0
  188. package/dist/plugins/integration.d.ts.map +1 -0
  189. package/dist/plugins/integration.js +233 -0
  190. package/dist/plugins/integration.js.map +1 -0
  191. package/dist/plugins/loader.d.ts +69 -0
  192. package/dist/plugins/loader.d.ts.map +1 -0
  193. package/dist/plugins/loader.js +243 -0
  194. package/dist/plugins/loader.js.map +1 -0
  195. package/dist/plugins/manifest.d.ts +23 -0
  196. package/dist/plugins/manifest.d.ts.map +1 -0
  197. package/dist/plugins/manifest.js +143 -0
  198. package/dist/plugins/manifest.js.map +1 -0
  199. package/dist/plugins/marketplace.d.ts +103 -0
  200. package/dist/plugins/marketplace.d.ts.map +1 -0
  201. package/dist/plugins/marketplace.js +532 -0
  202. package/dist/plugins/marketplace.js.map +1 -0
  203. package/dist/plugins/paths.d.ts +44 -0
  204. package/dist/plugins/paths.d.ts.map +1 -0
  205. package/dist/plugins/paths.js +89 -0
  206. package/dist/plugins/paths.js.map +1 -0
  207. package/dist/plugins/refresh.d.ts +61 -0
  208. package/dist/plugins/refresh.d.ts.map +1 -0
  209. package/dist/plugins/refresh.js +98 -0
  210. package/dist/plugins/refresh.js.map +1 -0
  211. package/dist/plugins/registry.d.ts +40 -0
  212. package/dist/plugins/registry.d.ts.map +1 -0
  213. package/dist/plugins/registry.js +80 -0
  214. package/dist/plugins/registry.js.map +1 -0
  215. package/dist/plugins/types.d.ts +225 -0
  216. package/dist/plugins/types.d.ts.map +1 -0
  217. package/dist/plugins/types.js +16 -0
  218. package/dist/plugins/types.js.map +1 -0
  219. package/dist/plugins/user-config.d.ts +22 -0
  220. package/dist/plugins/user-config.d.ts.map +1 -0
  221. package/dist/plugins/user-config.js +96 -0
  222. package/dist/plugins/user-config.js.map +1 -0
  223. package/dist/skills/loader.d.ts +19 -0
  224. package/dist/skills/loader.d.ts.map +1 -0
  225. package/dist/skills/loader.js +197 -0
  226. package/dist/skills/loader.js.map +1 -0
  227. package/dist/skills/registry.d.ts +74 -0
  228. package/dist/skills/registry.d.ts.map +1 -0
  229. package/dist/skills/registry.js +136 -0
  230. package/dist/skills/registry.js.map +1 -0
  231. package/dist/skills/settings.d.ts +13 -0
  232. package/dist/skills/settings.d.ts.map +1 -0
  233. package/dist/skills/settings.js +100 -0
  234. package/dist/skills/settings.js.map +1 -0
  235. package/dist/tools/activate-skill.d.ts +5 -0
  236. package/dist/tools/activate-skill.d.ts.map +1 -0
  237. package/dist/tools/activate-skill.js +33 -0
  238. package/dist/tools/activate-skill.js.map +1 -0
  239. package/dist/tools/index.d.ts +1 -1
  240. package/dist/tools/shell-utils.d.ts.map +1 -1
  241. package/dist/tools/shell-utils.js +157 -6
  242. package/dist/tools/shell-utils.js.map +1 -1
  243. package/dist/tools/todo-write.d.ts +1 -1
  244. package/dist/tools/web-fetch.d.ts.map +1 -1
  245. package/dist/tools/web-fetch.js +2 -1
  246. package/dist/tools/web-fetch.js.map +1 -1
  247. package/dist/types/index.d.ts +46 -1
  248. package/dist/types/index.d.ts.map +1 -1
  249. package/dist/types/index.js.map +1 -1
  250. package/dist/utils.d.ts +23 -2
  251. package/dist/utils.d.ts.map +1 -1
  252. package/dist/utils.js +76 -20
  253. package/dist/utils.js.map +1 -1
  254. package/dist/version.d.ts +2 -0
  255. package/dist/version.d.ts.map +1 -0
  256. package/dist/version.js +47 -0
  257. package/dist/version.js.map +1 -0
  258. package/package.json +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/plugins/types.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,wEAAwE;AACxE,uEAAuE;AACvE,yEAAyE;AACzE,oEAAoE;AACpE,wEAAwE;AACxE,gBAAgB;AAChB,EAAE;AACF,sEAAsE;AACtE,2EAA2E;AAC3E,sEAAsE;AACtE,yEAAyE;AACzE,0CAA0C"}
@@ -0,0 +1,22 @@
1
+ /** Value type for a single field. The manifest `type` field (string /
2
+ * number / boolean) is enforced at prompt time, but we round-trip
3
+ * through JSON which only knows these three primitives anyway. */
4
+ export type UserConfigValue = string | number | boolean;
5
+ /** Per-plugin user-config map: keyed by the manifest's `key` field. */
6
+ export type PluginUserConfig = Record<string, UserConfigValue>;
7
+ /** Read the saved config for one plugin. Returns an empty object when
8
+ * the plugin has no saved config yet — caller can default keys from
9
+ * the manifest. */
10
+ export declare function getPluginUserConfig(pluginId: string): Promise<PluginUserConfig>;
11
+ /** Write the config for one plugin. Merges with existing fields rather
12
+ * than replacing — caller can call this once per field if they want
13
+ * (e.g. interactive prompt loop). */
14
+ export declare function setPluginUserConfig(pluginId: string, values: PluginUserConfig): Promise<void>;
15
+ /** Drop the config for one plugin (e.g. on uninstall). */
16
+ export declare function clearPluginUserConfig(pluginId: string): Promise<void>;
17
+ /** Materialise a plugin's user-config map as an env-var record ready to
18
+ * be merged into a child process's environment. Each manifest key
19
+ * becomes the env var name; numbers and booleans coerce to their
20
+ * string forms. Unset fields are skipped (env vars left untouched). */
21
+ export declare function getPluginUserConfigEnv(pluginId: string): Promise<Record<string, string>>;
22
+ //# sourceMappingURL=user-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-config.d.ts","sourceRoot":"","sources":["../../src/plugins/user-config.ts"],"names":[],"mappings":"AAmCA;;mEAEmE;AACnE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAEvD,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;AAgC9D;;oBAEoB;AACpB,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGrF;AAED;;sCAEsC;AACtC,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAInG;AAED,0DAA0D;AAC1D,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3E;AAED;;;wEAGwE;AACxE,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAO9F"}
@@ -0,0 +1,96 @@
1
+ // @x-code-cli/core — Plugin userConfig storage
2
+ //
3
+ // Each plugin can declare a `userConfig` block in its manifest — a list of
4
+ // fields the plugin needs from the user (API keys, account ids, working
5
+ // directories, etc). At install time the CLI prompts for each field's
6
+ // value; this module owns the on-disk persistence layer.
7
+ //
8
+ // Layout:
9
+ //
10
+ // ~/.x-code/plugins/user-config.json → {
11
+ // [pluginId]: { [key]: <value> }
12
+ // }
13
+ //
14
+ // Storage format is a plain JSON map; the file is created with 0600
15
+ // (owner-read-write only) so a process in another user's session can't
16
+ // read sensitive values. This is NOT a substitute for a real OS keychain
17
+ // (macOS Keychain / Windows Credential Manager / Linux libsecret) — it's
18
+ // a pragmatic v1 that avoids the native-build complexity. The
19
+ // `sensitive: true` field still drives mask-on-input at prompt time; only
20
+ // the at-rest storage shares one file.
21
+ //
22
+ // A future enhancement will move `sensitive` entries to a real keychain.
23
+ // The reader merges from both sources, so adding it later is a non-
24
+ // breaking change.
25
+ //
26
+ // Why not split sensitive vs non-sensitive into separate files: it would
27
+ // just multiply file IO without raising the security bar (both files live
28
+ // in the same dir with the same perms). Real protection requires a real
29
+ // keychain; until then, one file is honest about what we're doing.
30
+ import fs from 'node:fs/promises';
31
+ import path from 'node:path';
32
+ import { debugLog } from '../utils.js';
33
+ import { pluginsRoot } from './paths.js';
34
+ function userConfigPath() {
35
+ return path.join(pluginsRoot(), 'user-config.json');
36
+ }
37
+ async function readFile() {
38
+ try {
39
+ const raw = await fs.readFile(userConfigPath(), 'utf-8');
40
+ const parsed = JSON.parse(raw);
41
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
42
+ return {};
43
+ return parsed;
44
+ }
45
+ catch (err) {
46
+ if (err.code === 'ENOENT')
47
+ return {};
48
+ debugLog('plugins.user-config-read-error', String(err));
49
+ return {};
50
+ }
51
+ }
52
+ async function writeFile(data) {
53
+ const p = userConfigPath();
54
+ await fs.mkdir(path.dirname(p), { recursive: true });
55
+ // 0600 keeps the file readable only by the owning user. On Windows
56
+ // this is a no-op (fs.chmod doesn't translate to ACLs the same way) —
57
+ // there's nothing meaningful we can do without shelling out to icacls.
58
+ // The keychain followup will solve Windows properly.
59
+ await fs.writeFile(p, JSON.stringify(data, null, 2) + '\n', { mode: 0o600 });
60
+ }
61
+ /** Read the saved config for one plugin. Returns an empty object when
62
+ * the plugin has no saved config yet — caller can default keys from
63
+ * the manifest. */
64
+ export async function getPluginUserConfig(pluginId) {
65
+ const all = await readFile();
66
+ return all[pluginId] ?? {};
67
+ }
68
+ /** Write the config for one plugin. Merges with existing fields rather
69
+ * than replacing — caller can call this once per field if they want
70
+ * (e.g. interactive prompt loop). */
71
+ export async function setPluginUserConfig(pluginId, values) {
72
+ const all = await readFile();
73
+ all[pluginId] = { ...(all[pluginId] ?? {}), ...values };
74
+ await writeFile(all);
75
+ }
76
+ /** Drop the config for one plugin (e.g. on uninstall). */
77
+ export async function clearPluginUserConfig(pluginId) {
78
+ const all = await readFile();
79
+ if (!(pluginId in all))
80
+ return;
81
+ delete all[pluginId];
82
+ await writeFile(all);
83
+ }
84
+ /** Materialise a plugin's user-config map as an env-var record ready to
85
+ * be merged into a child process's environment. Each manifest key
86
+ * becomes the env var name; numbers and booleans coerce to their
87
+ * string forms. Unset fields are skipped (env vars left untouched). */
88
+ export async function getPluginUserConfigEnv(pluginId) {
89
+ const cfg = await getPluginUserConfig(pluginId);
90
+ const env = {};
91
+ for (const [k, v] of Object.entries(cfg)) {
92
+ env[k] = String(v);
93
+ }
94
+ return env;
95
+ }
96
+ //# sourceMappingURL=user-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-config.js","sourceRoot":"","sources":["../../src/plugins/user-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { SkillDefinition } from './registry.js';
2
+ export interface LoadSkillsOptions {
3
+ /** Extra skill directories to scan after the built-in user + project
4
+ * paths. Used to fold plugin-contributed `skills/` directories into
5
+ * the same registry — see packages/core/src/plugins/integration.ts.
6
+ * Order matters: later entries win on name collision. Plugin skills
7
+ * are scanned BEFORE project skills (so a user-authored project skill
8
+ * can override a plugin skill of the same name). */
9
+ extraDirs?: ReadonlyArray<{
10
+ dir: string;
11
+ pluginId: string;
12
+ }>;
13
+ }
14
+ /** Load skills from user + project directories, plus any extra dirs
15
+ * passed in (used by the plugin system to fold in plugin-contributed
16
+ * skill directories). Environment variable `XC_SKILLS_DIR` overrides
17
+ * the built-in paths for testing (extras are still honoured). */
18
+ export declare function loadSkills(opts?: LoadSkillsOptions): Promise<SkillDefinition[]>;
19
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/skills/loader.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AA0KpD,MAAM,WAAW,iBAAiB;IAChC;;;;;yDAKqD;IACrD,SAAS,CAAC,EAAE,aAAa,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC7D;AAED;;;kEAGkE;AAClE,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAkBzF"}
@@ -0,0 +1,197 @@
1
+ // @x-code-cli/core — Skill loader
2
+ //
3
+ // Scans ~/.x-code/skills/*/SKILL.md and <repo-root>/.x-code/skills/*/SKILL.md
4
+ // for user-defined skills with YAML frontmatter. The subdirectory layout
5
+ // mirrors all major competitors (Gemini CLI, Opencode, Codex) and allows
6
+ // future support files alongside SKILL.md.
7
+ //
8
+ // Priority: project-level skills override user-level skills of the same name.
9
+ // Bad files are skipped with a warning — one broken SKILL.md must never
10
+ // crash the CLI.
11
+ import fs from 'node:fs/promises';
12
+ import path from 'node:path';
13
+ import { z } from 'zod';
14
+ import { USER_XCODE_DIR, XCODE_DIR } from '../utils.js';
15
+ const SKILL_FILENAME = 'SKILL.md';
16
+ /** Hard upper bound on the file count we list per skill — keeps the
17
+ * activation payload bounded even for skills that ship dozens of
18
+ * references / assets / scripts. Skills exceeding this get a truncation
19
+ * marker appended so the model knows the list isn't exhaustive. */
20
+ const MAX_LISTED_FILES = 50;
21
+ /** Directory names skipped while listing a skill's bundled files —
22
+ * hidden dirs and obvious heavy ones that almost never contain skill
23
+ * resources. Listed by basename, not glob. */
24
+ const SKILL_FILE_LIST_SKIP_DIRS = new Set([
25
+ 'node_modules',
26
+ '__pycache__',
27
+ '.git',
28
+ '.venv',
29
+ 'venv',
30
+ 'dist',
31
+ 'build',
32
+ 'target',
33
+ ]);
34
+ const frontmatterSchema = z.object({
35
+ name: z.string().min(1),
36
+ description: z.string().min(1),
37
+ });
38
+ /** Walk a skill directory and return relative paths of its non-hidden
39
+ * files (excluding SKILL.md itself). Used at load time so SkillRegistry
40
+ * has a ready-to-inject list of bundled resources — Opencode and
41
+ * Gemini CLI do the same listing at activation, but X-Code caches it
42
+ * alongside the SkillDefinition since the registry is frozen for the
43
+ * session anyway (`/skill refresh` rebuilds). */
44
+ async function listSkillFiles(skillDir) {
45
+ const out = [];
46
+ async function walk(currentDir) {
47
+ if (out.length >= MAX_LISTED_FILES)
48
+ return;
49
+ let entries;
50
+ try {
51
+ entries = await fs.readdir(currentDir, { withFileTypes: true });
52
+ }
53
+ catch {
54
+ return;
55
+ }
56
+ for (const entry of entries) {
57
+ if (out.length >= MAX_LISTED_FILES)
58
+ return;
59
+ if (entry.name.startsWith('.'))
60
+ continue;
61
+ if (entry.isDirectory()) {
62
+ if (SKILL_FILE_LIST_SKIP_DIRS.has(entry.name))
63
+ continue;
64
+ await walk(path.join(currentDir, entry.name));
65
+ continue;
66
+ }
67
+ if (!entry.isFile())
68
+ continue;
69
+ const fullPath = path.join(currentDir, entry.name);
70
+ const rel = path.relative(skillDir, fullPath).split(path.sep).join('/');
71
+ if (rel === SKILL_FILENAME)
72
+ continue;
73
+ out.push(rel);
74
+ }
75
+ }
76
+ await walk(skillDir);
77
+ return out.sort();
78
+ }
79
+ /** Minimal YAML frontmatter parser — reuses the same subset logic as
80
+ * sub-agent loader: string scalars only, no dependency on gray-matter. */
81
+ function parseFrontmatter(raw) {
82
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
83
+ if (!match)
84
+ return null;
85
+ const yamlBlock = match[1];
86
+ const body = match[2];
87
+ const data = {};
88
+ // Fold YAML continuation lines: an indented non-empty line is joined to
89
+ // the previous line with a single space. Mirrors the folded-scalar form
90
+ // used by skill SKILL.md files where a long `description:` is wrapped
91
+ // with 2-space indented continuations.
92
+ const foldedLines = [];
93
+ for (const line of yamlBlock.split(/\r?\n/)) {
94
+ if (/^\s/.test(line) && line.trim() && foldedLines.length > 0) {
95
+ foldedLines[foldedLines.length - 1] += ' ' + line.trim();
96
+ }
97
+ else {
98
+ foldedLines.push(line);
99
+ }
100
+ }
101
+ for (const line of foldedLines) {
102
+ const trimmed = line.trim();
103
+ if (!trimmed || trimmed.startsWith('#'))
104
+ continue;
105
+ const colonIdx = trimmed.indexOf(':');
106
+ if (colonIdx < 1)
107
+ continue;
108
+ const key = trimmed.slice(0, colonIdx).trim();
109
+ let value = trimmed.slice(colonIdx + 1).trim();
110
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
111
+ value = value.slice(1, -1);
112
+ }
113
+ data[key] = value;
114
+ }
115
+ return { data, body };
116
+ }
117
+ async function loadSkillsFromDir(dir, source, pluginId) {
118
+ const skills = [];
119
+ let entries;
120
+ try {
121
+ entries = await fs.readdir(dir);
122
+ }
123
+ catch {
124
+ return skills;
125
+ }
126
+ for (const entry of entries) {
127
+ const skillDir = path.join(dir, entry);
128
+ const skillFile = path.join(skillDir, SKILL_FILENAME);
129
+ try {
130
+ await fs.access(skillFile);
131
+ }
132
+ catch {
133
+ continue;
134
+ }
135
+ try {
136
+ const raw = await fs.readFile(skillFile, 'utf-8');
137
+ const parsed = parseFrontmatter(raw);
138
+ if (!parsed) {
139
+ console.error(`[skills] Skipping ${skillFile}: no valid YAML frontmatter`);
140
+ continue;
141
+ }
142
+ const result = frontmatterSchema.safeParse(parsed.data);
143
+ if (!result.success) {
144
+ console.error(`[skills] Skipping ${skillFile}: invalid frontmatter — ${result.error.issues.map((i) => i.message).join(', ')}`);
145
+ continue;
146
+ }
147
+ const files = await listSkillFiles(skillDir);
148
+ skills.push({
149
+ name: result.data.name,
150
+ description: result.data.description,
151
+ content: parsed.body.trim(),
152
+ source,
153
+ dir: skillDir,
154
+ files,
155
+ ...(pluginId ? { pluginId } : {}),
156
+ });
157
+ }
158
+ catch (err) {
159
+ console.error(`[skills] Skipping ${skillFile}: ${err instanceof Error ? err.message : String(err)}`);
160
+ }
161
+ }
162
+ return skills;
163
+ }
164
+ /** Load skills from user + project directories, plus any extra dirs
165
+ * passed in (used by the plugin system to fold in plugin-contributed
166
+ * skill directories). Environment variable `XC_SKILLS_DIR` overrides
167
+ * the built-in paths for testing (extras are still honoured). */
168
+ export async function loadSkills(opts = {}) {
169
+ const override = process.env.XC_SKILLS_DIR;
170
+ if (override) {
171
+ const overrideSkills = await loadSkillsFromDir(override, 'project');
172
+ return [...overrideSkills, ...(await loadFromExtras(opts.extraDirs))];
173
+ }
174
+ const userDir = path.join(USER_XCODE_DIR, 'skills');
175
+ const projectDir = path.join(process.cwd(), XCODE_DIR, 'skills');
176
+ const userSkills = await loadSkillsFromDir(userDir, 'user');
177
+ const pluginSkills = await loadFromExtras(opts.extraDirs);
178
+ const projectSkills = await loadSkillsFromDir(projectDir, 'project');
179
+ // Merge order — last-wins in the registry (project overrides plugin
180
+ // overrides user builtin). This matches the precedence we tell users:
181
+ // a project-level skill always overrides anything from a plugin.
182
+ return [...userSkills, ...pluginSkills, ...projectSkills];
183
+ }
184
+ async function loadFromExtras(extras) {
185
+ if (!extras || extras.length === 0)
186
+ return [];
187
+ const out = [];
188
+ for (const { dir, pluginId } of extras) {
189
+ // Plugin-provided skills' filesystem source is technically the cache
190
+ // dir under ~/.x-code/plugins/cache/..., which makes 'user' the
191
+ // closest fit (it's installed user-wide, not per-project). `pluginId`
192
+ // carries the real provenance for the UI.
193
+ out.push(...(await loadSkillsFromDir(dir, 'user', pluginId)));
194
+ }
195
+ return out;
196
+ }
197
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/skills/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { type LoadSkillsOptions } from './loader.js';
2
+ export interface SkillDefinition {
3
+ name: string;
4
+ description: string;
5
+ content: string;
6
+ source: 'user' | 'project';
7
+ /** Absolute path to the skill's directory (the one containing SKILL.md).
8
+ * Used at activation time so the model can resolve relative paths to
9
+ * bundled scripts / references / assets. */
10
+ dir: string;
11
+ /** Relative paths of files in the skill directory, excluding SKILL.md and
12
+ * hidden / heavy directories. Listed at activation time alongside the body
13
+ * so the model knows what bundled resources exist without globbing. Capped
14
+ * at MAX_LISTED_FILES — long lists get truncated with a "... N more" marker. */
15
+ files: string[];
16
+ /** When this skill comes from a plugin contribution, the owning plugin's
17
+ * id (`name@marketplace`). UI shows this as "(from plugin: …)" and
18
+ * `/skill uninstall` redirects to `/plugin uninstall`. */
19
+ pluginId?: string;
20
+ }
21
+ export interface SkillEntry extends SkillDefinition {
22
+ disabled: boolean;
23
+ }
24
+ /** Summary returned by `reloadSkillRegistry()`. Drives the message
25
+ * surface in /skill refresh — caller can show "added: a, b" /
26
+ * "removed: c" / "unchanged: d, e" the same way /mcp refresh does. */
27
+ export interface SkillReloadSummary {
28
+ added: string[];
29
+ removed: string[];
30
+ changed: string[];
31
+ unchanged: string[];
32
+ }
33
+ export declare class SkillRegistry {
34
+ private byName;
35
+ constructor(skills: SkillDefinition[], disabled?: ReadonlySet<string>);
36
+ /** Replace the in-memory skill list with a fresh load. Used by
37
+ * /skill refresh — keeps the same SkillRegistry object identity so
38
+ * every cached `options.skillRegistry` reference (agent loop, CLI
39
+ * slash completion, App.tsx handlers) keeps pointing at the right
40
+ * thing. Returns a per-name diff vs the previous state so the
41
+ * caller can render an "added / removed / changed / unchanged"
42
+ * summary in the user-facing message. */
43
+ reload(skills: SkillDefinition[], disabled: ReadonlySet<string>): SkillReloadSummary;
44
+ /** Enabled skill by name. Disabled skills are hidden from the agent loop
45
+ * and slash-command dispatch — use `getEntry()` if you need to inspect
46
+ * the disabled flag (the /skill list handler does). */
47
+ get(name: string): SkillDefinition | undefined;
48
+ /** Enabled skills only. */
49
+ list(): SkillDefinition[];
50
+ /** Enabled skill names only. */
51
+ names(): string[];
52
+ /** Every loaded skill, with `disabled` flag. Used by /skill list and the
53
+ * disable/enable/remove handlers so they can act on disabled skills too. */
54
+ listAll(): SkillEntry[];
55
+ getEntry(name: string): SkillEntry | undefined;
56
+ }
57
+ /** Build the exact text that goes inside `<activated_skill name="...">...</activated_skill>`
58
+ * for both activation paths (model self-decide via `activateSkill` tool, and
59
+ * user explicit `/<skillname>`). Format follows Opencode's convention: body
60
+ * first, then a footer with base directory + relative-paths hint + file list.
61
+ * Sharing one formatter between the two call sites keeps the byte stream the
62
+ * model sees identical regardless of who triggered activation. */
63
+ export declare function formatSkillActivationBody(skill: SkillDefinition): string;
64
+ /** Wrap a skill's activation body in the `<activated_skill name="X">`
65
+ * XML envelope. Used by both activation paths so the wrapper is byte-
66
+ * identical regardless of trigger. */
67
+ export declare function wrapActivatedSkill(skill: SkillDefinition): string;
68
+ export declare function createSkillRegistry(opts?: LoadSkillsOptions): Promise<SkillRegistry>;
69
+ /** Re-scan skill directories + settings.json, then mutate the given
70
+ * registry in place. Caller is responsible for invalidating any
71
+ * systemPromptCache that embedded the previous skill list — the
72
+ * /skill refresh handler does exactly this. */
73
+ export declare function reloadSkillRegistry(registry: SkillRegistry, opts?: LoadSkillsOptions): Promise<SkillReloadSummary>;
74
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/skills/registry.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,aAAa,CAAA;AAGhE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B;;iDAE6C;IAC7C,GAAG,EAAE,MAAM,CAAA;IACX;;;qFAGiF;IACjF,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;+DAE2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,UAAW,SAAQ,eAAe;IACjD,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;uEAEuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAyB;gBAE3B,MAAM,EAAE,eAAe,EAAE,EAAE,QAAQ,GAAE,WAAW,CAAC,MAAM,CAAa;IAShF;;;;;;8CAM0C;IAC1C,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,kBAAkB;IA+BpF;;4DAEwD;IACxD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAM9C,2BAA2B;IAC3B,IAAI,IAAI,eAAe,EAAE;IAIzB,gCAAgC;IAChC,KAAK,IAAI,MAAM,EAAE;IAIjB;iFAC6E;IAC7E,OAAO,IAAI,UAAU,EAAE;IAIvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAG/C;AAQD;;;;;mEAKmE;AACnE,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAexE;AAED;;uCAEuC;AACvC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAEjE;AAED,wBAAsB,mBAAmB,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,aAAa,CAAC,CAG9F;AAED;;;gDAGgD;AAChD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,aAAa,EACvB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAG7B"}
@@ -0,0 +1,136 @@
1
+ // @x-code-cli/core — Skill registry
2
+ //
3
+ // Built once at CLI startup and reused across the session. The skill list
4
+ // is embedded in two byte-stable surfaces — the system prompt's
5
+ // `## Available Skills` block and the `activateSkill` tool description —
6
+ // both cached on LoopState.systemPromptCache. Adding, removing, enabling,
7
+ // or disabling a skill therefore needs to either (a) run before the next
8
+ // streamText call AND invalidate that cache, or (b) wait for a CLI
9
+ // restart. The /skill disable|enable|remove handlers do (b) — they write
10
+ // settings to disk and print a "Restart the CLI to apply." hint. The
11
+ // /skill refresh handler does (a) — it calls `reloadSkillRegistry()` on
12
+ // this object to rebuild the internal map in place, then triggers a
13
+ // systemPromptCache invalidation so the next turn picks up the change.
14
+ // Keeping the same SkillRegistry object reference across refresh means
15
+ // every other code path that captured `options.skillRegistry` (agent
16
+ // loop's buildTools, App.tsx's slash-command tab completion, …) stays
17
+ // pointed at the right thing without needing to be re-wired.
18
+ import { loadSkills } from './loader.js';
19
+ import { loadDisabledSkillsSet } from './settings.js';
20
+ export class SkillRegistry {
21
+ byName;
22
+ constructor(skills, disabled = new Set()) {
23
+ this.byName = new Map();
24
+ // Last-write wins: project skills override user-scope skills of the
25
+ // same name because loadSkills() returns user-scope first, then project.
26
+ for (const skill of skills) {
27
+ this.byName.set(skill.name, { ...skill, disabled: disabled.has(skill.name) });
28
+ }
29
+ }
30
+ /** Replace the in-memory skill list with a fresh load. Used by
31
+ * /skill refresh — keeps the same SkillRegistry object identity so
32
+ * every cached `options.skillRegistry` reference (agent loop, CLI
33
+ * slash completion, App.tsx handlers) keeps pointing at the right
34
+ * thing. Returns a per-name diff vs the previous state so the
35
+ * caller can render an "added / removed / changed / unchanged"
36
+ * summary in the user-facing message. */
37
+ reload(skills, disabled) {
38
+ const previous = this.byName;
39
+ const next = new Map();
40
+ for (const skill of skills) {
41
+ next.set(skill.name, { ...skill, disabled: disabled.has(skill.name) });
42
+ }
43
+ const summary = { added: [], removed: [], changed: [], unchanged: [] };
44
+ for (const [name, entry] of next) {
45
+ const prev = previous.get(name);
46
+ if (!prev) {
47
+ summary.added.push(name);
48
+ }
49
+ else if (prev.description !== entry.description ||
50
+ prev.content !== entry.content ||
51
+ prev.source !== entry.source ||
52
+ prev.disabled !== entry.disabled) {
53
+ summary.changed.push(name);
54
+ }
55
+ else {
56
+ summary.unchanged.push(name);
57
+ }
58
+ }
59
+ for (const name of previous.keys()) {
60
+ if (!next.has(name))
61
+ summary.removed.push(name);
62
+ }
63
+ this.byName = next;
64
+ return summary;
65
+ }
66
+ /** Enabled skill by name. Disabled skills are hidden from the agent loop
67
+ * and slash-command dispatch — use `getEntry()` if you need to inspect
68
+ * the disabled flag (the /skill list handler does). */
69
+ get(name) {
70
+ const entry = this.byName.get(name);
71
+ if (!entry || entry.disabled)
72
+ return undefined;
73
+ return entry;
74
+ }
75
+ /** Enabled skills only. */
76
+ list() {
77
+ return [...this.byName.values()].filter((s) => !s.disabled);
78
+ }
79
+ /** Enabled skill names only. */
80
+ names() {
81
+ return [...this.byName.values()].filter((s) => !s.disabled).map((s) => s.name);
82
+ }
83
+ /** Every loaded skill, with `disabled` flag. Used by /skill list and the
84
+ * disable/enable/remove handlers so they can act on disabled skills too. */
85
+ listAll() {
86
+ return [...this.byName.values()];
87
+ }
88
+ getEntry(name) {
89
+ return this.byName.get(name);
90
+ }
91
+ }
92
+ /** Hard upper bound mirrored from loader's MAX_LISTED_FILES — the
93
+ * injection formatter caps the rendered file list at the same value so
94
+ * the two stay aligned. Loader sorts + truncates first, this function
95
+ * treats `skill.files` as already-bounded. */
96
+ const MAX_RENDERED_FILES = 50;
97
+ /** Build the exact text that goes inside `<activated_skill name="...">...</activated_skill>`
98
+ * for both activation paths (model self-decide via `activateSkill` tool, and
99
+ * user explicit `/<skillname>`). Format follows Opencode's convention: body
100
+ * first, then a footer with base directory + relative-paths hint + file list.
101
+ * Sharing one formatter between the two call sites keeps the byte stream the
102
+ * model sees identical regardless of who triggered activation. */
103
+ export function formatSkillActivationBody(skill) {
104
+ const lines = [skill.content.trim(), ''];
105
+ lines.push(`Base directory for this skill: ${skill.dir}`);
106
+ lines.push('Relative paths in this skill (e.g., scripts/foo.sh, references/api.md) are resolved against the base directory above.');
107
+ if (skill.files.length > 0) {
108
+ lines.push('', 'Files in this skill directory:');
109
+ const shown = skill.files.slice(0, MAX_RENDERED_FILES);
110
+ for (const f of shown)
111
+ lines.push(`- ${f}`);
112
+ if (skill.files.length > MAX_RENDERED_FILES) {
113
+ lines.push(`- ... and ${skill.files.length - MAX_RENDERED_FILES} more file(s) not shown`);
114
+ }
115
+ }
116
+ return lines.join('\n');
117
+ }
118
+ /** Wrap a skill's activation body in the `<activated_skill name="X">`
119
+ * XML envelope. Used by both activation paths so the wrapper is byte-
120
+ * identical regardless of trigger. */
121
+ export function wrapActivatedSkill(skill) {
122
+ return `<activated_skill name="${skill.name}">\n${formatSkillActivationBody(skill)}\n</activated_skill>`;
123
+ }
124
+ export async function createSkillRegistry(opts = {}) {
125
+ const [skills, disabled] = await Promise.all([loadSkills(opts), loadDisabledSkillsSet()]);
126
+ return new SkillRegistry(skills, disabled);
127
+ }
128
+ /** Re-scan skill directories + settings.json, then mutate the given
129
+ * registry in place. Caller is responsible for invalidating any
130
+ * systemPromptCache that embedded the previous skill list — the
131
+ * /skill refresh handler does exactly this. */
132
+ export async function reloadSkillRegistry(registry, opts = {}) {
133
+ const [skills, disabled] = await Promise.all([loadSkills(opts), loadDisabledSkillsSet()]);
134
+ return registry.reload(skills, disabled);
135
+ }
136
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/skills/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type SkillSettingsScope = 'user' | 'project';
2
+ export interface SkillSettings {
3
+ disabledSkills?: string[];
4
+ }
5
+ export declare function skillSettingsPath(scope: SkillSettingsScope): string;
6
+ export declare function loadDisabledSkillsSet(): Promise<Set<string>>;
7
+ /** Toggle a skill's disabled state in the given scope. `disable=true` adds
8
+ * the name; `disable=false` removes it. Returns the action that actually
9
+ * happened so the caller can render an accurate message
10
+ * ("already disabled" vs "disabled"). */
11
+ export declare function setSkillDisabled(name: string, scope: SkillSettingsScope, disable: boolean): Promise<'changed' | 'noop'>;
12
+ export declare function getScopedDisabledSkills(scope: SkillSettingsScope): Promise<string[]>;
13
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/skills/settings.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,CAAA;AAEnD,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAGnE;AA4CD,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAMlE;AAED;;;0CAG0C;AAC1C,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,CAa7B;AAED,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAG1F"}