@oh-my-pi/pi-coding-agent 0.1.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 (337) hide show
  1. package/CHANGELOG.md +1629 -0
  2. package/README.md +1041 -0
  3. package/docs/compaction.md +403 -0
  4. package/docs/config-usage.md +113 -0
  5. package/docs/custom-tools.md +541 -0
  6. package/docs/extension-loading.md +1004 -0
  7. package/docs/hooks.md +867 -0
  8. package/docs/rpc.md +1040 -0
  9. package/docs/sdk.md +994 -0
  10. package/docs/session-tree-plan.md +441 -0
  11. package/docs/session.md +240 -0
  12. package/docs/skills.md +290 -0
  13. package/docs/theme.md +670 -0
  14. package/docs/tree.md +197 -0
  15. package/docs/tui.md +341 -0
  16. package/examples/README.md +21 -0
  17. package/examples/custom-tools/README.md +124 -0
  18. package/examples/custom-tools/hello/index.ts +20 -0
  19. package/examples/custom-tools/question/index.ts +84 -0
  20. package/examples/custom-tools/subagent/README.md +172 -0
  21. package/examples/custom-tools/subagent/agents/planner.md +37 -0
  22. package/examples/custom-tools/subagent/agents/scout.md +50 -0
  23. package/examples/custom-tools/subagent/agents/worker.md +24 -0
  24. package/examples/custom-tools/subagent/agents.ts +156 -0
  25. package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
  26. package/examples/custom-tools/subagent/commands/implement.md +10 -0
  27. package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
  28. package/examples/custom-tools/subagent/index.ts +1002 -0
  29. package/examples/custom-tools/todo/index.ts +212 -0
  30. package/examples/hooks/README.md +56 -0
  31. package/examples/hooks/auto-commit-on-exit.ts +49 -0
  32. package/examples/hooks/confirm-destructive.ts +59 -0
  33. package/examples/hooks/custom-compaction.ts +116 -0
  34. package/examples/hooks/dirty-repo-guard.ts +52 -0
  35. package/examples/hooks/file-trigger.ts +41 -0
  36. package/examples/hooks/git-checkpoint.ts +53 -0
  37. package/examples/hooks/handoff.ts +150 -0
  38. package/examples/hooks/permission-gate.ts +34 -0
  39. package/examples/hooks/protected-paths.ts +30 -0
  40. package/examples/hooks/qna.ts +119 -0
  41. package/examples/hooks/snake.ts +343 -0
  42. package/examples/hooks/status-line.ts +40 -0
  43. package/examples/sdk/01-minimal.ts +22 -0
  44. package/examples/sdk/02-custom-model.ts +49 -0
  45. package/examples/sdk/03-custom-prompt.ts +44 -0
  46. package/examples/sdk/04-skills.ts +44 -0
  47. package/examples/sdk/05-tools.ts +90 -0
  48. package/examples/sdk/06-hooks.ts +61 -0
  49. package/examples/sdk/07-context-files.ts +36 -0
  50. package/examples/sdk/08-slash-commands.ts +42 -0
  51. package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
  52. package/examples/sdk/10-settings.ts +38 -0
  53. package/examples/sdk/11-sessions.ts +48 -0
  54. package/examples/sdk/12-full-control.ts +95 -0
  55. package/examples/sdk/README.md +154 -0
  56. package/package.json +89 -0
  57. package/src/bun-imports.d.ts +16 -0
  58. package/src/capability/context-file.ts +40 -0
  59. package/src/capability/extension.ts +48 -0
  60. package/src/capability/hook.ts +40 -0
  61. package/src/capability/index.ts +616 -0
  62. package/src/capability/instruction.ts +37 -0
  63. package/src/capability/mcp.ts +52 -0
  64. package/src/capability/prompt.ts +35 -0
  65. package/src/capability/rule.ts +56 -0
  66. package/src/capability/settings.ts +35 -0
  67. package/src/capability/skill.ts +49 -0
  68. package/src/capability/slash-command.ts +40 -0
  69. package/src/capability/system-prompt.ts +35 -0
  70. package/src/capability/tool.ts +38 -0
  71. package/src/capability/types.ts +166 -0
  72. package/src/cli/args.ts +259 -0
  73. package/src/cli/file-processor.ts +121 -0
  74. package/src/cli/list-models.ts +104 -0
  75. package/src/cli/plugin-cli.ts +661 -0
  76. package/src/cli/session-picker.ts +41 -0
  77. package/src/cli/update-cli.ts +274 -0
  78. package/src/cli.ts +10 -0
  79. package/src/config.ts +391 -0
  80. package/src/core/agent-session.ts +2178 -0
  81. package/src/core/auth-storage.ts +258 -0
  82. package/src/core/bash-executor.ts +197 -0
  83. package/src/core/compaction/branch-summarization.ts +315 -0
  84. package/src/core/compaction/compaction.ts +664 -0
  85. package/src/core/compaction/index.ts +7 -0
  86. package/src/core/compaction/utils.ts +153 -0
  87. package/src/core/custom-commands/bundled/review/index.ts +156 -0
  88. package/src/core/custom-commands/index.ts +15 -0
  89. package/src/core/custom-commands/loader.ts +226 -0
  90. package/src/core/custom-commands/types.ts +112 -0
  91. package/src/core/custom-tools/index.ts +22 -0
  92. package/src/core/custom-tools/loader.ts +248 -0
  93. package/src/core/custom-tools/types.ts +185 -0
  94. package/src/core/custom-tools/wrapper.ts +29 -0
  95. package/src/core/exec.ts +139 -0
  96. package/src/core/export-html/index.ts +159 -0
  97. package/src/core/export-html/template.css +774 -0
  98. package/src/core/export-html/template.generated.ts +2 -0
  99. package/src/core/export-html/template.html +45 -0
  100. package/src/core/export-html/template.js +1185 -0
  101. package/src/core/export-html/template.macro.ts +24 -0
  102. package/src/core/file-mentions.ts +54 -0
  103. package/src/core/hooks/index.ts +16 -0
  104. package/src/core/hooks/loader.ts +288 -0
  105. package/src/core/hooks/runner.ts +434 -0
  106. package/src/core/hooks/tool-wrapper.ts +98 -0
  107. package/src/core/hooks/types.ts +770 -0
  108. package/src/core/index.ts +53 -0
  109. package/src/core/logger.ts +112 -0
  110. package/src/core/mcp/client.ts +185 -0
  111. package/src/core/mcp/config.ts +248 -0
  112. package/src/core/mcp/index.ts +45 -0
  113. package/src/core/mcp/loader.ts +99 -0
  114. package/src/core/mcp/manager.ts +235 -0
  115. package/src/core/mcp/tool-bridge.ts +156 -0
  116. package/src/core/mcp/transports/http.ts +316 -0
  117. package/src/core/mcp/transports/index.ts +6 -0
  118. package/src/core/mcp/transports/stdio.ts +252 -0
  119. package/src/core/mcp/types.ts +228 -0
  120. package/src/core/messages.ts +211 -0
  121. package/src/core/model-registry.ts +334 -0
  122. package/src/core/model-resolver.ts +494 -0
  123. package/src/core/plugins/doctor.ts +67 -0
  124. package/src/core/plugins/index.ts +38 -0
  125. package/src/core/plugins/installer.ts +189 -0
  126. package/src/core/plugins/loader.ts +339 -0
  127. package/src/core/plugins/manager.ts +672 -0
  128. package/src/core/plugins/parser.ts +105 -0
  129. package/src/core/plugins/paths.ts +37 -0
  130. package/src/core/plugins/types.ts +190 -0
  131. package/src/core/sdk.ts +900 -0
  132. package/src/core/session-manager.ts +1837 -0
  133. package/src/core/settings-manager.ts +860 -0
  134. package/src/core/skills.ts +352 -0
  135. package/src/core/slash-commands.ts +132 -0
  136. package/src/core/system-prompt.ts +442 -0
  137. package/src/core/timings.ts +25 -0
  138. package/src/core/title-generator.ts +110 -0
  139. package/src/core/tools/ask.ts +193 -0
  140. package/src/core/tools/bash-interceptor.ts +120 -0
  141. package/src/core/tools/bash.ts +91 -0
  142. package/src/core/tools/context.ts +32 -0
  143. package/src/core/tools/edit-diff.ts +487 -0
  144. package/src/core/tools/edit.ts +140 -0
  145. package/src/core/tools/exa/company.ts +59 -0
  146. package/src/core/tools/exa/index.ts +63 -0
  147. package/src/core/tools/exa/linkedin.ts +59 -0
  148. package/src/core/tools/exa/mcp-client.ts +368 -0
  149. package/src/core/tools/exa/render.ts +200 -0
  150. package/src/core/tools/exa/researcher.ts +90 -0
  151. package/src/core/tools/exa/search.ts +338 -0
  152. package/src/core/tools/exa/types.ts +167 -0
  153. package/src/core/tools/exa/websets.ts +248 -0
  154. package/src/core/tools/find.ts +244 -0
  155. package/src/core/tools/grep.ts +584 -0
  156. package/src/core/tools/index.ts +283 -0
  157. package/src/core/tools/ls.ts +142 -0
  158. package/src/core/tools/lsp/client.ts +767 -0
  159. package/src/core/tools/lsp/clients/biome-client.ts +207 -0
  160. package/src/core/tools/lsp/clients/index.ts +49 -0
  161. package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
  162. package/src/core/tools/lsp/config.ts +845 -0
  163. package/src/core/tools/lsp/edits.ts +110 -0
  164. package/src/core/tools/lsp/index.ts +1364 -0
  165. package/src/core/tools/lsp/render.ts +560 -0
  166. package/src/core/tools/lsp/rust-analyzer.ts +145 -0
  167. package/src/core/tools/lsp/types.ts +495 -0
  168. package/src/core/tools/lsp/utils.ts +526 -0
  169. package/src/core/tools/notebook.ts +182 -0
  170. package/src/core/tools/output.ts +198 -0
  171. package/src/core/tools/path-utils.ts +61 -0
  172. package/src/core/tools/read.ts +507 -0
  173. package/src/core/tools/renderers.ts +820 -0
  174. package/src/core/tools/review.ts +275 -0
  175. package/src/core/tools/rulebook.ts +124 -0
  176. package/src/core/tools/task/agents.ts +158 -0
  177. package/src/core/tools/task/artifacts.ts +114 -0
  178. package/src/core/tools/task/commands.ts +157 -0
  179. package/src/core/tools/task/discovery.ts +217 -0
  180. package/src/core/tools/task/executor.ts +531 -0
  181. package/src/core/tools/task/index.ts +548 -0
  182. package/src/core/tools/task/model-resolver.ts +176 -0
  183. package/src/core/tools/task/parallel.ts +38 -0
  184. package/src/core/tools/task/render.ts +502 -0
  185. package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
  186. package/src/core/tools/task/types.ts +142 -0
  187. package/src/core/tools/truncate.ts +265 -0
  188. package/src/core/tools/web-fetch.ts +2511 -0
  189. package/src/core/tools/web-search/auth.ts +199 -0
  190. package/src/core/tools/web-search/index.ts +583 -0
  191. package/src/core/tools/web-search/providers/anthropic.ts +198 -0
  192. package/src/core/tools/web-search/providers/exa.ts +196 -0
  193. package/src/core/tools/web-search/providers/perplexity.ts +195 -0
  194. package/src/core/tools/web-search/render.ts +372 -0
  195. package/src/core/tools/web-search/types.ts +180 -0
  196. package/src/core/tools/write.ts +63 -0
  197. package/src/core/ttsr.ts +211 -0
  198. package/src/core/utils.ts +187 -0
  199. package/src/discovery/agents-md.ts +75 -0
  200. package/src/discovery/builtin.ts +647 -0
  201. package/src/discovery/claude.ts +623 -0
  202. package/src/discovery/cline.ts +104 -0
  203. package/src/discovery/codex.ts +571 -0
  204. package/src/discovery/cursor.ts +266 -0
  205. package/src/discovery/gemini.ts +368 -0
  206. package/src/discovery/github.ts +120 -0
  207. package/src/discovery/helpers.test.ts +127 -0
  208. package/src/discovery/helpers.ts +249 -0
  209. package/src/discovery/index.ts +84 -0
  210. package/src/discovery/mcp-json.ts +127 -0
  211. package/src/discovery/vscode.ts +99 -0
  212. package/src/discovery/windsurf.ts +219 -0
  213. package/src/index.ts +192 -0
  214. package/src/main.ts +507 -0
  215. package/src/migrations.ts +156 -0
  216. package/src/modes/cleanup.ts +23 -0
  217. package/src/modes/index.ts +48 -0
  218. package/src/modes/interactive/components/armin.ts +382 -0
  219. package/src/modes/interactive/components/assistant-message.ts +86 -0
  220. package/src/modes/interactive/components/bash-execution.ts +199 -0
  221. package/src/modes/interactive/components/bordered-loader.ts +41 -0
  222. package/src/modes/interactive/components/branch-summary-message.ts +42 -0
  223. package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
  224. package/src/modes/interactive/components/custom-editor.ts +122 -0
  225. package/src/modes/interactive/components/diff.ts +147 -0
  226. package/src/modes/interactive/components/dynamic-border.ts +25 -0
  227. package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
  228. package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
  229. package/src/modes/interactive/components/extensions/index.ts +9 -0
  230. package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
  231. package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
  232. package/src/modes/interactive/components/extensions/types.ts +191 -0
  233. package/src/modes/interactive/components/hook-editor.ts +117 -0
  234. package/src/modes/interactive/components/hook-input.ts +64 -0
  235. package/src/modes/interactive/components/hook-message.ts +96 -0
  236. package/src/modes/interactive/components/hook-selector.ts +91 -0
  237. package/src/modes/interactive/components/model-selector.ts +560 -0
  238. package/src/modes/interactive/components/oauth-selector.ts +136 -0
  239. package/src/modes/interactive/components/plugin-settings.ts +481 -0
  240. package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
  241. package/src/modes/interactive/components/session-selector.ts +220 -0
  242. package/src/modes/interactive/components/settings-defs.ts +597 -0
  243. package/src/modes/interactive/components/settings-selector.ts +545 -0
  244. package/src/modes/interactive/components/show-images-selector.ts +45 -0
  245. package/src/modes/interactive/components/status-line/index.ts +4 -0
  246. package/src/modes/interactive/components/status-line/presets.ts +94 -0
  247. package/src/modes/interactive/components/status-line/segments.ts +350 -0
  248. package/src/modes/interactive/components/status-line/separators.ts +55 -0
  249. package/src/modes/interactive/components/status-line/types.ts +81 -0
  250. package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
  251. package/src/modes/interactive/components/status-line.ts +384 -0
  252. package/src/modes/interactive/components/theme-selector.ts +62 -0
  253. package/src/modes/interactive/components/thinking-selector.ts +64 -0
  254. package/src/modes/interactive/components/tool-execution.ts +946 -0
  255. package/src/modes/interactive/components/tree-selector.ts +877 -0
  256. package/src/modes/interactive/components/ttsr-notification.ts +82 -0
  257. package/src/modes/interactive/components/user-message-selector.ts +159 -0
  258. package/src/modes/interactive/components/user-message.ts +18 -0
  259. package/src/modes/interactive/components/visual-truncate.ts +50 -0
  260. package/src/modes/interactive/components/welcome.ts +228 -0
  261. package/src/modes/interactive/interactive-mode.ts +2669 -0
  262. package/src/modes/interactive/theme/dark.json +102 -0
  263. package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
  264. package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
  265. package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
  266. package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
  267. package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
  268. package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
  269. package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
  270. package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
  271. package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
  272. package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
  273. package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
  274. package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
  275. package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
  276. package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
  277. package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
  278. package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
  279. package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
  280. package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
  281. package/src/modes/interactive/theme/defaults/index.ts +67 -0
  282. package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
  283. package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
  284. package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
  285. package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
  286. package/src/modes/interactive/theme/defaults/light-github.json +114 -0
  287. package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
  288. package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
  289. package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
  290. package/src/modes/interactive/theme/defaults/light-one.json +105 -0
  291. package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
  292. package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
  293. package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
  294. package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
  295. package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
  296. package/src/modes/interactive/theme/light.json +99 -0
  297. package/src/modes/interactive/theme/theme-schema.json +424 -0
  298. package/src/modes/interactive/theme/theme.ts +2211 -0
  299. package/src/modes/print-mode.ts +163 -0
  300. package/src/modes/rpc/rpc-client.ts +527 -0
  301. package/src/modes/rpc/rpc-mode.ts +494 -0
  302. package/src/modes/rpc/rpc-types.ts +203 -0
  303. package/src/prompts/architect-plan.md +10 -0
  304. package/src/prompts/branch-summary-preamble.md +3 -0
  305. package/src/prompts/branch-summary.md +28 -0
  306. package/src/prompts/browser.md +71 -0
  307. package/src/prompts/compaction-summary.md +34 -0
  308. package/src/prompts/compaction-turn-prefix.md +16 -0
  309. package/src/prompts/compaction-update-summary.md +41 -0
  310. package/src/prompts/explore.md +82 -0
  311. package/src/prompts/implement-with-critic.md +11 -0
  312. package/src/prompts/implement.md +11 -0
  313. package/src/prompts/init.md +30 -0
  314. package/src/prompts/plan.md +54 -0
  315. package/src/prompts/reviewer.md +81 -0
  316. package/src/prompts/summarization-system.md +3 -0
  317. package/src/prompts/system-prompt.md +27 -0
  318. package/src/prompts/task.md +56 -0
  319. package/src/prompts/title-system.md +8 -0
  320. package/src/prompts/tools/ask.md +24 -0
  321. package/src/prompts/tools/bash.md +23 -0
  322. package/src/prompts/tools/edit.md +9 -0
  323. package/src/prompts/tools/find.md +6 -0
  324. package/src/prompts/tools/grep.md +12 -0
  325. package/src/prompts/tools/lsp.md +14 -0
  326. package/src/prompts/tools/output.md +23 -0
  327. package/src/prompts/tools/read.md +25 -0
  328. package/src/prompts/tools/web-fetch.md +8 -0
  329. package/src/prompts/tools/web-search.md +10 -0
  330. package/src/prompts/tools/write.md +10 -0
  331. package/src/utils/changelog.ts +99 -0
  332. package/src/utils/clipboard.ts +265 -0
  333. package/src/utils/fuzzy.ts +108 -0
  334. package/src/utils/mime.ts +30 -0
  335. package/src/utils/shell-snapshot.ts +218 -0
  336. package/src/utils/shell.ts +364 -0
  337. package/src/utils/tools-manager.ts +265 -0
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Exa Researcher Tools
3
+ *
4
+ * Async research tasks with polling for completion.
5
+ */
6
+
7
+ import { Type } from "@sinclair/typebox";
8
+ import type { CustomTool } from "../../custom-tools/types";
9
+ import { callExaTool, findApiKey } from "./mcp-client";
10
+ import type { ExaRenderDetails } from "./types";
11
+
12
+ const researcherStartTool: CustomTool<any, ExaRenderDetails> = {
13
+ name: "exa_researcher_start",
14
+ label: "Start Deep Research",
15
+ description:
16
+ "Start an asynchronous deep research task using Exa's researcher. Returns a task_id for polling completion.",
17
+ parameters: Type.Object({
18
+ query: Type.String({ description: "Research query to investigate" }),
19
+ depth: Type.Optional(
20
+ Type.Number({
21
+ description: "Research depth (1-5, default: 3)",
22
+ minimum: 1,
23
+ maximum: 5,
24
+ }),
25
+ ),
26
+ breadth: Type.Optional(
27
+ Type.Number({
28
+ description: "Research breadth (1-5, default: 3)",
29
+ minimum: 1,
30
+ maximum: 5,
31
+ }),
32
+ ),
33
+ }),
34
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
35
+ try {
36
+ const apiKey = await findApiKey();
37
+ if (!apiKey) {
38
+ return {
39
+ content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
40
+ details: { error: "EXA_API_KEY not found", toolName: "exa_researcher_start" },
41
+ };
42
+ }
43
+ const result = await callExaTool("deep_researcher_start", params as Record<string, unknown>, apiKey);
44
+ return {
45
+ content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
46
+ details: { raw: result, toolName: "exa_researcher_start" },
47
+ };
48
+ } catch (error) {
49
+ const message = error instanceof Error ? error.message : String(error);
50
+ return {
51
+ content: [{ type: "text" as const, text: `Error: ${message}` }],
52
+ details: { error: message, toolName: "exa_researcher_start" },
53
+ };
54
+ }
55
+ },
56
+ };
57
+
58
+ const researcherPollTool: CustomTool<any, ExaRenderDetails> = {
59
+ name: "exa_researcher_poll",
60
+ label: "Poll Research Status",
61
+ description:
62
+ "Poll the status of an asynchronous research task. Returns status (pending|running|completed|failed) and result if completed.",
63
+ parameters: Type.Object({
64
+ task_id: Type.String({ description: "Task ID returned from exa_researcher_start" }),
65
+ }),
66
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
67
+ try {
68
+ const apiKey = await findApiKey();
69
+ if (!apiKey) {
70
+ return {
71
+ content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
72
+ details: { error: "EXA_API_KEY not found", toolName: "exa_researcher_poll" },
73
+ };
74
+ }
75
+ const result = await callExaTool("deep_researcher_check", params as Record<string, unknown>, apiKey);
76
+ return {
77
+ content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
78
+ details: { raw: result, toolName: "exa_researcher_poll" },
79
+ };
80
+ } catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ return {
83
+ content: [{ type: "text" as const, text: `Error: ${message}` }],
84
+ details: { error: message, toolName: "exa_researcher_poll" },
85
+ };
86
+ }
87
+ },
88
+ };
89
+
90
+ export const researcherTools: CustomTool<any, ExaRenderDetails>[] = [researcherStartTool, researcherPollTool];
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Exa Search Tools
3
+ *
4
+ * Basic neural/keyword search, deep research, code search, and URL crawling.
5
+ */
6
+
7
+ import { Type } from "@sinclair/typebox";
8
+ import type { CustomTool } from "../../custom-tools/types";
9
+ import type { ExaRenderDetails } from "./types";
10
+
11
+ /** exa_search - Basic neural/keyword search */
12
+ const exaSearchTool: CustomTool<any, ExaRenderDetails> = {
13
+ name: "exa_search",
14
+ label: "Exa Search",
15
+ description: `Search the web using Exa's neural or keyword search.
16
+
17
+ Returns structured search results with optional text content and highlights.
18
+
19
+ Parameters:
20
+ - query: Search query (required)
21
+ - type: Search type - "neural" (semantic), "keyword" (exact), or "auto" (default: auto)
22
+ - include_domains: Array of domains to include in results
23
+ - exclude_domains: Array of domains to exclude from results
24
+ - start_published_date: Filter results published after this date (ISO 8601)
25
+ - end_published_date: Filter results published before this date (ISO 8601)
26
+ - use_autoprompt: Let Exa optimize your query automatically (default: true)
27
+ - text: Include page text content in results (default: false, costs more)
28
+ - highlights: Include highlighted relevant snippets (default: false)
29
+ - num_results: Maximum number of results to return (default: 10, max: 100)`,
30
+
31
+ parameters: Type.Object({
32
+ query: Type.String({ description: "Search query" }),
33
+ type: Type.Optional(
34
+ Type.Union([Type.Literal("keyword"), Type.Literal("neural"), Type.Literal("auto")], {
35
+ description: "Search type - neural (semantic), keyword (exact), or auto",
36
+ }),
37
+ ),
38
+ include_domains: Type.Optional(
39
+ Type.Array(Type.String(), {
40
+ description: "Only include results from these domains",
41
+ }),
42
+ ),
43
+ exclude_domains: Type.Optional(
44
+ Type.Array(Type.String(), {
45
+ description: "Exclude results from these domains",
46
+ }),
47
+ ),
48
+ start_published_date: Type.Optional(
49
+ Type.String({
50
+ description: "Filter results published after this date (ISO 8601 format)",
51
+ }),
52
+ ),
53
+ end_published_date: Type.Optional(
54
+ Type.String({
55
+ description: "Filter results published before this date (ISO 8601 format)",
56
+ }),
57
+ ),
58
+ use_autoprompt: Type.Optional(
59
+ Type.Boolean({
60
+ description: "Let Exa optimize your query automatically (default: true)",
61
+ }),
62
+ ),
63
+ text: Type.Optional(
64
+ Type.Boolean({
65
+ description: "Include page text content in results (costs more, default: false)",
66
+ }),
67
+ ),
68
+ highlights: Type.Optional(
69
+ Type.Boolean({
70
+ description: "Include highlighted relevant snippets (default: false)",
71
+ }),
72
+ ),
73
+ num_results: Type.Optional(
74
+ Type.Number({
75
+ description: "Maximum number of results to return (default: 10, max: 100)",
76
+ minimum: 1,
77
+ maximum: 100,
78
+ }),
79
+ ),
80
+ }),
81
+
82
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
83
+ try {
84
+ // Dynamic imports to avoid circular dependencies
85
+ const { findApiKey, callExaTool, formatSearchResults, isSearchResponse } = await import("./mcp-client.js");
86
+
87
+ const apiKey = await findApiKey();
88
+ if (!apiKey) {
89
+ return {
90
+ content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
91
+ details: { error: "EXA_API_KEY not found", toolName: "exa_search" },
92
+ };
93
+ }
94
+ const response = await callExaTool("web_search_exa", params, apiKey);
95
+
96
+ if (isSearchResponse(response)) {
97
+ const formatted = formatSearchResults(response);
98
+ return {
99
+ content: [{ type: "text" as const, text: formatted }],
100
+ details: { response, toolName: "exa_search" },
101
+ };
102
+ }
103
+
104
+ return {
105
+ content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
106
+ details: { raw: response, toolName: "exa_search" },
107
+ };
108
+ } catch (error) {
109
+ const message = error instanceof Error ? error.message : String(error);
110
+ return {
111
+ content: [{ type: "text" as const, text: `Error: ${message}` }],
112
+ details: { error: message, toolName: "exa_search" },
113
+ };
114
+ }
115
+ },
116
+ };
117
+
118
+ /** exa_search_deep - AI-synthesized deep research */
119
+ const exaSearchDeepTool: CustomTool<any, ExaRenderDetails> = {
120
+ name: "exa_search_deep",
121
+ label: "Exa Deep Search",
122
+ description: `Perform AI-synthesized deep research using Exa.
123
+
124
+ Returns comprehensive research with synthesized answers and multiple sources.
125
+
126
+ Similar parameters to exa_search, optimized for research depth.`,
127
+
128
+ parameters: Type.Object({
129
+ query: Type.String({ description: "Research query" }),
130
+ type: Type.Optional(
131
+ Type.Union([Type.Literal("keyword"), Type.Literal("neural"), Type.Literal("auto")], {
132
+ description: "Search type - neural (semantic), keyword (exact), or auto",
133
+ }),
134
+ ),
135
+ include_domains: Type.Optional(
136
+ Type.Array(Type.String(), {
137
+ description: "Only include results from these domains",
138
+ }),
139
+ ),
140
+ exclude_domains: Type.Optional(
141
+ Type.Array(Type.String(), {
142
+ description: "Exclude results from these domains",
143
+ }),
144
+ ),
145
+ start_published_date: Type.Optional(
146
+ Type.String({
147
+ description: "Filter results published after this date (ISO 8601 format)",
148
+ }),
149
+ ),
150
+ end_published_date: Type.Optional(
151
+ Type.String({
152
+ description: "Filter results published before this date (ISO 8601 format)",
153
+ }),
154
+ ),
155
+ use_autoprompt: Type.Optional(
156
+ Type.Boolean({
157
+ description: "Let Exa optimize your query automatically (default: true)",
158
+ }),
159
+ ),
160
+ text: Type.Optional(
161
+ Type.Boolean({
162
+ description: "Include page text content in results (costs more, default: false)",
163
+ }),
164
+ ),
165
+ highlights: Type.Optional(
166
+ Type.Boolean({
167
+ description: "Include highlighted relevant snippets (default: false)",
168
+ }),
169
+ ),
170
+ num_results: Type.Optional(
171
+ Type.Number({
172
+ description: "Maximum number of results to return (default: 10, max: 100)",
173
+ minimum: 1,
174
+ maximum: 100,
175
+ }),
176
+ ),
177
+ }),
178
+
179
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
180
+ try {
181
+ const { findApiKey, callExaTool, formatSearchResults, isSearchResponse } = await import("./mcp-client.js");
182
+
183
+ const apiKey = await findApiKey();
184
+ if (!apiKey) {
185
+ return {
186
+ content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
187
+ details: { error: "EXA_API_KEY not found", toolName: "exa_search_deep" },
188
+ };
189
+ }
190
+ const args = { ...params, type: "deep" };
191
+ const response = await callExaTool("web_search_exa", args, apiKey);
192
+
193
+ if (isSearchResponse(response)) {
194
+ const formatted = formatSearchResults(response);
195
+ return {
196
+ content: [{ type: "text" as const, text: formatted }],
197
+ details: { response, toolName: "exa_search_deep" },
198
+ };
199
+ }
200
+
201
+ return {
202
+ content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
203
+ details: { raw: response, toolName: "exa_search_deep" },
204
+ };
205
+ } catch (error) {
206
+ const message = error instanceof Error ? error.message : String(error);
207
+ return {
208
+ content: [{ type: "text" as const, text: `Error: ${message}` }],
209
+ details: { error: message, toolName: "exa_search_deep" },
210
+ };
211
+ }
212
+ },
213
+ };
214
+
215
+ /** exa_search_code - Code-focused search */
216
+ const exaSearchCodeTool: CustomTool<any, ExaRenderDetails> = {
217
+ name: "exa_search_code",
218
+ label: "Exa Code Search",
219
+ description: `Search for code examples and technical documentation using Exa.
220
+
221
+ Optimized for finding code snippets, API documentation, and technical content.
222
+
223
+ Parameters:
224
+ - query: Code or technical search query (required)
225
+ - code_context: Additional context about what you're looking for`,
226
+
227
+ parameters: Type.Object({
228
+ query: Type.String({ description: "Code or technical search query" }),
229
+ code_context: Type.Optional(
230
+ Type.String({
231
+ description: "Additional context about what you're looking for",
232
+ }),
233
+ ),
234
+ }),
235
+
236
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
237
+ try {
238
+ const { findApiKey, callExaTool, formatSearchResults, isSearchResponse } = await import("./mcp-client.js");
239
+
240
+ const apiKey = await findApiKey();
241
+ if (!apiKey) {
242
+ return {
243
+ content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
244
+ details: { error: "EXA_API_KEY not found", toolName: "exa_search_code" },
245
+ };
246
+ }
247
+ const response = await callExaTool("get_code_context_exa", params, apiKey);
248
+
249
+ if (isSearchResponse(response)) {
250
+ const formatted = formatSearchResults(response);
251
+ return {
252
+ content: [{ type: "text" as const, text: formatted }],
253
+ details: { response, toolName: "exa_search_code" },
254
+ };
255
+ }
256
+
257
+ return {
258
+ content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
259
+ details: { raw: response, toolName: "exa_search_code" },
260
+ };
261
+ } catch (error) {
262
+ const message = error instanceof Error ? error.message : String(error);
263
+ return {
264
+ content: [{ type: "text" as const, text: `Error: ${message}` }],
265
+ details: { error: message, toolName: "exa_search_code" },
266
+ };
267
+ }
268
+ },
269
+ };
270
+
271
+ /** exa_crawl - URL content extraction */
272
+ const exaCrawlTool: CustomTool<any, ExaRenderDetails> = {
273
+ name: "exa_crawl",
274
+ label: "Exa Crawl",
275
+ description: `Extract content from a specific URL using Exa.
276
+
277
+ Returns the page content with optional text and highlights.
278
+
279
+ Parameters:
280
+ - url: URL to crawl (required)
281
+ - text: Include full page text content (default: false)
282
+ - highlights: Include highlighted relevant snippets (default: false)`,
283
+
284
+ parameters: Type.Object({
285
+ url: Type.String({ description: "URL to crawl and extract content from" }),
286
+ text: Type.Optional(
287
+ Type.Boolean({
288
+ description: "Include full page text content (default: false)",
289
+ }),
290
+ ),
291
+ highlights: Type.Optional(
292
+ Type.Boolean({
293
+ description: "Include highlighted relevant snippets (default: false)",
294
+ }),
295
+ ),
296
+ }),
297
+
298
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
299
+ try {
300
+ const { findApiKey, callExaTool, formatSearchResults, isSearchResponse } = await import("./mcp-client.js");
301
+
302
+ const apiKey = await findApiKey();
303
+ if (!apiKey) {
304
+ return {
305
+ content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
306
+ details: { error: "EXA_API_KEY not found", toolName: "exa_crawl" },
307
+ };
308
+ }
309
+ const response = await callExaTool("crawling", params, apiKey);
310
+
311
+ if (isSearchResponse(response)) {
312
+ const formatted = formatSearchResults(response);
313
+ return {
314
+ content: [{ type: "text" as const, text: formatted }],
315
+ details: { response, toolName: "exa_crawl" },
316
+ };
317
+ }
318
+
319
+ return {
320
+ content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
321
+ details: { raw: response, toolName: "exa_crawl" },
322
+ };
323
+ } catch (error) {
324
+ const message = error instanceof Error ? error.message : String(error);
325
+ return {
326
+ content: [{ type: "text" as const, text: `Error: ${message}` }],
327
+ details: { error: message, toolName: "exa_crawl" },
328
+ };
329
+ }
330
+ },
331
+ };
332
+
333
+ export const searchTools: CustomTool<any, ExaRenderDetails>[] = [
334
+ exaSearchTool,
335
+ exaSearchDeepTool,
336
+ exaSearchCodeTool,
337
+ exaCrawlTool,
338
+ ];
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Exa MCP Types
3
+ *
4
+ * Types for the Exa MCP client and tool implementations.
5
+ */
6
+
7
+ import type { TSchema } from "@sinclair/typebox";
8
+
9
+ /** MCP endpoint URLs */
10
+ export const EXA_MCP_URL = "https://mcp.exa.ai/mcp";
11
+ export const WEBSETS_MCP_URL = "https://websetsmcp.exa.ai/mcp";
12
+
13
+ /** MCP tool definition from server */
14
+ export interface MCPTool {
15
+ name: string;
16
+ description: string;
17
+ inputSchema: TSchema;
18
+ }
19
+
20
+ /** Tool wrapper config for dynamic MCP tool creation */
21
+ export interface MCPToolWrapperConfig {
22
+ /** Our tool name (e.g., "exa_search") */
23
+ name: string;
24
+ /** Display label for UI */
25
+ label: string;
26
+ /** MCP tool name to call (e.g., "web_search_exa") */
27
+ mcpToolName: string;
28
+ /** Whether this is a websets tool (uses different MCP endpoint) */
29
+ isWebsetsTool?: boolean;
30
+ }
31
+
32
+ /** MCP tools/list response */
33
+ export interface MCPToolsResponse {
34
+ result?: {
35
+ tools: MCPTool[];
36
+ };
37
+ error?: {
38
+ code: number;
39
+ message: string;
40
+ };
41
+ }
42
+
43
+ /** MCP tools/call response */
44
+ export interface MCPCallResponse {
45
+ result?: {
46
+ content?: Array<{ type: string; text?: string }>;
47
+ };
48
+ error?: {
49
+ code: number;
50
+ message: string;
51
+ };
52
+ }
53
+
54
+ /** Search result from Exa */
55
+ export interface ExaSearchResult {
56
+ id?: string;
57
+ title?: string;
58
+ url?: string;
59
+ author?: string;
60
+ publishedDate?: string;
61
+ text?: string;
62
+ highlights?: string[];
63
+ image?: string;
64
+ favicon?: string;
65
+ }
66
+
67
+ /** Search response from Exa */
68
+ export interface ExaSearchResponse {
69
+ results?: ExaSearchResult[];
70
+ statuses?: Array<{ id: string; status: string; source?: string }>;
71
+ costDollars?: { total: number };
72
+ searchTime?: number;
73
+ requestId?: string;
74
+ }
75
+
76
+ /** Researcher task status */
77
+ export interface ResearcherStatus {
78
+ id: string;
79
+ status: "pending" | "running" | "completed" | "failed";
80
+ result?: string;
81
+ error?: string;
82
+ }
83
+
84
+ /** Webset definition */
85
+ export interface Webset {
86
+ id: string;
87
+ name: string;
88
+ description?: string;
89
+ createdAt?: string;
90
+ updatedAt?: string;
91
+ }
92
+
93
+ /** Webset item */
94
+ export interface WebsetItem {
95
+ id: string;
96
+ websetId: string;
97
+ url: string;
98
+ title?: string;
99
+ content?: string;
100
+ metadata?: Record<string, unknown>;
101
+ }
102
+
103
+ /** Webset search */
104
+ export interface WebsetSearch {
105
+ id: string;
106
+ websetId: string;
107
+ query: string;
108
+ status: "pending" | "running" | "completed" | "cancelled";
109
+ resultCount?: number;
110
+ }
111
+
112
+ /** Webset enrichment */
113
+ export interface WebsetEnrichment {
114
+ id: string;
115
+ websetId: string;
116
+ name: string;
117
+ prompt: string;
118
+ status: "pending" | "running" | "completed" | "cancelled";
119
+ }
120
+
121
+ /** Tool name mappings: MCP name -> our tool name */
122
+ export const EXA_TOOL_MAPPINGS = {
123
+ // Search tools
124
+ web_search_exa: "exa_search",
125
+ get_code_context_exa: "exa_search_code",
126
+ crawling: "exa_crawl",
127
+ // LinkedIn
128
+ linkedin_search: "exa_linkedin",
129
+ // Company
130
+ company_research: "exa_company",
131
+ // Researcher
132
+ deep_researcher_start: "exa_researcher_start",
133
+ deep_researcher_check: "exa_researcher_poll",
134
+ } as const;
135
+
136
+ export const WEBSETS_TOOL_MAPPINGS = {
137
+ create_webset: "webset_create",
138
+ list_websets: "webset_list",
139
+ get_webset: "webset_get",
140
+ update_webset: "webset_update",
141
+ delete_webset: "webset_delete",
142
+ list_webset_items: "webset_items_list",
143
+ get_item: "webset_item_get",
144
+ create_search: "webset_search_create",
145
+ get_search: "webset_search_get",
146
+ cancel_search: "webset_search_cancel",
147
+ create_enrichment: "webset_enrichment_create",
148
+ get_enrichment: "webset_enrichment_get",
149
+ update_enrichment: "webset_enrichment_update",
150
+ delete_enrichment: "webset_enrichment_delete",
151
+ cancel_enrichment: "webset_enrichment_cancel",
152
+ create_monitor: "webset_monitor_create",
153
+ } as const;
154
+
155
+ export type ExaMcpToolName = keyof typeof EXA_TOOL_MAPPINGS;
156
+ export type WebsetsMcpToolName = keyof typeof WEBSETS_TOOL_MAPPINGS;
157
+ export type ExaToolName = (typeof EXA_TOOL_MAPPINGS)[ExaMcpToolName];
158
+ export type WebsetsToolName = (typeof WEBSETS_TOOL_MAPPINGS)[WebsetsMcpToolName];
159
+
160
+ /** Render details for TUI */
161
+ export interface ExaRenderDetails {
162
+ response?: ExaSearchResponse;
163
+ error?: string;
164
+ toolName?: string;
165
+ /** Raw result for non-search responses */
166
+ raw?: unknown;
167
+ }