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
cecli/coders/shell.py ADDED
@@ -0,0 +1,37 @@
1
+ shell_cmd_prompt = """
2
+ 4. *Concisely* suggest any shell commands the user might want to run in ```bash blocks.
3
+
4
+ Just suggest shell commands this way, not example code.
5
+ Only suggest complete shell commands that are ready to execute, without placeholders.
6
+ Only suggest at most a few shell commands at a time, not more than 1-3, one per line.
7
+ Do not suggest multi-line shell commands.
8
+ All shell commands will run from the root directory of the user's project.
9
+
10
+ Use the appropriate shell based on the user's system info:
11
+ {platform}
12
+ Examples of when to suggest shell commands:
13
+
14
+ - If you changed a self-contained html file, suggest an OS-appropriate command to open a browser to view it to see the updated content.
15
+ - If you changed a CLI program, suggest the command to run it to see the new behavior.
16
+ - If you added a test, suggest how to run it with the testing tool used by the project.
17
+ - Suggest OS-appropriate commands to delete or rename files/directories, or other file system operations.
18
+ - If your code changes add new dependencies, suggest the command to install them.
19
+ - Etc.
20
+ """ # noqa
21
+
22
+ no_shell_cmd_prompt = """
23
+ Keep in mind these details about the user's platform and environment:
24
+ {platform}
25
+ """ # noqa
26
+
27
+ shell_cmd_reminder = """
28
+ Examples of when to suggest shell commands:
29
+
30
+ - If you changed a self-contained html file, suggest an OS-appropriate command to open a browser to view it to see the updated content.
31
+ - If you changed a CLI program, suggest the command to run it to see the new behavior.
32
+ - If you added a test, suggest how to run it with the testing tool used by the project.
33
+ - Suggest OS-appropriate commands to delete or rename files/directories, or other file system operations.
34
+ - If your code changes add new dependencies, suggest the command to install them.
35
+ - Etc.
36
+
37
+ """ # noqa
@@ -0,0 +1,101 @@
1
+ from cecli import diffs
2
+
3
+ from ..dump import dump # noqa: F401
4
+ from .base_coder import Coder
5
+
6
+
7
+ class SingleWholeFileFunctionCoder(Coder):
8
+ edit_format = "func"
9
+ prompt_format = "single_wholefile_func"
10
+
11
+ functions = [
12
+ dict(
13
+ name="write_file",
14
+ description="write new content into the file",
15
+ # strict=True,
16
+ parameters=dict(
17
+ type="object",
18
+ properties=dict(
19
+ explanation=dict(
20
+ type="string",
21
+ description=(
22
+ "Step by step plan for the changes to be made to the code (future"
23
+ " tense, markdown format)"
24
+ ),
25
+ ),
26
+ content=dict(
27
+ type="string",
28
+ description="Content to write to the file",
29
+ ),
30
+ ),
31
+ required=["explanation", "content"],
32
+ additionalProperties=False,
33
+ ),
34
+ ),
35
+ ]
36
+
37
+ def __init__(self, *args, **kwargs):
38
+ super().__init__(*args, **kwargs)
39
+
40
+ def add_assistant_reply_to_cur_messages(self, edited):
41
+ if edited:
42
+ self.cur_messages += [
43
+ dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
44
+ ]
45
+ else:
46
+ self.cur_messages += [dict(role="assistant", content=self.partial_response_content)]
47
+
48
+ def render_incremental_response(self, final=False):
49
+ res = ""
50
+ if self.partial_response_content:
51
+ res += self.partial_response_content
52
+
53
+ args = self.parse_partial_args()
54
+
55
+ if not args:
56
+ return ""
57
+
58
+ for k, v in args.items():
59
+ res += "\n"
60
+ res += f"{k}:\n"
61
+ res += v
62
+
63
+ return res
64
+
65
+ def live_diffs(self, fname, content, final):
66
+ lines = content.splitlines(keepends=True)
67
+
68
+ # ending an existing block
69
+ full_path = self.abs_root_path(fname)
70
+
71
+ content = self.io.read_text(full_path)
72
+ if content is None:
73
+ orig_lines = []
74
+ else:
75
+ orig_lines = content.splitlines()
76
+
77
+ show_diff = diffs.diff_partial_update(
78
+ orig_lines,
79
+ lines,
80
+ final,
81
+ fname=fname,
82
+ ).splitlines()
83
+
84
+ return "\n".join(show_diff)
85
+
86
+ def get_edits(self):
87
+ chat_files = self.get_inchat_relative_files()
88
+ assert len(chat_files) == 1, chat_files
89
+
90
+ args = self.parse_partial_args()
91
+ if not args:
92
+ return []
93
+
94
+ res = chat_files[0], args["content"]
95
+ dump(res)
96
+ return [res]
97
+
98
+ def apply_edits(self, edits):
99
+ for path, content in edits:
100
+ full_path = self.abs_root_path(path)
101
+ self.io.write_text(full_path, content)
@@ -0,0 +1,428 @@
1
+ import difflib
2
+ from itertools import groupby
3
+ from pathlib import Path
4
+
5
+ from ..dump import dump # noqa: F401
6
+ from .base_coder import Coder
7
+ from .search_replace import (
8
+ SearchTextNotUnique,
9
+ all_preprocs,
10
+ diff_lines,
11
+ flexible_search_and_replace,
12
+ search_and_replace,
13
+ )
14
+
15
+ no_match_error = """UnifiedDiffNoMatch: hunk failed to apply!
16
+
17
+ {path} does not contain lines that match the diff you provided!
18
+ Try again.
19
+ DO NOT skip blank lines, comments, docstrings, etc!
20
+ The diff needs to apply cleanly to the lines in {path}!
21
+
22
+ {path} does not contain these {num_lines} exact lines in a row:
23
+ ```
24
+ {original}```
25
+ """
26
+
27
+
28
+ not_unique_error = """UnifiedDiffNotUnique: hunk failed to apply!
29
+
30
+ {path} contains multiple sets of lines that match the diff you provided!
31
+ Try again.
32
+ Use additional ` ` lines to provide context that uniquely indicates which code needs to be changed.
33
+ The diff needs to apply to a unique set of lines in {path}!
34
+
35
+ {path} contains multiple copies of these {num_lines} lines:
36
+ ```
37
+ {original}```
38
+ """
39
+
40
+ other_hunks_applied = (
41
+ "Note: some hunks did apply successfully. See the updated source code shown above.\n\n"
42
+ )
43
+
44
+
45
+ class UnifiedDiffCoder(Coder):
46
+ """A coder that uses unified diff format for code modifications."""
47
+
48
+ edit_format = "udiff"
49
+ prompt_format = "udiff"
50
+
51
+ def get_edits(self):
52
+ content = self.partial_response_content
53
+
54
+ # might raise ValueError for malformed ORIG/UPD blocks
55
+ raw_edits = list(find_diffs(content))
56
+
57
+ last_path = None
58
+ edits = []
59
+ for path, hunk in raw_edits:
60
+ if path:
61
+ last_path = path
62
+ else:
63
+ path = last_path
64
+ edits.append((path, hunk))
65
+
66
+ return edits
67
+
68
+ def apply_edits(self, edits):
69
+ seen = set()
70
+ uniq = []
71
+ for path, hunk in edits:
72
+ hunk = normalize_hunk(hunk)
73
+ if not hunk:
74
+ continue
75
+
76
+ this = [path + "\n"] + hunk
77
+ this = "".join(this)
78
+
79
+ if this in seen:
80
+ continue
81
+ seen.add(this)
82
+
83
+ uniq.append((path, hunk))
84
+
85
+ errors = []
86
+ for path, hunk in uniq:
87
+ full_path = self.abs_root_path(path)
88
+ content = self.io.read_text(full_path)
89
+
90
+ original, _ = hunk_to_before_after(hunk)
91
+
92
+ try:
93
+ content = do_replace(full_path, content, hunk)
94
+ except SearchTextNotUnique:
95
+ errors.append(
96
+ not_unique_error.format(
97
+ path=path, original=original, num_lines=len(original.splitlines())
98
+ )
99
+ )
100
+ continue
101
+
102
+ if not content:
103
+ errors.append(
104
+ no_match_error.format(
105
+ path=path, original=original, num_lines=len(original.splitlines())
106
+ )
107
+ )
108
+ continue
109
+
110
+ # SUCCESS!
111
+ self.io.write_text(full_path, content)
112
+
113
+ if errors:
114
+ errors = "\n\n".join(errors)
115
+ if len(errors) < len(uniq):
116
+ errors += other_hunks_applied
117
+ raise ValueError(errors)
118
+
119
+
120
+ def do_replace(fname, content, hunk):
121
+ fname = Path(fname)
122
+
123
+ before_text, after_text = hunk_to_before_after(hunk)
124
+
125
+ # does it want to make a new file?
126
+ if not fname.exists() and not before_text.strip():
127
+ fname.touch()
128
+ content = ""
129
+
130
+ if content is None:
131
+ return
132
+
133
+ # TODO: handle inserting into new file
134
+ if not before_text.strip():
135
+ # append to existing file, or start a new file
136
+ new_content = content + after_text
137
+ return new_content
138
+
139
+ new_content = None
140
+
141
+ new_content = apply_hunk(content, hunk)
142
+ if new_content:
143
+ return new_content
144
+
145
+
146
+ def collapse_repeats(s):
147
+ return "".join(k for k, g in groupby(s))
148
+
149
+
150
+ def apply_hunk(content, hunk):
151
+ before_text, after_text = hunk_to_before_after(hunk)
152
+
153
+ res = directly_apply_hunk(content, hunk)
154
+ if res:
155
+ return res
156
+
157
+ hunk = make_new_lines_explicit(content, hunk)
158
+
159
+ # just consider space vs not-space
160
+ ops = "".join([line[0] for line in hunk])
161
+ ops = ops.replace("-", "x")
162
+ ops = ops.replace("+", "x")
163
+ ops = ops.replace("\n", " ")
164
+
165
+ cur_op = " "
166
+ section = []
167
+ sections = []
168
+
169
+ for i in range(len(ops)):
170
+ op = ops[i]
171
+ if op != cur_op:
172
+ sections.append(section)
173
+ section = []
174
+ cur_op = op
175
+ section.append(hunk[i])
176
+
177
+ sections.append(section)
178
+ if cur_op != " ":
179
+ sections.append([])
180
+
181
+ all_done = True
182
+ for i in range(2, len(sections), 2):
183
+ preceding_context = sections[i - 2]
184
+ changes = sections[i - 1]
185
+ following_context = sections[i]
186
+
187
+ res = apply_partial_hunk(content, preceding_context, changes, following_context)
188
+ if res:
189
+ content = res
190
+ else:
191
+ all_done = False
192
+ # FAILED!
193
+ # this_hunk = preceding_context + changes + following_context
194
+ break
195
+
196
+ if all_done:
197
+ return content
198
+
199
+
200
+ def flexi_just_search_and_replace(texts):
201
+ strategies = [
202
+ (search_and_replace, all_preprocs),
203
+ ]
204
+
205
+ return flexible_search_and_replace(texts, strategies)
206
+
207
+
208
+ def make_new_lines_explicit(content, hunk):
209
+ before, after = hunk_to_before_after(hunk)
210
+
211
+ diff = diff_lines(before, content)
212
+
213
+ back_diff = []
214
+ for line in diff:
215
+ if line[0] == "+":
216
+ continue
217
+ # if line[0] == "-":
218
+ # line = "+" + line[1:]
219
+
220
+ back_diff.append(line)
221
+
222
+ new_before = directly_apply_hunk(before, back_diff)
223
+ if not new_before:
224
+ return hunk
225
+
226
+ if len(new_before.strip()) < 10:
227
+ return hunk
228
+
229
+ before = before.splitlines(keepends=True)
230
+ new_before = new_before.splitlines(keepends=True)
231
+ after = after.splitlines(keepends=True)
232
+
233
+ if len(new_before) < len(before) * 0.66:
234
+ return hunk
235
+
236
+ new_hunk = difflib.unified_diff(new_before, after, n=max(len(new_before), len(after)))
237
+ new_hunk = list(new_hunk)[3:]
238
+
239
+ return new_hunk
240
+
241
+
242
+ def cleanup_pure_whitespace_lines(lines):
243
+ res = [
244
+ line if line.strip() else line[-(len(line) - len(line.rstrip("\r\n")))] for line in lines
245
+ ]
246
+ return res
247
+
248
+
249
+ def normalize_hunk(hunk):
250
+ before, after = hunk_to_before_after(hunk, lines=True)
251
+
252
+ before = cleanup_pure_whitespace_lines(before)
253
+ after = cleanup_pure_whitespace_lines(after)
254
+
255
+ diff = difflib.unified_diff(before, after, n=max(len(before), len(after)))
256
+ diff = list(diff)[3:]
257
+ return diff
258
+
259
+
260
+ def directly_apply_hunk(content, hunk):
261
+ before, after = hunk_to_before_after(hunk)
262
+
263
+ if not before:
264
+ return
265
+
266
+ before_lines, _ = hunk_to_before_after(hunk, lines=True)
267
+ before_lines = "".join([line.strip() for line in before_lines])
268
+
269
+ # Refuse to do a repeated search and replace on a tiny bit of non-whitespace context
270
+ if len(before_lines) < 10 and content.count(before) > 1:
271
+ return
272
+
273
+ try:
274
+ new_content = flexi_just_search_and_replace([before, after, content])
275
+ except SearchTextNotUnique:
276
+ new_content = None
277
+
278
+ return new_content
279
+
280
+
281
+ def apply_partial_hunk(content, preceding_context, changes, following_context):
282
+ len_prec = len(preceding_context)
283
+ len_foll = len(following_context)
284
+
285
+ use_all = len_prec + len_foll
286
+
287
+ # if there is a - in the hunk, we can go all the way to `use=0`
288
+ for drop in range(use_all + 1):
289
+ use = use_all - drop
290
+
291
+ for use_prec in range(len_prec, -1, -1):
292
+ if use_prec > use:
293
+ continue
294
+
295
+ use_foll = use - use_prec
296
+ if use_foll > len_foll:
297
+ continue
298
+
299
+ if use_prec:
300
+ this_prec = preceding_context[-use_prec:]
301
+ else:
302
+ this_prec = []
303
+
304
+ this_foll = following_context[:use_foll]
305
+
306
+ res = directly_apply_hunk(content, this_prec + changes + this_foll)
307
+ if res:
308
+ return res
309
+
310
+
311
+ def find_diffs(content):
312
+ # We can always fence with triple-quotes, because all the udiff content
313
+ # is prefixed with +/-/space.
314
+
315
+ if not content.endswith("\n"):
316
+ content = content + "\n"
317
+
318
+ lines = content.splitlines(keepends=True)
319
+ line_num = 0
320
+ edits = []
321
+ while line_num < len(lines):
322
+ while line_num < len(lines):
323
+ line = lines[line_num]
324
+ if line.startswith("```diff"):
325
+ line_num, these_edits = process_fenced_block(lines, line_num + 1)
326
+ edits += these_edits
327
+ break
328
+ line_num += 1
329
+
330
+ # For now, just take 1!
331
+ # edits = edits[:1]
332
+
333
+ return edits
334
+
335
+
336
+ def process_fenced_block(lines, start_line_num):
337
+ for line_num in range(start_line_num, len(lines)):
338
+ line = lines[line_num]
339
+ if line.startswith("```"):
340
+ break
341
+
342
+ block = lines[start_line_num:line_num]
343
+ block.append("@@ @@")
344
+
345
+ if block[0].startswith("--- ") and block[1].startswith("+++ "):
346
+ # Extract the file path, considering that it might contain spaces
347
+ a_fname = block[0][4:].strip()
348
+ b_fname = block[1][4:].strip()
349
+
350
+ # Check if standard git diff prefixes are present (or /dev/null) and strip them
351
+ if (a_fname.startswith("a/") or a_fname == "/dev/null") and b_fname.startswith("b/"):
352
+ fname = b_fname[2:]
353
+ else:
354
+ # Otherwise, assume the path is as intended
355
+ fname = b_fname
356
+
357
+ block = block[2:]
358
+ else:
359
+ fname = None
360
+
361
+ edits = []
362
+
363
+ keeper = False
364
+ hunk = []
365
+ op = " "
366
+ for line in block:
367
+ hunk.append(line)
368
+ if len(line) < 2:
369
+ continue
370
+
371
+ if line.startswith("+++ ") and hunk[-2].startswith("--- "):
372
+ if hunk[-3] == "\n":
373
+ hunk = hunk[:-3]
374
+ else:
375
+ hunk = hunk[:-2]
376
+
377
+ edits.append((fname, hunk))
378
+ hunk = []
379
+ keeper = False
380
+
381
+ fname = line[4:].strip()
382
+ continue
383
+
384
+ op = line[0]
385
+ if op in "-+":
386
+ keeper = True
387
+ continue
388
+ if op != "@":
389
+ continue
390
+ if not keeper:
391
+ hunk = []
392
+ continue
393
+
394
+ hunk = hunk[:-1]
395
+ edits.append((fname, hunk))
396
+ hunk = []
397
+ keeper = False
398
+
399
+ return line_num + 1, edits
400
+
401
+
402
+ def hunk_to_before_after(hunk, lines=False):
403
+ before = []
404
+ after = []
405
+ op = " "
406
+ for line in hunk:
407
+ if len(line) < 2:
408
+ op = " "
409
+ line = line
410
+ else:
411
+ op = line[0]
412
+ line = line[1:]
413
+
414
+ if op == " ":
415
+ before.append(line)
416
+ after.append(line)
417
+ elif op == "-":
418
+ before.append(line)
419
+ elif op == "+":
420
+ after.append(line)
421
+
422
+ if lines:
423
+ return before, after
424
+
425
+ before = "".join(before)
426
+ after = "".join(after)
427
+
428
+ return before, after
@@ -0,0 +1,12 @@
1
+ from .udiff_coder import UnifiedDiffCoder
2
+
3
+
4
+ class UnifiedDiffSimpleCoder(UnifiedDiffCoder):
5
+ """
6
+ A coder that uses unified diff format for code modifications.
7
+ This variant uses a simpler prompt that doesn't mention specific
8
+ diff rules like using `@@ ... @@` lines or avoiding line numbers.
9
+ """
10
+
11
+ edit_format = "udiff-simple"
12
+ prompt_format = "udiff_simple"