@clinebot/core 0.0.21 → 0.0.23

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 (260) hide show
  1. package/dist/ClineCore.d.ts +110 -0
  2. package/dist/ClineCore.d.ts.map +1 -0
  3. package/dist/account/cline-account-service.d.ts +2 -1
  4. package/dist/account/cline-account-service.d.ts.map +1 -1
  5. package/dist/account/index.d.ts +1 -1
  6. package/dist/account/index.d.ts.map +1 -1
  7. package/dist/account/rpc.d.ts +3 -1
  8. package/dist/account/rpc.d.ts.map +1 -1
  9. package/dist/account/types.d.ts +3 -0
  10. package/dist/account/types.d.ts.map +1 -1
  11. package/dist/agents/plugin-loader.d.ts.map +1 -1
  12. package/dist/agents/plugin-sandbox-bootstrap.js +17 -17
  13. package/dist/auth/client.d.ts +1 -1
  14. package/dist/auth/client.d.ts.map +1 -1
  15. package/dist/auth/cline.d.ts +1 -1
  16. package/dist/auth/cline.d.ts.map +1 -1
  17. package/dist/auth/codex.d.ts +1 -1
  18. package/dist/auth/codex.d.ts.map +1 -1
  19. package/dist/auth/oca.d.ts +1 -1
  20. package/dist/auth/oca.d.ts.map +1 -1
  21. package/dist/auth/utils.d.ts +2 -2
  22. package/dist/auth/utils.d.ts.map +1 -1
  23. package/dist/index.d.ts +50 -5
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +949 -0
  26. package/dist/providers/local-provider-service.d.ts +4 -4
  27. package/dist/providers/local-provider-service.d.ts.map +1 -1
  28. package/dist/runtime/runtime-builder.d.ts +1 -0
  29. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  30. package/dist/runtime/session-runtime.d.ts +2 -1
  31. package/dist/runtime/session-runtime.d.ts.map +1 -1
  32. package/dist/runtime/team-runtime-registry.d.ts +13 -0
  33. package/dist/runtime/team-runtime-registry.d.ts.map +1 -0
  34. package/dist/session/default-session-manager.d.ts +2 -2
  35. package/dist/session/default-session-manager.d.ts.map +1 -1
  36. package/dist/session/rpc-runtime-ensure.d.ts +53 -0
  37. package/dist/session/rpc-runtime-ensure.d.ts.map +1 -0
  38. package/dist/session/session-config-builder.d.ts +2 -3
  39. package/dist/session/session-config-builder.d.ts.map +1 -1
  40. package/dist/session/session-host.d.ts +8 -18
  41. package/dist/session/session-host.d.ts.map +1 -1
  42. package/dist/session/session-manager.d.ts +1 -1
  43. package/dist/session/session-manager.d.ts.map +1 -1
  44. package/dist/session/session-manifest.d.ts +1 -2
  45. package/dist/session/session-manifest.d.ts.map +1 -1
  46. package/dist/session/unified-session-persistence-service.d.ts +2 -2
  47. package/dist/session/unified-session-persistence-service.d.ts.map +1 -1
  48. package/dist/session/utils/helpers.d.ts +1 -1
  49. package/dist/session/utils/helpers.d.ts.map +1 -1
  50. package/dist/session/utils/types.d.ts +1 -1
  51. package/dist/session/utils/types.d.ts.map +1 -1
  52. package/dist/storage/provider-settings-legacy-migration.d.ts.map +1 -1
  53. package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -1
  54. package/dist/telemetry/distinct-id.d.ts +2 -0
  55. package/dist/telemetry/distinct-id.d.ts.map +1 -0
  56. package/dist/telemetry/{opentelemetry.d.ts → index.d.ts} +1 -1
  57. package/dist/telemetry/index.d.ts.map +1 -0
  58. package/dist/telemetry/index.js +28 -0
  59. package/dist/tools/constants.d.ts +1 -1
  60. package/dist/tools/constants.d.ts.map +1 -1
  61. package/dist/tools/definitions.d.ts +3 -3
  62. package/dist/tools/definitions.d.ts.map +1 -1
  63. package/dist/tools/executors/apply-patch.d.ts +1 -1
  64. package/dist/tools/executors/apply-patch.d.ts.map +1 -1
  65. package/dist/tools/executors/bash.d.ts +1 -1
  66. package/dist/tools/executors/bash.d.ts.map +1 -1
  67. package/dist/tools/executors/editor.d.ts +1 -1
  68. package/dist/tools/executors/editor.d.ts.map +1 -1
  69. package/dist/tools/executors/file-read.d.ts +1 -1
  70. package/dist/tools/executors/file-read.d.ts.map +1 -1
  71. package/dist/tools/executors/index.d.ts +14 -14
  72. package/dist/tools/executors/index.d.ts.map +1 -1
  73. package/dist/tools/executors/search.d.ts +1 -1
  74. package/dist/tools/executors/search.d.ts.map +1 -1
  75. package/dist/tools/executors/web-fetch.d.ts +1 -1
  76. package/dist/tools/executors/web-fetch.d.ts.map +1 -1
  77. package/dist/tools/helpers.d.ts +1 -1
  78. package/dist/tools/helpers.d.ts.map +1 -1
  79. package/dist/tools/index.d.ts +10 -10
  80. package/dist/tools/index.d.ts.map +1 -1
  81. package/dist/tools/model-tool-routing.d.ts +1 -1
  82. package/dist/tools/model-tool-routing.d.ts.map +1 -1
  83. package/dist/tools/presets.d.ts +1 -1
  84. package/dist/tools/presets.d.ts.map +1 -1
  85. package/dist/types/common.d.ts +17 -8
  86. package/dist/types/common.d.ts.map +1 -1
  87. package/dist/types/config.d.ts +4 -3
  88. package/dist/types/config.d.ts.map +1 -1
  89. package/dist/types/provider-settings.d.ts +1 -1
  90. package/dist/types/provider-settings.d.ts.map +1 -1
  91. package/dist/types.d.ts +5 -2
  92. package/dist/types.d.ts.map +1 -1
  93. package/dist/version.d.ts +2 -0
  94. package/dist/version.d.ts.map +1 -0
  95. package/package.json +44 -38
  96. package/src/ClineCore.ts +137 -0
  97. package/src/account/cline-account-service.test.ts +101 -0
  98. package/src/account/cline-account-service.ts +300 -0
  99. package/src/account/featurebase-token.test.ts +175 -0
  100. package/src/account/index.ts +23 -0
  101. package/src/account/rpc.test.ts +63 -0
  102. package/src/account/rpc.ts +185 -0
  103. package/src/account/types.ts +102 -0
  104. package/src/agents/agent-config-loader.test.ts +236 -0
  105. package/src/agents/agent-config-loader.ts +108 -0
  106. package/src/agents/agent-config-parser.ts +198 -0
  107. package/src/agents/hooks-config-loader.test.ts +20 -0
  108. package/src/agents/hooks-config-loader.ts +118 -0
  109. package/src/agents/index.ts +85 -0
  110. package/src/agents/plugin-config-loader.test.ts +140 -0
  111. package/src/agents/plugin-config-loader.ts +97 -0
  112. package/src/agents/plugin-loader.test.ts +210 -0
  113. package/src/agents/plugin-loader.ts +175 -0
  114. package/src/agents/plugin-sandbox-bootstrap.ts +448 -0
  115. package/src/agents/plugin-sandbox.test.ts +296 -0
  116. package/src/agents/plugin-sandbox.ts +341 -0
  117. package/src/agents/unified-config-file-watcher.test.ts +196 -0
  118. package/src/agents/unified-config-file-watcher.ts +483 -0
  119. package/src/agents/user-instruction-config-loader.test.ts +158 -0
  120. package/src/agents/user-instruction-config-loader.ts +438 -0
  121. package/src/auth/client.test.ts +40 -0
  122. package/src/auth/client.ts +25 -0
  123. package/src/auth/cline.test.ts +130 -0
  124. package/src/auth/cline.ts +420 -0
  125. package/src/auth/codex.test.ts +170 -0
  126. package/src/auth/codex.ts +491 -0
  127. package/src/auth/oca.test.ts +215 -0
  128. package/src/auth/oca.ts +573 -0
  129. package/src/auth/server.ts +216 -0
  130. package/src/auth/types.ts +81 -0
  131. package/src/auth/utils.test.ts +128 -0
  132. package/src/auth/utils.ts +247 -0
  133. package/src/chat/chat-schema.ts +82 -0
  134. package/src/index.ts +479 -0
  135. package/src/input/file-indexer.d.ts +11 -0
  136. package/src/input/file-indexer.test.ts +127 -0
  137. package/src/input/file-indexer.ts +327 -0
  138. package/src/input/index.ts +7 -0
  139. package/src/input/mention-enricher.test.ts +85 -0
  140. package/src/input/mention-enricher.ts +122 -0
  141. package/src/mcp/config-loader.test.ts +238 -0
  142. package/src/mcp/config-loader.ts +219 -0
  143. package/src/mcp/index.ts +26 -0
  144. package/src/mcp/manager.test.ts +106 -0
  145. package/src/mcp/manager.ts +262 -0
  146. package/src/mcp/types.ts +88 -0
  147. package/src/providers/local-provider-registry.ts +232 -0
  148. package/src/providers/local-provider-service.test.ts +783 -0
  149. package/src/providers/local-provider-service.ts +471 -0
  150. package/src/runtime/commands.test.ts +98 -0
  151. package/src/runtime/commands.ts +83 -0
  152. package/src/runtime/hook-file-hooks.test.ts +237 -0
  153. package/src/runtime/hook-file-hooks.ts +859 -0
  154. package/src/runtime/index.ts +37 -0
  155. package/src/runtime/rules.ts +34 -0
  156. package/src/runtime/runtime-builder.team-persistence.test.ts +245 -0
  157. package/src/runtime/runtime-builder.test.ts +371 -0
  158. package/src/runtime/runtime-builder.ts +631 -0
  159. package/src/runtime/runtime-parity.test.ts +143 -0
  160. package/src/runtime/sandbox/subprocess-sandbox.ts +231 -0
  161. package/src/runtime/session-runtime.ts +49 -0
  162. package/src/runtime/skills.ts +44 -0
  163. package/src/runtime/team-runtime-registry.ts +46 -0
  164. package/src/runtime/tool-approval.ts +104 -0
  165. package/src/runtime/workflows.test.ts +119 -0
  166. package/src/runtime/workflows.ts +45 -0
  167. package/src/session/default-session-manager.e2e.test.ts +384 -0
  168. package/src/session/default-session-manager.test.ts +1931 -0
  169. package/src/session/default-session-manager.ts +1422 -0
  170. package/src/session/file-session-service.ts +280 -0
  171. package/src/session/index.ts +45 -0
  172. package/src/session/rpc-runtime-ensure.ts +521 -0
  173. package/src/session/rpc-session-service.ts +107 -0
  174. package/src/session/rpc-spawn-lease.test.ts +49 -0
  175. package/src/session/rpc-spawn-lease.ts +122 -0
  176. package/src/session/runtime-oauth-token-manager.test.ts +137 -0
  177. package/src/session/runtime-oauth-token-manager.ts +272 -0
  178. package/src/session/session-agent-events.ts +248 -0
  179. package/src/session/session-artifacts.ts +106 -0
  180. package/src/session/session-config-builder.ts +113 -0
  181. package/src/session/session-graph.ts +92 -0
  182. package/src/session/session-host.test.ts +89 -0
  183. package/src/session/session-host.ts +205 -0
  184. package/src/session/session-manager.ts +69 -0
  185. package/src/session/session-manifest.ts +29 -0
  186. package/src/session/session-service.team-persistence.test.ts +48 -0
  187. package/src/session/session-service.ts +673 -0
  188. package/src/session/session-team-coordination.ts +229 -0
  189. package/src/session/session-telemetry.ts +100 -0
  190. package/src/session/sqlite-rpc-session-backend.ts +303 -0
  191. package/src/session/unified-session-persistence-service.test.ts +85 -0
  192. package/src/session/unified-session-persistence-service.ts +994 -0
  193. package/src/session/utils/helpers.ts +139 -0
  194. package/src/session/utils/types.ts +57 -0
  195. package/src/session/utils/usage.ts +32 -0
  196. package/src/session/workspace-manager.ts +98 -0
  197. package/src/session/workspace-manifest.ts +100 -0
  198. package/src/storage/artifact-store.ts +1 -0
  199. package/src/storage/file-team-store.ts +257 -0
  200. package/src/storage/index.ts +11 -0
  201. package/src/storage/provider-settings-legacy-migration.test.ts +424 -0
  202. package/src/storage/provider-settings-legacy-migration.ts +826 -0
  203. package/src/storage/provider-settings-manager.test.ts +191 -0
  204. package/src/storage/provider-settings-manager.ts +152 -0
  205. package/src/storage/session-store.ts +1 -0
  206. package/src/storage/sqlite-session-store.ts +275 -0
  207. package/src/storage/sqlite-team-store.ts +454 -0
  208. package/src/storage/team-store.ts +40 -0
  209. package/src/team/index.ts +4 -0
  210. package/src/team/projections.ts +285 -0
  211. package/src/telemetry/ITelemetryAdapter.ts +94 -0
  212. package/src/telemetry/LoggerTelemetryAdapter.test.ts +42 -0
  213. package/src/telemetry/LoggerTelemetryAdapter.ts +114 -0
  214. package/src/telemetry/OpenTelemetryAdapter.test.ts +157 -0
  215. package/src/telemetry/OpenTelemetryAdapter.ts +348 -0
  216. package/src/telemetry/OpenTelemetryProvider.test.ts +113 -0
  217. package/src/telemetry/OpenTelemetryProvider.ts +325 -0
  218. package/src/telemetry/TelemetryService.test.ts +134 -0
  219. package/src/telemetry/TelemetryService.ts +141 -0
  220. package/src/telemetry/core-events.ts +400 -0
  221. package/src/telemetry/distinct-id.test.ts +57 -0
  222. package/src/telemetry/distinct-id.ts +58 -0
  223. package/src/telemetry/index.ts +20 -0
  224. package/src/tools/constants.ts +35 -0
  225. package/src/tools/definitions.test.ts +704 -0
  226. package/src/tools/definitions.ts +709 -0
  227. package/src/tools/executors/apply-patch-parser.ts +520 -0
  228. package/src/tools/executors/apply-patch.ts +359 -0
  229. package/src/tools/executors/bash.test.ts +87 -0
  230. package/src/tools/executors/bash.ts +207 -0
  231. package/src/tools/executors/editor.test.ts +35 -0
  232. package/src/tools/executors/editor.ts +219 -0
  233. package/src/tools/executors/file-read.test.ts +49 -0
  234. package/src/tools/executors/file-read.ts +110 -0
  235. package/src/tools/executors/index.ts +87 -0
  236. package/src/tools/executors/search.ts +278 -0
  237. package/src/tools/executors/web-fetch.ts +259 -0
  238. package/src/tools/helpers.ts +130 -0
  239. package/src/tools/index.ts +169 -0
  240. package/src/tools/model-tool-routing.test.ts +86 -0
  241. package/src/tools/model-tool-routing.ts +132 -0
  242. package/src/tools/presets.test.ts +62 -0
  243. package/src/tools/presets.ts +168 -0
  244. package/src/tools/schemas.ts +327 -0
  245. package/src/tools/types.ts +329 -0
  246. package/src/types/common.ts +26 -0
  247. package/src/types/config.ts +86 -0
  248. package/src/types/events.ts +74 -0
  249. package/src/types/index.ts +24 -0
  250. package/src/types/provider-settings.ts +43 -0
  251. package/src/types/sessions.ts +16 -0
  252. package/src/types/storage.ts +64 -0
  253. package/src/types/workspace.ts +7 -0
  254. package/src/types.ts +132 -0
  255. package/src/version.ts +3 -0
  256. package/dist/index.node.d.ts +0 -47
  257. package/dist/index.node.d.ts.map +0 -1
  258. package/dist/index.node.js +0 -948
  259. package/dist/telemetry/opentelemetry.d.ts.map +0 -1
  260. package/dist/telemetry/opentelemetry.js +0 -27
