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,98 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import List
4
+
5
+
6
+ class SaveLoadManager:
7
+ """Manager for saving and loading command files."""
8
+
9
+ def __init__(self, coder, io):
10
+ self.coder = coder
11
+ self.io = io
12
+
13
+ def get_saves_directory(self) -> Path:
14
+ """Get the saves directory, creating it if necessary."""
15
+ saves_dir = Path(self.coder.abs_root_path(".cecli/saves"))
16
+ os.makedirs(saves_dir, exist_ok=True)
17
+ return saves_dir
18
+
19
+ def resolve_filepath(self, filename: str) -> Path:
20
+ """Resolve a filename to an absolute path, using saves directory if needed."""
21
+ filepath = Path(filename)
22
+
23
+ # If it's a simple filename (no directory separators), save to .cecli/saves/
24
+ if not filepath.is_absolute() and str(filepath) == filepath.name:
25
+ saves_dir = self.get_saves_directory()
26
+ filepath = saves_dir / filepath
27
+
28
+ return filepath
29
+
30
+ def save_commands(self, filename: str) -> Path:
31
+ """Save commands to reconstruct the current chat session to a file."""
32
+ filepath = self.resolve_filepath(filename)
33
+
34
+ try:
35
+ # Ensure parent directory exists
36
+ os.makedirs(filepath.parent, exist_ok=True)
37
+
38
+ with open(filepath, "w", encoding=self.io.encoding) as f:
39
+ f.write("/drop\n")
40
+ # Write commands to add editable files
41
+ for fname in sorted(self.coder.abs_fnames):
42
+ rel_fname = self.coder.get_rel_fname(fname)
43
+ f.write(f"/add {rel_fname}\n")
44
+
45
+ # Write commands to add read-only files
46
+ for fname in sorted(self.coder.abs_read_only_fnames):
47
+ # Use absolute path for files outside repo root, relative path for files inside
48
+ if Path(fname).is_relative_to(self.coder.root):
49
+ rel_fname = self.coder.get_rel_fname(fname)
50
+ f.write(f"/read-only {rel_fname}\n")
51
+ else:
52
+ f.write(f"/read-only {fname}\n")
53
+ # Write commands to add read-only stubs files
54
+ for fname in sorted(self.coder.abs_read_only_stubs_fnames):
55
+ # Use absolute path for files outside repo root, relative path for files inside
56
+ if Path(fname).is_relative_to(self.coder.root):
57
+ rel_fname = self.coder.get_rel_fname(fname)
58
+ f.write(f"/read-only-stub {rel_fname}\n")
59
+ else:
60
+ f.write(f"/read-only-stub {fname}\n")
61
+
62
+ return filepath
63
+ except Exception as e:
64
+ raise IOError(f"Error saving commands to file: {e}")
65
+
66
+ def load_commands(self, filename: str) -> List[str]:
67
+ """Load commands from a file."""
68
+ filepath = self.resolve_filepath(filename)
69
+
70
+ try:
71
+ with open(filepath, "r", encoding=self.io.encoding, errors="replace") as f:
72
+ commands = f.readlines()
73
+ return [
74
+ cmd.strip() for cmd in commands if cmd.strip() and not cmd.strip().startswith("#")
75
+ ]
76
+ except FileNotFoundError:
77
+ raise FileNotFoundError(f"File not found: {filepath}")
78
+ except Exception as e:
79
+ raise IOError(f"Error reading file: {e}")
80
+
81
+ def list_files(self) -> List[str]:
82
+ """Return a list of all filenames (without extensions) in the saves directory.
83
+
84
+ Returns:
85
+ List[str]: List of filenames without extensions, sorted alphabetically
86
+ """
87
+ try:
88
+ saves_dir = self.get_saves_directory()
89
+
90
+ if not saves_dir.exists():
91
+ return []
92
+
93
+ # Get all files (not directories) in the saves directory
94
+ save_files = [f.name for f in saves_dir.iterdir() if f.is_file()]
95
+ return sorted(save_files)
96
+ except Exception:
97
+ # Return empty list on any error
98
+ return []
@@ -0,0 +1,78 @@
1
+ import os
2
+ from typing import List
3
+
4
+ import cecli.voice as voice
5
+ from cecli.commands.utils.base_command import BaseCommand
6
+ from cecli.commands.utils.helpers import format_command_result
7
+ from cecli.llm import litellm
8
+
9
+
10
+ class VoiceCommand(BaseCommand):
11
+ NORM_NAME = "voice"
12
+ DESCRIPTION = "Record and transcribe voice input"
13
+
14
+ @classmethod
15
+ async def execute(cls, io, coder, args, **kwargs):
16
+ """Execute the voice command with given parameters."""
17
+ # Get voice parameters from kwargs or coder
18
+ voice_language = kwargs.get("voice_language") or getattr(coder, "voice_language", None)
19
+ voice_format = kwargs.get("voice_format") or getattr(coder, "voice_format", None)
20
+ voice_input_device = kwargs.get("voice_input_device") or getattr(
21
+ coder, "voice_input_device", None
22
+ )
23
+
24
+ # Get voice instance from kwargs or create new one
25
+ voice_instance = kwargs.get("voice_instance")
26
+
27
+ if not voice_instance:
28
+ if "OPENAI_API_KEY" not in os.environ:
29
+ io.tool_error("To use /voice you must provide an OpenAI API key.")
30
+ return format_command_result(io, "voice", "OpenAI API key required")
31
+
32
+ try:
33
+ voice_instance = voice.Voice(
34
+ audio_format=voice_format or "wav", device_name=voice_input_device
35
+ )
36
+ except voice.SoundDeviceError:
37
+ io.tool_error(
38
+ "Unable to import `sounddevice` and/or `soundfile`, is portaudio installed?"
39
+ )
40
+ return format_command_result(io, "voice", "Sound device error")
41
+
42
+ try:
43
+ io.update_spinner("Recording...")
44
+ text = await voice_instance.record_and_transcribe(None, language=voice_language)
45
+ except litellm.OpenAIError as err:
46
+ io.tool_error(f"Unable to use OpenAI whisper model: {err}")
47
+ return format_command_result(io, "voice", f"OpenAI error: {err}")
48
+
49
+ if text:
50
+ io.placeholder = text
51
+
52
+ if coder.tui and coder.tui():
53
+ coder.tui().set_input_value(text)
54
+ coder.tui().refresh()
55
+
56
+ return format_command_result(io, "voice", "Voice recorded and transcribed")
57
+
58
+ @classmethod
59
+ def get_completions(cls, io, coder, args) -> List[str]:
60
+ """Get completion options for voice command."""
61
+ return []
62
+
63
+ @classmethod
64
+ def get_help(cls) -> str:
65
+ """Get help text for the voice command."""
66
+ help_text = super().get_help()
67
+ help_text += "\nUsage:\n"
68
+ help_text += " /voice # Record and transcribe voice input\n"
69
+ help_text += (
70
+ "\nThis command records audio from your microphone and transcribes it using OpenAI's"
71
+ " Whisper model.\n"
72
+ )
73
+ help_text += "Requirements:\n"
74
+ help_text += " - OPENAI_API_KEY environment variable must be set\n"
75
+ help_text += " - PortAudio library installed (for sounddevice)\n"
76
+ help_text += " - sounddevice and soundfile Python packages\n"
77
+ help_text += "\nThe transcribed text will be placed in the input prompt for editing.\n"
78
+ return help_text
@@ -0,0 +1,123 @@
1
+ from typing import List
2
+
3
+ import cecli.models as models
4
+ from cecli.commands.utils.base_command import BaseCommand
5
+ from cecli.commands.utils.helpers import format_command_result
6
+
7
+
8
+ class WeakModelCommand(BaseCommand):
9
+ NORM_NAME = "weak-model"
10
+ DESCRIPTION = "Switch the Weak Model to a new LLM"
11
+
12
+ @classmethod
13
+ async def execute(cls, io, coder, args, **kwargs):
14
+ """Execute the weak_model command with given parameters."""
15
+ arg_split = args.split(" ", 1)
16
+ model_name = arg_split[0].strip()
17
+ if not model_name:
18
+ # If no model name provided, show current weak model
19
+ current_weak_model = coder.main_model.weak_model.name
20
+ io.tool_output(f"Current weak model: {current_weak_model}")
21
+ return format_command_result(
22
+ io, "weak-model", f"Displayed current weak model: {current_weak_model}"
23
+ )
24
+
25
+ # Create a new model with the same main model and editor model, but updated weak model
26
+ model = models.Model(
27
+ coder.main_model.name,
28
+ editor_model=coder.main_model.editor_model.name,
29
+ weak_model=model_name,
30
+ io=io,
31
+ )
32
+ await models.sanity_check_models(io, model)
33
+
34
+ if len(arg_split) > 1:
35
+ # implement architect coder-like generation call for weak model
36
+ message = arg_split[1].strip()
37
+
38
+ # Store the original model configuration
39
+ original_main_model = coder.main_model
40
+ original_edit_format = coder.edit_format
41
+
42
+ # Create a temporary coder with the new model
43
+ from cecli.coders import Coder
44
+
45
+ kwargs = dict()
46
+ kwargs["main_model"] = model
47
+ kwargs["edit_format"] = coder.edit_format # Keep the same edit format
48
+ kwargs["suggest_shell_commands"] = False
49
+ kwargs["total_cost"] = coder.total_cost
50
+ kwargs["num_cache_warming_pings"] = 0
51
+ kwargs["summarize_from_coder"] = False
52
+
53
+ new_kwargs = dict(io=io, from_coder=coder)
54
+ new_kwargs.update(kwargs)
55
+
56
+ temp_coder = await Coder.create(**new_kwargs)
57
+ temp_coder.cur_messages = []
58
+ temp_coder.done_messages = []
59
+
60
+ verbose = kwargs.get("verbose", False)
61
+ if verbose:
62
+ temp_coder.show_announcements()
63
+
64
+ try:
65
+ await temp_coder.generate(user_message=message, preproc=False)
66
+ coder.move_back_cur_messages(
67
+ f"Weak model {model_name} made those changes to the files."
68
+ )
69
+ coder.total_cost = temp_coder.total_cost
70
+ coder.coder_commit_hashes = temp_coder.coder_commit_hashes
71
+
72
+ # Restore the original model configuration
73
+ from cecli.commands import SwitchCoderSignal
74
+
75
+ raise SwitchCoderSignal(
76
+ main_model=original_main_model, edit_format=original_edit_format
77
+ )
78
+ except Exception as e:
79
+ # If there's an error, still restore the original model
80
+ if not isinstance(e, SwitchCoderSignal):
81
+ io.tool_error(str(e))
82
+ raise SwitchCoderSignal(
83
+ main_model=original_main_model, edit_format=original_edit_format
84
+ )
85
+ else:
86
+ # Re-raise SwitchCoderSignal if that's what was thrown
87
+ raise
88
+ else:
89
+ from cecli.commands import SwitchCoderSignal
90
+
91
+ raise SwitchCoderSignal(main_model=model, edit_format=coder.edit_format)
92
+
93
+ @classmethod
94
+ def get_completions(cls, io, coder, args) -> List[str]:
95
+ """Get completion options for weak_model command."""
96
+ return models.get_chat_model_names()
97
+
98
+ @classmethod
99
+ def get_help(cls) -> str:
100
+ """Get help text for the weak_model command."""
101
+ help_text = super().get_help()
102
+ help_text += "\nUsage:\n"
103
+ help_text += " /weak_model <model-name> # Switch to a new weak model\n"
104
+ help_text += (
105
+ " /weak_model <model-name> <prompt> # Use a specific weak model for a single"
106
+ " prompt\n"
107
+ )
108
+ help_text += "\nExamples:\n"
109
+ help_text += (
110
+ " /weak_model gpt-4o-mini # Switch to GPT-4o Mini as weak model\n"
111
+ )
112
+ help_text += (
113
+ " /weak_model claude-3-haiku # Switch to Claude 3 Haiku as weak model\n"
114
+ )
115
+ help_text += ' /weak_model o1-mini "review this code" # Use o1-mini to review code\n'
116
+ help_text += (
117
+ "\nWhen switching weak models, the main model and editor model remain unchanged.\n"
118
+ )
119
+ help_text += (
120
+ "\nIf you provide a prompt after the model name, that weak model will be used\n"
121
+ )
122
+ help_text += "just for that prompt, then you'll return to your original weak model.\n"
123
+ return help_text
cecli/commands/web.py ADDED
@@ -0,0 +1,87 @@
1
+ from typing import List
2
+
3
+ from cecli.commands.utils.base_command import BaseCommand
4
+ from cecli.commands.utils.helpers import format_command_result
5
+ from cecli.scrape import Scraper, install_playwright
6
+
7
+
8
+ class WebCommand(BaseCommand):
9
+ NORM_NAME = "web"
10
+ DESCRIPTION = "Scrape a webpage, convert to markdown and send in a message"
11
+
12
+ @classmethod
13
+ async def execute(cls, io, coder, args, **kwargs):
14
+ """Execute the web command with given parameters."""
15
+ url = args.strip()
16
+ if not url:
17
+ io.tool_error("Please provide a URL to scrape.")
18
+ return format_command_result(io, "web", "No URL provided")
19
+
20
+ io.tool_output(f"Scraping {url}...")
21
+
22
+ # Get scraper instance from kwargs or create new one
23
+ scraper = kwargs.get("scraper")
24
+
25
+ if not scraper:
26
+ # Get disable_playwright from coder args
27
+ disable_playwright = (
28
+ getattr(coder.args, "disable_playwright", False) if coder and coder.args else False
29
+ )
30
+ if disable_playwright:
31
+ res = False
32
+ else:
33
+ try:
34
+ res = await install_playwright(io)
35
+ if not res:
36
+ io.tool_warning("Unable to initialize playwright.")
37
+ except Exception:
38
+ io.tool_warning("Unable to initialize playwright.")
39
+ res = False
40
+
41
+ # Get verify_ssl from kwargs or use default
42
+ verify_ssl = kwargs.get("verify_ssl", True)
43
+
44
+ scraper = Scraper(
45
+ print_error=io.tool_error,
46
+ playwright_available=res,
47
+ verify_ssl=verify_ssl,
48
+ )
49
+
50
+ content = await scraper.scrape(url) or ""
51
+ content = f"Here is the content of {url}:\n\n" + content
52
+
53
+ return_content = kwargs.get("return_content", False)
54
+ if return_content:
55
+ return content
56
+
57
+ io.tool_output("... added to chat.")
58
+
59
+ coder.cur_messages += [
60
+ dict(role="user", content=content),
61
+ dict(role="assistant", content="Ok."),
62
+ ]
63
+
64
+ return format_command_result(io, "web", f"Scraped and added content from {url} to chat")
65
+
66
+ @classmethod
67
+ def get_completions(cls, io, coder, args) -> List[str]:
68
+ """Get completion options for web command."""
69
+ # For web command, we could return recent URLs or common patterns
70
+ # For now, return empty list
71
+ return []
72
+
73
+ @classmethod
74
+ def get_help(cls) -> str:
75
+ """Get help text for the web command."""
76
+ help_text = super().get_help()
77
+ help_text += "\nUsage:\n"
78
+ help_text += " /web <url> # Scrape a webpage and add its content to the chat\n"
79
+ help_text += "\nExamples:\n"
80
+ help_text += " /web https://example.com # Scrape example.com\n"
81
+ help_text += " /web https://github.com/dwash96/aider-ce # Scrape cecli GitHub page\n"
82
+ help_text += (
83
+ "\nThis command scrapes a webpage, converts it to markdown, and adds it to the chat.\n"
84
+ )
85
+ help_text += "It uses Playwright for JavaScript-rendered pages when available.\n"
86
+ help_text += "Use --disable-playwright to disable Playwright and use simpler scraping.\n"
87
+ return help_text
@@ -0,0 +1,185 @@
1
+ import argparse
2
+
3
+
4
+ def add_deprecated_model_args(parser, group):
5
+ """Add deprecated model shortcut arguments to the argparse parser."""
6
+
7
+ group.add_argument(
8
+ "--opus",
9
+ action="store_true",
10
+ help=argparse.SUPPRESS,
11
+ default=False,
12
+ )
13
+
14
+ group.add_argument(
15
+ "--sonnet",
16
+ action="store_true",
17
+ help=argparse.SUPPRESS,
18
+ default=False,
19
+ )
20
+
21
+ group.add_argument(
22
+ "--haiku",
23
+ action="store_true",
24
+ help=argparse.SUPPRESS,
25
+ default=False,
26
+ )
27
+
28
+ group.add_argument(
29
+ "--4",
30
+ "-4",
31
+ action="store_true",
32
+ help=argparse.SUPPRESS,
33
+ default=False,
34
+ )
35
+
36
+ group.add_argument(
37
+ "--4o",
38
+ action="store_true",
39
+ help=argparse.SUPPRESS,
40
+ default=False,
41
+ )
42
+
43
+ group.add_argument(
44
+ "--mini",
45
+ action="store_true",
46
+ help=argparse.SUPPRESS,
47
+ default=False,
48
+ )
49
+
50
+ group.add_argument(
51
+ "--4-turbo",
52
+ action="store_true",
53
+ help=argparse.SUPPRESS,
54
+ default=False,
55
+ )
56
+
57
+ group.add_argument(
58
+ "--35turbo",
59
+ "--35-turbo",
60
+ "--3",
61
+ "-3",
62
+ action="store_true",
63
+ help=argparse.SUPPRESS,
64
+ default=False,
65
+ )
66
+
67
+ group.add_argument(
68
+ "--deepseek",
69
+ action="store_true",
70
+ help=argparse.SUPPRESS,
71
+ default=False,
72
+ )
73
+
74
+ group.add_argument(
75
+ "--o1-mini",
76
+ action="store_true",
77
+ help=argparse.SUPPRESS,
78
+ default=False,
79
+ )
80
+
81
+ group.add_argument(
82
+ "--o1-preview",
83
+ action="store_true",
84
+ help=argparse.SUPPRESS,
85
+ default=False,
86
+ )
87
+
88
+ #########
89
+ group = parser.add_argument_group("API Keys and Settings (Deprecated)")
90
+ group.add_argument(
91
+ "--openai-api-type",
92
+ help=argparse.SUPPRESS,
93
+ )
94
+ group.add_argument(
95
+ "--openai-api-version",
96
+ help=argparse.SUPPRESS,
97
+ )
98
+ group.add_argument(
99
+ "--openai-api-deployment-id",
100
+ help=argparse.SUPPRESS,
101
+ )
102
+ group.add_argument(
103
+ "--openai-organization-id",
104
+ help=argparse.SUPPRESS,
105
+ )
106
+
107
+ #########
108
+ group = parser.add_argument_group("History Files (Deprecated)")
109
+ group.add_argument(
110
+ "--llm-history-file",
111
+ help=argparse.SUPPRESS,
112
+ )
113
+
114
+ ##########
115
+ group = parser.add_argument_group("Analytics")
116
+ group.add_argument(
117
+ "--analytics",
118
+ action=argparse.BooleanOptionalAction,
119
+ default=None,
120
+ help=argparse.SUPPRESS,
121
+ )
122
+ group.add_argument(
123
+ "--analytics-log",
124
+ metavar="ANALYTICS_LOG_FILE",
125
+ help=argparse.SUPPRESS,
126
+ )
127
+ group.add_argument(
128
+ "--analytics-disable",
129
+ action="store_true",
130
+ help=argparse.SUPPRESS,
131
+ default=False,
132
+ )
133
+ group.add_argument(
134
+ "--analytics-posthog-host",
135
+ metavar="ANALYTICS_POSTHOG_HOST",
136
+ help=argparse.SUPPRESS,
137
+ )
138
+ group.add_argument(
139
+ "--analytics-posthog-project-api-key",
140
+ metavar="ANALYTICS_POSTHOG_PROJECT_API_KEY",
141
+ help=argparse.SUPPRESS,
142
+ )
143
+
144
+
145
+ def handle_deprecated_model_args(args, io):
146
+ """Handle deprecated model shortcut arguments and provide appropriate warnings."""
147
+ # Define model mapping
148
+ model_map = {
149
+ "opus": "claude-3-opus-20240229",
150
+ "sonnet": "anthropic/claude-3-7-sonnet-20250219",
151
+ "haiku": "claude-3-5-haiku-20241022",
152
+ "4": "gpt-4-0613",
153
+ "4o": "gpt-4o",
154
+ "mini": "gpt-4o-mini",
155
+ "4_turbo": "gpt-4-1106-preview",
156
+ "35turbo": "gpt-3.5-turbo",
157
+ "deepseek": "deepseek/deepseek-chat",
158
+ "o1_mini": "o1-mini",
159
+ "o1_preview": "o1-preview",
160
+ }
161
+
162
+ # Check if any deprecated args are used
163
+ for arg_name, model_name in model_map.items():
164
+ arg_name_clean = arg_name.replace("-", "_")
165
+ if hasattr(args, arg_name_clean) and getattr(args, arg_name_clean):
166
+ # Find preferred name to display in warning
167
+ from cecli.models import MODEL_ALIASES
168
+
169
+ display_name = model_name
170
+ # Check if there's a shorter alias for this model
171
+ for alias, full_name in MODEL_ALIASES.items():
172
+ if full_name == model_name:
173
+ display_name = alias
174
+ break
175
+
176
+ # Show the warning
177
+ io.tool_warning(
178
+ f"The --{arg_name.replace('_', '-')} flag is deprecated and will be removed in a"
179
+ f" future version. Please use --model {display_name} instead."
180
+ )
181
+
182
+ # Set the model
183
+ if not args.model:
184
+ args.model = model_name
185
+ break
cecli/diffs.py ADDED
@@ -0,0 +1,129 @@
1
+ import difflib
2
+ import sys
3
+
4
+ from .dump import dump # noqa: F401
5
+
6
+
7
+ def main():
8
+ if len(sys.argv) != 3:
9
+ print("Usage: python diffs.py file1 file")
10
+ sys.exit(1)
11
+
12
+ file_orig, file_updated = sys.argv[1], sys.argv[2]
13
+
14
+ with open(file_orig, "r", encoding="utf-8") as f:
15
+ lines_orig = f.readlines()
16
+
17
+ with open(file_updated, "r", encoding="utf-8") as f:
18
+ lines_updated = f.readlines()
19
+
20
+ for i in range(len(file_updated)):
21
+ res = diff_partial_update(lines_orig, lines_updated[:i])
22
+ print(res)
23
+ input()
24
+
25
+
26
+ def create_progress_bar(percentage):
27
+ block = "█"
28
+ empty = "░"
29
+ total_blocks = 30
30
+ filled_blocks = int(total_blocks * percentage // 100)
31
+ empty_blocks = total_blocks - filled_blocks
32
+ bar = block * filled_blocks + empty * empty_blocks
33
+ return bar
34
+
35
+
36
+ def assert_newlines(lines):
37
+ if not lines:
38
+ return
39
+ for line in lines[:-1]:
40
+ assert line and line[-1] == "\n", line
41
+
42
+
43
+ def diff_partial_update(lines_orig, lines_updated, final=False, fname=None):
44
+ """
45
+ Given only the first part of an updated file, show the diff while
46
+ ignoring the block of "deleted" lines that are past the end of the
47
+ partially complete update.
48
+ """
49
+
50
+ # dump(lines_orig)
51
+ # dump(lines_updated)
52
+
53
+ assert_newlines(lines_orig)
54
+
55
+ num_orig_lines = len(lines_orig)
56
+
57
+ if final:
58
+ last_non_deleted = num_orig_lines
59
+ else:
60
+ last_non_deleted = find_last_non_deleted(lines_orig, lines_updated)
61
+
62
+ # dump(last_non_deleted)
63
+ if last_non_deleted is None:
64
+ return ""
65
+
66
+ # if num_orig_lines:
67
+ # pct = last_non_deleted * 100 / num_orig_lines
68
+ # else:
69
+ # pct = 50
70
+ # bar = create_progress_bar(pct)
71
+ # bar = f" {last_non_deleted:3d} / {num_orig_lines:3d} lines [{bar}] {pct:3.0f}%\n"
72
+
73
+ lines_orig = lines_orig[:last_non_deleted]
74
+
75
+ if not final:
76
+ # lines_updated = lines_updated[:-1] + [bar]
77
+ lines_updated = lines_updated[:-1]
78
+
79
+ diff = difflib.unified_diff(lines_orig, lines_updated, n=5)
80
+
81
+ diff = list(diff)[2:]
82
+
83
+ diff = "".join(diff)
84
+ if not diff.endswith("\n"):
85
+ diff += "\n"
86
+
87
+ for i in range(3, 10):
88
+ backticks = "`" * i
89
+ if backticks not in diff:
90
+ break
91
+
92
+ show = "diff\n"
93
+ if fname:
94
+ show += f"--- {fname} original\n"
95
+ show += f"+++ {fname} updated\n"
96
+
97
+ show += diff
98
+
99
+ show += "\n\n"
100
+
101
+ # print(diff)
102
+
103
+ return show
104
+
105
+
106
+ def find_last_non_deleted(lines_orig, lines_updated):
107
+ diff = list(difflib.ndiff(lines_orig, lines_updated))
108
+
109
+ num_orig = 0
110
+ last_non_deleted_orig = None
111
+
112
+ for line in diff:
113
+ # print(f"{num_orig:2d} {num_updated:2d} {line}", end="")
114
+ code = line[0]
115
+ if code == " ":
116
+ num_orig += 1
117
+ last_non_deleted_orig = num_orig
118
+ elif code == "-":
119
+ # line only in orig
120
+ num_orig += 1
121
+ elif code == "+":
122
+ # line only in updated
123
+ pass
124
+
125
+ return last_non_deleted_orig
126
+
127
+
128
+ if __name__ == "__main__":
129
+ main()