chatmcp-cli 0.1.0__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 (228) hide show
  1. aider/__init__.py +20 -0
  2. aider/__main__.py +4 -0
  3. aider/_version.py +21 -0
  4. aider/analytics.py +250 -0
  5. aider/args.py +926 -0
  6. aider/args_formatter.py +228 -0
  7. aider/coders/__init__.py +34 -0
  8. aider/coders/architect_coder.py +48 -0
  9. aider/coders/architect_prompts.py +40 -0
  10. aider/coders/ask_coder.py +9 -0
  11. aider/coders/ask_prompts.py +35 -0
  12. aider/coders/base_coder.py +2483 -0
  13. aider/coders/base_prompts.py +60 -0
  14. aider/coders/chat_chunks.py +64 -0
  15. aider/coders/context_coder.py +53 -0
  16. aider/coders/context_prompts.py +75 -0
  17. aider/coders/editblock_coder.py +657 -0
  18. aider/coders/editblock_fenced_coder.py +10 -0
  19. aider/coders/editblock_fenced_prompts.py +143 -0
  20. aider/coders/editblock_func_coder.py +141 -0
  21. aider/coders/editblock_func_prompts.py +27 -0
  22. aider/coders/editblock_prompts.py +174 -0
  23. aider/coders/editor_diff_fenced_coder.py +9 -0
  24. aider/coders/editor_diff_fenced_prompts.py +11 -0
  25. aider/coders/editor_editblock_coder.py +8 -0
  26. aider/coders/editor_editblock_prompts.py +18 -0
  27. aider/coders/editor_whole_coder.py +8 -0
  28. aider/coders/editor_whole_prompts.py +10 -0
  29. aider/coders/help_coder.py +16 -0
  30. aider/coders/help_prompts.py +46 -0
  31. aider/coders/patch_coder.py +706 -0
  32. aider/coders/patch_prompts.py +161 -0
  33. aider/coders/search_replace.py +757 -0
  34. aider/coders/shell.py +37 -0
  35. aider/coders/single_wholefile_func_coder.py +102 -0
  36. aider/coders/single_wholefile_func_prompts.py +27 -0
  37. aider/coders/udiff_coder.py +429 -0
  38. aider/coders/udiff_prompts.py +115 -0
  39. aider/coders/udiff_simple.py +14 -0
  40. aider/coders/udiff_simple_prompts.py +25 -0
  41. aider/coders/wholefile_coder.py +144 -0
  42. aider/coders/wholefile_func_coder.py +134 -0
  43. aider/coders/wholefile_func_prompts.py +27 -0
  44. aider/coders/wholefile_prompts.py +67 -0
  45. aider/commands.py +1665 -0
  46. aider/copypaste.py +72 -0
  47. aider/deprecated.py +126 -0
  48. aider/diffs.py +128 -0
  49. aider/dump.py +29 -0
  50. aider/editor.py +147 -0
  51. aider/exceptions.py +107 -0
  52. aider/format_settings.py +26 -0
  53. aider/gui.py +545 -0
  54. aider/help.py +163 -0
  55. aider/help_pats.py +19 -0
  56. aider/history.py +143 -0
  57. aider/io.py +1175 -0
  58. aider/linter.py +304 -0
  59. aider/llm.py +47 -0
  60. aider/main.py +1267 -0
  61. aider/mdstream.py +243 -0
  62. aider/models.py +1286 -0
  63. aider/onboarding.py +428 -0
  64. aider/openrouter.py +128 -0
  65. aider/prompts.py +64 -0
  66. aider/queries/tree-sitter-language-pack/README.md +7 -0
  67. aider/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  68. aider/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  69. aider/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  70. aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  71. aider/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  72. aider/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  73. aider/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  74. aider/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  75. aider/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  76. aider/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  77. aider/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  78. aider/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  79. aider/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  80. aider/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  81. aider/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  82. aider/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  83. aider/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  84. aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  85. aider/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  86. aider/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  87. aider/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  88. aider/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  89. aider/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  90. aider/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  91. aider/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  92. aider/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  93. aider/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  94. aider/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  95. aider/queries/tree-sitter-languages/README.md +23 -0
  96. aider/queries/tree-sitter-languages/c-tags.scm +9 -0
  97. aider/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  98. aider/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  99. aider/queries/tree-sitter-languages/dart-tags.scm +91 -0
  100. aider/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  101. aider/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  102. aider/queries/tree-sitter-languages/elm-tags.scm +19 -0
  103. aider/queries/tree-sitter-languages/go-tags.scm +30 -0
  104. aider/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  105. aider/queries/tree-sitter-languages/java-tags.scm +20 -0
  106. aider/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  107. aider/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  108. aider/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  109. aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  110. aider/queries/tree-sitter-languages/php-tags.scm +26 -0
  111. aider/queries/tree-sitter-languages/python-tags.scm +12 -0
  112. aider/queries/tree-sitter-languages/ql-tags.scm +26 -0
  113. aider/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  114. aider/queries/tree-sitter-languages/rust-tags.scm +60 -0
  115. aider/queries/tree-sitter-languages/scala-tags.scm +65 -0
  116. aider/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  117. aider/reasoning_tags.py +82 -0
  118. aider/repo.py +623 -0
  119. aider/repomap.py +847 -0
  120. aider/report.py +200 -0
  121. aider/resources/__init__.py +3 -0
  122. aider/resources/model-metadata.json +468 -0
  123. aider/resources/model-settings.yml +1767 -0
  124. aider/run_cmd.py +132 -0
  125. aider/scrape.py +284 -0
  126. aider/sendchat.py +61 -0
  127. aider/special.py +203 -0
  128. aider/urls.py +17 -0
  129. aider/utils.py +338 -0
  130. aider/versioncheck.py +113 -0
  131. aider/voice.py +187 -0
  132. aider/waiting.py +221 -0
  133. aider/watch.py +318 -0
  134. aider/watch_prompts.py +12 -0
  135. aider/website/Gemfile +8 -0
  136. aider/website/_includes/blame.md +162 -0
  137. aider/website/_includes/get-started.md +22 -0
  138. aider/website/_includes/help-tip.md +5 -0
  139. aider/website/_includes/help.md +24 -0
  140. aider/website/_includes/install.md +5 -0
  141. aider/website/_includes/keys.md +4 -0
  142. aider/website/_includes/model-warnings.md +67 -0
  143. aider/website/_includes/multi-line.md +22 -0
  144. aider/website/_includes/python-m-aider.md +5 -0
  145. aider/website/_includes/recording.css +228 -0
  146. aider/website/_includes/recording.md +34 -0
  147. aider/website/_includes/replit-pipx.md +9 -0
  148. aider/website/_includes/works-best.md +1 -0
  149. aider/website/_sass/custom/custom.scss +103 -0
  150. aider/website/docs/config/adv-model-settings.md +1881 -0
  151. aider/website/docs/config/aider_conf.md +527 -0
  152. aider/website/docs/config/api-keys.md +90 -0
  153. aider/website/docs/config/dotenv.md +478 -0
  154. aider/website/docs/config/editor.md +127 -0
  155. aider/website/docs/config/model-aliases.md +103 -0
  156. aider/website/docs/config/options.md +843 -0
  157. aider/website/docs/config/reasoning.md +209 -0
  158. aider/website/docs/config.md +44 -0
  159. aider/website/docs/faq.md +378 -0
  160. aider/website/docs/git.md +76 -0
  161. aider/website/docs/index.md +47 -0
  162. aider/website/docs/install/codespaces.md +39 -0
  163. aider/website/docs/install/docker.md +57 -0
  164. aider/website/docs/install/optional.md +100 -0
  165. aider/website/docs/install/replit.md +8 -0
  166. aider/website/docs/install.md +115 -0
  167. aider/website/docs/languages.md +264 -0
  168. aider/website/docs/legal/contributor-agreement.md +111 -0
  169. aider/website/docs/legal/privacy.md +104 -0
  170. aider/website/docs/llms/anthropic.md +77 -0
  171. aider/website/docs/llms/azure.md +48 -0
  172. aider/website/docs/llms/bedrock.md +132 -0
  173. aider/website/docs/llms/cohere.md +34 -0
  174. aider/website/docs/llms/deepseek.md +32 -0
  175. aider/website/docs/llms/gemini.md +49 -0
  176. aider/website/docs/llms/github.md +105 -0
  177. aider/website/docs/llms/groq.md +36 -0
  178. aider/website/docs/llms/lm-studio.md +39 -0
  179. aider/website/docs/llms/ollama.md +75 -0
  180. aider/website/docs/llms/openai-compat.md +39 -0
  181. aider/website/docs/llms/openai.md +58 -0
  182. aider/website/docs/llms/openrouter.md +78 -0
  183. aider/website/docs/llms/other.md +103 -0
  184. aider/website/docs/llms/vertex.md +50 -0
  185. aider/website/docs/llms/warnings.md +10 -0
  186. aider/website/docs/llms/xai.md +53 -0
  187. aider/website/docs/llms.md +54 -0
  188. aider/website/docs/more/analytics.md +122 -0
  189. aider/website/docs/more/edit-formats.md +116 -0
  190. aider/website/docs/more/infinite-output.md +137 -0
  191. aider/website/docs/more-info.md +8 -0
  192. aider/website/docs/recordings/auto-accept-architect.md +31 -0
  193. aider/website/docs/recordings/dont-drop-original-read-files.md +35 -0
  194. aider/website/docs/recordings/index.md +21 -0
  195. aider/website/docs/recordings/model-accepts-settings.md +69 -0
  196. aider/website/docs/recordings/tree-sitter-language-pack.md +80 -0
  197. aider/website/docs/repomap.md +112 -0
  198. aider/website/docs/scripting.md +100 -0
  199. aider/website/docs/troubleshooting/aider-not-found.md +24 -0
  200. aider/website/docs/troubleshooting/edit-errors.md +76 -0
  201. aider/website/docs/troubleshooting/imports.md +62 -0
  202. aider/website/docs/troubleshooting/models-and-keys.md +54 -0
  203. aider/website/docs/troubleshooting/support.md +79 -0
  204. aider/website/docs/troubleshooting/token-limits.md +96 -0
  205. aider/website/docs/troubleshooting/warnings.md +12 -0
  206. aider/website/docs/troubleshooting.md +11 -0
  207. aider/website/docs/usage/browser.md +57 -0
  208. aider/website/docs/usage/caching.md +49 -0
  209. aider/website/docs/usage/commands.md +132 -0
  210. aider/website/docs/usage/conventions.md +119 -0
  211. aider/website/docs/usage/copypaste.md +121 -0
  212. aider/website/docs/usage/images-urls.md +48 -0
  213. aider/website/docs/usage/lint-test.md +118 -0
  214. aider/website/docs/usage/modes.md +211 -0
  215. aider/website/docs/usage/not-code.md +179 -0
  216. aider/website/docs/usage/notifications.md +87 -0
  217. aider/website/docs/usage/tips.md +79 -0
  218. aider/website/docs/usage/tutorials.md +30 -0
  219. aider/website/docs/usage/voice.md +121 -0
  220. aider/website/docs/usage/watch.md +294 -0
  221. aider/website/docs/usage.md +92 -0
  222. aider/website/share/index.md +101 -0
  223. chatmcp_cli-0.1.0.dist-info/METADATA +502 -0
  224. chatmcp_cli-0.1.0.dist-info/RECORD +228 -0
  225. chatmcp_cli-0.1.0.dist-info/WHEEL +5 -0
  226. chatmcp_cli-0.1.0.dist-info/entry_points.txt +3 -0
  227. chatmcp_cli-0.1.0.dist-info/licenses/LICENSE.txt +202 -0
  228. chatmcp_cli-0.1.0.dist-info/top_level.txt +1 -0
