@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,248 @@
1
+ /**
2
+ * Exa Websets Tools
3
+ *
4
+ * CRUD operations for websets, items, searches, enrichments, and monitoring.
5
+ */
6
+
7
+ import { Type } from "@sinclair/typebox";
8
+ import type { CustomTool } from "../../custom-tools/types";
9
+ import { callWebsetsTool, findApiKey } from "./mcp-client";
10
+ import type { ExaRenderDetails } from "./types";
11
+
12
+ /** Helper to create a websets tool with proper execute signature */
13
+ function createWebsetTool(
14
+ name: string,
15
+ label: string,
16
+ description: string,
17
+ parameters: ReturnType<typeof Type.Object>,
18
+ mcpToolName: string,
19
+ ): CustomTool<any, ExaRenderDetails> {
20
+ return {
21
+ name,
22
+ label,
23
+ description,
24
+ parameters,
25
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
26
+ try {
27
+ const apiKey = await findApiKey();
28
+ if (!apiKey) {
29
+ return {
30
+ content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
31
+ details: { error: "EXA_API_KEY not found", toolName: name },
32
+ };
33
+ }
34
+ const result = await callWebsetsTool(apiKey, mcpToolName, params as Record<string, unknown>);
35
+ return {
36
+ content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
37
+ details: { raw: result, toolName: name },
38
+ };
39
+ } catch (error) {
40
+ const message = error instanceof Error ? error.message : String(error);
41
+ return {
42
+ content: [{ type: "text" as const, text: `Error: ${message}` }],
43
+ details: { error: message, toolName: name },
44
+ };
45
+ }
46
+ },
47
+ };
48
+ }
49
+
50
+ // CRUD Operations
51
+ const websetCreateTool = createWebsetTool(
52
+ "webset_create",
53
+ "Create Webset",
54
+ "Create a new webset collection for organizing web content.",
55
+ Type.Object({
56
+ name: Type.String({ description: "Name of the webset" }),
57
+ description: Type.Optional(Type.String({ description: "Optional description" })),
58
+ }),
59
+ "create_webset",
60
+ );
61
+
62
+ const websetListTool = createWebsetTool(
63
+ "webset_list",
64
+ "List Websets",
65
+ "List all websets in your account.",
66
+ Type.Object({}),
67
+ "list_websets",
68
+ );
69
+
70
+ const websetGetTool = createWebsetTool(
71
+ "webset_get",
72
+ "Get Webset",
73
+ "Get details of a specific webset by ID.",
74
+ Type.Object({
75
+ id: Type.String({ description: "Webset ID" }),
76
+ }),
77
+ "get_webset",
78
+ );
79
+
80
+ const websetUpdateTool = createWebsetTool(
81
+ "webset_update",
82
+ "Update Webset",
83
+ "Update a webset's name or description.",
84
+ Type.Object({
85
+ id: Type.String({ description: "Webset ID" }),
86
+ name: Type.Optional(Type.String({ description: "New name" })),
87
+ description: Type.Optional(Type.String({ description: "New description" })),
88
+ }),
89
+ "update_webset",
90
+ );
91
+
92
+ const websetDeleteTool = createWebsetTool(
93
+ "webset_delete",
94
+ "Delete Webset",
95
+ "Delete a webset and all its contents.",
96
+ Type.Object({
97
+ id: Type.String({ description: "Webset ID" }),
98
+ }),
99
+ "delete_webset",
100
+ );
101
+
102
+ // Item Management
103
+ const websetItemsListTool = createWebsetTool(
104
+ "webset_items_list",
105
+ "List Webset Items",
106
+ "List items in a webset with optional pagination.",
107
+ Type.Object({
108
+ webset_id: Type.String({ description: "Webset ID" }),
109
+ limit: Type.Optional(Type.Number({ description: "Number of items to return" })),
110
+ offset: Type.Optional(Type.Number({ description: "Pagination offset" })),
111
+ }),
112
+ "list_webset_items",
113
+ );
114
+
115
+ const websetItemGetTool = createWebsetTool(
116
+ "webset_item_get",
117
+ "Get Webset Item",
118
+ "Get a specific item from a webset.",
119
+ Type.Object({
120
+ webset_id: Type.String({ description: "Webset ID" }),
121
+ item_id: Type.String({ description: "Item ID" }),
122
+ }),
123
+ "get_item",
124
+ );
125
+
126
+ // Search Operations
127
+ const websetSearchCreateTool = createWebsetTool(
128
+ "webset_search_create",
129
+ "Create Webset Search",
130
+ "Create a new search within a webset.",
131
+ Type.Object({
132
+ webset_id: Type.String({ description: "Webset ID" }),
133
+ query: Type.String({ description: "Search query" }),
134
+ }),
135
+ "create_search",
136
+ );
137
+
138
+ const websetSearchGetTool = createWebsetTool(
139
+ "webset_search_get",
140
+ "Get Webset Search",
141
+ "Get the status and results of a webset search.",
142
+ Type.Object({
143
+ webset_id: Type.String({ description: "Webset ID" }),
144
+ search_id: Type.String({ description: "Search ID" }),
145
+ }),
146
+ "get_search",
147
+ );
148
+
149
+ const websetSearchCancelTool = createWebsetTool(
150
+ "webset_search_cancel",
151
+ "Cancel Webset Search",
152
+ "Cancel a running webset search.",
153
+ Type.Object({
154
+ webset_id: Type.String({ description: "Webset ID" }),
155
+ search_id: Type.String({ description: "Search ID" }),
156
+ }),
157
+ "cancel_search",
158
+ );
159
+
160
+ // Enrichment Operations
161
+ const websetEnrichmentCreateTool = createWebsetTool(
162
+ "webset_enrichment_create",
163
+ "Create Enrichment",
164
+ "Create a new enrichment task for a webset.",
165
+ Type.Object({
166
+ webset_id: Type.String({ description: "Webset ID" }),
167
+ name: Type.String({ description: "Enrichment name" }),
168
+ prompt: Type.String({ description: "Enrichment prompt" }),
169
+ }),
170
+ "create_enrichment",
171
+ );
172
+
173
+ const websetEnrichmentGetTool = createWebsetTool(
174
+ "webset_enrichment_get",
175
+ "Get Enrichment",
176
+ "Get the status and results of an enrichment task.",
177
+ Type.Object({
178
+ webset_id: Type.String({ description: "Webset ID" }),
179
+ enrichment_id: Type.String({ description: "Enrichment ID" }),
180
+ }),
181
+ "get_enrichment",
182
+ );
183
+
184
+ const websetEnrichmentUpdateTool = createWebsetTool(
185
+ "webset_enrichment_update",
186
+ "Update Enrichment",
187
+ "Update an enrichment's name or prompt.",
188
+ Type.Object({
189
+ webset_id: Type.String({ description: "Webset ID" }),
190
+ enrichment_id: Type.String({ description: "Enrichment ID" }),
191
+ name: Type.Optional(Type.String({ description: "New name" })),
192
+ prompt: Type.Optional(Type.String({ description: "New prompt" })),
193
+ }),
194
+ "update_enrichment",
195
+ );
196
+
197
+ const websetEnrichmentDeleteTool = createWebsetTool(
198
+ "webset_enrichment_delete",
199
+ "Delete Enrichment",
200
+ "Delete an enrichment task.",
201
+ Type.Object({
202
+ webset_id: Type.String({ description: "Webset ID" }),
203
+ enrichment_id: Type.String({ description: "Enrichment ID" }),
204
+ }),
205
+ "delete_enrichment",
206
+ );
207
+
208
+ const websetEnrichmentCancelTool = createWebsetTool(
209
+ "webset_enrichment_cancel",
210
+ "Cancel Enrichment",
211
+ "Cancel a running enrichment task.",
212
+ Type.Object({
213
+ webset_id: Type.String({ description: "Webset ID" }),
214
+ enrichment_id: Type.String({ description: "Enrichment ID" }),
215
+ }),
216
+ "cancel_enrichment",
217
+ );
218
+
219
+ // Monitoring
220
+ const websetMonitorCreateTool = createWebsetTool(
221
+ "webset_monitor_create",
222
+ "Create Monitor",
223
+ "Create a monitoring task for a webset with optional webhook notifications.",
224
+ Type.Object({
225
+ webset_id: Type.String({ description: "Webset ID" }),
226
+ webhook_url: Type.Optional(Type.String({ description: "Webhook URL for notifications" })),
227
+ }),
228
+ "create_monitor",
229
+ );
230
+
231
+ export const websetsTools: CustomTool<any, ExaRenderDetails>[] = [
232
+ websetCreateTool,
233
+ websetListTool,
234
+ websetGetTool,
235
+ websetUpdateTool,
236
+ websetDeleteTool,
237
+ websetItemsListTool,
238
+ websetItemGetTool,
239
+ websetSearchCreateTool,
240
+ websetSearchGetTool,
241
+ websetSearchCancelTool,
242
+ websetEnrichmentCreateTool,
243
+ websetEnrichmentGetTool,
244
+ websetEnrichmentUpdateTool,
245
+ websetEnrichmentDeleteTool,
246
+ websetEnrichmentCancelTool,
247
+ websetMonitorCreateTool,
248
+ ];
@@ -0,0 +1,244 @@
1
+ import { existsSync, type Stats, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import type { AgentTool } from "@oh-my-pi/pi-agent-core";
4
+ import { Type } from "@sinclair/typebox";
5
+ import { globSync } from "glob";
6
+ import findDescription from "../../prompts/tools/find.md" with { type: "text" };
7
+ import { ensureTool } from "../../utils/tools-manager";
8
+ import { untilAborted } from "../utils";
9
+ import { resolveToCwd } from "./path-utils";
10
+ import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate";
11
+
12
+ const findSchema = Type.Object({
13
+ pattern: Type.String({
14
+ description: "Glob pattern to match files, e.g. '*.ts', '**/*.json', or 'src/**/*.spec.ts'",
15
+ }),
16
+ path: Type.Optional(Type.String({ description: "Directory to search in (default: current directory)" })),
17
+ limit: Type.Optional(Type.Number({ description: "Maximum number of results (default: 1000)" })),
18
+ hidden: Type.Optional(Type.Boolean({ description: "Include hidden files (default: false)" })),
19
+ sortByMtime: Type.Optional(
20
+ Type.Boolean({ description: "Sort results by modification time, most recent first (default: false)" }),
21
+ ),
22
+ type: Type.Optional(
23
+ Type.Union([Type.Literal("file"), Type.Literal("dir"), Type.Literal("all")], {
24
+ description:
25
+ "Filter by type: 'file' for files only, 'dir' for directories only, 'all' for both (default: 'all')",
26
+ }),
27
+ ),
28
+ });
29
+
30
+ const DEFAULT_LIMIT = 1000;
31
+
32
+ export interface FindToolDetails {
33
+ truncation?: TruncationResult;
34
+ resultLimitReached?: number;
35
+ // Fields for TUI rendering
36
+ scopePath?: string;
37
+ fileCount?: number;
38
+ files?: string[];
39
+ truncated?: boolean;
40
+ error?: string;
41
+ }
42
+
43
+ export function createFindTool(cwd: string): AgentTool<typeof findSchema> {
44
+ return {
45
+ name: "find",
46
+ label: "Find",
47
+ description: findDescription,
48
+ parameters: findSchema,
49
+ execute: async (
50
+ _toolCallId: string,
51
+ {
52
+ pattern,
53
+ path: searchDir,
54
+ limit,
55
+ hidden,
56
+ sortByMtime,
57
+ type,
58
+ }: {
59
+ pattern: string;
60
+ path?: string;
61
+ limit?: number;
62
+ hidden?: boolean;
63
+ sortByMtime?: boolean;
64
+ type?: "file" | "dir" | "all";
65
+ },
66
+ signal?: AbortSignal,
67
+ ) => {
68
+ return untilAborted(signal, async () => {
69
+ // Ensure fd is available
70
+ const fdPath = await ensureTool("fd", true);
71
+ if (!fdPath) {
72
+ throw new Error("fd is not available and could not be downloaded");
73
+ }
74
+
75
+ const searchPath = resolveToCwd(searchDir || ".", cwd);
76
+ const scopePath = (() => {
77
+ const relative = path.relative(cwd, searchPath).replace(/\\/g, "/");
78
+ return relative.length === 0 ? "." : relative;
79
+ })();
80
+ const effectiveLimit = limit ?? DEFAULT_LIMIT;
81
+ const effectiveType = type ?? "all";
82
+ const includeHidden = hidden ?? false;
83
+ const shouldSortByMtime = sortByMtime ?? false;
84
+
85
+ // Build fd arguments
86
+ const args: string[] = [
87
+ "--glob", // Use glob pattern
88
+ "--color=never", // No ANSI colors
89
+ "--max-results",
90
+ String(effectiveLimit),
91
+ ];
92
+
93
+ if (includeHidden) {
94
+ args.push("--hidden");
95
+ }
96
+
97
+ // Add type filter
98
+ if (effectiveType === "file") {
99
+ args.push("--type", "f");
100
+ } else if (effectiveType === "dir") {
101
+ args.push("--type", "d");
102
+ }
103
+
104
+ // Include .gitignore files (root + nested) so fd respects them even outside git repos
105
+ const gitignoreFiles = new Set<string>();
106
+ const rootGitignore = path.join(searchPath, ".gitignore");
107
+ if (existsSync(rootGitignore)) {
108
+ gitignoreFiles.add(rootGitignore);
109
+ }
110
+
111
+ try {
112
+ const nestedGitignores = globSync("**/.gitignore", {
113
+ cwd: searchPath,
114
+ dot: true,
115
+ absolute: true,
116
+ ignore: ["**/node_modules/**", "**/.git/**"],
117
+ });
118
+ for (const file of nestedGitignores) {
119
+ gitignoreFiles.add(file);
120
+ }
121
+ } catch {
122
+ // Ignore glob errors
123
+ }
124
+
125
+ for (const gitignorePath of gitignoreFiles) {
126
+ args.push("--ignore-file", gitignorePath);
127
+ }
128
+
129
+ // Pattern and path
130
+ args.push(pattern, searchPath);
131
+
132
+ // Run fd
133
+ const result = Bun.spawnSync([fdPath, ...args], {
134
+ stdin: "ignore",
135
+ stdout: "pipe",
136
+ stderr: "pipe",
137
+ });
138
+
139
+ const output = result.stdout.toString().trim();
140
+
141
+ if (result.exitCode !== 0) {
142
+ const errorMsg = result.stderr.toString().trim() || `fd exited with code ${result.exitCode}`;
143
+ // fd returns non-zero for some errors but may still have partial output
144
+ if (!output) {
145
+ throw new Error(errorMsg);
146
+ }
147
+ }
148
+
149
+ if (!output) {
150
+ return {
151
+ content: [{ type: "text", text: "No files found matching pattern" }],
152
+ details: { scopePath, fileCount: 0, files: [], truncated: false },
153
+ };
154
+ }
155
+
156
+ const lines = output.split("\n");
157
+ const relativized: string[] = [];
158
+ const mtimes: number[] = [];
159
+
160
+ for (const rawLine of lines) {
161
+ const line = rawLine.replace(/\r$/, "").trim();
162
+ if (!line) {
163
+ continue;
164
+ }
165
+
166
+ const hadTrailingSlash = line.endsWith("/") || line.endsWith("\\");
167
+ let relativePath = line;
168
+ if (line.startsWith(searchPath)) {
169
+ relativePath = line.slice(searchPath.length + 1); // +1 for the /
170
+ } else {
171
+ relativePath = path.relative(searchPath, line);
172
+ }
173
+
174
+ if (hadTrailingSlash && !relativePath.endsWith("/")) {
175
+ relativePath += "/";
176
+ }
177
+
178
+ relativized.push(relativePath);
179
+
180
+ // Collect mtime if sorting is requested
181
+ if (shouldSortByMtime) {
182
+ try {
183
+ const fullPath = path.join(searchPath, relativePath);
184
+ const stat: Stats = statSync(fullPath);
185
+ mtimes.push(stat.mtimeMs);
186
+ } catch {
187
+ mtimes.push(0);
188
+ }
189
+ }
190
+ }
191
+
192
+ // Sort by mtime if requested (most recent first)
193
+ if (shouldSortByMtime && relativized.length > 0) {
194
+ const indexed = relativized.map((path, idx) => ({ path, mtime: mtimes[idx] || 0 }));
195
+ indexed.sort((a, b) => b.mtime - a.mtime);
196
+ relativized.length = 0;
197
+ relativized.push(...indexed.map((item) => item.path));
198
+ }
199
+
200
+ // Check if we hit the result limit
201
+ const resultLimitReached = relativized.length >= effectiveLimit;
202
+
203
+ // Apply byte truncation (no line limit since we already have result limit)
204
+ const rawOutput = relativized.join("\n");
205
+ const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
206
+
207
+ let resultOutput = truncation.content;
208
+ const details: FindToolDetails = {
209
+ scopePath,
210
+ fileCount: relativized.length,
211
+ files: relativized,
212
+ truncated: resultLimitReached || truncation.truncated,
213
+ };
214
+
215
+ // Build notices
216
+ const notices: string[] = [];
217
+
218
+ if (resultLimitReached) {
219
+ notices.push(
220
+ `${effectiveLimit} results limit reached. Use limit=${effectiveLimit * 2} for more, or refine pattern`,
221
+ );
222
+ details.resultLimitReached = effectiveLimit;
223
+ }
224
+
225
+ if (truncation.truncated) {
226
+ notices.push(`${formatSize(DEFAULT_MAX_BYTES)} limit reached`);
227
+ details.truncation = truncation;
228
+ }
229
+
230
+ if (notices.length > 0) {
231
+ resultOutput += `\n\n[${notices.join(". ")}]`;
232
+ }
233
+
234
+ return {
235
+ content: [{ type: "text", text: resultOutput }],
236
+ details: Object.keys(details).length > 0 ? details : undefined,
237
+ };
238
+ });
239
+ },
240
+ };
241
+ }
242
+
243
+ /** Default find tool using process.cwd() - for backwards compatibility */
244
+ export const findTool = createFindTool(process.cwd());