@chengchenccc/oh-my-agent 0.1.1-rc.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 (276) hide show
  1. package/README.md +79 -0
  2. package/dist/cli/args.d.ts +24 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/args.js +120 -0
  5. package/dist/cli/initial-input.d.ts +32 -0
  6. package/dist/cli/initial-input.d.ts.map +1 -0
  7. package/dist/cli/initial-input.js +131 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +3 -0
  11. package/dist/core/__fixtures__/echo-model.d.ts +6 -0
  12. package/dist/core/__fixtures__/echo-model.d.ts.map +1 -0
  13. package/dist/core/__fixtures__/echo-model.js +10 -0
  14. package/dist/core/__fixtures__/mcp-echo-server.d.ts +2 -0
  15. package/dist/core/__fixtures__/mcp-echo-server.d.ts.map +1 -0
  16. package/dist/core/__fixtures__/mcp-echo-server.js +54 -0
  17. package/dist/core/agent-runtime.d.ts +21 -0
  18. package/dist/core/agent-runtime.d.ts.map +1 -0
  19. package/dist/core/agent-runtime.js +14 -0
  20. package/dist/core/memory/autonomous-memory.d.ts +21 -0
  21. package/dist/core/memory/autonomous-memory.d.ts.map +1 -0
  22. package/dist/core/memory/autonomous-memory.js +202 -0
  23. package/dist/core/persistence/in-memory-session-store.d.ts +3 -0
  24. package/dist/core/persistence/in-memory-session-store.d.ts.map +1 -0
  25. package/dist/core/persistence/in-memory-session-store.js +109 -0
  26. package/dist/core/persistence/session-store.contract.d.ts +5 -0
  27. package/dist/core/persistence/session-store.contract.d.ts.map +1 -0
  28. package/dist/core/persistence/session-store.contract.js +183 -0
  29. package/dist/core/persistence/session-store.d.ts +33 -0
  30. package/dist/core/persistence/session-store.d.ts.map +1 -0
  31. package/dist/core/persistence/session-store.js +22 -0
  32. package/dist/core/persistence/session-tree.d.ts +49 -0
  33. package/dist/core/persistence/session-tree.d.ts.map +1 -0
  34. package/dist/core/persistence/session-tree.js +1 -0
  35. package/dist/core/plugins/plugin-code.d.ts +13 -0
  36. package/dist/core/plugins/plugin-code.d.ts.map +1 -0
  37. package/dist/core/plugins/plugin-code.js +62 -0
  38. package/dist/core/plugins/plugin-marketplace.d.ts +72 -0
  39. package/dist/core/plugins/plugin-marketplace.d.ts.map +1 -0
  40. package/dist/core/plugins/plugin-marketplace.js +340 -0
  41. package/dist/core/plugins/plugin-resolve.d.ts +32 -0
  42. package/dist/core/plugins/plugin-resolve.d.ts.map +1 -0
  43. package/dist/core/plugins/plugin-resolve.js +101 -0
  44. package/dist/core/plugins/plugin-trust.d.ts +24 -0
  45. package/dist/core/plugins/plugin-trust.d.ts.map +1 -0
  46. package/dist/core/plugins/plugin-trust.js +89 -0
  47. package/dist/core/runtime/agent-event.d.ts +98 -0
  48. package/dist/core/runtime/agent-event.d.ts.map +1 -0
  49. package/dist/core/runtime/agent-event.js +1 -0
  50. package/dist/core/runtime/agent-loop.d.ts +142 -0
  51. package/dist/core/runtime/agent-loop.d.ts.map +1 -0
  52. package/dist/core/runtime/agent-loop.js +1008 -0
  53. package/dist/core/runtime/approval.d.ts +23 -0
  54. package/dist/core/runtime/approval.d.ts.map +1 -0
  55. package/dist/core/runtime/approval.js +25 -0
  56. package/dist/core/runtime/compaction.d.ts +22 -0
  57. package/dist/core/runtime/compaction.d.ts.map +1 -0
  58. package/dist/core/runtime/compaction.js +90 -0
  59. package/dist/core/runtime/context-estimate.d.ts +42 -0
  60. package/dist/core/runtime/context-estimate.d.ts.map +1 -0
  61. package/dist/core/runtime/context-estimate.js +34 -0
  62. package/dist/core/runtime/create-runtime.d.ts +81 -0
  63. package/dist/core/runtime/create-runtime.d.ts.map +1 -0
  64. package/dist/core/runtime/create-runtime.js +250 -0
  65. package/dist/core/runtime/fake-provider.d.ts +3 -0
  66. package/dist/core/runtime/fake-provider.d.ts.map +1 -0
  67. package/dist/core/runtime/fake-provider.js +87 -0
  68. package/dist/core/runtime/logger.d.ts +13 -0
  69. package/dist/core/runtime/logger.d.ts.map +1 -0
  70. package/dist/core/runtime/logger.js +38 -0
  71. package/dist/core/runtime/loop-input.d.ts +39 -0
  72. package/dist/core/runtime/loop-input.d.ts.map +1 -0
  73. package/dist/core/runtime/loop-input.js +38 -0
  74. package/dist/core/runtime/message-cache.d.ts +16 -0
  75. package/dist/core/runtime/message-cache.d.ts.map +1 -0
  76. package/dist/core/runtime/message-cache.js +23 -0
  77. package/dist/core/runtime/model-catalog.d.ts +10 -0
  78. package/dist/core/runtime/model-catalog.d.ts.map +1 -0
  79. package/dist/core/runtime/model-catalog.js +14 -0
  80. package/dist/core/runtime/plugin-runtime.d.ts +41 -0
  81. package/dist/core/runtime/plugin-runtime.d.ts.map +1 -0
  82. package/dist/core/runtime/plugin-runtime.js +1 -0
  83. package/dist/core/runtime/plugin.d.ts +86 -0
  84. package/dist/core/runtime/plugin.d.ts.map +1 -0
  85. package/dist/core/runtime/plugin.js +23 -0
  86. package/dist/core/runtime/process-tree.d.ts +12 -0
  87. package/dist/core/runtime/process-tree.d.ts.map +1 -0
  88. package/dist/core/runtime/process-tree.js +40 -0
  89. package/dist/core/runtime/prompt.d.ts +23 -0
  90. package/dist/core/runtime/prompt.d.ts.map +1 -0
  91. package/dist/core/runtime/prompt.js +29 -0
  92. package/dist/core/runtime/prompts.d.ts +16 -0
  93. package/dist/core/runtime/prompts.d.ts.map +1 -0
  94. package/dist/core/runtime/prompts.js +36 -0
  95. package/dist/core/runtime/retry.d.ts +20 -0
  96. package/dist/core/runtime/retry.d.ts.map +1 -0
  97. package/dist/core/runtime/retry.js +60 -0
  98. package/dist/core/runtime/run-runtime.d.ts +93 -0
  99. package/dist/core/runtime/run-runtime.d.ts.map +1 -0
  100. package/dist/core/runtime/run-runtime.js +664 -0
  101. package/dist/core/runtime/runtime-catalog.d.ts +12 -0
  102. package/dist/core/runtime/runtime-catalog.d.ts.map +1 -0
  103. package/dist/core/runtime/runtime-catalog.js +89 -0
  104. package/dist/core/runtime/stream-rules.d.ts +9 -0
  105. package/dist/core/runtime/stream-rules.d.ts.map +1 -0
  106. package/dist/core/runtime/stream-rules.js +44 -0
  107. package/dist/core/runtime/title.d.ts +7 -0
  108. package/dist/core/runtime/title.d.ts.map +1 -0
  109. package/dist/core/runtime/title.js +114 -0
  110. package/dist/core/runtime/todo.d.ts +16 -0
  111. package/dist/core/runtime/todo.d.ts.map +1 -0
  112. package/dist/core/runtime/todo.js +40 -0
  113. package/dist/core/runtime/tool-filter.d.ts +12 -0
  114. package/dist/core/runtime/tool-filter.d.ts.map +1 -0
  115. package/dist/core/runtime/tool-filter.js +23 -0
  116. package/dist/core/runtime/tool-pruning.d.ts +29 -0
  117. package/dist/core/runtime/tool-pruning.d.ts.map +1 -0
  118. package/dist/core/runtime/tool-pruning.js +105 -0
  119. package/dist/core/session/input-history.d.ts +8 -0
  120. package/dist/core/session/input-history.d.ts.map +1 -0
  121. package/dist/core/session/input-history.js +41 -0
  122. package/dist/core/session/session-file.d.ts +84 -0
  123. package/dist/core/session/session-file.d.ts.map +1 -0
  124. package/dist/core/session/session-file.js +392 -0
  125. package/dist/core/session/session-loop.d.ts +33 -0
  126. package/dist/core/session/session-loop.d.ts.map +1 -0
  127. package/dist/core/session/session-loop.js +30 -0
  128. package/dist/core/settings/project-settings.d.ts +46 -0
  129. package/dist/core/settings/project-settings.d.ts.map +1 -0
  130. package/dist/core/settings/project-settings.js +85 -0
  131. package/dist/core/settings/workspace-context.d.ts +10 -0
  132. package/dist/core/settings/workspace-context.d.ts.map +1 -0
  133. package/dist/core/settings/workspace-context.js +48 -0
  134. package/dist/core/tools/agent-fs-like.d.ts +15 -0
  135. package/dist/core/tools/agent-fs-like.d.ts.map +1 -0
  136. package/dist/core/tools/agent-fs-like.js +4 -0
  137. package/dist/core/tools/ask-question.d.ts +5 -0
  138. package/dist/core/tools/ask-question.d.ts.map +1 -0
  139. package/dist/core/tools/ask-question.js +51 -0
  140. package/dist/core/tools/bash.d.ts +5 -0
  141. package/dist/core/tools/bash.d.ts.map +1 -0
  142. package/dist/core/tools/bash.js +109 -0
  143. package/dist/core/tools/eval.d.ts +10 -0
  144. package/dist/core/tools/eval.d.ts.map +1 -0
  145. package/dist/core/tools/eval.js +83 -0
  146. package/dist/core/tools/file-tools.d.ts +18 -0
  147. package/dist/core/tools/file-tools.d.ts.map +1 -0
  148. package/dist/core/tools/file-tools.js +249 -0
  149. package/dist/core/tools/glob.d.ts +5 -0
  150. package/dist/core/tools/glob.d.ts.map +1 -0
  151. package/dist/core/tools/glob.js +54 -0
  152. package/dist/core/tools/grep.d.ts +5 -0
  153. package/dist/core/tools/grep.d.ts.map +1 -0
  154. package/dist/core/tools/grep.js +61 -0
  155. package/dist/core/tools/index.d.ts +14 -0
  156. package/dist/core/tools/index.d.ts.map +1 -0
  157. package/dist/core/tools/index.js +14 -0
  158. package/dist/core/tools/ls-tree.d.ts +10 -0
  159. package/dist/core/tools/ls-tree.d.ts.map +1 -0
  160. package/dist/core/tools/ls-tree.js +177 -0
  161. package/dist/core/tools/mcp-mount.d.ts +63 -0
  162. package/dist/core/tools/mcp-mount.d.ts.map +1 -0
  163. package/dist/core/tools/mcp-mount.js +287 -0
  164. package/dist/core/tools/read-image.d.ts +6 -0
  165. package/dist/core/tools/read-image.d.ts.map +1 -0
  166. package/dist/core/tools/read-image.js +98 -0
  167. package/dist/core/tools/skill.d.ts +11 -0
  168. package/dist/core/tools/skill.d.ts.map +1 -0
  169. package/dist/core/tools/skill.js +65 -0
  170. package/dist/core/tools/skills.d.ts +10 -0
  171. package/dist/core/tools/skills.d.ts.map +1 -0
  172. package/dist/core/tools/skills.js +116 -0
  173. package/dist/core/tools/todo-store.d.ts +8 -0
  174. package/dist/core/tools/todo-store.d.ts.map +1 -0
  175. package/dist/core/tools/todo-store.js +100 -0
  176. package/dist/core/tools/todo.d.ts +8 -0
  177. package/dist/core/tools/todo.d.ts.map +1 -0
  178. package/dist/core/tools/todo.js +91 -0
  179. package/dist/core/tools/url-guard.d.ts +12 -0
  180. package/dist/core/tools/url-guard.d.ts.map +1 -0
  181. package/dist/core/tools/url-guard.js +58 -0
  182. package/dist/core/tools/web-ports-std.d.ts +6 -0
  183. package/dist/core/tools/web-ports-std.d.ts.map +1 -0
  184. package/dist/core/tools/web-ports-std.js +145 -0
  185. package/dist/core/tools/web-ports.d.ts +56 -0
  186. package/dist/core/tools/web-ports.d.ts.map +1 -0
  187. package/dist/core/tools/web-ports.js +62 -0
  188. package/dist/core/tools/workspace-sandbox.d.ts +20 -0
  189. package/dist/core/tools/workspace-sandbox.d.ts.map +1 -0
  190. package/dist/core/tools/workspace-sandbox.js +84 -0
  191. package/dist/core/workflow/subagent-registry.d.ts +40 -0
  192. package/dist/core/workflow/subagent-registry.d.ts.map +1 -0
  193. package/dist/core/workflow/subagent-registry.js +118 -0
  194. package/dist/core/workflow/workflow-evaluator.d.ts +23 -0
  195. package/dist/core/workflow/workflow-evaluator.d.ts.map +1 -0
  196. package/dist/core/workflow/workflow-evaluator.js +21 -0
  197. package/dist/core/workflow/workflow-executor.d.ts +108 -0
  198. package/dist/core/workflow/workflow-executor.d.ts.map +1 -0
  199. package/dist/core/workflow/workflow-executor.js +549 -0
  200. package/dist/core/workflow/workflow-tools.d.ts +50 -0
  201. package/dist/core/workflow/workflow-tools.d.ts.map +1 -0
  202. package/dist/core/workflow/workflow-tools.js +217 -0
  203. package/dist/index.d.ts +14 -0
  204. package/dist/index.d.ts.map +1 -0
  205. package/dist/index.js +13 -0
  206. package/dist/main.d.ts +9 -0
  207. package/dist/main.d.ts.map +1 -0
  208. package/dist/main.js +97 -0
  209. package/dist/modes/json-mode.d.ts +5 -0
  210. package/dist/modes/json-mode.d.ts.map +1 -0
  211. package/dist/modes/json-mode.js +46 -0
  212. package/dist/modes/print-mode.d.ts +20 -0
  213. package/dist/modes/print-mode.d.ts.map +1 -0
  214. package/dist/modes/print-mode.js +82 -0
  215. package/dist/modes/rpc/jsonl.d.ts +23 -0
  216. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  217. package/dist/modes/rpc/jsonl.js +88 -0
  218. package/dist/modes/rpc/rpc-mode.d.ts +18 -0
  219. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
  220. package/dist/modes/rpc/rpc-mode.js +326 -0
  221. package/dist/modes/tui/settings-overlay.d.ts +23 -0
  222. package/dist/modes/tui/settings-overlay.d.ts.map +1 -0
  223. package/dist/modes/tui/settings-overlay.js +136 -0
  224. package/dist/modes/tui/tui-commands-session.d.ts +3 -0
  225. package/dist/modes/tui/tui-commands-session.d.ts.map +1 -0
  226. package/dist/modes/tui/tui-commands-session.js +191 -0
  227. package/dist/modes/tui/tui-commands.d.ts +52 -0
  228. package/dist/modes/tui/tui-commands.d.ts.map +1 -0
  229. package/dist/modes/tui/tui-commands.js +373 -0
  230. package/dist/modes/tui/tui-components.d.ts +81 -0
  231. package/dist/modes/tui/tui-components.d.ts.map +1 -0
  232. package/dist/modes/tui/tui-components.js +248 -0
  233. package/dist/modes/tui/tui-format.d.ts +65 -0
  234. package/dist/modes/tui/tui-format.d.ts.map +1 -0
  235. package/dist/modes/tui/tui-format.js +246 -0
  236. package/dist/modes/tui/tui-frame-provider.d.ts +16 -0
  237. package/dist/modes/tui/tui-frame-provider.d.ts.map +1 -0
  238. package/dist/modes/tui/tui-frame-provider.js +31 -0
  239. package/dist/modes/tui/tui-interactive.d.ts +16 -0
  240. package/dist/modes/tui/tui-interactive.d.ts.map +1 -0
  241. package/dist/modes/tui/tui-interactive.js +123 -0
  242. package/dist/modes/tui/tui-io.d.ts +12 -0
  243. package/dist/modes/tui/tui-io.d.ts.map +1 -0
  244. package/dist/modes/tui/tui-io.js +620 -0
  245. package/dist/modes/tui/tui-mode.d.ts +120 -0
  246. package/dist/modes/tui/tui-mode.d.ts.map +1 -0
  247. package/dist/modes/tui/tui-mode.js +391 -0
  248. package/dist/modes/tui/tui-render.d.ts +54 -0
  249. package/dist/modes/tui/tui-render.d.ts.map +1 -0
  250. package/dist/modes/tui/tui-render.js +399 -0
  251. package/dist/modes/tui/tui-slash.d.ts +12 -0
  252. package/dist/modes/tui/tui-slash.d.ts.map +1 -0
  253. package/dist/modes/tui/tui-slash.js +64 -0
  254. package/dist/modes/tui/tui-tool-render.d.ts +6 -0
  255. package/dist/modes/tui/tui-tool-render.d.ts.map +1 -0
  256. package/dist/modes/tui/tui-tool-render.js +79 -0
  257. package/dist/modes/tui/tui-transcript-reconciler.d.ts +25 -0
  258. package/dist/modes/tui/tui-transcript-reconciler.d.ts.map +1 -0
  259. package/dist/modes/tui/tui-transcript-reconciler.js +97 -0
  260. package/dist/modes/tui/view-state.d.ts +67 -0
  261. package/dist/modes/tui/view-state.d.ts.map +1 -0
  262. package/dist/modes/tui/view-state.js +353 -0
  263. package/dist/prompts/agents/subagent.md +26 -0
  264. package/dist/prompts/system/base.md +20 -0
  265. package/dist/prompts/system/memory.md +23 -0
  266. package/dist/prompts/system/safety.md +12 -0
  267. package/dist/protocol/index.d.ts +5 -0
  268. package/dist/protocol/index.d.ts.map +1 -0
  269. package/dist/protocol/index.js +2 -0
  270. package/dist/protocol/mapping.d.ts +25 -0
  271. package/dist/protocol/mapping.d.ts.map +1 -0
  272. package/dist/protocol/mapping.js +115 -0
  273. package/dist/protocol/transport.d.ts +1396 -0
  274. package/dist/protocol/transport.d.ts.map +1 -0
  275. package/dist/protocol/transport.js +170 -0
  276. package/package.json +49 -0
