cecli-dev 0.93.1__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.93.1.dist-info/METADATA +549 -0
  362. cecli_dev-0.93.1.dist-info/RECORD +366 -0
  363. cecli_dev-0.93.1.dist-info/WHEEL +5 -0
  364. cecli_dev-0.93.1.dist-info/entry_points.txt +4 -0
  365. cecli_dev-0.93.1.dist-info/licenses/LICENSE.txt +202 -0
  366. cecli_dev-0.93.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,263 @@
1
+ import re
2
+ import traceback
3
+
4
+ from cecli.tools.utils.base_tool import BaseTool
5
+ from cecli.tools.utils.helpers import (
6
+ ToolError,
7
+ apply_change,
8
+ find_pattern_indices,
9
+ format_tool_result,
10
+ generate_unified_diff_snippet,
11
+ handle_tool_error,
12
+ is_provided,
13
+ select_occurrence_index,
14
+ validate_file_for_edit,
15
+ )
16
+ from cecli.tools.utils.output import tool_body_unwrapped, tool_footer, tool_header
17
+
18
+
19
+ class Tool(BaseTool):
20
+ NORM_NAME = "insertblock"
21
+ SCHEMA = {
22
+ "type": "function",
23
+ "function": {
24
+ "name": "InsertBlock",
25
+ "description": (
26
+ "Insert a block of content into a file. Mutually Exclusive Parameters:"
27
+ " after_pattern, before_pattern, position."
28
+ ),
29
+ "parameters": {
30
+ "type": "object",
31
+ "properties": {
32
+ "file_path": {"type": "string"},
33
+ "content": {"type": "string"},
34
+ "after_pattern": {"type": "string"},
35
+ "before_pattern": {"type": "string"},
36
+ "occurrence": {"type": "integer", "default": 1},
37
+ "change_id": {"type": "string"},
38
+ "dry_run": {"type": "boolean", "default": False},
39
+ "position": {"type": "string", "enum": ["top", "bottom", ""]},
40
+ "auto_indent": {"type": "boolean", "default": True},
41
+ "use_regex": {"type": "boolean", "default": False},
42
+ },
43
+ "required": ["file_path", "content"],
44
+ },
45
+ },
46
+ }
47
+
48
+ @classmethod
49
+ def execute(
50
+ cls,
51
+ coder,
52
+ file_path,
53
+ content,
54
+ after_pattern=None,
55
+ before_pattern=None,
56
+ occurrence=1,
57
+ change_id=None,
58
+ dry_run=False,
59
+ position=None,
60
+ auto_indent=True,
61
+ use_regex=False,
62
+ ):
63
+ """
64
+ Insert a block of text after or before a specified pattern using utility functions.
65
+
66
+ Args:
67
+ coder: The coder instance
68
+ file_path: Path to the file to modify
69
+ content: The content to insert
70
+ after_pattern: Pattern to insert after (mutually exclusive with before_pattern and position)
71
+ before_pattern: Pattern to insert before (mutually exclusive with after_pattern and position)
72
+ occurrence: Which occurrence of the pattern to use (1-based, or -1 for last)
73
+ change_id: Optional ID for tracking changes
74
+ dry_run: If True, only simulate the change
75
+ position: Special position like "top" or "bottom" (mutually exclusive with before_pattern and after_pattern)
76
+ auto_indent: If True, automatically adjust indentation of inserted content
77
+ use_regex: If True, treat patterns as regular expressions
78
+ """
79
+ tool_name = "InsertBlock"
80
+ try:
81
+ # 1. Validate parameters
82
+ if sum(is_provided(x) for x in [after_pattern, before_pattern, position]) != 1:
83
+ raise ToolError(
84
+ "Must specify exactly one of: after_pattern, before_pattern, or position"
85
+ )
86
+
87
+ # 2. Validate file and get content
88
+ abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
89
+ lines = original_content.splitlines()
90
+
91
+ # Handle empty files
92
+ if not lines:
93
+ lines = [""]
94
+
95
+ # 3. Determine insertion point
96
+ insertion_line_idx = 0
97
+ pattern_type = ""
98
+ pattern_desc = ""
99
+ occurrence_str = ""
100
+
101
+ if position:
102
+ # Handle special positions
103
+ if position == "start_of_file" or position == "top":
104
+ insertion_line_idx = 0
105
+ pattern_type = "at start of"
106
+ elif position == "end_of_file" or position == "bottom":
107
+ insertion_line_idx = len(lines)
108
+ pattern_type = "at end of"
109
+ else:
110
+ raise ToolError(
111
+ f"Invalid position: '{position}'. Valid values are 'start_of_file' or"
112
+ " 'end_of_file'"
113
+ )
114
+ else:
115
+ # Handle pattern-based insertion
116
+ pattern = after_pattern if after_pattern else before_pattern
117
+ pattern_type = "after" if after_pattern else "before"
118
+ pattern_desc = f"Pattern '{pattern}'"
119
+
120
+ # Find pattern matches
121
+ pattern_line_indices = find_pattern_indices(lines, pattern, use_regex=use_regex)
122
+
123
+ # Select the target occurrence
124
+ target_line_idx = select_occurrence_index(
125
+ pattern_line_indices, occurrence, pattern_desc
126
+ )
127
+
128
+ # Determine insertion point
129
+ insertion_line_idx = target_line_idx
130
+ if pattern_type == "after":
131
+ insertion_line_idx += 1 # Insert on the line *after* the matched line
132
+
133
+ # Format occurrence info for output
134
+ num_occurrences = len(pattern_line_indices)
135
+ occurrence_str = f"occurrence {occurrence} of " if num_occurrences > 1 else ""
136
+
137
+ # 4. Handle indentation if requested
138
+ content_lines = content.splitlines()
139
+
140
+ if auto_indent and content_lines:
141
+ # Determine base indentation level
142
+ base_indent = ""
143
+ if insertion_line_idx > 0 and lines:
144
+ # Use indentation from the line before insertion point
145
+ reference_line_idx = min(insertion_line_idx - 1, len(lines) - 1)
146
+ reference_line = lines[reference_line_idx]
147
+ base_indent = re.match(r"^(\s*)", reference_line).group(1)
148
+
149
+ # Apply indentation to content lines, preserving relative indentation
150
+ if content_lines:
151
+ # Find minimum indentation in content to preserve relative indentation
152
+ content_indents = [
153
+ len(re.match(r"^(\s*)", line).group(1))
154
+ for line in content_lines
155
+ if line.strip()
156
+ ]
157
+ min_content_indent = min(content_indents) if content_indents else 0
158
+
159
+ # Apply base indentation while preserving relative indentation
160
+ indented_content_lines = []
161
+ for line in content_lines:
162
+ if not line.strip(): # Empty or whitespace-only line
163
+ indented_content_lines.append("")
164
+ else:
165
+ # Remove existing indentation and add new base indentation
166
+ stripped_line = (
167
+ line[min_content_indent:]
168
+ if min_content_indent <= len(line)
169
+ else line
170
+ )
171
+ indented_content_lines.append(base_indent + stripped_line)
172
+
173
+ content_lines = indented_content_lines
174
+
175
+ # 5. Prepare the insertion
176
+ new_lines = lines[:insertion_line_idx] + content_lines + lines[insertion_line_idx:]
177
+ new_content = "\n".join(new_lines)
178
+
179
+ # Restore trailing newline if original file had one
180
+ if original_content.endswith("\n"):
181
+ new_content += "\n"
182
+
183
+ if original_content == new_content:
184
+ coder.io.tool_warning("No changes made: insertion would not change file")
185
+ return "Warning: No changes made (insertion would not change file)"
186
+
187
+ # 6. Generate diff for feedback
188
+ diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
189
+
190
+ # 7. Handle dry run
191
+ if dry_run:
192
+ if position:
193
+ dry_run_message = f"Dry run: Would insert block {pattern_type} {file_path}."
194
+ else:
195
+ dry_run_message = (
196
+ f"Dry run: Would insert block {pattern_type} {occurrence_str}pattern"
197
+ f" '{pattern}' in {file_path} at line {insertion_line_idx + 1}."
198
+ )
199
+ return format_tool_result(
200
+ coder,
201
+ tool_name,
202
+ "",
203
+ dry_run=True,
204
+ dry_run_message=dry_run_message,
205
+ diff_snippet=diff_snippet,
206
+ )
207
+
208
+ # 8. Apply Change (Not dry run)
209
+ metadata = {
210
+ "insertion_line_idx": insertion_line_idx,
211
+ "after_pattern": after_pattern,
212
+ "before_pattern": before_pattern,
213
+ "position": position,
214
+ "occurrence": occurrence,
215
+ "content": content,
216
+ "auto_indent": auto_indent,
217
+ "use_regex": use_regex,
218
+ }
219
+ final_change_id = apply_change(
220
+ coder,
221
+ abs_path,
222
+ rel_path,
223
+ original_content,
224
+ new_content,
225
+ "insertblock",
226
+ metadata,
227
+ change_id,
228
+ )
229
+
230
+ coder.files_edited_by_tools.add(rel_path)
231
+
232
+ # 9. Format and return result
233
+ if position:
234
+ success_message = f"Inserted block {pattern_type} {file_path}"
235
+ else:
236
+ success_message = (
237
+ f"Inserted block {pattern_type} {occurrence_str}pattern in {file_path} at line"
238
+ f" {insertion_line_idx + 1}"
239
+ )
240
+
241
+ return format_tool_result(
242
+ coder,
243
+ tool_name,
244
+ success_message,
245
+ change_id=final_change_id,
246
+ diff_snippet=diff_snippet,
247
+ )
248
+
249
+ except ToolError as e:
250
+ # Handle errors raised by utility functions (expected errors)
251
+ return handle_tool_error(coder, tool_name, e, add_traceback=False)
252
+
253
+ except Exception as e:
254
+ coder.io.tool_error(
255
+ f"Error in InsertBlock: {str(e)}\n{traceback.format_exc()}"
256
+ ) # Add traceback
257
+ return f"Error: {str(e)}"
258
+
259
+ @classmethod
260
+ def format_output(cls, coder, mcp_server, tool_response):
261
+ tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
262
+ tool_body_unwrapped(coder=coder, tool_response=tool_response)
263
+ tool_footer(coder=coder, tool_response=tool_response)
@@ -0,0 +1,71 @@
1
+ import traceback
2
+ from datetime import datetime
3
+
4
+ from cecli.tools.utils.base_tool import BaseTool
5
+
6
+
7
+ class Tool(BaseTool):
8
+ NORM_NAME = "listchanges"
9
+ SCHEMA = {
10
+ "type": "function",
11
+ "function": {
12
+ "name": "ListChanges",
13
+ "description": "List recent changes made.",
14
+ "parameters": {
15
+ "type": "object",
16
+ "properties": {
17
+ "file_path": {"type": "string"},
18
+ "limit": {"type": "integer", "default": 10},
19
+ },
20
+ },
21
+ },
22
+ }
23
+
24
+ @classmethod
25
+ def execute(cls, coder, file_path=None, limit=10):
26
+ """
27
+ List recent changes made to files.
28
+
29
+ Parameters:
30
+ - coder: The Coder instance
31
+ - file_path: Optional path to filter changes by file
32
+ - limit: Maximum number of changes to list
33
+
34
+ Returns a formatted list of changes.
35
+ """
36
+ try:
37
+ # If file_path is specified, get the absolute path
38
+ rel_file_path = None
39
+ if file_path:
40
+ abs_path = coder.abs_root_path(file_path)
41
+ rel_file_path = coder.get_rel_fname(abs_path)
42
+
43
+ # Get the list of changes
44
+ changes = coder.change_tracker.list_changes(rel_file_path, limit)
45
+
46
+ if not changes:
47
+ if file_path:
48
+ return f"No changes found for file '{file_path}'"
49
+ else:
50
+ return "No changes have been made yet"
51
+
52
+ # Format the changes into a readable list
53
+ result = "Recent changes:\n"
54
+ for i, change in enumerate(changes):
55
+ change_time = datetime.fromtimestamp(change["timestamp"]).strftime("%H:%M:%S")
56
+ change_type = change["type"]
57
+ file_path = change["file_path"]
58
+ change_id = change["id"]
59
+
60
+ result += (
61
+ f"{i + 1}. [{change_id}] {change_time} - {change_type.upper()} on {file_path}\n"
62
+ )
63
+
64
+ coder.io.tool_output(result) # Also print to console for user
65
+ return result
66
+
67
+ except Exception as e:
68
+ coder.io.tool_error(
69
+ f"Error in ListChanges: {str(e)}\n{traceback.format_exc()}"
70
+ ) # Add traceback
71
+ return f"Error: {str(e)}"
@@ -0,0 +1,51 @@
1
+ from cecli.tools.utils.base_tool import BaseTool
2
+
3
+
4
+ class Tool(BaseTool):
5
+ NORM_NAME = "loadskill"
6
+ SCHEMA = {
7
+ "type": "function",
8
+ "function": {
9
+ "name": "LoadSkill",
10
+ "description": (
11
+ "Load a skill by name (agent mode only). Adds skill to include list and removes"
12
+ " from exclude list."
13
+ ),
14
+ "parameters": {
15
+ "type": "object",
16
+ "properties": {
17
+ "skill_name": {
18
+ "type": "string",
19
+ "description": "Name of the skill to load",
20
+ },
21
+ },
22
+ "required": ["skill_name"],
23
+ },
24
+ },
25
+ }
26
+
27
+ @classmethod
28
+ def execute(cls, coder, skill_name):
29
+ """
30
+ Load a skill by name (agent mode only).
31
+ """
32
+ if not skill_name:
33
+ return "Error: Skill name is required."
34
+
35
+ # Check if we're in agent mode
36
+ if not hasattr(coder, "edit_format") or coder.edit_format != "agent":
37
+ return "Error: Skill loading is only available in agent mode."
38
+
39
+ # Check if skills_manager is available
40
+ if not hasattr(coder, "skills_manager") or coder.skills_manager is None:
41
+ error_msg = "Error: Skills manager is not initialized. Skills may not be configured."
42
+ # Check if skills directories are configured
43
+ if hasattr(coder, "skills_directory_paths") and not coder.skills_directory_paths:
44
+ error_msg += (
45
+ "\nNo skills directories configured. Use --skills-paths to configure skill"
46
+ " directories."
47
+ )
48
+ return error_msg
49
+
50
+ # Use the instance method on skills_manager
51
+ return coder.skills_manager.load_skill(skill_name)
cecli/tools/ls.py ADDED
@@ -0,0 +1,77 @@
1
+ import os
2
+
3
+ from cecli.tools.utils.base_tool import BaseTool
4
+
5
+
6
+ class Tool(BaseTool):
7
+ NORM_NAME = "ls"
8
+ SCHEMA = {
9
+ "type": "function",
10
+ "function": {
11
+ "name": "Ls",
12
+ "description": "List files in a directory.",
13
+ "parameters": {
14
+ "type": "object",
15
+ "properties": {
16
+ "directory": {
17
+ "type": "string",
18
+ "description": "The directory to list.",
19
+ },
20
+ },
21
+ "required": ["directory"],
22
+ },
23
+ },
24
+ }
25
+
26
+ @classmethod
27
+ def execute(cls, coder, dir_path=None, directory=None):
28
+ # Handle both positional and keyword arguments for backward compatibility
29
+ if dir_path is None and directory is not None:
30
+ dir_path = directory
31
+ elif dir_path is None:
32
+ return "Error: Missing directory parameter"
33
+ """
34
+ List files in directory and optionally add some to context.
35
+
36
+ This provides information about the structure of the codebase,
37
+ similar to how a developer would explore directories.
38
+ """
39
+ try:
40
+ # Make the path relative to root if it's absolute
41
+ if dir_path.startswith("/"):
42
+ rel_dir = os.path.relpath(dir_path, coder.root)
43
+ else:
44
+ rel_dir = dir_path
45
+
46
+ # Get absolute path
47
+ abs_dir = coder.abs_root_path(rel_dir)
48
+
49
+ # Check if path exists
50
+ if not os.path.exists(abs_dir):
51
+ coder.io.tool_output(f"⚠️ Directory '{dir_path}' not found")
52
+ return "Directory not found"
53
+
54
+ # Get directory contents
55
+ contents = []
56
+ try:
57
+ with os.scandir(abs_dir) as entries:
58
+ for entry in entries:
59
+ if entry.is_file() and not entry.name.startswith("."):
60
+ rel_path = os.path.join(rel_dir, entry.name)
61
+ contents.append(rel_path)
62
+ except NotADirectoryError:
63
+ # If it's a file, just return the file
64
+ contents = [rel_dir]
65
+
66
+ if contents:
67
+ coder.io.tool_output(f"📋 Listed {len(contents)} file(s) in '{dir_path}'")
68
+ if len(contents) > 10:
69
+ return f"Found {len(contents)} files: {', '.join(contents[:10])}..."
70
+ else:
71
+ return f"Found {len(contents)} files: {', '.join(contents)}"
72
+ else:
73
+ coder.io.tool_output(f"📋 No files found in '{dir_path}'")
74
+ return "No files found in directory"
75
+ except Exception as e:
76
+ coder.io.tool_error(f"Error in ls: {str(e)}")
77
+ return f"Error: {str(e)}"
@@ -0,0 +1,51 @@
1
+ from cecli.tools.utils.base_tool import BaseTool
2
+
3
+
4
+ class Tool(BaseTool):
5
+ NORM_NAME = "removeskill"
6
+ SCHEMA = {
7
+ "type": "function",
8
+ "function": {
9
+ "name": "RemoveSkill",
10
+ "description": (
11
+ "Remove a skill by name (agent mode only). Removes skill from include list and adds"
12
+ " to exclude list."
13
+ ),
14
+ "parameters": {
15
+ "type": "object",
16
+ "properties": {
17
+ "skill_name": {
18
+ "type": "string",
19
+ "description": "Name of the skill to remove",
20
+ },
21
+ },
22
+ "required": ["skill_name"],
23
+ },
24
+ },
25
+ }
26
+
27
+ @classmethod
28
+ def execute(cls, coder, skill_name):
29
+ """
30
+ Remove a skill by name (agent mode only).
31
+ """
32
+ if not skill_name:
33
+ return "Error: Skill name is required."
34
+
35
+ # Check if we're in agent mode
36
+ if not hasattr(coder, "edit_format") or coder.edit_format != "agent":
37
+ return "Error: Skill removal is only available in agent mode."
38
+
39
+ # Check if skills_manager is available
40
+ if not hasattr(coder, "skills_manager") or coder.skills_manager is None:
41
+ error_msg = "Error: Skills manager is not initialized. Skills may not be configured."
42
+ # Check if skills directories are configured
43
+ if hasattr(coder, "skills_directory_paths") and not coder.skills_directory_paths:
44
+ error_msg += (
45
+ "\nNo skills directories configured. Use --skills-paths to configure skill"
46
+ " directories."
47
+ )
48
+ return error_msg
49
+
50
+ # Use the instance method on skills_manager
51
+ return coder.skills_manager.remove_skill(skill_name)
@@ -0,0 +1,113 @@
1
+ from cecli.tools.utils.base_tool import BaseTool
2
+ from cecli.tools.utils.helpers import (
3
+ ToolError,
4
+ apply_change,
5
+ format_tool_result,
6
+ generate_unified_diff_snippet,
7
+ handle_tool_error,
8
+ validate_file_for_edit,
9
+ )
10
+ from cecli.tools.utils.output import tool_body_unwrapped, tool_footer, tool_header
11
+
12
+
13
+ class Tool(BaseTool):
14
+ NORM_NAME = "replaceall"
15
+ SCHEMA = {
16
+ "type": "function",
17
+ "function": {
18
+ "name": "ReplaceAll",
19
+ "description": "Replace all occurrences of text in a file.",
20
+ "parameters": {
21
+ "type": "object",
22
+ "properties": {
23
+ "file_path": {"type": "string"},
24
+ "find_text": {"type": "string"},
25
+ "replace_text": {"type": "string"},
26
+ "change_id": {"type": "string"},
27
+ "dry_run": {"type": "boolean", "default": False},
28
+ },
29
+ "required": ["file_path", "find_text", "replace_text"],
30
+ },
31
+ },
32
+ }
33
+
34
+ @classmethod
35
+ def execute(cls, coder, file_path, find_text, replace_text, change_id=None, dry_run=False):
36
+ """
37
+ Replace all occurrences of text in a file using utility functions.
38
+ """
39
+ # Get absolute file path
40
+ abs_path = coder.abs_root_path(file_path)
41
+ rel_path = coder.get_rel_fname(abs_path)
42
+ tool_name = "ReplaceAll"
43
+ try:
44
+ # 1. Validate file and get content
45
+ abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
46
+
47
+ # 2. Count occurrences
48
+ count = original_content.count(find_text)
49
+ if count == 0:
50
+ coder.io.tool_warning(f"Text '{find_text}' not found in file '{file_path}'")
51
+ return "Warning: Text not found in file"
52
+
53
+ # 3. Perform the replacement
54
+ new_content = original_content.replace(find_text, replace_text)
55
+
56
+ if original_content == new_content:
57
+ coder.io.tool_warning("No changes made: replacement text is identical to original")
58
+ return "Warning: No changes made (replacement identical to original)"
59
+
60
+ # 4. Generate diff for feedback
61
+ diff_examples = generate_unified_diff_snippet(original_content, new_content, rel_path)
62
+
63
+ # 5. Handle dry run
64
+ if dry_run:
65
+ dry_run_message = (
66
+ f"Dry run: Would replace {count} occurrences of '{find_text}' in {file_path}."
67
+ )
68
+ return format_tool_result(
69
+ coder,
70
+ tool_name,
71
+ "",
72
+ dry_run=True,
73
+ dry_run_message=dry_run_message,
74
+ diff_snippet=diff_examples,
75
+ )
76
+
77
+ # 6. Apply Change (Not dry run)
78
+ metadata = {"find_text": find_text, "replace_text": replace_text, "occurrences": count}
79
+ final_change_id = apply_change(
80
+ coder,
81
+ abs_path,
82
+ rel_path,
83
+ original_content,
84
+ new_content,
85
+ "replaceall",
86
+ metadata,
87
+ change_id,
88
+ )
89
+
90
+ coder.files_edited_by_tools.add(rel_path)
91
+
92
+ # 7. Format and return result
93
+ success_message = f"Replaced {count} occurrences in {file_path}"
94
+ return format_tool_result(
95
+ coder,
96
+ tool_name,
97
+ success_message,
98
+ change_id=final_change_id,
99
+ diff_snippet=diff_examples,
100
+ )
101
+
102
+ except ToolError as e:
103
+ # Handle errors raised by utility functions
104
+ return handle_tool_error(coder, tool_name, e, add_traceback=False)
105
+ except Exception as e:
106
+ # Handle unexpected errors
107
+ return handle_tool_error(coder, tool_name, e)
108
+
109
+ @classmethod
110
+ def format_output(cls, coder, mcp_server, tool_response):
111
+ tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
112
+ tool_body_unwrapped(coder=coder, tool_response=tool_response)
113
+ tool_footer(coder=coder, tool_response=tool_response)