@x-code-cli/core 0.2.9 → 0.3.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 (255) 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 +140 -9
  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 +13 -0
  46. package/dist/commands/loader.d.ts.map +1 -0
  47. package/dist/commands/loader.js +93 -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 +56 -1
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +37 -1
  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/session-store.d.ts +13 -2
  168. package/dist/permissions/session-store.d.ts.map +1 -1
  169. package/dist/permissions/session-store.js +264 -62
  170. package/dist/permissions/session-store.js.map +1 -1
  171. package/dist/plugins/consent.d.ts +87 -0
  172. package/dist/plugins/consent.d.ts.map +1 -0
  173. package/dist/plugins/consent.js +181 -0
  174. package/dist/plugins/consent.js.map +1 -0
  175. package/dist/plugins/enable-state.d.ts +34 -0
  176. package/dist/plugins/enable-state.d.ts.map +1 -0
  177. package/dist/plugins/enable-state.js +159 -0
  178. package/dist/plugins/enable-state.js.map +1 -0
  179. package/dist/plugins/installer.d.ts +64 -0
  180. package/dist/plugins/installer.d.ts.map +1 -0
  181. package/dist/plugins/installer.js +416 -0
  182. package/dist/plugins/installer.js.map +1 -0
  183. package/dist/plugins/integration.d.ts +91 -0
  184. package/dist/plugins/integration.d.ts.map +1 -0
  185. package/dist/plugins/integration.js +233 -0
  186. package/dist/plugins/integration.js.map +1 -0
  187. package/dist/plugins/loader.d.ts +69 -0
  188. package/dist/plugins/loader.d.ts.map +1 -0
  189. package/dist/plugins/loader.js +243 -0
  190. package/dist/plugins/loader.js.map +1 -0
  191. package/dist/plugins/manifest.d.ts +23 -0
  192. package/dist/plugins/manifest.d.ts.map +1 -0
  193. package/dist/plugins/manifest.js +143 -0
  194. package/dist/plugins/manifest.js.map +1 -0
  195. package/dist/plugins/marketplace.d.ts +100 -0
  196. package/dist/plugins/marketplace.d.ts.map +1 -0
  197. package/dist/plugins/marketplace.js +529 -0
  198. package/dist/plugins/marketplace.js.map +1 -0
  199. package/dist/plugins/paths.d.ts +44 -0
  200. package/dist/plugins/paths.d.ts.map +1 -0
  201. package/dist/plugins/paths.js +89 -0
  202. package/dist/plugins/paths.js.map +1 -0
  203. package/dist/plugins/refresh.d.ts +61 -0
  204. package/dist/plugins/refresh.d.ts.map +1 -0
  205. package/dist/plugins/refresh.js +98 -0
  206. package/dist/plugins/refresh.js.map +1 -0
  207. package/dist/plugins/registry.d.ts +40 -0
  208. package/dist/plugins/registry.d.ts.map +1 -0
  209. package/dist/plugins/registry.js +80 -0
  210. package/dist/plugins/registry.js.map +1 -0
  211. package/dist/plugins/types.d.ts +225 -0
  212. package/dist/plugins/types.d.ts.map +1 -0
  213. package/dist/plugins/types.js +16 -0
  214. package/dist/plugins/types.js.map +1 -0
  215. package/dist/plugins/user-config.d.ts +22 -0
  216. package/dist/plugins/user-config.d.ts.map +1 -0
  217. package/dist/plugins/user-config.js +96 -0
  218. package/dist/plugins/user-config.js.map +1 -0
  219. package/dist/providers/cache-control.d.ts +9 -0
  220. package/dist/providers/cache-control.d.ts.map +1 -1
  221. package/dist/providers/cache-control.js +31 -4
  222. package/dist/providers/cache-control.js.map +1 -1
  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/todo-write.d.ts +1 -1
  241. package/dist/tools/web-fetch.d.ts.map +1 -1
  242. package/dist/tools/web-fetch.js +2 -1
  243. package/dist/tools/web-fetch.js.map +1 -1
  244. package/dist/types/index.d.ts +46 -1
  245. package/dist/types/index.d.ts.map +1 -1
  246. package/dist/types/index.js.map +1 -1
  247. package/dist/utils.d.ts +23 -2
  248. package/dist/utils.d.ts.map +1 -1
  249. package/dist/utils.js +76 -20
  250. package/dist/utils.js.map +1 -1
  251. package/dist/version.d.ts +2 -0
  252. package/dist/version.d.ts.map +1 -0
  253. package/dist/version.js +47 -0
  254. package/dist/version.js.map +1 -0
  255. package/package.json +2 -1
