cecli-dev 0.95.5__py3-none-any.whl

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 (366) hide show
  1. cecli/__init__.py +20 -0
  2. cecli/__main__.py +4 -0
  3. cecli/_version.py +34 -0
  4. cecli/args.py +1092 -0
  5. cecli/args_formatter.py +228 -0
  6. cecli/change_tracker.py +133 -0
  7. cecli/coders/__init__.py +38 -0
  8. cecli/coders/agent_coder.py +1872 -0
  9. cecli/coders/architect_coder.py +63 -0
  10. cecli/coders/ask_coder.py +8 -0
  11. cecli/coders/base_coder.py +3993 -0
  12. cecli/coders/chat_chunks.py +116 -0
  13. cecli/coders/context_coder.py +52 -0
  14. cecli/coders/copypaste_coder.py +269 -0
  15. cecli/coders/editblock_coder.py +656 -0
  16. cecli/coders/editblock_fenced_coder.py +9 -0
  17. cecli/coders/editblock_func_coder.py +140 -0
  18. cecli/coders/editor_diff_fenced_coder.py +8 -0
  19. cecli/coders/editor_editblock_coder.py +8 -0
  20. cecli/coders/editor_whole_coder.py +8 -0
  21. cecli/coders/help_coder.py +15 -0
  22. cecli/coders/patch_coder.py +705 -0
  23. cecli/coders/search_replace.py +757 -0
  24. cecli/coders/shell.py +37 -0
  25. cecli/coders/single_wholefile_func_coder.py +101 -0
  26. cecli/coders/udiff_coder.py +428 -0
  27. cecli/coders/udiff_simple.py +12 -0
  28. cecli/coders/wholefile_coder.py +143 -0
  29. cecli/coders/wholefile_func_coder.py +133 -0
  30. cecli/commands/__init__.py +192 -0
  31. cecli/commands/add.py +226 -0
  32. cecli/commands/agent.py +51 -0
  33. cecli/commands/architect.py +46 -0
  34. cecli/commands/ask.py +44 -0
  35. cecli/commands/chat_mode.py +0 -0
  36. cecli/commands/clear.py +37 -0
  37. cecli/commands/code.py +46 -0
  38. cecli/commands/command_prefix.py +44 -0
  39. cecli/commands/commit.py +52 -0
  40. cecli/commands/context.py +47 -0
  41. cecli/commands/context_blocks.py +124 -0
  42. cecli/commands/context_management.py +51 -0
  43. cecli/commands/copy.py +62 -0
  44. cecli/commands/copy_context.py +81 -0
  45. cecli/commands/core.py +287 -0
  46. cecli/commands/diff.py +68 -0
  47. cecli/commands/drop.py +217 -0
  48. cecli/commands/editor.py +78 -0
  49. cecli/commands/exit.py +55 -0
  50. cecli/commands/git.py +57 -0
  51. cecli/commands/help.py +140 -0
  52. cecli/commands/history_search.py +40 -0
  53. cecli/commands/lint.py +109 -0
  54. cecli/commands/list_sessions.py +56 -0
  55. cecli/commands/load.py +85 -0
  56. cecli/commands/load_session.py +48 -0
  57. cecli/commands/load_skill.py +68 -0
  58. cecli/commands/ls.py +75 -0
  59. cecli/commands/map.py +37 -0
  60. cecli/commands/map_refresh.py +35 -0
  61. cecli/commands/model.py +118 -0
  62. cecli/commands/models.py +41 -0
  63. cecli/commands/multiline_mode.py +38 -0
  64. cecli/commands/paste.py +91 -0
  65. cecli/commands/quit.py +32 -0
  66. cecli/commands/read_only.py +267 -0
  67. cecli/commands/read_only_stub.py +270 -0
  68. cecli/commands/reasoning_effort.py +70 -0
  69. cecli/commands/remove_skill.py +68 -0
  70. cecli/commands/report.py +40 -0
  71. cecli/commands/reset.py +88 -0
  72. cecli/commands/run.py +99 -0
  73. cecli/commands/save.py +49 -0
  74. cecli/commands/save_session.py +43 -0
  75. cecli/commands/settings.py +69 -0
  76. cecli/commands/test.py +58 -0
  77. cecli/commands/think_tokens.py +74 -0
  78. cecli/commands/tokens.py +207 -0
  79. cecli/commands/undo.py +145 -0
  80. cecli/commands/utils/__init__.py +0 -0
  81. cecli/commands/utils/base_command.py +131 -0
  82. cecli/commands/utils/helpers.py +142 -0
  83. cecli/commands/utils/registry.py +53 -0
  84. cecli/commands/utils/save_load_manager.py +98 -0
  85. cecli/commands/voice.py +78 -0
  86. cecli/commands/weak_model.py +123 -0
  87. cecli/commands/web.py +87 -0
  88. cecli/deprecated_args.py +185 -0
  89. cecli/diffs.py +129 -0
  90. cecli/dump.py +29 -0
  91. cecli/editor.py +147 -0
  92. cecli/exceptions.py +115 -0
  93. cecli/format_settings.py +26 -0
  94. cecli/help.py +119 -0
  95. cecli/help_pats.py +19 -0
  96. cecli/helpers/__init__.py +9 -0
  97. cecli/helpers/copypaste.py +123 -0
  98. cecli/helpers/coroutines.py +8 -0
  99. cecli/helpers/file_searcher.py +142 -0
  100. cecli/helpers/model_providers.py +552 -0
  101. cecli/helpers/plugin_manager.py +81 -0
  102. cecli/helpers/profiler.py +162 -0
  103. cecli/helpers/requests.py +77 -0
  104. cecli/helpers/similarity.py +98 -0
  105. cecli/helpers/skills.py +577 -0
  106. cecli/history.py +186 -0
  107. cecli/io.py +1782 -0
  108. cecli/linter.py +304 -0
  109. cecli/llm.py +101 -0
  110. cecli/main.py +1280 -0
  111. cecli/mcp/__init__.py +154 -0
  112. cecli/mcp/oauth.py +250 -0
  113. cecli/mcp/server.py +278 -0
  114. cecli/mdstream.py +243 -0
  115. cecli/models.py +1255 -0
  116. cecli/onboarding.py +301 -0
  117. cecli/prompts/__init__.py +0 -0
  118. cecli/prompts/agent.yml +71 -0
  119. cecli/prompts/architect.yml +35 -0
  120. cecli/prompts/ask.yml +31 -0
  121. cecli/prompts/base.yml +99 -0
  122. cecli/prompts/context.yml +60 -0
  123. cecli/prompts/copypaste.yml +5 -0
  124. cecli/prompts/editblock.yml +143 -0
  125. cecli/prompts/editblock_fenced.yml +106 -0
  126. cecli/prompts/editblock_func.yml +25 -0
  127. cecli/prompts/editor_diff_fenced.yml +115 -0
  128. cecli/prompts/editor_editblock.yml +121 -0
  129. cecli/prompts/editor_whole.yml +46 -0
  130. cecli/prompts/help.yml +37 -0
  131. cecli/prompts/patch.yml +110 -0
  132. cecli/prompts/single_wholefile_func.yml +24 -0
  133. cecli/prompts/udiff.yml +106 -0
  134. cecli/prompts/udiff_simple.yml +13 -0
  135. cecli/prompts/utils/__init__.py +0 -0
  136. cecli/prompts/utils/prompt_registry.py +167 -0
  137. cecli/prompts/utils/system.py +56 -0
  138. cecli/prompts/wholefile.yml +50 -0
  139. cecli/prompts/wholefile_func.yml +24 -0
  140. cecli/queries/tree-sitter-language-pack/README.md +7 -0
  141. cecli/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  142. cecli/queries/tree-sitter-language-pack/c-tags.scm +12 -0
  143. cecli/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  144. cecli/queries/tree-sitter-language-pack/clojure-tags.scm +12 -0
  145. cecli/queries/tree-sitter-language-pack/commonlisp-tags.scm +127 -0
  146. cecli/queries/tree-sitter-language-pack/cpp-tags.scm +18 -0
  147. cecli/queries/tree-sitter-language-pack/csharp-tags.scm +32 -0
  148. cecli/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  149. cecli/queries/tree-sitter-language-pack/dart-tags.scm +97 -0
  150. cecli/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  151. cecli/queries/tree-sitter-language-pack/elixir-tags.scm +59 -0
  152. cecli/queries/tree-sitter-language-pack/elm-tags.scm +22 -0
  153. cecli/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  154. cecli/queries/tree-sitter-language-pack/go-tags.scm +49 -0
  155. cecli/queries/tree-sitter-language-pack/java-tags.scm +26 -0
  156. cecli/queries/tree-sitter-language-pack/javascript-tags.scm +96 -0
  157. cecli/queries/tree-sitter-language-pack/lua-tags.scm +39 -0
  158. cecli/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
  159. cecli/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  160. cecli/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +101 -0
  161. cecli/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  162. cecli/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  163. cecli/queries/tree-sitter-language-pack/python-tags.scm +24 -0
  164. cecli/queries/tree-sitter-language-pack/r-tags.scm +27 -0
  165. cecli/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  166. cecli/queries/tree-sitter-language-pack/ruby-tags.scm +69 -0
  167. cecli/queries/tree-sitter-language-pack/rust-tags.scm +63 -0
  168. cecli/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  169. cecli/queries/tree-sitter-language-pack/swift-tags.scm +54 -0
  170. cecli/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  171. cecli/queries/tree-sitter-languages/README.md +24 -0
  172. cecli/queries/tree-sitter-languages/c-tags.scm +12 -0
  173. cecli/queries/tree-sitter-languages/c_sharp-tags.scm +52 -0
  174. cecli/queries/tree-sitter-languages/cpp-tags.scm +18 -0
  175. cecli/queries/tree-sitter-languages/dart-tags.scm +92 -0
  176. cecli/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  177. cecli/queries/tree-sitter-languages/elixir-tags.scm +59 -0
  178. cecli/queries/tree-sitter-languages/elm-tags.scm +22 -0
  179. cecli/queries/tree-sitter-languages/fortran-tags.scm +18 -0
  180. cecli/queries/tree-sitter-languages/go-tags.scm +36 -0
  181. cecli/queries/tree-sitter-languages/haskell-tags.scm +5 -0
  182. cecli/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  183. cecli/queries/tree-sitter-languages/java-tags.scm +26 -0
  184. cecli/queries/tree-sitter-languages/javascript-tags.scm +96 -0
  185. cecli/queries/tree-sitter-languages/julia-tags.scm +60 -0
  186. cecli/queries/tree-sitter-languages/kotlin-tags.scm +30 -0
  187. cecli/queries/tree-sitter-languages/matlab-tags.scm +10 -0
  188. cecli/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  189. cecli/queries/tree-sitter-languages/ocaml_interface-tags.scm +104 -0
  190. cecli/queries/tree-sitter-languages/php-tags.scm +32 -0
  191. cecli/queries/tree-sitter-languages/python-tags.scm +22 -0
  192. cecli/queries/tree-sitter-languages/ql-tags.scm +26 -0
  193. cecli/queries/tree-sitter-languages/ruby-tags.scm +69 -0
  194. cecli/queries/tree-sitter-languages/rust-tags.scm +63 -0
  195. cecli/queries/tree-sitter-languages/scala-tags.scm +64 -0
  196. cecli/queries/tree-sitter-languages/typescript-tags.scm +44 -0
  197. cecli/queries/tree-sitter-languages/zig-tags.scm +20 -0
  198. cecli/reasoning_tags.py +82 -0
  199. cecli/repo.py +626 -0
  200. cecli/repomap.py +1368 -0
  201. cecli/report.py +260 -0
  202. cecli/resources/__init__.py +3 -0
  203. cecli/resources/model-metadata.json +25751 -0
  204. cecli/resources/model-settings.yml +2394 -0
  205. cecli/resources/providers.json +67 -0
  206. cecli/run_cmd.py +143 -0
  207. cecli/scrape.py +295 -0
  208. cecli/sendchat.py +250 -0
  209. cecli/sessions.py +281 -0
  210. cecli/special.py +203 -0
  211. cecli/tools/__init__.py +72 -0
  212. cecli/tools/command.py +103 -0
  213. cecli/tools/command_interactive.py +113 -0
  214. cecli/tools/context_manager.py +175 -0
  215. cecli/tools/delete_block.py +154 -0
  216. cecli/tools/delete_line.py +120 -0
  217. cecli/tools/delete_lines.py +144 -0
  218. cecli/tools/extract_lines.py +281 -0
  219. cecli/tools/finished.py +35 -0
  220. cecli/tools/git_branch.py +132 -0
  221. cecli/tools/git_diff.py +49 -0
  222. cecli/tools/git_log.py +43 -0
  223. cecli/tools/git_remote.py +39 -0
  224. cecli/tools/git_show.py +37 -0
  225. cecli/tools/git_status.py +32 -0
  226. cecli/tools/grep.py +242 -0
  227. cecli/tools/indent_lines.py +195 -0
  228. cecli/tools/insert_block.py +263 -0
  229. cecli/tools/list_changes.py +71 -0
  230. cecli/tools/load_skill.py +51 -0
  231. cecli/tools/ls.py +77 -0
  232. cecli/tools/remove_skill.py +51 -0
  233. cecli/tools/replace_all.py +113 -0
  234. cecli/tools/replace_line.py +135 -0
  235. cecli/tools/replace_lines.py +180 -0
  236. cecli/tools/replace_text.py +186 -0
  237. cecli/tools/show_numbered_context.py +137 -0
  238. cecli/tools/thinking.py +52 -0
  239. cecli/tools/undo_change.py +82 -0
  240. cecli/tools/update_todo_list.py +148 -0
  241. cecli/tools/utils/base_tool.py +64 -0
  242. cecli/tools/utils/helpers.py +359 -0
  243. cecli/tools/utils/output.py +119 -0
  244. cecli/tools/utils/registry.py +145 -0
  245. cecli/tools/view_files_matching.py +138 -0
  246. cecli/tools/view_files_with_symbol.py +117 -0
  247. cecli/tui/__init__.py +83 -0
  248. cecli/tui/app.py +971 -0
  249. cecli/tui/io.py +566 -0
  250. cecli/tui/styles.tcss +117 -0
  251. cecli/tui/widgets/__init__.py +19 -0
  252. cecli/tui/widgets/completion_bar.py +331 -0
  253. cecli/tui/widgets/file_list.py +76 -0
  254. cecli/tui/widgets/footer.py +165 -0
  255. cecli/tui/widgets/input_area.py +320 -0
  256. cecli/tui/widgets/key_hints.py +16 -0
  257. cecli/tui/widgets/output.py +354 -0
  258. cecli/tui/widgets/status_bar.py +279 -0
  259. cecli/tui/worker.py +160 -0
  260. cecli/urls.py +16 -0
  261. cecli/utils.py +499 -0
  262. cecli/versioncheck.py +90 -0
  263. cecli/voice.py +90 -0
  264. cecli/waiting.py +38 -0
  265. cecli/watch.py +316 -0
  266. cecli/watch_prompts.py +12 -0
  267. cecli/website/Gemfile +8 -0
  268. cecli/website/_includes/blame.md +162 -0
  269. cecli/website/_includes/get-started.md +22 -0
  270. cecli/website/_includes/help-tip.md +5 -0
  271. cecli/website/_includes/help.md +24 -0
  272. cecli/website/_includes/install.md +5 -0
  273. cecli/website/_includes/keys.md +4 -0
  274. cecli/website/_includes/model-warnings.md +67 -0
  275. cecli/website/_includes/multi-line.md +22 -0
  276. cecli/website/_includes/python-m-aider.md +5 -0
  277. cecli/website/_includes/recording.css +228 -0
  278. cecli/website/_includes/recording.md +34 -0
  279. cecli/website/_includes/replit-pipx.md +9 -0
  280. cecli/website/_includes/works-best.md +1 -0
  281. cecli/website/_sass/custom/custom.scss +103 -0
  282. cecli/website/docs/config/adv-model-settings.md +2498 -0
  283. cecli/website/docs/config/agent-mode.md +320 -0
  284. cecli/website/docs/config/aider_conf.md +548 -0
  285. cecli/website/docs/config/api-keys.md +90 -0
  286. cecli/website/docs/config/custom-commands.md +187 -0
  287. cecli/website/docs/config/dotenv.md +493 -0
  288. cecli/website/docs/config/editor.md +127 -0
  289. cecli/website/docs/config/mcp.md +210 -0
  290. cecli/website/docs/config/model-aliases.md +173 -0
  291. cecli/website/docs/config/options.md +890 -0
  292. cecli/website/docs/config/reasoning.md +210 -0
  293. cecli/website/docs/config/skills.md +172 -0
  294. cecli/website/docs/config/tui.md +126 -0
  295. cecli/website/docs/config.md +44 -0
  296. cecli/website/docs/faq.md +379 -0
  297. cecli/website/docs/git.md +76 -0
  298. cecli/website/docs/index.md +47 -0
  299. cecli/website/docs/install/codespaces.md +39 -0
  300. cecli/website/docs/install/docker.md +48 -0
  301. cecli/website/docs/install/optional.md +100 -0
  302. cecli/website/docs/install/replit.md +8 -0
  303. cecli/website/docs/install.md +115 -0
  304. cecli/website/docs/languages.md +264 -0
  305. cecli/website/docs/legal/contributor-agreement.md +111 -0
  306. cecli/website/docs/legal/privacy.md +104 -0
  307. cecli/website/docs/llms/anthropic.md +77 -0
  308. cecli/website/docs/llms/azure.md +48 -0
  309. cecli/website/docs/llms/bedrock.md +132 -0
  310. cecli/website/docs/llms/cohere.md +34 -0
  311. cecli/website/docs/llms/deepseek.md +32 -0
  312. cecli/website/docs/llms/gemini.md +49 -0
  313. cecli/website/docs/llms/github.md +111 -0
  314. cecli/website/docs/llms/groq.md +36 -0
  315. cecli/website/docs/llms/lm-studio.md +39 -0
  316. cecli/website/docs/llms/ollama.md +75 -0
  317. cecli/website/docs/llms/openai-compat.md +39 -0
  318. cecli/website/docs/llms/openai.md +58 -0
  319. cecli/website/docs/llms/openrouter.md +78 -0
  320. cecli/website/docs/llms/other.md +117 -0
  321. cecli/website/docs/llms/vertex.md +50 -0
  322. cecli/website/docs/llms/warnings.md +10 -0
  323. cecli/website/docs/llms/xai.md +53 -0
  324. cecli/website/docs/llms.md +54 -0
  325. cecli/website/docs/more/analytics.md +127 -0
  326. cecli/website/docs/more/edit-formats.md +116 -0
  327. cecli/website/docs/more/infinite-output.md +192 -0
  328. cecli/website/docs/more-info.md +8 -0
  329. cecli/website/docs/recordings/auto-accept-architect.md +31 -0
  330. cecli/website/docs/recordings/dont-drop-original-read-files.md +35 -0
  331. cecli/website/docs/recordings/index.md +21 -0
  332. cecli/website/docs/recordings/model-accepts-settings.md +69 -0
  333. cecli/website/docs/recordings/tree-sitter-language-pack.md +80 -0
  334. cecli/website/docs/repomap.md +112 -0
  335. cecli/website/docs/scripting.md +100 -0
  336. cecli/website/docs/sessions.md +213 -0
  337. cecli/website/docs/troubleshooting/aider-not-found.md +24 -0
  338. cecli/website/docs/troubleshooting/edit-errors.md +76 -0
  339. cecli/website/docs/troubleshooting/imports.md +62 -0
  340. cecli/website/docs/troubleshooting/models-and-keys.md +54 -0
  341. cecli/website/docs/troubleshooting/support.md +79 -0
  342. cecli/website/docs/troubleshooting/token-limits.md +96 -0
  343. cecli/website/docs/troubleshooting/warnings.md +12 -0
  344. cecli/website/docs/troubleshooting.md +11 -0
  345. cecli/website/docs/usage/browser.md +57 -0
  346. cecli/website/docs/usage/caching.md +49 -0
  347. cecli/website/docs/usage/commands.md +133 -0
  348. cecli/website/docs/usage/conventions.md +119 -0
  349. cecli/website/docs/usage/copypaste.md +136 -0
  350. cecli/website/docs/usage/images-urls.md +48 -0
  351. cecli/website/docs/usage/lint-test.md +118 -0
  352. cecli/website/docs/usage/modes.md +211 -0
  353. cecli/website/docs/usage/not-code.md +179 -0
  354. cecli/website/docs/usage/notifications.md +87 -0
  355. cecli/website/docs/usage/tips.md +79 -0
  356. cecli/website/docs/usage/tutorials.md +30 -0
  357. cecli/website/docs/usage/voice.md +121 -0
  358. cecli/website/docs/usage/watch.md +294 -0
  359. cecli/website/docs/usage.md +102 -0
  360. cecli/website/share/index.md +101 -0
  361. cecli_dev-0.95.5.dist-info/METADATA +549 -0
  362. cecli_dev-0.95.5.dist-info/RECORD +366 -0
  363. cecli_dev-0.95.5.dist-info/WHEEL +5 -0
  364. cecli_dev-0.95.5.dist-info/entry_points.txt +4 -0
  365. cecli_dev-0.95.5.dist-info/licenses/LICENSE.txt +202 -0
  366. cecli_dev-0.95.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,119 @@
