@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,14 @@
1
+ Interact with Language Server Protocol (LSP) servers to get code intelligence features.
2
+
3
+ Standard operations:
4
+ - diagnostics: Get errors/warnings for a file
5
+ - workspace_diagnostics: Check entire project for errors (uses tsc, cargo check, go build, etc.)
6
+ - definition: Go to symbol definition
7
+ - references: Find all references to a symbol
8
+ - hover: Get type info and documentation
9
+ - symbols: List symbols in a file (functions, classes, etc.)
10
+ - workspace_symbols: Search for symbols across the project
11
+ - rename: Rename a symbol across the codebase
12
+ - actions: List and apply code actions (quick fixes, refactors)
13
+ - incoming_calls: Find all callers of a function
14
+ - outgoing_calls: Find all functions called by a function
@@ -0,0 +1,23 @@
1
+ # TaskOutput
2
+
3
+ Retrieves complete output from background tasks spawned with the Task tool.
4
+
5
+ ## When to Use
6
+
7
+ Use TaskOutput when:
8
+ - Task tool returns truncated preview with "Output truncated" message
9
+ - You need full output to debug errors or analyze detailed results
10
+ - Task tool's summary shows substantial line/character counts but preview is incomplete
11
+ - You're analyzing multi-step task output requiring full context
12
+
13
+ Do NOT use when:
14
+ - Task preview already shows complete output (no truncation indicator)
15
+ - Summary alone answers your question
16
+
17
+ ## Parameters
18
+
19
+ - `ids`: Array of output IDs from Task results (e.g., `["reviewer_0", "explore_1"]`)
20
+ - `format` (optional):
21
+ - `"raw"` (default): Full output with ANSI codes preserved
22
+ - `"json"`: Structured object with metadata
23
+ - `"stripped"`: Plain text with ANSI codes removed for parsing
@@ -0,0 +1,25 @@
1
+ Reads a file from the local filesystem. You can access any file directly by using this tool.
2
+ Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
3
+
4
+ Usage:
5
+ - By default, it reads up to {{DEFAULT_MAX_LINES}} lines starting from the beginning of the file
6
+ - You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
7
+ - Any lines longer than 500 characters will be truncated
8
+ - Results are returned using cat -n format, with line numbers starting at 1
9
+ - This tool allows Claude Code to read images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as Claude Code is a multimodal LLM.
10
+ - This tool can read PDF files (.pdf). PDFs are processed page by page, extracting both text and visual content for analysis.
11
+ - This tool can read Jupyter notebooks (.ipynb files) and returns all cells with their outputs, combining code, text, and visualizations.
12
+ - This tool can only read files, not directories. To read a directory, use an ls command via the bash tool.
13
+ - You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.
14
+ - You will regularly be asked to read screenshots. If the user provides a path to a screenshot, ALWAYS use this tool to view the file at the path. This tool will work with all temporary file paths.
15
+ - If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.
16
+
17
+ Empty files trigger a warning. Directory paths return an ls-style listing. Missing files return an error with closest matches (gitignore respected).
18
+
19
+ ## Best Practices
20
+
21
+ - Parallelize reads when exploring related files
22
+ - Read before editing (required in current session)
23
+ - Trust user-provided paths; attempt the read
24
+ - Screenshots: Read tool renders images visually
25
+ - Skip re-reading after edits (Edit/Write report errors)
@@ -0,0 +1,8 @@
1
+ Fetches and analyzes web content by retrieving a URL
2
+
3
+ Use this tool when you need to:
4
+ - Extract specific information from web pages (documentation, articles, API references)
5
+ - Analyze GitHub issues, pull requests, or repository content
6
+ - Retrieve information from Stack Overflow, Wikipedia, Reddit, NPM, arXiv, or technical blogs
7
+ - Access RSS/Atom feeds or JSON endpoints
8
+ - Read PDF or DOCX files hosted at a URL
@@ -0,0 +1,10 @@
1
+ Allows OMP to search the web and use the results to inform responses
2
+ - Provides up-to-date information for current events and recent data
3
+ - Returns search result information formatted as search result blocks, including links as markdown hyperlinks
4
+ - Use this tool for accessing information beyond Claude's knowledge cutoff
5
+ - Searches are performed automatically within a single API call
6
+
7
+ Common: system_prompt (guides response style)
8
+ Anthropic-specific: max_tokens
9
+ Perplexity-specific: model (sonar/sonar-pro), search_recency_filter, search_domain_filter, search_context_size, return_related_questions
10
+ Exa-specific: num_results
@@ -0,0 +1,10 @@
1
+ Creates or overwrites a file at the specified path.
2
+
3
+ When to use:
4
+ - Creating new files explicitly required by the task
5
+ - Replacing entire file contents when editing would be more complex
6
+
7
+ Critical requirements:
8
+ - Prefer Edit tool for modifying existing files (more precise, preserves formatting)
9
+ - Create documentation files (*.md, README) only when explicitly requested
10
+ - Include emojis only when explicitly requested
@@ -0,0 +1,99 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+
3
+ export interface ChangelogEntry {
4
+ major: number;
5
+ minor: number;
6
+ patch: number;
7
+ content: string;
8
+ }
9
+
10
+ /**
11
+ * Parse changelog entries from CHANGELOG.md
12
+ * Scans for ## lines and collects content until next ## or EOF
13
+ */
14
+ export function parseChangelog(changelogPath: string): ChangelogEntry[] {
15
+ if (!existsSync(changelogPath)) {
16
+ return [];
17
+ }
18
+
19
+ try {
20
+ const content = readFileSync(changelogPath, "utf-8");
21
+ const lines = content.split("\n");
22
+ const entries: ChangelogEntry[] = [];
23
+
24
+ let currentLines: string[] = [];
25
+ let currentVersion: { major: number; minor: number; patch: number } | null = null;
26
+
27
+ for (const line of lines) {
28
+ // Check if this is a version header (## [x.y.z] ...)
29
+ if (line.startsWith("## ")) {
30
+ // Save previous entry if exists
31
+ if (currentVersion && currentLines.length > 0) {
32
+ entries.push({
33
+ ...currentVersion,
34
+ content: currentLines.join("\n").trim(),
35
+ });
36
+ }
37
+
38
+ // Try to parse version from this line
39
+ const versionMatch = line.match(/##\s+\[?(\d+)\.(\d+)\.(\d+)\]?/);
40
+ if (versionMatch) {
41
+ currentVersion = {
42
+ major: Number.parseInt(versionMatch[1], 10),
43
+ minor: Number.parseInt(versionMatch[2], 10),
44
+ patch: Number.parseInt(versionMatch[3], 10),
45
+ };
46
+ currentLines = [line];
47
+ } else {
48
+ // Reset if we can't parse version
49
+ currentVersion = null;
50
+ currentLines = [];
51
+ }
52
+ } else if (currentVersion) {
53
+ // Collect lines for current version
54
+ currentLines.push(line);
55
+ }
56
+ }
57
+
58
+ // Save last entry
59
+ if (currentVersion && currentLines.length > 0) {
60
+ entries.push({
61
+ ...currentVersion,
62
+ content: currentLines.join("\n").trim(),
63
+ });
64
+ }
65
+
66
+ return entries;
67
+ } catch (error) {
68
+ console.error(`Warning: Could not parse changelog: ${error}`);
69
+ return [];
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
75
+ */
76
+ export function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number {
77
+ if (v1.major !== v2.major) return v1.major - v2.major;
78
+ if (v1.minor !== v2.minor) return v1.minor - v2.minor;
79
+ return v1.patch - v2.patch;
80
+ }
81
+
82
+ /**
83
+ * Get entries newer than lastVersion
84
+ */
85
+ export function getNewEntries(entries: ChangelogEntry[], lastVersion: string): ChangelogEntry[] {
86
+ // Parse lastVersion
87
+ const parts = lastVersion.split(".").map(Number);
88
+ const last: ChangelogEntry = {
89
+ major: parts[0] || 0,
90
+ minor: parts[1] || 0,
91
+ patch: parts[2] || 0,
92
+ content: "",
93
+ };
94
+
95
+ return entries.filter((entry) => compareVersions(entry, last) > 0);
96
+ }
97
+
98
+ // Re-export getChangelogPath from paths.ts for convenience
99
+ export { getChangelogPath } from "../config";
@@ -0,0 +1,265 @@
1
+ import { platform } from "node:os";
2
+
3
+ async function spawnWithTimeout(cmd: string[], input: string, timeoutMs: number): Promise<void> {
4
+ const proc = Bun.spawn(cmd, { stdin: "pipe" });
5
+
6
+ const timeoutPromise = new Promise<never>((_, reject) => {
7
+ setTimeout(() => reject(new Error("Clipboard operation timed out")), timeoutMs);
8
+ });
9
+
10
+ try {
11
+ proc.stdin.write(input);
12
+ proc.stdin.end();
13
+ await Promise.race([proc.exited, timeoutPromise]);
14
+
15
+ if (proc.exitCode !== 0) {
16
+ throw new Error(`Command failed with exit code ${proc.exitCode}`);
17
+ }
18
+ } finally {
19
+ proc.kill();
20
+ }
21
+ }
22
+
23
+ async function spawnAndRead(cmd: string[], timeoutMs: number): Promise<Buffer | null> {
24
+ const proc = Bun.spawn(cmd, { stdout: "pipe", stderr: "pipe" });
25
+
26
+ const timeoutPromise = new Promise<never>((_, reject) => {
27
+ setTimeout(() => reject(new Error("Clipboard operation timed out")), timeoutMs);
28
+ });
29
+
30
+ try {
31
+ const [exitCode, stdout] = await Promise.race([
32
+ Promise.all([proc.exited, new Response(proc.stdout).arrayBuffer()]),
33
+ timeoutPromise,
34
+ ]);
35
+
36
+ if (exitCode !== 0) {
37
+ return null;
38
+ }
39
+
40
+ return Buffer.from(stdout);
41
+ } catch {
42
+ return null;
43
+ } finally {
44
+ proc.kill();
45
+ }
46
+ }
47
+
48
+ export async function copyToClipboard(text: string): Promise<void> {
49
+ const p = platform();
50
+ const timeout = 5000;
51
+
52
+ try {
53
+ if (p === "darwin") {
54
+ await spawnWithTimeout(["pbcopy"], text, timeout);
55
+ } else if (p === "win32") {
56
+ await spawnWithTimeout(["clip"], text, timeout);
57
+ } else {
58
+ // Linux - try xclip first, fall back to xsel
59
+ try {
60
+ await spawnWithTimeout(["xclip", "-selection", "clipboard"], text, timeout);
61
+ } catch {
62
+ await spawnWithTimeout(["xsel", "--clipboard", "--input"], text, timeout);
63
+ }
64
+ }
65
+ } catch (error) {
66
+ const msg = error instanceof Error ? error.message : String(error);
67
+ if (p === "linux") {
68
+ throw new Error(`Failed to copy to clipboard. Install xclip or xsel: ${msg}`);
69
+ }
70
+ throw new Error(`Failed to copy to clipboard: ${msg}`);
71
+ }
72
+ }
73
+
74
+ export interface ClipboardImage {
75
+ data: string; // base64 encoded
76
+ mimeType: string;
77
+ }
78
+
79
+ /**
80
+ * Read image from system clipboard if available.
81
+ * Returns null if no image is in clipboard or clipboard access fails.
82
+ *
83
+ * Supported platforms:
84
+ * - Linux: requires xclip
85
+ * - macOS: uses osascript + pbpaste
86
+ * - Windows: uses PowerShell
87
+ */
88
+ export async function readImageFromClipboard(): Promise<ClipboardImage | null> {
89
+ const p = platform();
90
+ const timeout = 3000;
91
+
92
+ try {
93
+ if (p === "linux") {
94
+ return await readImageLinux(timeout);
95
+ } else if (p === "darwin") {
96
+ return await readImageMacOS(timeout);
97
+ } else if (p === "win32") {
98
+ return await readImageWindows(timeout);
99
+ }
100
+ } catch {
101
+ // Clipboard access failed silently
102
+ }
103
+
104
+ return null;
105
+ }
106
+
107
+ async function readImageLinux(timeout: number): Promise<ClipboardImage | null> {
108
+ // Try Wayland first (wl-paste), then X11 (xclip)
109
+ const wayland = await readImageWayland(timeout);
110
+ if (wayland) return wayland;
111
+
112
+ return await readImageX11(timeout);
113
+ }
114
+
115
+ async function readImageWayland(timeout: number): Promise<ClipboardImage | null> {
116
+ // wl-paste --list-types shows available MIME types
117
+ const types = await spawnAndRead(["wl-paste", "--list-types"], timeout);
118
+ if (!types) return null;
119
+
120
+ const typeList = types.toString("utf-8");
121
+
122
+ // Try PNG first, then JPEG
123
+ const imageTypes = [
124
+ { type: "image/png", mimeType: "image/png" },
125
+ { type: "image/jpeg", mimeType: "image/jpeg" },
126
+ ];
127
+
128
+ for (const { type, mimeType } of imageTypes) {
129
+ if (typeList.includes(type)) {
130
+ const imageData = await spawnAndRead(["wl-paste", "--type", type], timeout);
131
+ if (imageData && imageData.length > 0) {
132
+ return {
133
+ data: imageData.toString("base64"),
134
+ mimeType,
135
+ };
136
+ }
137
+ }
138
+ }
139
+
140
+ return null;
141
+ }
142
+
143
+ async function readImageX11(timeout: number): Promise<ClipboardImage | null> {
144
+ // Check available targets in clipboard
145
+ const targets = await spawnAndRead(["xclip", "-selection", "clipboard", "-t", "TARGETS", "-o"], timeout);
146
+ if (!targets) return null;
147
+
148
+ const targetList = targets.toString("utf-8");
149
+
150
+ // Try PNG first (preferred), then JPEG
151
+ const imageTypes = [
152
+ { target: "image/png", mimeType: "image/png" },
153
+ { target: "image/jpeg", mimeType: "image/jpeg" },
154
+ { target: "image/jpg", mimeType: "image/jpeg" },
155
+ ];
156
+
157
+ for (const { target, mimeType } of imageTypes) {
158
+ if (targetList.includes(target)) {
159
+ const imageData = await spawnAndRead(["xclip", "-selection", "clipboard", "-t", target, "-o"], timeout);
160
+ if (imageData && imageData.length > 0) {
161
+ return {
162
+ data: imageData.toString("base64"),
163
+ mimeType,
164
+ };
165
+ }
166
+ }
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ async function readImageMacOS(timeout: number): Promise<ClipboardImage | null> {
173
+ // Use osascript to check clipboard class and read PNG data
174
+ // First check if clipboard has image data
175
+ const checkScript = `
176
+ try
177
+ clipboard info for «class PNGf»
178
+ return "png"
179
+ on error
180
+ try
181
+ clipboard info for «class JPEG»
182
+ return "jpeg"
183
+ on error
184
+ return "none"
185
+ end try
186
+ end try
187
+ `;
188
+
189
+ const checkProc = Bun.spawn(["osascript", "-e", checkScript], { stdout: "pipe", stderr: "pipe" });
190
+ const checkResult = await Promise.race([
191
+ new Response(checkProc.stdout).text(),
192
+ new Promise<string>((_, reject) => setTimeout(() => reject(new Error("timeout")), timeout)),
193
+ ]).catch(() => "none");
194
+
195
+ await checkProc.exited;
196
+ const imageType = checkResult.trim();
197
+
198
+ if (imageType === "none") return null;
199
+
200
+ // Read the actual image data using a temp file approach
201
+ // osascript can't output binary directly, so we write to a temp file
202
+ const tempFile = `/tmp/omp-clipboard-${Date.now()}.${imageType === "png" ? "png" : "jpg"}`;
203
+ const clipboardClass = imageType === "png" ? "«class PNGf»" : "«class JPEG»";
204
+
205
+ const readScript = `
206
+ set imageData to the clipboard as ${clipboardClass}
207
+ set filePath to POSIX file "${tempFile}"
208
+ set fileRef to open for access filePath with write permission
209
+ write imageData to fileRef
210
+ close access fileRef
211
+ `;
212
+
213
+ const writeProc = Bun.spawn(["osascript", "-e", readScript], { stdout: "pipe", stderr: "pipe" });
214
+ await Promise.race([
215
+ writeProc.exited,
216
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), timeout)),
217
+ ]).catch(() => null);
218
+
219
+ try {
220
+ const file = Bun.file(tempFile);
221
+ if (await file.exists()) {
222
+ const buffer = await file.arrayBuffer();
223
+ await Bun.write(tempFile, ""); // Clear file
224
+ const { unlink } = await import("fs/promises");
225
+ await unlink(tempFile).catch(() => {});
226
+
227
+ if (buffer.byteLength > 0) {
228
+ return {
229
+ data: Buffer.from(buffer).toString("base64"),
230
+ mimeType: imageType === "png" ? "image/png" : "image/jpeg",
231
+ };
232
+ }
233
+ }
234
+ } catch {
235
+ // File read failed
236
+ }
237
+
238
+ return null;
239
+ }
240
+
241
+ async function readImageWindows(timeout: number): Promise<ClipboardImage | null> {
242
+ // PowerShell script to read image from clipboard as base64
243
+ const script = `
244
+ Add-Type -AssemblyName System.Windows.Forms
245
+ $clipboard = [System.Windows.Forms.Clipboard]::GetImage()
246
+ if ($clipboard -ne $null) {
247
+ $ms = New-Object System.IO.MemoryStream
248
+ $clipboard.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
249
+ [Convert]::ToBase64String($ms.ToArray())
250
+ }
251
+ `;
252
+
253
+ const result = await spawnAndRead(["powershell", "-NoProfile", "-Command", script], timeout);
254
+ if (result && result.length > 0) {
255
+ const base64 = result.toString("utf-8").trim();
256
+ if (base64.length > 0) {
257
+ return {
258
+ data: base64,
259
+ mimeType: "image/png",
260
+ };
261
+ }
262
+ }
263
+
264
+ return null;
265
+ }
@@ -0,0 +1,108 @@
1
+ // Fuzzy search. Matches if all query characters appear in order (not necessarily consecutive).
2
+ // Lower score = better match.
3
+
4
+ export interface FuzzyMatch {
5
+ matches: boolean;
6
+ score: number;
7
+ }
8
+
9
+ export function fuzzyMatch(query: string, text: string): FuzzyMatch {
10
+ const queryLower = query.toLowerCase();
11
+ const textLower = text.toLowerCase();
12
+
13
+ if (queryLower.length === 0) {
14
+ return { matches: true, score: 0 };
15
+ }
16
+
17
+ if (queryLower.length > textLower.length) {
18
+ return { matches: false, score: 0 };
19
+ }
20
+
21
+ let queryIndex = 0;
22
+ let score = 0;
23
+ let lastMatchIndex = -1;
24
+ let consecutiveMatches = 0;
25
+
26
+ for (let i = 0; i < textLower.length && queryIndex < queryLower.length; i++) {
27
+ if (textLower[i] === queryLower[queryIndex]) {
28
+ const isWordBoundary = i === 0 || /[\s\-_./]/.test(textLower[i - 1]!);
29
+
30
+ // Reward consecutive character matches (e.g., typing "foo" matches "foobar" better than "f_o_o")
31
+ if (lastMatchIndex === i - 1) {
32
+ consecutiveMatches++;
33
+ score -= consecutiveMatches * 5;
34
+ } else {
35
+ consecutiveMatches = 0;
36
+ // Penalize gaps between matched characters
37
+ if (lastMatchIndex >= 0) {
38
+ score += (i - lastMatchIndex - 1) * 2;
39
+ }
40
+ }
41
+
42
+ // Reward matches at word boundaries (start of words are more likely intentional targets)
43
+ if (isWordBoundary) {
44
+ score -= 10;
45
+ }
46
+
47
+ // Slight penalty for matches later in the string (prefer earlier matches)
48
+ score += i * 0.1;
49
+
50
+ lastMatchIndex = i;
51
+ queryIndex++;
52
+ }
53
+ }
54
+
55
+ // Not all query characters were found in order
56
+ if (queryIndex < queryLower.length) {
57
+ return { matches: false, score: 0 };
58
+ }
59
+
60
+ return { matches: true, score };
61
+ }
62
+
63
+ // Filter and sort items by fuzzy match quality (best matches first)
64
+ // Supports space-separated tokens: all tokens must match, sorted by match count then score
65
+ export function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) => string): T[] {
66
+ if (!query.trim()) {
67
+ return items;
68
+ }
69
+
70
+ // Split query into tokens
71
+ const tokens = query
72
+ .trim()
73
+ .split(/\s+/)
74
+ .filter((t) => t.length > 0);
75
+
76
+ if (tokens.length === 0) {
77
+ return items;
78
+ }
79
+
80
+ const results: { item: T; totalScore: number }[] = [];
81
+
82
+ for (const item of items) {
83
+ const text = getText(item);
84
+ let totalScore = 0;
85
+ let allMatch = true;
86
+
87
+ // Check each token against the text - ALL must match
88
+ for (const token of tokens) {
89
+ const match = fuzzyMatch(token, text);
90
+ if (match.matches) {
91
+ totalScore += match.score;
92
+ } else {
93
+ allMatch = false;
94
+ break;
95
+ }
96
+ }
97
+
98
+ // Only include if all tokens match
99
+ if (allMatch) {
100
+ results.push({ item, totalScore });
101
+ }
102
+ }
103
+
104
+ // Sort by score (asc, lower is better)
105
+ results.sort((a, b) => a.totalScore - b.totalScore);
106
+
107
+ return results.map((r) => r.item);
108
+ }
@@ -0,0 +1,30 @@
1
+ import { open } from "node:fs/promises";
2
+ import { fileTypeFromBuffer } from "file-type";
3
+
4
+ const IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
5
+
6
+ const FILE_TYPE_SNIFF_BYTES = 4100;
7
+
8
+ export async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {
9
+ const fileHandle = await open(filePath, "r");
10
+ try {
11
+ const buffer = Buffer.alloc(FILE_TYPE_SNIFF_BYTES);
12
+ const { bytesRead } = await fileHandle.read(buffer, 0, FILE_TYPE_SNIFF_BYTES, 0);
13
+ if (bytesRead === 0) {
14
+ return null;
15
+ }
16
+
17
+ const fileType = await fileTypeFromBuffer(buffer.subarray(0, bytesRead));
18
+ if (!fileType) {
19
+ return null;
20
+ }
21
+
22
+ if (!IMAGE_MIME_TYPES.has(fileType.mime)) {
23
+ return null;
24
+ }
25
+
26
+ return fileType.mime;
27
+ } finally {
28
+ await fileHandle.close();
29
+ }
30
+ }