@@ -0,0 +1,233 @@
1
+ // @x-code-cli/core — Plugin contributions → existing loaders integration
2
+ //
3
+ // Takes the output of [[loader]].loadAllPlugins and converts it into the
4
+ // shapes that the pre-existing skill / sub-agent / MCP loaders consume,
5
+ // so a CLI startup call sequence looks like:
6
+ //
7
+ // const pluginLoad = await loadAllPlugins({ cwd })
8
+ // const integration = await buildPluginIntegration(pluginLoad)
9
+ // const skillRegistry = await createSkillRegistry({ extraDirs: integration.skillsDirs })
10
+ // const agentRegistry = await createSubAgentRegistry({ extraDirs: integration.agentsDirs })
11
+ // const mcpRegistry = await loadMcpFromDisk({ ..., extraServers: integration.mcpServers })
12
+ //
13
+ // Three concerns this module owns and the others don't:
14
+ //
15
+ // 1. Resolving plugin `mcpServers` from the manifest (a path or an
16
+ // inline object) into a typed `Record<string, McpServerConfig>`.
17
+ // The path form is a JSON file shaped `{ mcpServers: {...} }`
18
+ // (same as ~/.x-code/config.json). The inline form is the raw
19
+ // record itself.
20
+ //
21
+ // 2. Detecting name collisions across plugins. We deduplicate by
22
+ // server name — first plugin in iteration order wins, the second's
23
+ // entry is dropped with a warning. A future improvement: namespace
24
+ // server names with the plugin id.
25
+ //
26
+ // 3. Logging plugin contributions we don't (yet) support: `commands`
27
+ // (we lack a file-based slash command loader) and `hooks` (Task 9
28
+ // will add the hook subsystem; until then, declared hooks are
29
+ // noted but not executed).
30
+ //
31
+ // Plugin order is deterministic — driven by the iteration order of
32
+ // `loadAllPlugins`'s `contributions` Map, which itself reflects the
33
+ // order of installed_plugins.json + project-local discovery. Stable
34
+ // across boots when the same plugins are installed.
35
+ import fs from 'node:fs/promises';
36
+ import { HookBus } from '../hooks/bus.js';
37
+ import { HookConfigParseError, parseHookConfig } from '../hooks/config-schema.js';
38
+ import { HookRegistry, buildHookRegistry } from '../hooks/registry.js';
39
+ import { parseServersBlock } from '../mcp/config-schema.js';
40
+ import { isStdioConfig } from '../mcp/types.js';
41
+ import { debugLog } from '../utils.js';
42
+ import { loadAllPlugins } from './loader.js';
43
+ import { getPluginUserConfigEnv } from './user-config.js';
44
+ export async function buildPluginIntegration(load) {
45
+ // Hook registry is built last so we can collect the per-plugin configs
46
+ // along the way (we don't know all plugins' rootDirs in advance — they
47
+ // come from LoadedPlugin).
48
+ const hookInputs = [];
49
+ const out = {
50
+ skillsDirs: [],
51
+ agentsDirs: [],
52
+ commandsDirs: [],
53
+ mcpServers: {},
54
+ hookRegistry: new HookRegistry(),
55
+ hookBus: new HookBus(new HookRegistry()),
56
+ pluginHooks: [],
57
+ mcpCollisions: [],
58
+ mcpErrors: [],
59
+ hookErrors: [],
60
+ };
61
+ const mcpOwners = new Map();
62
+ for (const plugin of load.registry.list()) {
63
+ const contrib = load.contributions.get(plugin.id);
64
+ if (!contrib)
65
+ continue;
66
+ if (contrib.skillsDir)
67
+ out.skillsDirs.push({ dir: contrib.skillsDir, pluginId: plugin.id });
68
+ if (contrib.agentsDir)
69
+ out.agentsDirs.push({ dir: contrib.agentsDir, pluginId: plugin.id });
70
+ if (contrib.commandsDir) {
71
+ out.commandsDirs.push({ dir: contrib.commandsDir, pluginId: plugin.id, pluginRoot: plugin.rootDir });
72
+ }
73
+ if (contrib.hooks) {
74
+ const config = await resolvePluginHooks(plugin, contrib.hooks, out);
75
+ if (config) {
76
+ hookInputs.push({ pluginId: plugin.id, pluginDir: plugin.rootDir, config });
77
+ out.pluginHooks.push({ pluginId: plugin.id, events: Object.keys(config) });
78
+ }
79
+ }
80
+ if (contrib.mcpServers) {
81
+ const servers = await resolvePluginMcpServers(plugin, contrib.mcpServers, out);
82
+ for (const [name, cfg] of Object.entries(servers)) {
83
+ const prevOwner = mcpOwners.get(name);
84
+ if (prevOwner !== undefined) {
85
+ out.mcpCollisions.push({ name, droppedFrom: plugin.id, keptFrom: prevOwner });
86
+ continue;
87
+ }
88
+ out.mcpServers[name] = cfg;
89
+ mcpOwners.set(name, plugin.id);
90
+ }
91
+ }
92
+ }
93
+ out.hookRegistry = buildHookRegistry(hookInputs);
94
+ out.hookBus = new HookBus(out.hookRegistry);
95
+ return out;
96
+ }
97
+ async function resolvePluginHooks(plugin, contrib, out) {
98
+ let raw;
99
+ if (contrib.kind === 'inline') {
100
+ raw = contrib.data;
101
+ }
102
+ else {
103
+ try {
104
+ const text = await fs.readFile(contrib.path, 'utf-8');
105
+ raw = JSON.parse(text);
106
+ }
107
+ catch (err) {
108
+ out.hookErrors.push({
109
+ pluginId: plugin.id,
110
+ message: `failed to read hooks file ${contrib.path}: ${err instanceof Error ? err.message : String(err)}`,
111
+ });
112
+ return null;
113
+ }
114
+ }
115
+ try {
116
+ return parseHookConfig(raw, plugin.id);
117
+ }
118
+ catch (err) {
119
+ out.hookErrors.push({
120
+ pluginId: plugin.id,
121
+ message: err instanceof HookConfigParseError ? err.message : String(err),
122
+ });
123
+ return null;
124
+ }
125
+ }
126
+ /** Extract the `name → cfg` block from the contents of a `.mcp.json`
127
+ * file. Two shapes are accepted:
128
+ *
129
+ * - Wrapped: `{ "mcpServers": { "name": cfg, ... } }`
130
+ * - Flat: `{ "name": cfg, ... }` (no wrapper key)
131
+ *
132
+ * Claude Code's official plugins (e.g. linear@anthropic-marketplace)
133
+ * ship the flat form; the wrapped form matches our own config.json
134
+ * layout. The detection rule is: if the parsed object has a
135
+ * `mcpServers` key at all, treat it as wrapped (and pass through the
136
+ * value as-is so the schema parser produces a clean error on
137
+ * misshape). Otherwise treat the whole object as the flat block. */
138
+ export function extractMcpServersBlock(parsed) {
139
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
140
+ return {};
141
+ const obj = parsed;
142
+ if ('mcpServers' in obj)
143
+ return obj.mcpServers;
144
+ return obj;
145
+ }
146
+ async function resolvePluginMcpServers(plugin, contrib, out) {
147
+ let rawBlock;
148
+ if (contrib.kind === 'inline') {
149
+ rawBlock = contrib.data;
150
+ }
151
+ else {
152
+ try {
153
+ const raw = await fs.readFile(contrib.path, 'utf-8');
154
+ const parsed = JSON.parse(raw);
155
+ rawBlock = extractMcpServersBlock(parsed);
156
+ }
157
+ catch (err) {
158
+ out.mcpErrors.push({
159
+ pluginId: plugin.id,
160
+ message: `failed to read mcpServers file ${contrib.path}: ${err instanceof Error ? err.message : String(err)}`,
161
+ });
162
+ return {};
163
+ }
164
+ }
165
+ const { servers, errors } = parseServersBlock(rawBlock);
166
+ for (const e of errors) {
167
+ out.mcpErrors.push({ pluginId: plugin.id, message: `mcpServers.${e.name}: ${e.message}` });
168
+ }
169
+ // Merge the owning plugin's userConfig values into each server's env
170
+ // map. Authors who want an API key from a userConfig field declared
171
+ // in the manifest just reference it as a normal env var inside the
172
+ // mcpServers entry (or skip the explicit reference and rely on the
173
+ // child process's inherited env). Pre-existing server env entries win
174
+ // so an author can override a userConfig value per-server if needed.
175
+ try {
176
+ const pluginEnv = await getPluginUserConfigEnv(plugin.id);
177
+ if (Object.keys(pluginEnv).length > 0) {
178
+ for (const name of Object.keys(servers)) {
179
+ const cfg = servers[name];
180
+ // Only stdio servers spawn a child process and accept env vars —
181
+ // HTTP servers are remote endpoints, env merging is meaningless.
182
+ if (isStdioConfig(cfg)) {
183
+ servers[name] = { ...cfg, env: { ...pluginEnv, ...(cfg.env ?? {}) } };
184
+ }
185
+ }
186
+ }
187
+ }
188
+ catch (err) {
189
+ out.mcpErrors.push({ pluginId: plugin.id, message: `userConfig env merge: ${String(err)}` });
190
+ }
191
+ return servers;
192
+ }
193
+ /** Convenience: log non-fatal integration diagnostics to debug.log so
194
+ * `/plugin doctor` and ad-hoc support can find them. CLI startup calls
195
+ * this after `buildPluginIntegration` returns. */
196
+ export function debugLogIntegrationDiagnostics(integration) {
197
+ for (const c of integration.commandsDirs) {
198
+ debugLog('plugins.commands-loaded', `${c.pluginId} commands dir: ${c.dir}`);
199
+ }
200
+ for (const h of integration.pluginHooks) {
201
+ debugLog('plugins.hooks-registered', `${h.pluginId} hooks: [${h.events.join(', ')}]`);
202
+ }
203
+ for (const e of integration.hookErrors) {
204
+ debugLog('plugins.hook-error', `${e.pluginId}: ${e.message}`);
205
+ }
206
+ for (const c of integration.mcpCollisions) {
207
+ debugLog('plugins.mcp-collision', `mcpServer "${c.name}" from ${c.droppedFrom} dropped (kept ${c.keptFrom})`);
208
+ }
209
+ for (const e of integration.mcpErrors) {
210
+ debugLog('plugins.mcp-error', `${e.pluginId}: ${e.message}`);
211
+ }
212
+ }
213
+ /** Re-scan plugins from disk and return just the merged plugin-contributed
214
+ * mcpServers block. Used by `/mcp refresh` to include plugin servers in
215
+ * the merged map (so they aren't silently dropped on standalone MCP
216
+ * refresh) and by `/plugin refresh` indirectly via buildPluginIntegration.
217
+ *
218
+ * Returns `{}` (not undefined) so callers can spread it unconditionally.
219
+ * Scan failures degrade to `{}` + debug log — callers shouldn't have an
220
+ * MCP-only refresh fail because of a plugin-system hiccup. */
221
+ export async function getPluginMcpServersFromDisk(cwd) {
222
+ try {
223
+ const load = await loadAllPlugins({ cwd });
224
+ const integration = await buildPluginIntegration(load);
225
+ return integration.mcpServers;
226
+ }
227
+ catch (err) {
228
+ debugLog('plugins.mcp-scan-failed', `getPluginMcpServersFromDisk: ${String(err)}`);
229
+ return {};
230
+ }
231
+ }
232
+ export { loadAllPlugins };
233
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.js","sourceRoot":"","sources":["../../src/plugins/integration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import { PluginRegistry } from './registry.js';
2
+ import type { InlineHookConfig, InlineMcpServers, LoadedPlugin } from './types.js';
3
+ export interface LoadOptions {
4
+ /** Current working directory. Used to find project-local plugins. */
5
+ cwd: string;
6
+ /** Skip plugin loading entirely. Wired to the `--no-plugins` startup
7
+ * flag. Returns an empty registry. */
8
+ disabled?: boolean;
9
+ }
10
+ export interface LoadResult {
11
+ registry: PluginRegistry;
12
+ /** Per-plugin resolved contribution paths. Workflow B (skill / agent /
13
+ * mcp loader integration) reads from here to merge in plugin-provided
14
+ * content. Keyed by plugin id. */
15
+ contributions: Map<string, ResolvedContributions>;
16
+ }
17
+ /** A plugin's manifest contributions, with relative paths resolved
18
+ * against `rootDir`. The `path` / `inline` discriminator on `mcpServers`
19
+ * and `hooks` reflects the manifest's union — authors can either point
20
+ * at a file or inline the config. */
21
+ export interface ResolvedContributions {
22
+ /** Absolute path to the plugin's skills directory, if any. Each
23
+ * subdir under here is expected to follow the existing
24
+ * `<name>/SKILL.md` layout (so the skill loader can scan it
25
+ * without changes). */
26
+ skillsDir?: string;
27
+ /** Absolute path to the plugin's sub-agent .md files dir. */
28
+ agentsDir?: string;
29
+ /** Absolute path to the plugin's slash-command .md files dir. */
30
+ commandsDir?: string;
31
+ /** mcpServers contribution — either a path to a JSON file shaped like
32
+ * `{ mcpServers: { ... } }` or the inline record (matches the
33
+ * ~/.x-code/config.json `mcpServers` shape). */
34
+ mcpServers?: {
35
+ kind: 'path';
36
+ path: string;
37
+ } | {
38
+ kind: 'inline';
39
+ data: InlineMcpServers;
40
+ };
41
+ /** hooks contribution — path to hooks.json or inline object. Schema
42
+ * validation lives in packages/core/src/hooks (workflow C). */
43
+ hooks?: {
44
+ kind: 'path';
45
+ path: string;
46
+ } | {
47
+ kind: 'inline';
48
+ data: InlineHookConfig;
49
+ };
50
+ }
51
+ export declare function loadAllPlugins(opts: LoadOptions): Promise<LoadResult>;
52
+ /** Resolve a plugin's manifest contribution fields into absolute paths
53
+ * (or inline objects). Exported because individual callers occasionally
54
+ * need to recompute this (e.g. `/plugin info` for a single plugin).
55
+ *
56
+ * **Two discovery passes per contribution kind:**
57
+ *
58
+ * 1. **Manifest-declared** — if the manifest names a path (e.g.
59
+ * `"skills": "./my-skills"`), use that.
60
+ * 2. **Convention-based fallback** — if not declared, probe the
61
+ * conventional directory (`skills/`, `agents/`, `commands/`) and
62
+ * the conventional file (`hooks/hooks.json`, `.mcp.json`,
63
+ * `mcp.json`). This is how real Claude Code plugins work — their
64
+ * manifests typically only carry `name`/`version`/`description` and
65
+ * drop the contributions next to it.
66
+ *
67
+ * Async because the convention probe has to stat directories. */
68
+ export declare function resolveContributions(plugin: LoadedPlugin): Promise<ResolvedContributions>;
69
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EAKb,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAA;IACX;2CACuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,cAAc,CAAA;IACxB;;uCAEmC;IACnC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAA;CAClD;AAED;;;sCAGsC;AACtC,MAAM,WAAW,qBAAqB;IACpC;;;4BAGwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;qDAEiD;IACjD,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,gBAAgB,CAAA;KAAE,CAAA;IACxF;oEACgE;IAChE,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,gBAAgB,CAAA;KAAE,CAAA;CACpF;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAsD3E;AA4ED;;;;;;;;;;;;;;;kEAekE;AAClE,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0D/F"}
@@ -0,0 +1,243 @@
1
+ // @x-code-cli/core — Plugin startup loader
2
+ //
3
+ // One-shot orchestration called from the CLI entry. Two passes:
4
+ //
5
+ // Pass 1 — user-scope installs from installed_plugins.json. Each
6
+ // record points at a versioned cache dir; we load whichever
7
+ // version the record names. Orphan records (record present but
8
+ // cache dir missing) surface as PluginLoadError.
9
+ //
10
+ // Pass 2 — project-local plugins under <cwd>/.x-code/plugins/<name>/.
11
+ // These aren't tracked in installed_plugins.json — they're
12
+ // committed to the repo as in-tree plugins. Marketplace name
13
+ // is always "local" for these.
14
+ //
15
+ // `installed_plugins.json` is the source of truth for user-scope installs.
16
+ // Orphan cache dirs (no record) are silently ignored — they'll be cleaned
17
+ // up next time the user runs `/plugin uninstall`.
18
+ //
19
+ // One broken plugin (bad JSON, missing manifest, schema violation) never
20
+ // aborts the boot — errors collect into a `PluginLoadError[]` for
21
+ // `/plugin doctor` to surface.
22
+ //
23
+ // The returned `PluginRegistry` is meant to be frozen for the session
24
+ // (same byte-stability constraint as MCP / skills — see CLAUDE.md). The
25
+ // CLI calls `loadAllPlugins()` once at startup and stashes the result on
26
+ // `AgentOptions`. `/plugin refresh` swaps the in-memory state via
27
+ // `registry.reload(...)` and invalidates `systemPromptCache`.
28
+ import fs from 'node:fs/promises';
29
+ import path from 'node:path';
30
+ import { EnableState } from './enable-state.js';
31
+ import { listInstalledPlugins } from './installer.js';
32
+ import { ManifestParseError, discoverManifest, parseManifest } from './manifest.js';
33
+ import { pluginCacheDir, projectPluginsDir } from './paths.js';
34
+ import { PluginRegistry } from './registry.js';
35
+ export async function loadAllPlugins(opts) {
36
+ if (opts.disabled) {
37
+ return { registry: new PluginRegistry([], []), contributions: new Map() };
38
+ }
39
+ const enableState = await EnableState.load(opts.cwd);
40
+ const plugins = [];
41
+ const errors = [];
42
+ const contributions = new Map();
43
+ // ── Pass 1: user-scope installs ────────────────────────────────────────
44
+ const installed = await listInstalledPlugins();
45
+ for (const record of installed) {
46
+ const rootDir = pluginCacheDir(record.marketplace, record.name, record.version);
47
+ await loadOnePlugin({
48
+ rootDir,
49
+ fallbackId: record.id,
50
+ marketplace: record.marketplace,
51
+ scope: record.installScope,
52
+ source: record.source,
53
+ enableState,
54
+ plugins,
55
+ errors,
56
+ contributions,
57
+ });
58
+ }
59
+ // ── Pass 2: project-local plugins ──────────────────────────────────────
60
+ const projectRoot = projectPluginsDir(opts.cwd);
61
+ let projectEntries = [];
62
+ try {
63
+ projectEntries = await fs.readdir(projectRoot, { withFileTypes: true });
64
+ }
65
+ catch {
66
+ /* no project plugins dir — common case */
67
+ }
68
+ for (const entry of projectEntries) {
69
+ if (!entry.isDirectory())
70
+ continue;
71
+ const pluginRoot = path.join(projectRoot, entry.name);
72
+ await loadOnePlugin({
73
+ rootDir: pluginRoot,
74
+ // Provisional id from dirname; overridden by manifest.name when we
75
+ // parse it.
76
+ fallbackId: `${entry.name}@local`,
77
+ marketplace: 'local',
78
+ scope: 'project',
79
+ source: undefined,
80
+ enableState,
81
+ plugins,
82
+ errors,
83
+ contributions,
84
+ });
85
+ }
86
+ return { registry: new PluginRegistry(plugins, errors), contributions };
87
+ }
88
+ async function loadOnePlugin(args) {
89
+ try {
90
+ const discovery = await discoverManifest(args.rootDir);
91
+ if (!discovery) {
92
+ args.errors.push({
93
+ id: args.fallbackId,
94
+ path: args.rootDir,
95
+ message: 'no plugin manifest found (looked for .x-code-plugin/plugin.json, .claude-plugin/plugin.json, plugin.json)',
96
+ });
97
+ return;
98
+ }
99
+ if (discovery.format === 'gemini') {
100
+ args.errors.push({
101
+ id: args.fallbackId,
102
+ path: args.rootDir,
103
+ message: 'Gemini extensions are not supported (gemini-extension.json detected); see docs/plugins.md',
104
+ });
105
+ return;
106
+ }
107
+ let manifest;
108
+ try {
109
+ manifest = await parseManifest(discovery.manifestPath);
110
+ }
111
+ catch (err) {
112
+ args.errors.push({
113
+ id: args.fallbackId,
114
+ path: args.rootDir,
115
+ message: err instanceof ManifestParseError ? err.message : String(err),
116
+ });
117
+ return;
118
+ }
119
+ // Canonical id always comes from the manifest, never from the cache
120
+ // dir name. For installed plugins this matches the recorded id; for
121
+ // project-local plugins it may differ from the dirname and the
122
+ // manifest wins.
123
+ const id = `${manifest.name}@${args.marketplace}`;
124
+ const enableResolution = args.enableState.resolve(id);
125
+ const plugin = {
126
+ id,
127
+ manifest,
128
+ rootDir: args.rootDir,
129
+ manifestPath: discovery.manifestPath,
130
+ manifestFormat: discovery.format,
131
+ source: args.source,
132
+ marketplace: args.marketplace,
133
+ scope: args.scope,
134
+ enabled: enableResolution.enabled,
135
+ };
136
+ args.plugins.push(plugin);
137
+ args.contributions.set(id, await resolveContributions(plugin));
138
+ }
139
+ catch (err) {
140
+ args.errors.push({
141
+ id: args.fallbackId,
142
+ path: args.rootDir,
143
+ message: err instanceof Error ? err.message : String(err),
144
+ });
145
+ }
146
+ }
147
+ /** Resolve a plugin's manifest contribution fields into absolute paths
148
+ * (or inline objects). Exported because individual callers occasionally
149
+ * need to recompute this (e.g. `/plugin info` for a single plugin).
150
+ *
151
+ * **Two discovery passes per contribution kind:**
152
+ *
153
+ * 1. **Manifest-declared** — if the manifest names a path (e.g.
154
+ * `"skills": "./my-skills"`), use that.
155
+ * 2. **Convention-based fallback** — if not declared, probe the
156
+ * conventional directory (`skills/`, `agents/`, `commands/`) and
157
+ * the conventional file (`hooks/hooks.json`, `.mcp.json`,
158
+ * `mcp.json`). This is how real Claude Code plugins work — their
159
+ * manifests typically only carry `name`/`version`/`description` and
160
+ * drop the contributions next to it.
161
+ *
162
+ * Async because the convention probe has to stat directories. */
163
+ export async function resolveContributions(plugin) {
164
+ const m = plugin.manifest;
165
+ const root = plugin.rootDir;
166
+ const result = {};
167
+ // skills / agents / commands — directory contributions
168
+ if (m.skills) {
169
+ result.skillsDir = path.resolve(root, m.skills);
170
+ }
171
+ else if (await isDir(path.join(root, 'skills'))) {
172
+ result.skillsDir = path.join(root, 'skills');
173
+ }
174
+ if (m.agents) {
175
+ result.agentsDir = path.resolve(root, m.agents);
176
+ }
177
+ else if (await isDir(path.join(root, 'agents'))) {
178
+ result.agentsDir = path.join(root, 'agents');
179
+ }
180
+ if (m.commands) {
181
+ result.commandsDir = path.resolve(root, m.commands);
182
+ }
183
+ else if (await isDir(path.join(root, 'commands'))) {
184
+ result.commandsDir = path.join(root, 'commands');
185
+ }
186
+ // mcpServers — either declared (path / inline) or auto-discovered
187
+ // from a conventional file
188
+ if (m.mcpServers !== undefined) {
189
+ if (typeof m.mcpServers === 'string') {
190
+ result.mcpServers = { kind: 'path', path: path.resolve(root, m.mcpServers) };
191
+ }
192
+ else {
193
+ result.mcpServers = { kind: 'inline', data: m.mcpServers };
194
+ }
195
+ }
196
+ else {
197
+ // Claude Code convention: `.mcp.json` at plugin root. We also
198
+ // accept `mcp.json` (without dot) as a pragmatic fallback —
199
+ // some authors use the visible form.
200
+ for (const conv of ['.mcp.json', 'mcp.json']) {
201
+ const p = path.join(root, conv);
202
+ if (await isFile(p)) {
203
+ result.mcpServers = { kind: 'path', path: p };
204
+ break;
205
+ }
206
+ }
207
+ }
208
+ // hooks — same pattern, conventional file `hooks/hooks.json`
209
+ if (m.hooks !== undefined) {
210
+ if (typeof m.hooks === 'string') {
211
+ result.hooks = { kind: 'path', path: path.resolve(root, m.hooks) };
212
+ }
213
+ else {
214
+ result.hooks = { kind: 'inline', data: m.hooks };
215
+ }
216
+ }
217
+ else {
218
+ const conv = path.join(root, 'hooks', 'hooks.json');
219
+ if (await isFile(conv)) {
220
+ result.hooks = { kind: 'path', path: conv };
221
+ }
222
+ }
223
+ return result;
224
+ }
225
+ async function isDir(p) {
226
+ try {
227
+ const s = await fs.stat(p);
228
+ return s.isDirectory();
229
+ }
230
+ catch {
231
+ return false;
232
+ }
233
+ }
234
+ async function isFile(p) {
235
+ try {
236
+ const s = await fs.stat(p);
237
+ return s.isFile();
238
+ }
239
+ catch {
240
+ return false;
241
+ }
242
+ }
243
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { ManifestFormat, PluginManifest } from './types.js';
2
+ export interface ManifestDiscovery {
3
+ /** Absolute path to the manifest file. */
4
+ manifestPath: string;
5
+ format: ManifestFormat;
6
+ }
7
+ /** Probe a plugin root for a manifest. Returns the highest-priority
8
+ * match. Returns `{ format: 'gemini', ... }` when ONLY a Gemini manifest
9
+ * exists — the installer uses this to produce a friendly "we don't
10
+ * support Gemini extensions" error rather than a confusing
11
+ * "no manifest found". */
12
+ export declare function discoverManifest(rootDir: string): Promise<ManifestDiscovery | null>;
13
+ export declare class ManifestParseError extends Error {
14
+ readonly manifestPath: string;
15
+ constructor(message: string, manifestPath: string);
16
+ }
17
+ /** Parse + validate a manifest JSON file. Fills in `schemaVersion: "1"`
18
+ * when absent (the implicit default — most existing Claude Code plugins
19
+ * don't set this field). Throws `ManifestParseError` with a path-tagged
20
+ * message on failure so the loader can collect it as a doctor entry
21
+ * without aborting the whole boot. */
22
+ export declare function parseManifest(manifestPath: string): Promise<PluginManifest>;
23
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/plugins/manifest.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAiEhE,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,cAAc,CAAA;CACvB;AAED;;;;2BAI2B;AAC3B,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAYzF;AAID,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,YAAY,EAAE,MAAM;gBADpC,OAAO,EAAE,MAAM,EACC,YAAY,EAAE,MAAM;CAKvC;AAED;;;;uCAIuC;AACvC,wBAAsB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAoCjF"}