1
+ import json
2
+ import re
3
+
4
+
5
+ def print_tool_response(coder, mcp_server, tool_response):
6
+ """
7
+ Format the output for display.
8
+ Prints a Header to identify the tool, a body for the relevant information
9
+ for the user and a footer for verbose information
10
+
11
+ Args:
12
+ coder: An instance of base_coder
13
+ mcp_server: An mcp server instance
14
+ tool_response: a tool_response dictionary
15
+ """
16
+ tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
17
+ tool_body(coder=coder, tool_response=tool_response)
18
+ tool_footer(coder=coder, tool_response=tool_response)
19
+
20
+
21
+ def tool_header(coder, mcp_server, tool_response):
22
+ """
23
+ Prints the header for the tool call output
24
+
25
+ Args:
26
+ coder: An instance of base_coder
27
+ mcp_server: An mcp server instance
28
+ tool_response: a tool_response dictionary
29
+ """
30
+ color_start, color_end = color_markers(coder)
31
+
32
+ coder.io.tool_output(
33
+ f"{color_start}Tool Call:{color_end} {mcp_server.name} • {tool_response.function.name}",
34
+ type="Tool Call",
35
+ )
36
+
37
+
38
+ def tool_body(coder, tool_response):
39
+ """
40
+ Prints the output body of a tool call as the raw json returned from the model
41
+
42
+ Args:
43
+ coder: An instance of base_coder
44
+ tool_response: a tool_response dictionary
45
+ """
46
+ color_start, color_end = color_markers(coder)
47
+
48
+ # Parse and format arguments as headers with values
49
+ if tool_response.function.arguments:
50
+ # For non-replace tools, show raw arguments
51
+ raw_args = tool_response.function.arguments
52
+ coder.io.tool_output(f"{color_start}Arguments:{color_end} {raw_args}")
53
+
54
+
55
+ def tool_body_unwrapped(coder, tool_response):
56
+ """
57
+ Prints the output body of a tool call with the argument
58
+ and content sections separated
59
+
60
+ Args:
61
+ coder: An instance of base_coder
62
+ tool_response: a tool_response dictionary
63
+ """
64
+
65
+ color_start, color_end = color_markers(coder)
66
+
67
+ try:
68
+ args_dict = json.loads(tool_response.function.arguments)
69
+ first_key = True
70
+ for key, value in args_dict.items():
71
+ # Convert explicit \\n sequences to actual newlines using regex
72
+ # Only match \\n that is not preceded by any other backslashes
73
+ if isinstance(value, str):
74
+ value = re.sub(r"(?<!\\)\\n", "\n", value)
75
+ # Add extra newline before first key/header
76
+ if first_key:
77
+ coder.io.tool_output("\n")
78
+ first_key = False
79
+ coder.io.tool_output(f"{color_start}{key}:{color_end}")
80
+ # Split the value by newlines and output each line separately
81
+ if isinstance(value, str):
82
+ for line in value.split("\n"):
83
+ coder.io.tool_output(f"{line}")
84
+ else:
85
+ coder.io.tool_output(f"{str(value)}")
86
+ coder.io.tool_output("")
87
+ except json.JSONDecodeError:
88
+ # If JSON parsing fails, show raw arguments
89
+ raw_args = tool_response.function.arguments
90
+ coder.io.tool_output(f"{color_start}Arguments:{color_end} {raw_args}")
91
+
92
+
93
+ def tool_footer(coder, tool_response):
94
+ """
95
+ Prints the output footer of a tool call, generally a new line
96
+ But can include id's if ran in verbose mode
97
+
98
+ Args:
99
+ coder: An instance of base_coder
100
+ tool_response: a tool_response dictionary
101
+ """
102
+ if coder.verbose:
103
+ coder.io.tool_output(f"Tool ID: {tool_response.id}")
104
+ coder.io.tool_output(f"Tool type: {tool_response.type}")
105
+
106
+ coder.io.tool_output("\n", type="tool-footer")
107
+
108
+
109
+ def color_markers(coder):
110
+ """
111
+ Rich.console color markers
112
+
113
+ Args:
114
+ coder: An instance of base_coder
115
+ """
116
+ color_start = "[bright_cyan]" if coder.pretty else ""
117
+ color_end = "[/bright_cyan]" if coder.pretty else ""
118
+
119
+ return color_start, color_end
@@ -0,0 +1,145 @@
1
+ """
2
+ Registry for tool discovery and management.
3
+
4
+ Similar to the command registry in cecli/commands/utils/registry.py,
5
+ this provides centralized tool registration, discovery, and filtering
6
+ based on agent configuration.
7
+ """
8
+
9
+ from pathlib import Path
10
+ from typing import Dict, List, Optional, Set, Type
11
+
12
+ from cecli.helpers import plugin_manager
13
+ from cecli.tools import TOOL_MODULES
14
+
15
+
16
+ class ToolRegistry:
17
+ """Registry for tool discovery and management."""
18
+
19
+ _tools: Dict[str, Type] = {} # normalized name -> Tool class
20
+ _essential_tools: Set[str] = {"contextmanager", "replacetext", "finished"}
21
+ _registry: Dict[str, Type] = {} # cached filtered registry
22
+
23
+ @classmethod
24
+ def register(cls, tool_class):
25
+ """Register a tool class."""
26
+ name = tool_class.NORM_NAME
27
+ cls._tools[name] = tool_class
28
+
29
+ @classmethod
30
+ def get_tool(cls, name: str) -> Optional[Type]:
31
+ """Get tool class by normalized name."""
32
+ return cls._tools.get(name, None)
33
+
34
+ @classmethod
35
+ def list_tools(cls) -> List[str]:
36
+ """List all registered tool names."""
37
+ return list(cls._tools.keys())
38
+
39
+ @classmethod
40
+ def build_registry(cls, agent_config: Optional[Dict] = None) -> Dict[str, Type]:
41
+ """
42
+ Build a filtered registry of tools based on agent configuration.
43
+
44
+ Args:
45
+ agent_config: Agent configuration dictionary with optional
46
+ tools_includelist/tools_excludelist keys
47
+
48
+ Returns:
49
+ Dictionary mapping normalized tool names to tool classes
50
+ """
51
+ if agent_config is None:
52
+ agent_config = {}
53
+
54
+ # Load tools from tool_paths if specified
55
+ tool_paths = agent_config.get("tool_paths", [])
56
+
57
+ for tool_path in tool_paths:
58
+ path = Path(tool_path)
59
+ if path.is_dir():
60
+ # Find all Python files in the directory
61
+ for py_file in path.glob("*.py"):
62
+ try:
63
+ # Load the module using plugin_manager
64
+ module = plugin_manager.load_module(str(py_file))
65
+ # Check if module has a Tool class
66
+ if hasattr(module, "Tool"):
67
+ cls.register(module.Tool)
68
+ except Exception as e:
69
+ # Log error but continue with other files
70
+ print(f"Error loading tool from {py_file}: {e}")
71
+ else:
72
+ # If it's a file, try to load it directly
73
+ if path.exists() and path.suffix == ".py":
74
+ try:
75
+ module = plugin_manager.load_module(str(path))
76
+ if hasattr(module, "Tool"):
77
+ cls.register(module.Tool)
78
+ except Exception as e:
79
+ print(f"Error loading tool from {path}: {e}")
80
+
81
+ # Get include/exclude lists from config
82
+ tools_includelist = agent_config.get(
83
+ "tools_includelist", agent_config.get("tools_whitelist", [])
84
+ )
85
+ tools_excludelist = agent_config.get(
86
+ "tools_excludelist", agent_config.get("tools_blacklist", [])
87
+ )
88
+
89
+ registry = {}
90
+
91
+ for tool_name, tool_class in cls._tools.items():
92
+ should_include = True
93
+
94
+ # Apply include list if specified
95
+ if tools_includelist:
96
+ should_include = tool_name in tools_includelist
97
+
98
+ # Essential tools are always included
99
+ if tool_name in cls._essential_tools:
100
+ should_include = True
101
+
102
+ # Apply exclude list (unless essential)
103
+ if tool_name in tools_excludelist and tool_name not in cls._essential_tools:
104
+ should_include = False
105
+
106
+ if should_include:
107
+ registry[tool_name] = tool_class
108
+
109
+ # Store the built registry in the class attribute
110
+ cls._registry = registry
111
+ return registry
112
+
113
+ @classmethod
114
+ def get_registered_tools(cls) -> List[str]:
115
+ """
116
+ Get the list of registered tools from the cached registry.
117
+
118
+ Returns:
119
+ List of normalized tool names that are currently registered
120
+
121
+ Raises:
122
+ RuntimeError: If no tools are registered (registry is empty)
123
+ """
124
+ if not cls._registry:
125
+ raise RuntimeError(
126
+ "No tools are currently registered in the registry. "
127
+ "Call build_registry() first to initialize the registry."
128
+ )
129
+ return list(cls._registry.keys())
130
+
131
+ @classmethod
132
+ def initialize_registry(cls):
133
+ """Initialize the registry by importing and registering all tools."""
134
+ # Clear existing registry
135
+ cls._tools.clear()
136
+
137
+ # Register all tools from TOOL_MODULES
138
+ for module in TOOL_MODULES:
139
+ if hasattr(module, "Tool"):
140
+ tool_class = module.Tool
141
+ cls.register(tool_class)
142
+
143
+
144
+ # Initialize the registry when module is imported
145
+ ToolRegistry.initialize_registry()
@@ -0,0 +1,138 @@
1
+ import fnmatch
2
+ import re
3
+
4
+ from cecli.tools.utils.base_tool import BaseTool
5
+
6
+
7
+ class Tool(BaseTool):
8
+ NORM_NAME = "viewfilesmatching"
9
+ SCHEMA = {
10
+ "type": "function",
11
+ "function": {
12
+ "name": "ViewFilesMatching",
13
+ "description": "View files containing a specific pattern.",
14
+ "parameters": {
15
+ "type": "object",
16
+ "properties": {
17
+ "pattern": {
18
+ "type": "string",
19
+ "description": "The pattern to search for in file contents.",
20
+ },
21
+ "file_pattern": {
22
+ "type": "string",
23
+ "description": (
24
+ "An optional glob pattern to filter which files are searched."
25
+ ),
26
+ },
27
+ "regex": {
28
+ "type": "boolean",
29
+ "description": (
30
+ "Whether the pattern is a regular expression. Defaults to False."
31
+ ),
32
+ },
33
+ },
34
+ "required": ["pattern"],
35
+ },
36
+ },
37
+ }
38
+
39
+ @classmethod
40
+ def execute(cls, coder, pattern, file_pattern=None, regex=False):
41
+ """
42
+ Search for pattern (literal string or regex) in files and return matching files as text.
43
+
44
+ Args:
45
+ coder: The Coder instance.
46
+ pattern (str): The pattern to search for.
47
+ Treated as a literal string by default.
48
+ file_pattern (str, optional): Glob pattern to filter which files are searched.
49
+ Defaults to None (search all files).
50
+ regex (bool, optional): If True, treat pattern as a regular expression.
51
+ Defaults to False.
52
+
53
+ This tool lets the LLM search for content within files, mimicking
54
+ how a developer would use grep or regex search to find relevant code.
55
+ """
56
+ try:
57
+ # Get list of files to search
58
+ if file_pattern:
59
+ # Use glob pattern to filter files
60
+ all_files = coder.get_all_relative_files()
61
+ files_to_search = []
62
+ for file in all_files:
63
+ if fnmatch.fnmatch(file, file_pattern):
64
+ files_to_search.append(file)
65
+
66
+ if not files_to_search:
67
+ return f"No files matching '{file_pattern}' to search for pattern '{pattern}'"
68
+ else:
69
+ # Search all files if no pattern provided
70
+ files_to_search = coder.get_all_relative_files()
71
+
72
+ # Search for pattern in files
73
+ matches = {}
74
+ num_matches = 0
75
+ inspecific_search_flag = False
76
+
77
+ for file in files_to_search:
78
+ abs_path = coder.abs_root_path(file)
79
+
80
+ if num_matches >= 25:
81
+ inspecific_search_flag = True
82
+
83
+ try:
84
+ if coder.repo.ignored_file(abs_path):
85
+ continue
86
+
87
+ with open(abs_path, "r", encoding="utf-8") as f:
88
+ content = f.read()
89
+ match_count = 0
90
+ if regex:
91
+ try:
92
+ matches_found = re.findall(pattern, content)
93
+ match_count = len(matches_found)
94
+ except re.error as e:
95
+ # Handle invalid regex patterns gracefully
96
+ coder.io.tool_error(f"Invalid regex pattern '{pattern}': {e}")
97
+ # Skip this file for this search if regex is invalid
98
+ continue
99
+ else:
100
+ # Exact string matching
101
+ match_count = content.count(pattern)
102
+
103
+ if match_count > 0:
104
+ matches[file] = match_count
105
+ num_matches += 1
106
+ except Exception:
107
+ # Skip files that can't be read (binary, etc.)
108
+ pass
109
+
110
+ # Return formatted text instead of adding to context
111
+ if matches:
112
+ # Sort by number of matches (most matches first)
113
+ sorted_matches = sorted(matches.items(), key=lambda x: x[1], reverse=True)
114
+ match_list = [f"{file} ({count} matches)" for file, count in sorted_matches]
115
+
116
+ if len(matches) > 10:
117
+ result = (
118
+ f"Found '{pattern}' in {len(matches)} files:"
119
+ f" {', '.join(match_list[:10])} and {len(matches) - 10} more"
120
+ "\nTry more specific search terms going forward"
121
+ if inspecific_search_flag
122
+ else ""
123
+ )
124
+ coder.io.tool_output(f"🔍 Found '{pattern}' in {len(matches)} files")
125
+ else:
126
+ result = f"Found '{pattern}' in {len(matches)} files: {', '.join(match_list)}"
127
+ coder.io.tool_output(
128
+ f"🔍 Found '{pattern}' in:"
129
+ f" {', '.join(match_list[:5])}{' and more' if len(matches) > 5 else ''}"
130
+ )
131
+
132
+ return result
133
+ else:
134
+ coder.io.tool_output(f"⚠️ Pattern '{pattern}' not found in any files")
135
+ return "Pattern not found in any files"
136
+ except Exception as e:
137
+ coder.io.tool_error(f"Error in ViewFilesMatching: {str(e)}")
138
+ return f"Error: {str(e)}"
@@ -0,0 +1,117 @@
1
+ from cecli.tools.utils.base_tool import BaseTool
2
+
3
+
4
+ class Tool(BaseTool):
5
+ NORM_NAME = "viewfileswithsymbol"
6
+ SCHEMA = {
7
+ "type": "function",
8
+ "function": {
9
+ "name": "ViewFilesWithSymbol",
10
+ "description": "View files that contain a specific symbol (e.g., class, function).",
11
+ "parameters": {
12
+ "type": "object",
13
+ "properties": {
14
+ "symbol": {
15
+ "type": "string",
16
+ "description": "The symbol to search for.",
17
+ },
18
+ },
19
+ "required": ["symbol"],
20
+ },
21
+ },
22
+ }
23
+
24
+ @classmethod
25
+ def execute(cls, coder, symbol):
26
+ """
27
+ Find files containing a symbol using RepoMap and return them as text.
28
+ Checks files already in context first.
29
+ """
30
+ if not coder.repo_map:
31
+ coder.io.tool_output("⚠️ Repo map not available, cannot use ViewFilesWithSymbol tool.")
32
+ return "Repo map not available"
33
+
34
+ if not symbol:
35
+ return "Error: Missing 'symbol' parameter for ViewFilesWithSymbol"
36
+
37
+ # 1. Check files already in context
38
+ files_in_context = list(coder.abs_fnames) + list(coder.abs_read_only_fnames)
39
+ found_in_context = []
40
+ for abs_fname in files_in_context:
41
+ rel_fname = coder.get_rel_fname(abs_fname)
42
+ try:
43
+ # Use get_tags for consistency with RepoMap usage elsewhere for now.
44
+ tags = coder.repo_map.get_tags(abs_fname, rel_fname)
45
+ for tag in tags:
46
+ if tag.name == symbol:
47
+ found_in_context.append(rel_fname)
48
+ break # Found in this file, move to next
49
+ except Exception as e:
50
+ coder.io.tool_warning(
51
+ f"Could not get symbols for {rel_fname} while checking context: {e}"
52
+ )
53
+
54
+ if found_in_context:
55
+ # Symbol found in already loaded files. Report this and stop.
56
+ file_list = ", ".join(sorted(list(set(found_in_context))))
57
+ coder.io.tool_output(f"Symbol '{symbol}' found in already loaded file(s): {file_list}")
58
+ return f"Symbol '{symbol}' found in already loaded file(s): {file_list}"
59
+
60
+ # 2. If not found in context, search the repository using RepoMap
61
+ coder.io.tool_output(f"🔎 Searching for symbol '{symbol}' in repository...")
62
+ try:
63
+ found_files = set()
64
+ current_context_files = coder.abs_fnames | coder.abs_read_only_fnames
65
+ files_to_search = set(coder.get_all_abs_files()) - current_context_files
66
+
67
+ rel_fname_to_abs = {}
68
+ all_tags = []
69
+
70
+ for fname in files_to_search:
71
+ rel_fname = coder.get_rel_fname(fname)
72
+ rel_fname_to_abs[rel_fname] = fname
73
+ try:
74
+ tags = coder.repo_map.get_tags(fname, rel_fname)
75
+ all_tags.extend(tags)
76
+ except Exception as e:
77
+ coder.io.tool_warning(f"Could not get tags for {rel_fname}: {e}")
78
+
79
+ # Find matching symbols
80
+ for tag in all_tags:
81
+ if tag.name == symbol:
82
+ # Use absolute path directly if available, otherwise resolve from relative path
83
+ abs_fname = rel_fname_to_abs.get(tag.rel_fname) or coder.abs_root_path(
84
+ tag.fname
85
+ )
86
+ if (
87
+ abs_fname in files_to_search
88
+ ): # Ensure we only add files we intended to search
89
+ found_files.add(coder.get_rel_fname(abs_fname))
90
+
91
+ # Return formatted text instead of adding to context
92
+ if found_files:
93
+ found_files_list = sorted(list(found_files))
94
+ if len(found_files) > 10:
95
+ result = (
96
+ f"Found symbol '{symbol}' in {len(found_files)} files:"
97
+ f" {', '.join(found_files_list[:10])} and {len(found_files) - 10} more"
98
+ )
99
+ coder.io.tool_output(f"🔎 Found '{symbol}' in {len(found_files)} files")
100
+ else:
101
+ result = (
102
+ f"Found symbol '{symbol}' in {len(found_files)} files:"
103
+ f" {', '.join(found_files_list)}"
104
+ )
105
+ coder.io.tool_output(
106
+ f"🔎 Found '{symbol}' in files:"
107
+ f" {', '.join(found_files_list[:5])}{' and more' if len(found_files) > 5 else ''}"
108
+ )
109
+
110
+ return result
111
+ else:
112
+ coder.io.tool_output(f"⚠️ Symbol '{symbol}' not found in searchable files")
113
+ return f"Symbol '{symbol}' not found in searchable files"
114
+
115
+ except Exception as e:
116
+ coder.io.tool_error(f"Error in ViewFilesWithSymbol: {str(e)}")
117
+ return f"Error: {str(e)}"
cecli/tui/__init__.py ADDED
@@ -0,0 +1,83 @@
1
+ """Textual TUI interface for cecli.
2
+
3
+ This package provides an experimental TUI (Terminal User Interface) for cecli
4
+ using the Textual framework. Launch with: cecli --tui
5
+ """
6
+
7
+ import queue
8
+ import weakref
9
+
10
+ from .app import TUI
11
+ from .io import TextualInputOutput
12
+ from .worker import CoderWorker
13
+
14
+ __all__ = ["TUI", "TextualInputOutput", "CoderWorker", "create_tui_io", "launch_tui"]
15
+
16
+
17
+ def create_tui_io(args, editing_mode):
18
+ """Create TUI IO instance and communication queues.
19
+
20
+ Args:
21
+ args: Parsed command line arguments
22
+ editing_mode: EditingMode.VI or EditingMode.EMACS
23
+
24
+ Returns:
25
+ Tuple of (io, output_queue, input_queue)
26
+ """
27
+ output_queue = queue.Queue()
28
+ input_queue = queue.Queue()
29
+
30
+ io = TextualInputOutput(
31
+ output_queue=output_queue,
32
+ input_queue=input_queue,
33
+ pretty=True,
34
+ yes=args.yes_always,
35
+ input_history_file=args.input_history_file,
36
+ chat_history_file=args.chat_history_file,
37
+ input=None,
38
+ output=None,
39
+ user_input_color=args.user_input_color,
40
+ tool_output_color=args.tool_output_color,
41
+ tool_warning_color=args.tool_warning_color,
42
+ tool_error_color=args.tool_error_color,
43
+ completion_menu_color=args.completion_menu_color,
44
+ completion_menu_bg_color=args.completion_menu_bg_color,
45
+ completion_menu_current_color=args.completion_menu_current_color,
46
+ completion_menu_current_bg_color=args.completion_menu_current_bg_color,
47
+ assistant_output_color=args.assistant_output_color,
48
+ code_theme=args.code_theme,
49
+ dry_run=args.dry_run,
50
+ encoding=args.encoding,
51
+ line_endings=args.line_endings,
52
+ editingmode=editing_mode,
53
+ fancy_input=False,
54
+ multiline_mode=args.multiline,
55
+ notifications=args.notifications,
56
+ notifications_command=args.notifications_command,
57
+ verbose=args.verbose,
58
+ )
59
+
60
+ return io, output_queue, input_queue
61
+
62
+
63
+ async def launch_tui(coder, output_queue, input_queue, args):
64
+ """Launch the TUI application.
65
+
66
+ Args:
67
+ coder: Initialized Coder instance
68
+ output_queue: Queue for output messages
69
+ input_queue: Queue for input messages
70
+
71
+ Returns:
72
+ Exit code from TUI
73
+ """
74
+ worker = CoderWorker(coder, output_queue, input_queue)
75
+ app = TUI(worker, output_queue, input_queue, args)
76
+
77
+ # Set weak reference to TUI app on the coder instance
78
+ coder.tui = weakref.ref(app)
79
+
80
+ return_code = await app.run_async()
81
+
82
+ worker.stop()
83
+ return return_code if return_code else 0