@@ -0,0 +1,61 @@
1
+ import { WorkspaceSandbox } from "./workspace-sandbox.js";
2
+ const descriptionParam = {
3
+ type: "string",
4
+ description: "Must be the first parameter. A short human-readable summary explaining why this search is being performed.",
5
+ };
6
+ export function createGrepTool(opts) {
7
+ const sandbox = new WorkspaceSandbox(opts.workspaceRoot);
8
+ return {
9
+ name: "grep",
10
+ description: "Search files using ripgrep. Returns matching lines with line numbers. Requires `rg`.",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: {
14
+ description: descriptionParam,
15
+ pattern: { type: "string", description: "The regex pattern to search for" },
16
+ path: {
17
+ type: "string",
18
+ description: "File or directory to search in (relative to workspace root)",
19
+ },
20
+ glob: { type: "string", description: "Glob pattern to filter files (e.g. '*.ts')" },
21
+ },
22
+ required: ["pattern"],
23
+ },
24
+ async execute(input) {
25
+ const { pattern, path: searchPath, glob, } = input;
26
+ let validatedPath = opts.workspaceRoot;
27
+ if (searchPath) {
28
+ try {
29
+ validatedPath = sandbox.validate(searchPath);
30
+ }
31
+ catch {
32
+ return {
33
+ content: `Error: path escapes workspace root: ${String(searchPath)}`,
34
+ isError: true,
35
+ };
36
+ }
37
+ }
38
+ const args = ["rg", "-n", "--color=never"];
39
+ if (glob)
40
+ args.push("--glob", glob);
41
+ args.push(pattern, validatedPath);
42
+ let proc;
43
+ try {
44
+ proc = Bun.spawn(args, { stdout: "pipe", stderr: "pipe" });
45
+ }
46
+ catch {
47
+ return { content: "grep failed: ripgrep not found", isError: true };
48
+ }
49
+ const [stdout, stderr, exitCode] = await Promise.all([
50
+ new Response(proc.stdout).text(),
51
+ new Response(proc.stderr).text(),
52
+ proc.exited,
53
+ ]);
54
+ if (exitCode === 1)
55
+ return { content: "" };
56
+ if (exitCode >= 2)
57
+ return { content: `grep failed (exit=${exitCode}): ${stderr}`, isError: true };
58
+ return { content: stdout };
59
+ },
60
+ };
61
+ }
@@ -0,0 +1,14 @@
1
+ export { type AgentFsLike, pjoin } from "./agent-fs-like.js";
2
+ export { createBashTool } from "./bash.js";
3
+ export { createEvalTool } from "./eval.js";
4
+ export { createEditTool, createReadTool, createWriteTool, withDefaultCwd } from "./file-tools.js";
5
+ export { createGlobTool } from "./glob.js";
6
+ export { createGrepTool } from "./grep.js";
7
+ export { createLsTool, createTreeTool } from "./ls-tree.js";
8
+ export { createReadImageTool } from "./read-image.js";
9
+ export { buildSkillIndex, type SkillIndexEntry } from "./skills.js";
10
+ export type { WebFetchPort, WebSearchPort } from "./web-ports.js";
11
+ export { createWebFetchTool as createPortWebFetchTool, createWebSearchTool as createPortWebSearchTool, } from "./web-ports.js";
12
+ export { createDdgWebSearchPort, createStdWebFetchPort, } from "./web-ports-std.js";
13
+ export { WorkspaceEscapeError, WorkspaceSandbox } from "./workspace-sandbox.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG7D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClG,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EACL,kBAAkB,IAAI,sBAAsB,EAC5C,mBAAmB,IAAI,uBAAuB,GAC/C,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,14 @@
1
+ // cwd-based tool factories (Phase 1)
2
+ export { pjoin } from "./agent-fs-like.js";
3
+ // standalone tools
4
+ export { createBashTool } from "./bash.js";
5
+ export { createEvalTool } from "./eval.js";
6
+ export { createEditTool, createReadTool, createWriteTool, withDefaultCwd } from "./file-tools.js";
7
+ export { createGlobTool } from "./glob.js";
8
+ export { createGrepTool } from "./grep.js";
9
+ export { createLsTool, createTreeTool } from "./ls-tree.js";
10
+ export { createReadImageTool } from "./read-image.js";
11
+ export { buildSkillIndex } from "./skills.js";
12
+ export { createWebFetchTool as createPortWebFetchTool, createWebSearchTool as createPortWebSearchTool, } from "./web-ports.js";
13
+ export { createDdgWebSearchPort, createStdWebFetchPort, } from "./web-ports-std.js";
14
+ export { WorkspaceEscapeError, WorkspaceSandbox } from "./workspace-sandbox.js";
@@ -0,0 +1,10 @@
1
+ import type { Tool } from "@chengchenccc/message";
2
+ /** ls tool: list directory entries sorted by mtime (newest first). */
3
+ export declare function createLsTool(opts: {
4
+ cwd: string;
5
+ }): Tool;
6
+ /** tree tool: recursive directory listing with depth control. */
7
+ export declare function createTreeTool(opts: {
8
+ cwd: string;
9
+ }): Tool;
10
+ //# sourceMappingURL=ls-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ls-tree.d.ts","sourceRoot":"","sources":["../../../src/core/tools/ls-tree.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AA0ClD,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4DxD;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAyE1D"}
@@ -0,0 +1,177 @@
1
+ import { readdirSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { WorkspaceSandbox } from "./workspace-sandbox.js";
4
+ const descriptionParam = {
5
+ type: "string",
6
+ description: "Must be the first parameter. A short human-readable summary explaining why this tool is being called.",
7
+ };
8
+ const DEFAULT_IGNORES = new Set([
9
+ "node_modules",
10
+ ".git",
11
+ ".svn",
12
+ ".hg",
13
+ ".DS_Store",
14
+ "Thumbs.db",
15
+ ".cache",
16
+ ".next",
17
+ ".nuxt",
18
+ ".turbo",
19
+ ".parcel-cache",
20
+ "dist",
21
+ "build",
22
+ "out",
23
+ "coverage",
24
+ "__pycache__",
25
+ ".pytest_cache",
26
+ ".mypy_cache",
27
+ ".venv",
28
+ "venv",
29
+ ]);
30
+ function isIgnored(name) {
31
+ return DEFAULT_IGNORES.has(name);
32
+ }
33
+ function formatSize(bytes) {
34
+ if (bytes < 1024)
35
+ return `${bytes}B`;
36
+ if (bytes < 1024 * 1024)
37
+ return `${(bytes / 1024).toFixed(1)}KB`;
38
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
39
+ }
40
+ /** ls tool: list directory entries sorted by mtime (newest first). */
41
+ export function createLsTool(opts) {
42
+ const { cwd } = opts;
43
+ const sandbox = new WorkspaceSandbox(cwd);
44
+ return {
45
+ name: "ls",
46
+ description: "List files and directories at a given path. Returns entry names sorted by modification time (newest first). " +
47
+ "Common noise directories (node_modules, .git, build output) are omitted.",
48
+ inputSchema: {
49
+ type: "object",
50
+ properties: {
51
+ description: descriptionParam,
52
+ path: {
53
+ type: "string",
54
+ description: "Directory path to list, relative to workspace root. Defaults to workspace root.",
55
+ },
56
+ },
57
+ required: [],
58
+ },
59
+ async execute(input) {
60
+ const rec = input;
61
+ let dir = cwd;
62
+ if (rec.path) {
63
+ try {
64
+ dir = sandbox.validate(rec.path);
65
+ }
66
+ catch {
67
+ return {
68
+ content: `Error: path escapes workspace root: ${String(rec.path)}`,
69
+ isError: true,
70
+ };
71
+ }
72
+ }
73
+ try {
74
+ const entries = readdirSync(dir, { withFileTypes: true })
75
+ .filter((e) => !isIgnored(e.name))
76
+ .map((e) => {
77
+ const fullPath = join(dir, e.name);
78
+ try {
79
+ const stat = statSync(fullPath);
80
+ return { name: e.name, isDir: e.isDirectory(), mtime: stat.mtimeMs, size: stat.size };
81
+ }
82
+ catch {
83
+ return { name: e.name, isDir: e.isDirectory(), mtime: 0, size: 0 };
84
+ }
85
+ })
86
+ .sort((a, b) => b.mtime - a.mtime);
87
+ if (entries.length === 0)
88
+ return { content: "(empty)" };
89
+ const lines = entries.map((e) => {
90
+ const type = e.isDir ? "dir " : "file";
91
+ const size = e.isDir ? "" : formatSize(e.size).padStart(8);
92
+ return `${type} ${size} ${e.name}`;
93
+ });
94
+ return { content: lines.join("\n") };
95
+ }
96
+ catch (err) {
97
+ return { content: `Error: ${err instanceof Error ? err.message : err}`, isError: true };
98
+ }
99
+ },
100
+ };
101
+ }
102
+ /** tree tool: recursive directory listing with depth control. */
103
+ export function createTreeTool(opts) {
104
+ const { cwd } = opts;
105
+ const sandbox = new WorkspaceSandbox(cwd);
106
+ return {
107
+ name: "tree",
108
+ description: "Show a recursive tree view of a directory. Useful for understanding project structure. " +
109
+ "Common noise directories are omitted. Use max_depth to limit recursion.",
110
+ inputSchema: {
111
+ type: "object",
112
+ properties: {
113
+ description: descriptionParam,
114
+ path: {
115
+ type: "string",
116
+ description: "Directory path, relative to workspace root. Defaults to workspace root.",
117
+ },
118
+ max_depth: {
119
+ type: "number",
120
+ description: "Maximum recursion depth. Defaults to 3.",
121
+ },
122
+ },
123
+ required: [],
124
+ },
125
+ async execute(input) {
126
+ const rec = input;
127
+ let dir = cwd;
128
+ if (rec.path) {
129
+ try {
130
+ dir = sandbox.validate(rec.path);
131
+ }
132
+ catch {
133
+ return {
134
+ content: `Error: path escapes workspace root: ${String(rec.path)}`,
135
+ isError: true,
136
+ };
137
+ }
138
+ }
139
+ const maxDepth = rec.max_depth ?? 3;
140
+ const lines = [];
141
+ function walk(currentDir, prefix, depth) {
142
+ if (depth > maxDepth)
143
+ return;
144
+ let entries;
145
+ try {
146
+ entries = readdirSync(currentDir, { withFileTypes: true })
147
+ .filter((e) => !isIgnored(e.name))
148
+ .sort((a, b) => {
149
+ if (a.isDirectory() !== b.isDirectory())
150
+ return a.isDirectory() ? -1 : 1;
151
+ return a.name.localeCompare(b.name);
152
+ });
153
+ }
154
+ catch {
155
+ return;
156
+ }
157
+ for (let i = 0; i < entries.length; i++) {
158
+ const e = entries[i];
159
+ const isLast = i === entries.length - 1;
160
+ const connector = isLast ? "└── " : "├── ";
161
+ lines.push(`${prefix}${connector}${e.name}${e.isDirectory() ? "/" : ""}`);
162
+ if (e.isDirectory()) {
163
+ walk(join(currentDir, e.name), prefix + (isLast ? " " : "│ "), depth + 1);
164
+ }
165
+ }
166
+ }
167
+ try {
168
+ lines.push(rec.path ?? ".");
169
+ walk(dir, "", 1);
170
+ return { content: lines.join("\n") };
171
+ }
172
+ catch (err) {
173
+ return { content: `Error: ${err instanceof Error ? err.message : err}`, isError: true };
174
+ }
175
+ },
176
+ };
177
+ }
@@ -0,0 +1,63 @@
1
+ import type { PluginTool } from "../agent-runtime.js";
2
+ import type { PluginMcpConfig } from "../plugins/plugin-resolve.js";
3
+ /** Generic .mcp.json mounting (ADR 0022): the workspace bridge writes one
4
+ * .mcp.json (user servers + product-tools + knowledge); the child mounts
5
+ * every server. Tool names that collide with the native tool table are
6
+ * skipped (the native table wins). One client per server, kept alive for
7
+ * the run. */
8
+ interface McpJsonServer {
9
+ command?: string;
10
+ args?: string[];
11
+ env?: Record<string, string>;
12
+ url?: string;
13
+ headers?: Record<string, string>;
14
+ }
15
+ /** One configured server as a listing row (pi's /mcp list). */
16
+ export interface McpServerInfo {
17
+ name: string;
18
+ kind: "stdio" | "url" | "invalid";
19
+ detail: string;
20
+ }
21
+ /** Configured servers from the workspace .mcp.json, sorted by name.
22
+ * Never throws — a corrupt file yields []. */
23
+ export declare function listMcpServers(workspaceRoot: string): McpServerInfo[];
24
+ /** Live-connect one configured server and list its tools (pi's /mcp test).
25
+ * Reports failure reasons; never throws.
26
+ * ponytail: a connect that hangs past the timeout leaves the stdio child
27
+ * running (connectServer exposes no pid) — rare in practice, pkill by
28
+ * command if it bites. */
29
+ export declare function testMcpServer(workspaceRoot: string, name: string): Promise<{
30
+ ok: boolean;
31
+ tools: string[];
32
+ error?: string;
33
+ }>;
34
+ /** Per-call timeout for mounted MCP tools (ms). A hung server must never
35
+ * block the Run forever — product tools already bound theirs. Default 120s
36
+ * (MCP tools legitimately run longer than file tools); OMA_MCP_TIMEOUT_MS
37
+ * overrides, 0 disables. */
38
+ export declare function mcpCallTimeoutMs(): number;
39
+ /** Race a tool call against a wall-clock timeout and the run's abort
40
+ * signal. The losing call keeps running server-side (the MCP SDK's
41
+ * callTool takes no signal) but its result lands nowhere. The Bun timer
42
+ * MUST be cleared or the process lingers — hence the finally. */
43
+ export declare function withCallTimeout<T>(call: Promise<T>, label: string, timeoutMs: number, signal?: AbortSignal): Promise<T>;
44
+ /** Mount the workspace .mcp.json servers as plugin tools. Never throws;
45
+ * per-server failures degrade to "server absent". */
46
+ export interface MountedMcpServers {
47
+ tools: PluginTool[];
48
+ /** Close every mounted server's transport (kills stdio children). */
49
+ close(): Promise<void>;
50
+ }
51
+ export declare function mountWorkspaceMcpServers(workspaceRoot: string, nativeNames: ReadonlySet<string>, pluginServers?: readonly PluginMcpConfig[], includeWorkspace?: boolean): Promise<MountedMcpServers>;
52
+ /** Expand ${CLAUDE_PLUGIN_ROOT}/${CLAUDE_PROJECT_DIR} in a plugin server
53
+ * config (spec: Claude plugin .mcp.json compatibility) and export both as
54
+ * env vars to the spawned server process. */
55
+ export declare function substitutePluginVars(server: McpJsonServer, ctx: {
56
+ pluginRoot: string;
57
+ workspaceRoot: string;
58
+ }): McpJsonServer;
59
+ /** Workspace .mcp.json wins on name conflicts (spec conflict matrix);
60
+ * plugin servers keep resolver order otherwise. */
61
+ export declare function mergeMcpConfigs(workspaceRoot: string, plugins: readonly PluginMcpConfig[]): Record<string, McpJsonServer>;
62
+ export {};
63
+ //# sourceMappingURL=mcp-mount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-mount.d.ts","sourceRoot":"","sources":["../../../src/core/tools/mcp-mount.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAGpE;;;;eAIe;AAEf,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AA8FD,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;+CAC+C;AAC/C,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,EAAE,CAerE;AAID;;;;2BAI2B;AAC3B,wBAAsB,aAAa,CACjC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmC3D;AAED;;;4BAG4B;AAC5B,wBAAgB,gBAAgB,IAAI,MAAM,CAQzC;AAED;;;iEAGiE;AACjE,wBAAsB,eAAe,CAAC,CAAC,EACrC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CAkBZ;AAED;sDACsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,qEAAqE;IACrE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,GAAE,SAAS,eAAe,EAAO,EAC9C,gBAAgB,UAAO,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAkD5B;AAED;;8CAE8C;AAC9C,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACjD,aAAa,CAqBf;AAED;oDACoD;AACpD,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAa/B"}
@@ -0,0 +1,287 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { killProcessTree } from "../runtime/process-tree.js";
4
+ function loadMcpConfig(workspaceRoot) {
5
+ const path = join(workspaceRoot, ".mcp.json");
6
+ if (!existsSync(path))
7
+ return {};
8
+ try {
9
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
10
+ return parsed.mcpServers ?? {};
11
+ }
12
+ catch {
13
+ return {};
14
+ }
15
+ }
16
+ /** Expand `${VAR}` placeholders in .mcp.json headers/env from process.env. */
17
+ function expandEnvVars(value) {
18
+ return value.replace(/\$\{([A-Z_][A-Z0-9_]*)\}/g, (_, name) => process.env[name] ?? "");
19
+ }
20
+ async function connectServer(name, server) {
21
+ try {
22
+ const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
23
+ let stdioRootPid = null;
24
+ let closeSdk;
25
+ let callTool;
26
+ let listTools;
27
+ if (server.url) {
28
+ const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
29
+ const headers = server.headers
30
+ ? Object.fromEntries(Object.entries(server.headers).map(([k, v]) => [k, expandEnvVars(v)]))
31
+ : undefined;
32
+ const transport = new SSEClientTransport(new URL(server.url), headers ? { requestInit: { headers } } : undefined);
33
+ const client = new Client({ name: "oma", version: "0.1.0" }, { capabilities: {} });
34
+ await client.connect(transport);
35
+ callTool = (params) =>
36
+ // MCP wire boundary: the SDK returns a wide content union; our
37
+ // consumer only reads text blocks and isError.
38
+ client.callTool(params);
39
+ listTools = () => client.listTools();
40
+ closeSdk = () => client.close();
41
+ }
42
+ else if (server.command) {
43
+ const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
44
+ const env = server.env
45
+ ? Object.fromEntries(Object.entries(server.env).map(([k, v]) => [k, expandEnvVars(v)]))
46
+ : undefined;
47
+ const transport = new StdioClientTransport({
48
+ command: server.command,
49
+ args: server.args ?? [],
50
+ env,
51
+ });
52
+ const client = new Client({ name: "oma", version: "0.1.0" }, { capabilities: {} });
53
+ await client.connect(transport);
54
+ stdioRootPid = transport.pid;
55
+ callTool = (params) =>
56
+ // MCP wire boundary: see the sse branch above.
57
+ client.callTool(params);
58
+ listTools = () => client.listTools();
59
+ closeSdk = () => client.close();
60
+ }
61
+ else {
62
+ return null;
63
+ }
64
+ return {
65
+ callTool,
66
+ listTools,
67
+ async close() {
68
+ await closeSdk();
69
+ if (stdioRootPid !== null)
70
+ killProcessTree(stdioRootPid);
71
+ },
72
+ };
73
+ }
74
+ catch (err) {
75
+ console.error(`[mcp-mount] server ${name} failed:`, err instanceof Error ? err.message : err);
76
+ return null;
77
+ }
78
+ }
79
+ /** Configured servers from the workspace .mcp.json, sorted by name.
80
+ * Never throws — a corrupt file yields []. */
81
+ export function listMcpServers(workspaceRoot) {
82
+ const servers = loadMcpConfig(workspaceRoot);
83
+ return Object.entries(servers)
84
+ .sort(([a], [b]) => a.localeCompare(b))
85
+ .map(([name, server]) => {
86
+ if (server.url)
87
+ return { name, kind: "url", detail: server.url };
88
+ if (server.command) {
89
+ return {
90
+ name,
91
+ kind: "stdio",
92
+ detail: [server.command, ...(server.args ?? [])].join(" "),
93
+ };
94
+ }
95
+ return { name, kind: "invalid", detail: "no command or url" };
96
+ });
97
+ }
98
+ const MCP_TEST_TIMEOUT_MS = 10_000;
99
+ /** Live-connect one configured server and list its tools (pi's /mcp test).
100
+ * Reports failure reasons; never throws.
101
+ * ponytail: a connect that hangs past the timeout leaves the stdio child
102
+ * running (connectServer exposes no pid) — rare in practice, pkill by
103
+ * command if it bites. */
104
+ export async function testMcpServer(workspaceRoot, name) {
105
+ const servers = loadMcpConfig(workspaceRoot);
106
+ const server = servers[name];
107
+ if (!server) {
108
+ return { ok: false, tools: [], error: `no server named "${name}" in .mcp.json` };
109
+ }
110
+ let timer;
111
+ const timeout = new Promise((resolve) => {
112
+ timer = setTimeout(() => resolve(null), MCP_TEST_TIMEOUT_MS);
113
+ });
114
+ let client;
115
+ try {
116
+ client = await Promise.race([connectServer(name, server), timeout]);
117
+ }
118
+ finally {
119
+ clearTimeout(timer);
120
+ }
121
+ if (!client) {
122
+ return {
123
+ ok: false,
124
+ tools: [],
125
+ error: `connect failed or timed out (${MCP_TEST_TIMEOUT_MS / 1000}s)`,
126
+ };
127
+ }
128
+ try {
129
+ const { tools } = await client.listTools();
130
+ return { ok: true, tools: tools.map((t) => t.name) };
131
+ }
132
+ catch (err) {
133
+ return {
134
+ ok: false,
135
+ tools: [],
136
+ error: err instanceof Error ? err.message : String(err),
137
+ };
138
+ }
139
+ finally {
140
+ await client.close().catch(() => { });
141
+ }
142
+ }
143
+ /** Per-call timeout for mounted MCP tools (ms). A hung server must never
144
+ * block the Run forever — product tools already bound theirs. Default 120s
145
+ * (MCP tools legitimately run longer than file tools); OMA_MCP_TIMEOUT_MS
146
+ * overrides, 0 disables. */
147
+ export function mcpCallTimeoutMs() {
148
+ const raw = process.env.OMA_MCP_TIMEOUT_MS;
149
+ let n = raw === undefined ? 120_000 : Number(raw);
150
+ if (!Number.isFinite(n))
151
+ n = 120_000;
152
+ const capRaw = process.env.OMA_MAX_TOOL_TIMEOUT_MS;
153
+ const cap = capRaw ? Number(capRaw) : 0;
154
+ if (Number.isFinite(cap) && cap > 0)
155
+ n = Math.min(n, cap);
156
+ return n;
157
+ }
158
+ /** Race a tool call against a wall-clock timeout and the run's abort
159
+ * signal. The losing call keeps running server-side (the MCP SDK's
160
+ * callTool takes no signal) but its result lands nowhere. The Bun timer
161
+ * MUST be cleared or the process lingers — hence the finally. */
162
+ export async function withCallTimeout(call, label, timeoutMs, signal) {
163
+ if (signal?.aborted)
164
+ throw new Error("Aborted");
165
+ if (timeoutMs <= 0 && !signal)
166
+ return call;
167
+ let timer;
168
+ const bail = new Promise((_, reject) => {
169
+ if (timeoutMs > 0) {
170
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
171
+ }
172
+ signal?.addEventListener("abort", () => reject(new Error("Aborted")), { once: true });
173
+ });
174
+ try {
175
+ return await Promise.race([call, bail]);
176
+ }
177
+ finally {
178
+ clearTimeout(timer);
179
+ }
180
+ }
181
+ export async function mountWorkspaceMcpServers(workspaceRoot, nativeNames, pluginServers = [], includeWorkspace = true) {
182
+ const servers = includeWorkspace
183
+ ? mergeMcpConfigs(workspaceRoot, pluginServers)
184
+ : mergePluginMcpConfigs(pluginServers);
185
+ const tools = [];
186
+ const clients = [];
187
+ for (const [name, server] of Object.entries(servers)) {
188
+ const client = await connectServer(name, server);
189
+ if (!client)
190
+ continue;
191
+ clients.push(client);
192
+ let listed;
193
+ try {
194
+ listed = (await client.listTools()).tools;
195
+ }
196
+ catch (err) {
197
+ console.error(`[mcp-mount] listTools ${name} failed:`, err instanceof Error ? err.message : err);
198
+ continue;
199
+ }
200
+ for (const t of listed) {
201
+ if (nativeNames.has(t.name))
202
+ continue; // native table wins on collision
203
+ tools.push({
204
+ name: t.name,
205
+ description: t.description ?? `MCP tool ${t.name} (server ${name})`,
206
+ inputSchema: (t.inputSchema ?? { type: "object" }),
207
+ timeoutMs: mcpCallTimeoutMs(),
208
+ async execute(args, signal) {
209
+ const res = await withCallTimeout(client.callTool({ name: t.name, arguments: args }), `mcp tool ${t.name}`, mcpCallTimeoutMs(), signal);
210
+ const text = (res.content ?? [])
211
+ .filter((c) => c.type === "text" && c.text)
212
+ .map((c) => c.text)
213
+ .join("\n");
214
+ if (res.isError)
215
+ throw new Error(text || `mcp tool ${t.name} failed`);
216
+ return { content: text };
217
+ },
218
+ });
219
+ }
220
+ }
221
+ return {
222
+ tools,
223
+ async close() {
224
+ await Promise.allSettled(clients.map((c) => c.close().catch(() => { })));
225
+ },
226
+ };
227
+ }
228
+ /** Expand ${CLAUDE_PLUGIN_ROOT}/${CLAUDE_PROJECT_DIR} in a plugin server
229
+ * config (spec: Claude plugin .mcp.json compatibility) and export both as
230
+ * env vars to the spawned server process. */
231
+ export function substitutePluginVars(server, ctx) {
232
+ const sub = (s) => s
233
+ .replaceAll("${CLAUDE_PLUGIN_ROOT}", ctx.pluginRoot)
234
+ .replaceAll("${CLAUDE_PROJECT_DIR}", ctx.workspaceRoot);
235
+ const out = { ...server };
236
+ if (server.command)
237
+ out.command = sub(server.command);
238
+ if (server.args)
239
+ out.args = server.args.map(sub);
240
+ if (server.env) {
241
+ const env = {};
242
+ for (const [k, v] of Object.entries(server.env))
243
+ env[k] = sub(String(v));
244
+ env.CLAUDE_PLUGIN_ROOT = ctx.pluginRoot;
245
+ env.CLAUDE_PROJECT_DIR = ctx.workspaceRoot;
246
+ out.env = env;
247
+ }
248
+ if (server.headers) {
249
+ const headers = {};
250
+ for (const [k, v] of Object.entries(server.headers))
251
+ headers[k] = sub(String(v));
252
+ out.headers = headers;
253
+ }
254
+ return out;
255
+ }
256
+ /** Workspace .mcp.json wins on name conflicts (spec conflict matrix);
257
+ * plugin servers keep resolver order otherwise. */
258
+ export function mergeMcpConfigs(workspaceRoot, plugins) {
259
+ const merged = loadMcpConfig(workspaceRoot);
260
+ for (const p of plugins) {
261
+ for (const [name, raw] of Object.entries(p.servers)) {
262
+ if (name in merged)
263
+ continue;
264
+ if (typeof raw !== "object" || raw === null)
265
+ continue;
266
+ merged[name] = substitutePluginVars(raw, {
267
+ pluginRoot: p.pluginRoot,
268
+ workspaceRoot,
269
+ });
270
+ }
271
+ }
272
+ return merged;
273
+ }
274
+ /** Plugin servers only (workspace .mcp.json gated off — standalone trust). */
275
+ function mergePluginMcpConfigs(plugins) {
276
+ const merged = {};
277
+ for (const p of plugins) {
278
+ for (const [name, raw] of Object.entries(p.servers)) {
279
+ if (name in merged)
280
+ continue;
281
+ if (typeof raw !== "object" || raw === null)
282
+ continue;
283
+ merged[name] = raw;
284
+ }
285
+ }
286
+ return merged;
287
+ }
@@ -0,0 +1,6 @@
1
+ import type { Tool } from "@chengchenccc/message";
2
+ /** read_image: return a workspace image as a vision tool result block. */
3
+ export declare function createReadImageTool(opts: {
4
+ cwd: string;
5
+ }): Tool;
6
+ //# sourceMappingURL=read-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-image.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read-image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,uBAAuB,CAAC;AA0BrE,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAoE/D"}