@@ -0,0 +1,85 @@
1
+ export type {
2
+ AgentConfigWatcher,
3
+ AgentConfigWatcherEvent,
4
+ AgentYamlConfig,
5
+ BuildAgentConfigOverridesOptions,
6
+ CreateAgentConfigWatcherOptions,
7
+ ParseYamlFrontmatterResult,
8
+ PartialAgentConfigOverrides,
9
+ } from "./agent-config-loader";
10
+ export {
11
+ AGENT_CONFIG_DIRECTORY_NAME,
12
+ createAgentConfigDefinition,
13
+ createAgentConfigWatcher,
14
+ parseAgentConfigFromYaml,
15
+ parsePartialAgentConfigFromYaml,
16
+ readAgentConfigsFromDisk,
17
+ resolveAgentConfigSearchPaths,
18
+ resolveAgentsConfigDirPath,
19
+ resolveAgentTools,
20
+ resolveDocumentsAgentConfigDirectoryPath,
21
+ toPartialAgentConfig,
22
+ } from "./agent-config-loader";
23
+ export {
24
+ HOOK_CONFIG_FILE_EVENT_MAP,
25
+ HOOKS_CONFIG_DIRECTORY_NAME,
26
+ type HookConfigFileEntry,
27
+ HookConfigFileName,
28
+ listHookConfigFiles,
29
+ resolveDocumentsHooksDirectoryPath,
30
+ resolveHooksConfigSearchPaths,
31
+ toHookConfigFileName,
32
+ } from "./hooks-config-loader";
33
+ export type { ResolveAgentPluginPathsOptions } from "./plugin-config-loader";
34
+ export {
35
+ discoverPluginModulePaths,
36
+ resolveAgentPluginPaths,
37
+ resolveAndLoadAgentPlugins,
38
+ resolvePluginConfigSearchPaths,
39
+ } from "./plugin-config-loader";
40
+ export type { LoadAgentPluginFromPathOptions } from "./plugin-loader";
41
+ export {
42
+ loadAgentPluginFromPath,
43
+ loadAgentPluginsFromPaths,
44
+ } from "./plugin-loader";
45
+ export type {
46
+ UnifiedConfigDefinition,
47
+ UnifiedConfigFileCandidate,
48
+ UnifiedConfigFileContext,
49
+ UnifiedConfigRecord,
50
+ UnifiedConfigWatcherEvent,
51
+ UnifiedConfigWatcherOptions,
52
+ } from "./unified-config-file-watcher";
53
+ export { UnifiedConfigFileWatcher } from "./unified-config-file-watcher";
54
+ export type {
55
+ CreateInstructionWatcherOptions,
56
+ CreateRulesConfigDefinitionOptions,
57
+ CreateSkillsConfigDefinitionOptions,
58
+ CreateUserInstructionConfigWatcherOptions,
59
+ CreateWorkflowsConfigDefinitionOptions,
60
+ ParseMarkdownFrontmatterResult,
61
+ RuleConfig,
62
+ SkillConfig,
63
+ UserInstructionConfig,
64
+ UserInstructionConfigType,
65
+ UserInstructionConfigWatcher,
66
+ UserInstructionConfigWatcherEvent,
67
+ WorkflowConfig,
68
+ } from "./user-instruction-config-loader";
69
+ export {
70
+ createRulesConfigDefinition,
71
+ createSkillsConfigDefinition,
72
+ createUserInstructionConfigWatcher,
73
+ createWorkflowsConfigDefinition,
74
+ parseRuleConfigFromMarkdown,
75
+ parseSkillConfigFromMarkdown,
76
+ parseWorkflowConfigFromMarkdown,
77
+ RULES_CONFIG_DIRECTORY_NAME,
78
+ resolveDocumentsRulesDirectoryPath,
79
+ resolveDocumentsWorkflowsDirectoryPath,
80
+ resolveRulesConfigSearchPaths,
81
+ resolveSkillsConfigSearchPaths,
82
+ resolveWorkflowsConfigSearchPaths,
83
+ SKILLS_CONFIG_DIRECTORY_NAME,
84
+ WORKFLOWS_CONFIG_DIRECTORY_NAME,
85
+ } from "./user-instruction-config-loader";
@@ -0,0 +1,140 @@
1
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { setHomeDir } from "@clinebot/shared/storage";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import {
7
+ discoverPluginModulePaths,
8
+ resolveAgentPluginPaths,
9
+ resolvePluginConfigSearchPaths,
10
+ } from "./plugin-config-loader";
11
+
12
+ describe("plugin-config-loader", () => {
13
+ const envSnapshot = {
14
+ HOME: process.env.HOME,
15
+ };
16
+
17
+ afterEach(() => {
18
+ process.env.HOME = envSnapshot.HOME;
19
+ setHomeDir(envSnapshot.HOME ?? "~");
20
+ });
21
+
22
+ it("discovers plugin modules recursively", async () => {
23
+ const root = await mkdtemp(join(tmpdir(), "core-plugin-config-loader-"));
24
+ try {
25
+ const nested = join(root, "nested");
26
+ await mkdir(nested, { recursive: true });
27
+ await writeFile(join(root, "a.mjs"), "export default {}", "utf8");
28
+ await writeFile(join(nested, "b.ts"), "export default {}", "utf8");
29
+ await writeFile(
30
+ join(root, ".a.mjs.cline-plugin.mjs"),
31
+ "export default {}",
32
+ "utf8",
33
+ );
34
+ await writeFile(join(root, "ignore.txt"), "noop", "utf8");
35
+
36
+ const discovered = discoverPluginModulePaths(root);
37
+ expect(discovered).toEqual([join(root, "a.mjs"), join(nested, "b.ts")]);
38
+ } finally {
39
+ await rm(root, { recursive: true, force: true });
40
+ }
41
+ });
42
+
43
+ it("resolves plugin paths from explicit files/directories", async () => {
44
+ const root = await mkdtemp(join(tmpdir(), "core-plugin-config-loader-"));
45
+ try {
46
+ process.env.HOME = root;
47
+ setHomeDir(root);
48
+ const pluginsDir = join(root, "plugins");
49
+ await mkdir(pluginsDir, { recursive: true });
50
+ const filePath = join(root, "direct.mjs");
51
+ const dirPluginPath = join(pluginsDir, "dir-plugin.mjs");
52
+ await writeFile(filePath, "export default {}", "utf8");
53
+ await writeFile(dirPluginPath, "export default {}", "utf8");
54
+
55
+ const resolved = resolveAgentPluginPaths({
56
+ pluginPaths: ["./direct.mjs", "./plugins"],
57
+ workspacePath: join(root, "workspace"),
58
+ cwd: root,
59
+ });
60
+
61
+ expect(resolved).toEqual([filePath, dirPluginPath]);
62
+ } finally {
63
+ await rm(root, { recursive: true, force: true });
64
+ }
65
+ });
66
+
67
+ it("prefers package manifest plugin entries for configured directories", async () => {
68
+ const root = await mkdtemp(join(tmpdir(), "core-plugin-config-loader-"));
69
+ try {
70
+ const pluginDir = join(root, "plugin-package");
71
+ const srcDir = join(pluginDir, "src");
72
+ await mkdir(srcDir, { recursive: true });
73
+ const declaredEntry = join(srcDir, "index.ts");
74
+ const ignoredEntry = join(pluginDir, "ignored.mjs");
75
+ await writeFile(
76
+ join(pluginDir, "package.json"),
77
+ JSON.stringify({
78
+ name: "plugin-package",
79
+ private: true,
80
+ cline: {
81
+ plugins: ["./src/index.ts"],
82
+ },
83
+ }),
84
+ "utf8",
85
+ );
86
+ await writeFile(declaredEntry, "export default {}", "utf8");
87
+ await writeFile(ignoredEntry, "export default {}", "utf8");
88
+
89
+ const resolved = resolveAgentPluginPaths({
90
+ pluginPaths: ["./plugin-package"],
91
+ cwd: root,
92
+ workspacePath: join(root, "workspace"),
93
+ });
94
+
95
+ expect(resolved).toContain(declaredEntry);
96
+ expect(resolved).not.toContain(ignoredEntry);
97
+ } finally {
98
+ await rm(root, { recursive: true, force: true });
99
+ }
100
+ });
101
+
102
+ it("includes shared search-path plugins", async () => {
103
+ const home = await mkdtemp(
104
+ join(tmpdir(), "core-plugin-config-loader-home-"),
105
+ );
106
+ const workspace = await mkdtemp(
107
+ join(tmpdir(), "core-plugin-config-loader-workspace-"),
108
+ );
109
+ try {
110
+ process.env.HOME = home;
111
+ setHomeDir(home);
112
+ const workspacePlugins = join(workspace, ".clinerules", "plugins");
113
+ const userPlugins = join(home, ".cline", "plugins");
114
+ const documentsPlugins = join(home, "Documents", "Plugins");
115
+ await mkdir(workspacePlugins, { recursive: true });
116
+ await mkdir(userPlugins, { recursive: true });
117
+ await mkdir(documentsPlugins, { recursive: true });
118
+ const workspacePlugin = join(workspacePlugins, "workspace.mjs");
119
+ const userPlugin = join(userPlugins, "user.mjs");
120
+ const documentsPlugin = join(documentsPlugins, "documents.mjs");
121
+ await writeFile(workspacePlugin, "export default {}", "utf8");
122
+ await writeFile(userPlugin, "export default {}", "utf8");
123
+ await writeFile(documentsPlugin, "export default {}", "utf8");
124
+
125
+ const searchPaths = resolvePluginConfigSearchPaths(workspace);
126
+ expect(searchPaths).toHaveLength(3);
127
+ expect(searchPaths).toContain(workspacePlugins);
128
+ expect(searchPaths).toContain(userPlugins);
129
+ expect(searchPaths).toContain(documentsPlugins);
130
+
131
+ const resolved = resolveAgentPluginPaths({ workspacePath: workspace });
132
+ expect(resolved).toContain(workspacePlugin);
133
+ expect(resolved).toContain(userPlugin);
134
+ expect(resolved).toContain(documentsPlugin);
135
+ } finally {
136
+ await rm(home, { recursive: true, force: true });
137
+ await rm(workspace, { recursive: true, force: true });
138
+ }
139
+ });
140
+ });
@@ -0,0 +1,97 @@
1
+ import { existsSync } from "node:fs";
2
+ import type { AgentConfig } from "@clinebot/agents";
3
+ import {
4
+ discoverPluginModulePaths as discoverPluginModulePathsFromShared,
5
+ resolveConfiguredPluginModulePaths,
6
+ resolvePluginConfigSearchPaths as resolvePluginConfigSearchPathsFromShared,
7
+ } from "@clinebot/shared/storage";
8
+ import { loadAgentPluginsFromPaths } from "./plugin-loader";
9
+ import { loadSandboxedPlugins } from "./plugin-sandbox";
10
+
11
+ type AgentPlugin = NonNullable<AgentConfig["extensions"]>[number];
12
+
13
+ export function resolvePluginConfigSearchPaths(
14
+ workspacePath?: string,
15
+ ): string[] {
16
+ return resolvePluginConfigSearchPathsFromShared(workspacePath);
17
+ }
18
+
19
+ export function discoverPluginModulePaths(directoryPath: string): string[] {
20
+ return discoverPluginModulePathsFromShared(directoryPath);
21
+ }
22
+
23
+ export interface ResolveAgentPluginPathsOptions {
24
+ pluginPaths?: ReadonlyArray<string>;
25
+ workspacePath?: string;
26
+ cwd?: string;
27
+ }
28
+
29
+ export function resolveAgentPluginPaths(
30
+ options: ResolveAgentPluginPathsOptions = {},
31
+ ): string[] {
32
+ const cwd = options.cwd ?? process.cwd();
33
+ const discoveredFromSearchPaths = resolvePluginConfigSearchPaths(
34
+ options.workspacePath,
35
+ )
36
+ .flatMap((directoryPath) => discoverPluginModulePaths(directoryPath))
37
+ .filter((path) => existsSync(path));
38
+ const configuredPaths = resolveConfiguredPluginModulePaths(
39
+ options.pluginPaths ?? [],
40
+ cwd,
41
+ );
42
+
43
+ const deduped: string[] = [];
44
+ const seen = new Set<string>();
45
+ for (const path of [...configuredPaths, ...discoveredFromSearchPaths]) {
46
+ if (seen.has(path)) {
47
+ continue;
48
+ }
49
+ seen.add(path);
50
+ deduped.push(path);
51
+ }
52
+ return deduped;
53
+ }
54
+
55
+ export interface ResolveAndLoadAgentPluginsOptions
56
+ extends ResolveAgentPluginPathsOptions {
57
+ mode?: "sandbox" | "in_process";
58
+ exportName?: string;
59
+ importTimeoutMs?: number;
60
+ hookTimeoutMs?: number;
61
+ contributionTimeoutMs?: number;
62
+ onEvent?: (event: { name: string; payload?: unknown }) => void;
63
+ }
64
+
65
+ export async function resolveAndLoadAgentPlugins(
66
+ options: ResolveAndLoadAgentPluginsOptions = {},
67
+ ): Promise<{
68
+ extensions: AgentPlugin[];
69
+ shutdown?: () => Promise<void>;
70
+ }> {
71
+ const paths = resolveAgentPluginPaths(options);
72
+ if (paths.length === 0) {
73
+ return { extensions: [] };
74
+ }
75
+
76
+ if (options.mode === "in_process") {
77
+ return {
78
+ extensions: await loadAgentPluginsFromPaths(paths, {
79
+ cwd: options.cwd,
80
+ exportName: options.exportName,
81
+ }),
82
+ };
83
+ }
84
+
85
+ const sandboxed = await loadSandboxedPlugins({
86
+ pluginPaths: paths,
87
+ exportName: options.exportName,
88
+ importTimeoutMs: options.importTimeoutMs,
89
+ hookTimeoutMs: options.hookTimeoutMs,
90
+ contributionTimeoutMs: options.contributionTimeoutMs,
91
+ onEvent: options.onEvent,
92
+ });
93
+ return {
94
+ extensions: sandboxed.extensions ?? [],
95
+ shutdown: sandboxed.shutdown,
96
+ };
97
+ }
@@ -0,0 +1,210 @@
1
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
5
+ import {
6
+ loadAgentPluginFromPath,
7
+ loadAgentPluginsFromPaths,
8
+ } from "./plugin-loader";
9
+
10
+ describe("plugin-loader", () => {
11
+ let dir = "";
12
+ let copyDir = "";
13
+
14
+ beforeAll(async () => {
15
+ dir = await mkdtemp(join(tmpdir(), "core-plugin-loader-"));
16
+ copyDir = await mkdtemp(join(tmpdir(), "core-plugin-loader-copy-"));
17
+
18
+ await writeFile(
19
+ join(dir, "plugin-default.mjs"),
20
+ [
21
+ "export default {",
22
+ " name: 'from-default',",
23
+ " manifest: { capabilities: ['hooks'], hookStages: ['input'] },",
24
+ " onInput: ({ input }) => ({ overrideInput: input })",
25
+ "};",
26
+ ].join("\n"),
27
+ "utf8",
28
+ );
29
+ await writeFile(
30
+ join(dir, "plugin-named.mjs"),
31
+ [
32
+ "export const plugin = {",
33
+ " name: 'from-named',",
34
+ " manifest: { capabilities: ['tools'] },",
35
+ "};",
36
+ ].join("\n"),
37
+ "utf8",
38
+ );
39
+ await writeFile(
40
+ join(dir, "plugin-a.mjs"),
41
+ "export default { name: 'plugin-a', manifest: { capabilities: ['tools'] } };",
42
+ "utf8",
43
+ );
44
+ await writeFile(
45
+ join(dir, "plugin-b.mjs"),
46
+ "export default { name: 'plugin-b', manifest: { capabilities: ['commands'] } };",
47
+ "utf8",
48
+ );
49
+ await writeFile(
50
+ join(dir, "plugin-ts.ts"),
51
+ [
52
+ "const name: string = 'plugin-ts';",
53
+ "export default {",
54
+ " name,",
55
+ " manifest: { capabilities: ['tools'] },",
56
+ "};",
57
+ ].join("\n"),
58
+ "utf8",
59
+ );
60
+
61
+ const depDir = join(dir, "node_modules", "plugin-local-dep");
62
+ await mkdir(depDir, { recursive: true });
63
+ await writeFile(
64
+ join(depDir, "package.json"),
65
+ JSON.stringify({
66
+ name: "plugin-local-dep",
67
+ type: "module",
68
+ exports: "./index.js",
69
+ }),
70
+ "utf8",
71
+ );
72
+ await writeFile(
73
+ join(depDir, "index.js"),
74
+ "export const depName = 'plugin-local-dep';\n",
75
+ "utf8",
76
+ );
77
+ await writeFile(
78
+ join(dir, "plugin-with-dep.ts"),
79
+ [
80
+ "import { depName } from 'plugin-local-dep';",
81
+ "export default {",
82
+ " name: depName,",
83
+ " manifest: { capabilities: ['tools'] },",
84
+ "};",
85
+ ].join("\n"),
86
+ "utf8",
87
+ );
88
+
89
+ const sdkDir = join(dir, "node_modules", "@clinebot", "shared");
90
+ await mkdir(sdkDir, { recursive: true });
91
+ await writeFile(
92
+ join(sdkDir, "package.json"),
93
+ JSON.stringify({
94
+ name: "@clinebot/shared",
95
+ type: "module",
96
+ exports: "./index.js",
97
+ }),
98
+ "utf8",
99
+ );
100
+ await writeFile(
101
+ join(sdkDir, "index.js"),
102
+ "export const sdkMarker = 'plugin-installed-sdk';\n",
103
+ "utf8",
104
+ );
105
+ await writeFile(
106
+ join(dir, "plugin-with-sdk-dep.ts"),
107
+ [
108
+ "import { sdkMarker } from '@clinebot/shared';",
109
+ "export default {",
110
+ " name: sdkMarker,",
111
+ " manifest: { capabilities: ['tools'] },",
112
+ "};",
113
+ ].join("\n"),
114
+ "utf8",
115
+ );
116
+
117
+ await writeFile(
118
+ join(copyDir, "portable-subagents.ts"),
119
+ [
120
+ "import { resolveClineDataDir } from '@clinebot/shared/storage';",
121
+ "import YAML from 'yaml';",
122
+ "export default {",
123
+ " name: typeof resolveClineDataDir === 'function' ? YAML.stringify({ ok: true }) : 'invalid',",
124
+ " manifest: { capabilities: ['tools'] },",
125
+ "};",
126
+ ].join("\n"),
127
+ "utf8",
128
+ );
129
+
130
+ await writeFile(
131
+ join(dir, "invalid-plugin.mjs"),
132
+ "export default { name: 'invalid-plugin' };",
133
+ "utf8",
134
+ );
135
+ });
136
+
137
+ afterAll(async () => {
138
+ if (dir) {
139
+ await rm(dir, { recursive: true, force: true });
140
+ }
141
+ if (copyDir) {
142
+ await rm(copyDir, { recursive: true, force: true });
143
+ }
144
+ });
145
+
146
+ it("loads default-exported plugin from path", async () => {
147
+ const plugin = await loadAgentPluginFromPath(
148
+ join(dir, "plugin-default.mjs"),
149
+ );
150
+ expect(plugin.name).toBe("from-default");
151
+ expect(plugin.manifest.capabilities).toContain("hooks");
152
+ });
153
+
154
+ it("loads named plugin export from path", async () => {
155
+ const plugin = await loadAgentPluginFromPath(
156
+ join(dir, "plugin-named.mjs"),
157
+ {
158
+ exportName: "plugin",
159
+ },
160
+ );
161
+ expect(plugin.name).toBe("from-named");
162
+ });
163
+
164
+ it("loads multiple plugins from file paths", async () => {
165
+ const plugins = await loadAgentPluginsFromPaths([
166
+ join(dir, "plugin-a.mjs"),
167
+ join(dir, "plugin-b.mjs"),
168
+ ]);
169
+ expect(plugins.map((plugin) => plugin.name)).toEqual([
170
+ "plugin-a",
171
+ "plugin-b",
172
+ ]);
173
+ });
174
+
175
+ it("loads TypeScript plugins from file paths", async () => {
176
+ const plugin = await loadAgentPluginFromPath(join(dir, "plugin-ts.ts"));
177
+ expect(plugin.name).toBe("plugin-ts");
178
+ });
179
+
180
+ it("resolves plugin-local dependencies from the plugin path", async () => {
181
+ const plugin = await loadAgentPluginFromPath(
182
+ join(dir, "plugin-with-dep.ts"),
183
+ { cwd: dir },
184
+ );
185
+ expect(plugin.name).toBe("plugin-local-dep");
186
+ });
187
+
188
+ it("prefers plugin-installed SDK packages over workspace aliases", async () => {
189
+ const plugin = await loadAgentPluginFromPath(
190
+ join(dir, "plugin-with-sdk-dep.ts"),
191
+ { cwd: dir },
192
+ );
193
+ expect(plugin.name).toBe("plugin-installed-sdk");
194
+ });
195
+
196
+ it("requires copied plugins to provide their own non-SDK dependencies", async () => {
197
+ await expect(
198
+ loadAgentPluginFromPath(join(copyDir, "portable-subagents.ts"), {
199
+ cwd: copyDir,
200
+ useCache: true,
201
+ }),
202
+ ).rejects.toThrow(/Cannot find (package|module) 'yaml'/i);
203
+ });
204
+
205
+ it("rejects invalid plugin export missing manifest", async () => {
206
+ await expect(
207
+ loadAgentPluginFromPath(join(dir, "invalid-plugin.mjs")),
208
+ ).rejects.toThrow(/missing required "manifest"/i);
209
+ });
210
+ });
@@ -0,0 +1,175 @@
1
+ import { existsSync } from "node:fs";
2
+ import { builtinModules, createRequire } from "node:module";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import type { AgentConfig } from "@clinebot/agents";
6
+ import createJiti from "jiti";
7
+
8
+ type AgentPlugin = NonNullable<AgentConfig["extensions"]>[number];
9
+ type PluginLike = {
10
+ name: string;
11
+ manifest: {
12
+ capabilities: string[];
13
+ hookStages?: string[];
14
+ };
15
+ };
16
+
17
+ export interface LoadAgentPluginFromPathOptions {
18
+ exportName?: string;
19
+ cwd?: string;
20
+ useCache?: boolean;
21
+ }
22
+
23
+ const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
24
+ const WORKSPACE_ALIASES = collectWorkspaceAliases(MODULE_DIR);
25
+ const BUILTIN_MODULES = new Set(
26
+ builtinModules.flatMap((id) => [id, id.replace(/^node:/, "")]),
27
+ );
28
+
29
+ function isObject(value: unknown): value is Record<string, unknown> {
30
+ return typeof value === "object" && value !== null;
31
+ }
32
+
33
+ function hasValidStringArray(value: unknown): value is string[] {
34
+ return (
35
+ Array.isArray(value) && value.every((entry) => typeof entry === "string")
36
+ );
37
+ }
38
+
39
+ function validatePluginManifest(
40
+ plugin: PluginLike,
41
+ absolutePath: string,
42
+ ): void {
43
+ if (!isObject(plugin.manifest)) {
44
+ throw new Error(
45
+ `Invalid plugin module at ${absolutePath}: missing required "manifest"`,
46
+ );
47
+ }
48
+ if (!hasValidStringArray(plugin.manifest.capabilities)) {
49
+ throw new Error(
50
+ `Invalid plugin module at ${absolutePath}: manifest.capabilities must be a string array`,
51
+ );
52
+ }
53
+ if (plugin.manifest.capabilities.length === 0) {
54
+ throw new Error(
55
+ `Invalid plugin module at ${absolutePath}: manifest.capabilities cannot be empty`,
56
+ );
57
+ }
58
+ if (
59
+ Object.hasOwn(plugin.manifest, "hookStages") &&
60
+ !hasValidStringArray(plugin.manifest.hookStages)
61
+ ) {
62
+ throw new Error(
63
+ `Invalid plugin module at ${absolutePath}: manifest.hookStages must be a string array when provided`,
64
+ );
65
+ }
66
+ }
67
+
68
+ function validatePluginExport(
69
+ plugin: unknown,
70
+ absolutePath: string,
71
+ ): asserts plugin is PluginLike {
72
+ if (!isObject(plugin)) {
73
+ throw new Error(
74
+ `Invalid plugin module at ${absolutePath}: expected object export`,
75
+ );
76
+ }
77
+ if (typeof plugin.name !== "string" || plugin.name.length === 0) {
78
+ throw new Error(
79
+ `Invalid plugin module at ${absolutePath}: expected non-empty "name"`,
80
+ );
81
+ }
82
+ if (!Object.hasOwn(plugin, "manifest")) {
83
+ throw new Error(
84
+ `Invalid plugin module at ${absolutePath}: missing required "manifest"`,
85
+ );
86
+ }
87
+ validatePluginManifest(plugin as PluginLike, absolutePath);
88
+ }
89
+
90
+ function collectWorkspaceAliases(startDir: string): Record<string, string> {
91
+ const root = resolve(startDir, "..", "..", "..", "..");
92
+ const aliases: Record<string, string> = {};
93
+ const candidates: Record<string, string> = {
94
+ "@clinebot/agents": resolve(root, "packages/agents/src/index.ts"),
95
+ "@clinebot/core": resolve(root, "packages/core/src/index.ts"),
96
+ "@clinebot/llms": resolve(root, "packages/llms/src/index.ts"),
97
+ "@clinebot/llms/models": resolve(root, "packages/llms/src/models.ts"),
98
+ "@clinebot/llms/providers": resolve(root, "packages/llms/src/providers.ts"),
99
+ "@clinebot/llms/runtime": resolve(root, "packages/llms/src/runtime.ts"),
100
+ "@clinebot/rpc": resolve(root, "packages/rpc/src/index.ts"),
101
+ "@clinebot/scheduler": resolve(root, "packages/scheduler/src/index.ts"),
102
+ "@clinebot/shared": resolve(root, "packages/shared/src/index.ts"),
103
+ "@clinebot/shared/storage": resolve(
104
+ root,
105
+ "packages/shared/src/storage/index.ts",
106
+ ),
107
+ "@clinebot/shared/db": resolve(root, "packages/shared/src/db/index.ts"),
108
+ };
109
+ for (const [key, value] of Object.entries(candidates)) {
110
+ if (existsSync(value)) {
111
+ aliases[key] = value;
112
+ }
113
+ }
114
+ return aliases;
115
+ }
116
+
117
+ function collectPluginImportAliases(
118
+ pluginPath: string,
119
+ ): Record<string, string> {
120
+ const pluginRequire = createRequire(pluginPath);
121
+ const aliases: Record<string, string> = {};
122
+ for (const [specifier, sourcePath] of Object.entries(WORKSPACE_ALIASES)) {
123
+ try {
124
+ pluginRequire.resolve(specifier);
125
+ continue;
126
+ } catch {
127
+ // Use the workspace source only when the plugin package does not provide
128
+ // its own installed SDK dependency.
129
+ }
130
+ aliases[specifier] = sourcePath;
131
+ }
132
+ return aliases;
133
+ }
134
+
135
+ async function importPluginModule(
136
+ absolutePath: string,
137
+ options: LoadAgentPluginFromPathOptions = {},
138
+ ): Promise<Record<string, unknown>> {
139
+ const aliases = collectPluginImportAliases(absolutePath);
140
+ const jiti = createJiti(absolutePath, {
141
+ alias: aliases,
142
+ cache: options.useCache,
143
+ requireCache: options.useCache,
144
+ esmResolve: true,
145
+ interopDefault: false,
146
+ nativeModules: [...BUILTIN_MODULES],
147
+ transformModules: Object.keys(aliases),
148
+ });
149
+ return (await jiti.import(absolutePath, {})) as Record<string, unknown>;
150
+ }
151
+
152
+ export async function loadAgentPluginFromPath(
153
+ pluginPath: string,
154
+ options: LoadAgentPluginFromPathOptions = {},
155
+ ): Promise<AgentPlugin> {
156
+ const absolutePath = resolve(options.cwd ?? process.cwd(), pluginPath);
157
+ const moduleExports = await importPluginModule(absolutePath, options);
158
+ const exportName = options.exportName ?? "plugin";
159
+ const plugin = (moduleExports.default ??
160
+ moduleExports[exportName]) as unknown;
161
+
162
+ validatePluginExport(plugin, absolutePath);
163
+ return plugin as AgentPlugin;
164
+ }
165
+
166
+ export async function loadAgentPluginsFromPaths(
167
+ pluginPaths: string[],
168
+ options: LoadAgentPluginFromPathOptions = {},
169
+ ): Promise<AgentPlugin[]> {
170
+ const loaded: AgentPlugin[] = [];
171
+ for (const pluginPath of pluginPaths) {
172
+ loaded.push(await loadAgentPluginFromPath(pluginPath, options));
173
+ }
174
+ return loaded;
175
+ }