aider/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,102 @@
1
+ from aider import diffs
2
+
3
+ from ..dump import dump # noqa: F401
4
+ from .base_coder import Coder
5
+ from .single_wholefile_func_prompts import SingleWholeFileFunctionPrompts
6
+
7
+
8
+ class SingleWholeFileFunctionCoder(Coder):
9
+ edit_format = "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
+ self.gpt_prompts = SingleWholeFileFunctionPrompts()
39
+ super().__init__(*args, **kwargs)
40
+
41
+ def add_assistant_reply_to_cur_messages(self, edited):
42
+ if edited:
43
+ self.cur_messages += [
44
+ dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
45
+ ]
46
+ else:
47
+ self.cur_messages += [dict(role="assistant", content=self.partial_response_content)]
48
+
49
+ def render_incremental_response(self, final=False):
50
+ res = ""
51
+ if self.partial_response_content:
52
+ res += self.partial_response_content
53
+
54
+ args = self.parse_partial_args()
55
+
56
+ if not args:
57
+ return ""
58
+
59
+ for k, v in args.items():
60
+ res += "\n"
61
+ res += f"{k}:\n"
62
+ res += v
63
+
64
+ return res
65
+
66
+ def live_diffs(self, fname, content, final):
67
+ lines = content.splitlines(keepends=True)
68
+
69
+ # ending an existing block
70
+ full_path = self.abs_root_path(fname)
71
+
72
+ content = self.io.read_text(full_path)
73
+ if content is None:
74
+ orig_lines = []
75
+ else:
76
+ orig_lines = content.splitlines()
77
+
78
+ show_diff = diffs.diff_partial_update(
79
+ orig_lines,
80
+ lines,
81
+ final,
82
+ fname=fname,
83
+ ).splitlines()
84
+
85
+ return "\n".join(show_diff)
86
+
87
+ def get_edits(self):
88
+ chat_files = self.get_inchat_relative_files()
89
+ assert len(chat_files) == 1, chat_files
90
+
91
+ args = self.parse_partial_args()
92
+ if not args:
93
+ return []
94
+
95
+ res = chat_files[0], args["content"]
96
+ dump(res)
97
+ return [res]
98
+
99
+ def apply_edits(self, edits):
100
+ for path, content in edits:
101
+ full_path = self.abs_root_path(path)
102
+ self.io.write_text(full_path, content)
@@ -0,0 +1,27 @@
1
+ # flake8: noqa: E501
2
+
3
+ from .base_prompts import CoderPrompts
4
+
5
+
6
+ class SingleWholeFileFunctionPrompts(CoderPrompts):
7
+ main_system = """Act as an expert software developer.
8
+ Take requests for changes to the supplied code.
9
+ If the request is ambiguous, ask questions.
10
+
11
+ Once you understand the request you MUST use the `write_file` function to update the file to make the changes.
12
+ """
13
+
14
+ system_reminder = """
15
+ ONLY return code using the `write_file` function.
16
+ NEVER return code outside the `write_file` function.
17
+ """
18
+
19
+ files_content_prefix = "Here is the current content of the file:\n"
20
+ files_no_full_files = "I am not sharing any files yet."
21
+
22
+ redacted_edit_message = "No changes are needed."
23
+
24
+ # TODO: should this be present for using this with gpt-4?
25
+ repo_content_prefix = None
26
+
27
+ # TODO: fix the chat history, except we can't keep the whole file
@@ -0,0 +1,429 @@
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
+ from .udiff_prompts import UnifiedDiffPrompts
15
+
16
+ no_match_error = """UnifiedDiffNoMatch: hunk failed to apply!
17
+
18
+ {path} does not contain lines that match the diff you provided!
19
+ Try again.
20
+ DO NOT skip blank lines, comments, docstrings, etc!
21
+ The diff needs to apply cleanly to the lines in {path}!
22
+
23
+ {path} does not contain these {num_lines} exact lines in a row:
24
+ ```
25
+ {original}```
26
+ """
27
+
28
+
29
+ not_unique_error = """UnifiedDiffNotUnique: hunk failed to apply!
30
+
31
+ {path} contains multiple sets of lines that match the diff you provided!
32
+ Try again.
33
+ Use additional ` ` lines to provide context that uniquely indicates which code needs to be changed.
34
+ The diff needs to apply to a unique set of lines in {path}!
35
+
36
+ {path} contains multiple copies of these {num_lines} lines:
37
+ ```
38
+ {original}```
39
+ """
40
+
41
+ other_hunks_applied = (
42
+ "Note: some hunks did apply successfully. See the updated source code shown above.\n\n"
43
+ )
44
+
45
+
46
+ class UnifiedDiffCoder(Coder):
47
+ """A coder that uses unified diff format for code modifications."""
48
+
49
+ edit_format = "udiff"
50
+ gpt_prompts = UnifiedDiffPrompts()
51
+
52
+ def get_edits(self):
53
+ content = self.partial_response_content
54
+
55
+ # might raise ValueError for malformed ORIG/UPD blocks
56
+ raw_edits = list(find_diffs(content))
57
+
58
+ last_path = None
59
+ edits = []
60
+ for path, hunk in raw_edits:
61
+ if path:
62
+ last_path = path
63
+ else:
64
+ path = last_path
65
+ edits.append((path, hunk))
66
+
67
+ return edits
68
+
69
+ def apply_edits(self, edits):
70
+ seen = set()
71
+ uniq = []
72
+ for path, hunk in edits:
73
+ hunk = normalize_hunk(hunk)
74
+ if not hunk:
75
+ continue
76
+
77
+ this = [path + "\n"] + hunk
78
+ this = "".join(this)
79
+
80
+ if this in seen:
81
+ continue
82
+ seen.add(this)
83
+
84
+ uniq.append((path, hunk))
85
+
86
+ errors = []
87
+ for path, hunk in uniq:
88
+ full_path = self.abs_root_path(path)
89
+ content = self.io.read_text(full_path)
90
+
91
+ original, _ = hunk_to_before_after(hunk)
92
+
93
+ try:
94
+ content = do_replace(full_path, content, hunk)
95
+ except SearchTextNotUnique:
96
+ errors.append(
97
+ not_unique_error.format(
98
+ path=path, original=original, num_lines=len(original.splitlines())
99
+ )
100
+ )
101
+ continue
102
+
103
+ if not content:
104
+ errors.append(
105
+ no_match_error.format(
106
+ path=path, original=original, num_lines=len(original.splitlines())
107
+ )
108
+ )
109
+ continue
110
+
111
+ # SUCCESS!
112
+ self.io.write_text(full_path, content)
113
+
114
+ if errors:
115
+ errors = "\n\n".join(errors)
116
+ if len(errors) < len(uniq):
117
+ errors += other_hunks_applied
118
+ raise ValueError(errors)
119
+
120
+
121
+ def do_replace(fname, content, hunk):
122
+ fname = Path(fname)
123
+
124
+ before_text, after_text = hunk_to_before_after(hunk)
125
+
126
+ # does it want to make a new file?
127
+ if not fname.exists() and not before_text.strip():
128
+ fname.touch()
129
+ content = ""
130
+
131
+ if content is None:
132
+ return
133
+
134
+ # TODO: handle inserting into new file
135
+ if not before_text.strip():
136
+ # append to existing file, or start a new file
137
+ new_content = content + after_text
138
+ return new_content
139
+
140
+ new_content = None
141
+
142
+ new_content = apply_hunk(content, hunk)
143
+ if new_content:
144
+ return new_content
145
+
146
+
147
+ def collapse_repeats(s):
148
+ return "".join(k for k, g in groupby(s))
149
+
150
+
151
+ def apply_hunk(content, hunk):
152
+ before_text, after_text = hunk_to_before_after(hunk)
153
+
154
+ res = directly_apply_hunk(content, hunk)
155
+ if res:
156
+ return res
157
+
158
+ hunk = make_new_lines_explicit(content, hunk)
159
+
160
+ # just consider space vs not-space
161
+ ops = "".join([line[0] for line in hunk])
162
+ ops = ops.replace("-", "x")
163
+ ops = ops.replace("+", "x")
164
+ ops = ops.replace("\n", " ")
165
+
166
+ cur_op = " "
167
+ section = []
168
+ sections = []
169
+
170
+ for i in range(len(ops)):
171
+ op = ops[i]
172
+ if op != cur_op:
173
+ sections.append(section)
174
+ section = []
175
+ cur_op = op
176
+ section.append(hunk[i])
177
+
178
+ sections.append(section)
179
+ if cur_op != " ":
180
+ sections.append([])
181
+
182
+ all_done = True
183
+ for i in range(2, len(sections), 2):
184
+ preceding_context = sections[i - 2]
185
+ changes = sections[i - 1]
186
+ following_context = sections[i]
187
+
188
+ res = apply_partial_hunk(content, preceding_context, changes, following_context)
189
+ if res:
190
+ content = res
191
+ else:
192
+ all_done = False
193
+ # FAILED!
194
+ # this_hunk = preceding_context + changes + following_context
195
+ break
196
+
197
+ if all_done:
198
+ return content
199
+
200
+
201
+ def flexi_just_search_and_replace(texts):
202
+ strategies = [
203
+ (search_and_replace, all_preprocs),
204
+ ]
205
+
206
+ return flexible_search_and_replace(texts, strategies)
207
+
208
+
209
+ def make_new_lines_explicit(content, hunk):
210
+ before, after = hunk_to_before_after(hunk)
211
+
212
+ diff = diff_lines(before, content)
213
+
214
+ back_diff = []
215
+ for line in diff:
216
+ if line[0] == "+":
217
+ continue
218
+ # if line[0] == "-":
219
+ # line = "+" + line[1:]
220
+
221
+ back_diff.append(line)
222
+
223
+ new_before = directly_apply_hunk(before, back_diff)
224
+ if not new_before:
225
+ return hunk
226
+
227
+ if len(new_before.strip()) < 10:
228
+ return hunk
229
+
230
+ before = before.splitlines(keepends=True)
231
+ new_before = new_before.splitlines(keepends=True)
232
+ after = after.splitlines(keepends=True)
233
+
234
+ if len(new_before) < len(before) * 0.66:
235
+ return hunk
236
+
237
+ new_hunk = difflib.unified_diff(new_before, after, n=max(len(new_before), len(after)))
238
+ new_hunk = list(new_hunk)[3:]
239
+
240
+ return new_hunk
241
+
242
+
243
+ def cleanup_pure_whitespace_lines(lines):
244
+ res = [
245
+ line if line.strip() else line[-(len(line) - len(line.rstrip("\r\n")))] for line in lines
246
+ ]
247
+ return res
248
+
249
+
250
+ def normalize_hunk(hunk):
251
+ before, after = hunk_to_before_after(hunk, lines=True)
252
+
253
+ before = cleanup_pure_whitespace_lines(before)
254
+ after = cleanup_pure_whitespace_lines(after)
255
+
256
+ diff = difflib.unified_diff(before, after, n=max(len(before), len(after)))
257
+ diff = list(diff)[3:]
258
+ return diff
259
+
260
+
261
+ def directly_apply_hunk(content, hunk):
262
+ before, after = hunk_to_before_after(hunk)
263
+
264
+ if not before:
265
+ return
266
+
267
+ before_lines, _ = hunk_to_before_after(hunk, lines=True)
268
+ before_lines = "".join([line.strip() for line in before_lines])
269
+
270
+ # Refuse to do a repeated search and replace on a tiny bit of non-whitespace context
271
+ if len(before_lines) < 10 and content.count(before) > 1:
272
+ return
273
+
274
+ try:
275
+ new_content = flexi_just_search_and_replace([before, after, content])
276
+ except SearchTextNotUnique:
277
+ new_content = None
278
+
279
+ return new_content
280
+
281
+
282
+ def apply_partial_hunk(content, preceding_context, changes, following_context):
283
+ len_prec = len(preceding_context)
284
+ len_foll = len(following_context)
285
+
286
+ use_all = len_prec + len_foll
287
+
288
+ # if there is a - in the hunk, we can go all the way to `use=0`
289
+ for drop in range(use_all + 1):
290
+ use = use_all - drop
291
+
292
+ for use_prec in range(len_prec, -1, -1):
293
+ if use_prec > use:
294
+ continue
295
+
296
+ use_foll = use - use_prec
297
+ if use_foll > len_foll:
298
+ continue
299
+
300
+ if use_prec:
301
+ this_prec = preceding_context[-use_prec:]
302
+ else:
303
+ this_prec = []
304
+
305
+ this_foll = following_context[:use_foll]
306
+
307
+ res = directly_apply_hunk(content, this_prec + changes + this_foll)
308
+ if res:
309
+ return res
310
+
311
+
312
+ def find_diffs(content):
313
+ # We can always fence with triple-quotes, because all the udiff content
314
+ # is prefixed with +/-/space.
315
+
316
+ if not content.endswith("\n"):
317
+ content = content + "\n"
318
+
319
+ lines = content.splitlines(keepends=True)
320
+ line_num = 0
321
+ edits = []
322
+ while line_num < len(lines):
323
+ while line_num < len(lines):
324
+ line = lines[line_num]
325
+ if line.startswith("```diff"):
326
+ line_num, these_edits = process_fenced_block(lines, line_num + 1)
327
+ edits += these_edits
328
+ break
329
+ line_num += 1
330
+
331
+ # For now, just take 1!
332
+ # edits = edits[:1]
333
+
334
+ return edits
335
+
336
+
337
+ def process_fenced_block(lines, start_line_num):
338
+ for line_num in range(start_line_num, len(lines)):
339
+ line = lines[line_num]
340
+ if line.startswith("```"):
341
+ break
342
+
343
+ block = lines[start_line_num:line_num]
344
+ block.append("@@ @@")
345
+
346
+ if block[0].startswith("--- ") and block[1].startswith("+++ "):
347
+ # Extract the file path, considering that it might contain spaces
348
+ a_fname = block[0][4:].strip()
349
+ b_fname = block[1][4:].strip()
350
+
351
+ # Check if standard git diff prefixes are present (or /dev/null) and strip them
352
+ if (a_fname.startswith("a/") or a_fname == "/dev/null") and b_fname.startswith("b/"):
353
+ fname = b_fname[2:]
354
+ else:
355
+ # Otherwise, assume the path is as intended
356
+ fname = b_fname
357
+
358
+ block = block[2:]
359
+ else:
360
+ fname = None
361
+
362
+ edits = []
363
+
364
+ keeper = False
365
+ hunk = []
366
+ op = " "
367
+ for line in block:
368
+ hunk.append(line)
369
+ if len(line) < 2:
370
+ continue
371
+
372
+ if line.startswith("+++ ") and hunk[-2].startswith("--- "):
373
+ if hunk[-3] == "\n":
374
+ hunk = hunk[:-3]
375
+ else:
376
+ hunk = hunk[:-2]
377
+
378
+ edits.append((fname, hunk))
379
+ hunk = []
380
+ keeper = False
381
+
382
+ fname = line[4:].strip()
383
+ continue
384
+
385
+ op = line[0]
386
+ if op in "-+":
387
+ keeper = True
388
+ continue
389
+ if op != "@":
390
+ continue
391
+ if not keeper:
392
+ hunk = []
393
+ continue
394
+
395
+ hunk = hunk[:-1]
396
+ edits.append((fname, hunk))
397
+ hunk = []
398
+ keeper = False
399
+
400
+ return line_num + 1, edits
401
+
402
+
403
+ def hunk_to_before_after(hunk, lines=False):
404
+ before = []
405
+ after = []
406
+ op = " "
407
+ for line in hunk:
408
+ if len(line) < 2:
409
+ op = " "
410
+ line = line
411
+ else:
412
+ op = line[0]
413
+ line = line[1:]
414
+
415
+ if op == " ":
416
+ before.append(line)
417
+ after.append(line)
418
+ elif op == "-":
419
+ before.append(line)
420
+ elif op == "+":
421
+ after.append(line)
422
+
423
+ if lines:
424
+ return before, after
425
+
426
+ before = "".join(before)
427
+ after = "".join(after)
428
+
429
+ return before, after
@@ -0,0 +1,115 @@
1
+ # flake8: noqa: E501
2
+
3
+ from . import shell
4
+ from .base_prompts import CoderPrompts
5
+
6
+
7
+ class UnifiedDiffPrompts(CoderPrompts):
8
+ main_system = """Act as an expert software developer.
9
+ {final_reminders}
10
+ Always use best practices when coding.
11
+ Respect and use existing conventions, libraries, etc that are already present in the code base.
12
+
13
+ Take requests for changes to the supplied code.
14
+ If the request is ambiguous, ask questions.
15
+
16
+ Always reply to the user in {language}.
17
+
18
+ For each file that needs to be changed, write out the changes similar to a unified diff like `diff -U0` would produce.
19
+ """
20
+
21
+ example_messages = [
22
+ dict(
23
+ role="user",
24
+ content="Replace is_prime with a call to sympy.",
25
+ ),
26
+ dict(
27
+ role="assistant",
28
+ content="""Ok, I will:
29
+
30
+ 1. Add an imports of sympy.
31
+ 2. Remove the is_prime() function.
32
+ 3. Replace the existing call to is_prime() with a call to sympy.isprime().
33
+
34
+ Here are the diffs for those changes:
35
+
36
+ ```diff
37
+ --- mathweb/flask/app.py
38
+ +++ mathweb/flask/app.py
39
+ @@ ... @@
40
+ -class MathWeb:
41
+ +import sympy
42
+ +
43
+ +class MathWeb:
44
+ @@ ... @@
45
+ -def is_prime(x):
46
+ - if x < 2:
47
+ - return False
48
+ - for i in range(2, int(math.sqrt(x)) + 1):
49
+ - if x % i == 0:
50
+ - return False
51
+ - return True
52
+ @@ ... @@
53
+ -@app.route('/prime/<int:n>')
54
+ -def nth_prime(n):
55
+ - count = 0
56
+ - num = 1
57
+ - while count < n:
58
+ - num += 1
59
+ - if is_prime(num):
60
+ - count += 1
61
+ - return str(num)
62
+ +@app.route('/prime/<int:n>')
63
+ +def nth_prime(n):
64
+ + count = 0
65
+ + num = 1
66
+ + while count < n:
67
+ + num += 1
68
+ + if sympy.isprime(num):
69
+ + count += 1
70
+ + return str(num)
71
+ ```
72
+ """,
73
+ ),
74
+ ]
75
+
76
+ system_reminder = """# File editing rules:
77
+
78
+ Return edits similar to unified diffs that `diff -U0` would produce.
79
+
80
+ Make sure you include the first 2 lines with the file paths.
81
+ Don't include timestamps with the file paths.
82
+
83
+ Start each hunk of changes with a `@@ ... @@` line.
84
+ Don't include line numbers like `diff -U0` does.
85
+ The user's patch tool doesn't need them.
86
+
87
+ The user's patch tool needs CORRECT patches that apply cleanly against the current contents of the file!
88
+ Think carefully and make sure you include and mark all lines that need to be removed or changed as `-` lines.
89
+ Make sure you mark all new or modified lines with `+`.
90
+ Don't leave out any lines or the diff patch won't apply correctly.
91
+
92
+ Indentation matters in the diffs!
93
+
94
+ Start a new hunk for each section of the file that needs changes.
95
+
96
+ Only output hunks that specify changes with `+` or `-` lines.
97
+ Skip any hunks that are entirely unchanging ` ` lines.
98
+
99
+ Output hunks in whatever order makes the most sense.
100
+ Hunks don't need to be in any particular order.
101
+
102
+ When editing a function, method, loop, etc use a hunk to replace the *entire* code block.
103
+ Delete the entire existing version with `-` lines and then add a new, updated version with `+` lines.
104
+ This will help you generate correct code and correct diffs.
105
+
106
+ To move code within a file, use 2 hunks: 1 to delete it from its current location, 1 to insert it in the new location.
107
+
108
+ To make a new file, show a diff from `--- /dev/null` to `+++ path/to/new/file.ext`.
109
+
110
+ {final_reminders}
111
+ """
112
+
113
+ shell_cmd_prompt = shell.shell_cmd_prompt
114
+ no_shell_cmd_prompt = shell.no_shell_cmd_prompt
115
+ shell_cmd_reminder = shell.shell_cmd_reminder