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.
- aider/__init__.py +20 -0
- aider/__main__.py +4 -0
- aider/_version.py +21 -0
- aider/analytics.py +250 -0
- aider/args.py +926 -0
- aider/args_formatter.py +228 -0
- aider/coders/__init__.py +34 -0
- aider/coders/architect_coder.py +48 -0
- aider/coders/architect_prompts.py +40 -0
- aider/coders/ask_coder.py +9 -0
- aider/coders/ask_prompts.py +35 -0
- aider/coders/base_coder.py +2483 -0
- aider/coders/base_prompts.py +60 -0
- aider/coders/chat_chunks.py +64 -0
- aider/coders/context_coder.py +53 -0
- aider/coders/context_prompts.py +75 -0
- aider/coders/editblock_coder.py +657 -0
- aider/coders/editblock_fenced_coder.py +10 -0
- aider/coders/editblock_fenced_prompts.py +143 -0
- aider/coders/editblock_func_coder.py +141 -0
- aider/coders/editblock_func_prompts.py +27 -0
- aider/coders/editblock_prompts.py +174 -0
- aider/coders/editor_diff_fenced_coder.py +9 -0
- aider/coders/editor_diff_fenced_prompts.py +11 -0
- aider/coders/editor_editblock_coder.py +8 -0
- aider/coders/editor_editblock_prompts.py +18 -0
- aider/coders/editor_whole_coder.py +8 -0
- aider/coders/editor_whole_prompts.py +10 -0
- aider/coders/help_coder.py +16 -0
- aider/coders/help_prompts.py +46 -0
- aider/coders/patch_coder.py +706 -0
- aider/coders/patch_prompts.py +161 -0
- aider/coders/search_replace.py +757 -0
- aider/coders/shell.py +37 -0
- aider/coders/single_wholefile_func_coder.py +102 -0
- aider/coders/single_wholefile_func_prompts.py +27 -0
- aider/coders/udiff_coder.py +429 -0
- aider/coders/udiff_prompts.py +115 -0
- aider/coders/udiff_simple.py +14 -0
- aider/coders/udiff_simple_prompts.py +25 -0
- aider/coders/wholefile_coder.py +144 -0
- aider/coders/wholefile_func_coder.py +134 -0
- aider/coders/wholefile_func_prompts.py +27 -0
- aider/coders/wholefile_prompts.py +67 -0
- aider/commands.py +1665 -0
- aider/copypaste.py +72 -0
- aider/deprecated.py +126 -0
- aider/diffs.py +128 -0
- aider/dump.py +29 -0
- aider/editor.py +147 -0
- aider/exceptions.py +107 -0
- aider/format_settings.py +26 -0
- aider/gui.py +545 -0
- aider/help.py +163 -0
- aider/help_pats.py +19 -0
- aider/history.py +143 -0
- aider/io.py +1175 -0
- aider/linter.py +304 -0
- aider/llm.py +47 -0
- aider/main.py +1267 -0
- aider/mdstream.py +243 -0
- aider/models.py +1286 -0
- aider/onboarding.py +428 -0
- aider/openrouter.py +128 -0
- aider/prompts.py +64 -0
- aider/queries/tree-sitter-language-pack/README.md +7 -0
- aider/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- aider/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- aider/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- aider/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- aider/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- aider/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- aider/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- aider/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- aider/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- aider/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- aider/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- aider/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- aider/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- aider/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- aider/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- aider/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- aider/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- aider/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- aider/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- aider/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- aider/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- aider/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- aider/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- aider/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- aider/queries/tree-sitter-languages/README.md +23 -0
- aider/queries/tree-sitter-languages/c-tags.scm +9 -0
- aider/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- aider/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- aider/queries/tree-sitter-languages/dart-tags.scm +91 -0
- aider/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- aider/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- aider/queries/tree-sitter-languages/elm-tags.scm +19 -0
- aider/queries/tree-sitter-languages/go-tags.scm +30 -0
- aider/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- aider/queries/tree-sitter-languages/java-tags.scm +20 -0
- aider/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- aider/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- aider/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- aider/queries/tree-sitter-languages/php-tags.scm +26 -0
- aider/queries/tree-sitter-languages/python-tags.scm +12 -0
- aider/queries/tree-sitter-languages/ql-tags.scm +26 -0
- aider/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- aider/queries/tree-sitter-languages/rust-tags.scm +60 -0
- aider/queries/tree-sitter-languages/scala-tags.scm +65 -0
- aider/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- aider/reasoning_tags.py +82 -0
- aider/repo.py +623 -0
- aider/repomap.py +847 -0
- aider/report.py +200 -0
- aider/resources/__init__.py +3 -0
- aider/resources/model-metadata.json +468 -0
- aider/resources/model-settings.yml +1767 -0
- aider/run_cmd.py +132 -0
- aider/scrape.py +284 -0
- aider/sendchat.py +61 -0
- aider/special.py +203 -0
- aider/urls.py +17 -0
- aider/utils.py +338 -0
- aider/versioncheck.py +113 -0
- aider/voice.py +187 -0
- aider/waiting.py +221 -0
- aider/watch.py +318 -0
- aider/watch_prompts.py +12 -0
- aider/website/Gemfile +8 -0
- aider/website/_includes/blame.md +162 -0
- aider/website/_includes/get-started.md +22 -0
- aider/website/_includes/help-tip.md +5 -0
- aider/website/_includes/help.md +24 -0
- aider/website/_includes/install.md +5 -0
- aider/website/_includes/keys.md +4 -0
- aider/website/_includes/model-warnings.md +67 -0
- aider/website/_includes/multi-line.md +22 -0
- aider/website/_includes/python-m-aider.md +5 -0
- aider/website/_includes/recording.css +228 -0
- aider/website/_includes/recording.md +34 -0
- aider/website/_includes/replit-pipx.md +9 -0
- aider/website/_includes/works-best.md +1 -0
- aider/website/_sass/custom/custom.scss +103 -0
- aider/website/docs/config/adv-model-settings.md +1881 -0
- aider/website/docs/config/aider_conf.md +527 -0
- aider/website/docs/config/api-keys.md +90 -0
- aider/website/docs/config/dotenv.md +478 -0
- aider/website/docs/config/editor.md +127 -0
- aider/website/docs/config/model-aliases.md +103 -0
- aider/website/docs/config/options.md +843 -0
- aider/website/docs/config/reasoning.md +209 -0
- aider/website/docs/config.md +44 -0
- aider/website/docs/faq.md +378 -0
- aider/website/docs/git.md +76 -0
- aider/website/docs/index.md +47 -0
- aider/website/docs/install/codespaces.md +39 -0
- aider/website/docs/install/docker.md +57 -0
- aider/website/docs/install/optional.md +100 -0
- aider/website/docs/install/replit.md +8 -0
- aider/website/docs/install.md +115 -0
- aider/website/docs/languages.md +264 -0
- aider/website/docs/legal/contributor-agreement.md +111 -0
- aider/website/docs/legal/privacy.md +104 -0
- aider/website/docs/llms/anthropic.md +77 -0
- aider/website/docs/llms/azure.md +48 -0
- aider/website/docs/llms/bedrock.md +132 -0
- aider/website/docs/llms/cohere.md +34 -0
- aider/website/docs/llms/deepseek.md +32 -0
- aider/website/docs/llms/gemini.md +49 -0
- aider/website/docs/llms/github.md +105 -0
- aider/website/docs/llms/groq.md +36 -0
- aider/website/docs/llms/lm-studio.md +39 -0
- aider/website/docs/llms/ollama.md +75 -0
- aider/website/docs/llms/openai-compat.md +39 -0
- aider/website/docs/llms/openai.md +58 -0
- aider/website/docs/llms/openrouter.md +78 -0
- aider/website/docs/llms/other.md +103 -0
- aider/website/docs/llms/vertex.md +50 -0
- aider/website/docs/llms/warnings.md +10 -0
- aider/website/docs/llms/xai.md +53 -0
- aider/website/docs/llms.md +54 -0
- aider/website/docs/more/analytics.md +122 -0
- aider/website/docs/more/edit-formats.md +116 -0
- aider/website/docs/more/infinite-output.md +137 -0
- aider/website/docs/more-info.md +8 -0
- aider/website/docs/recordings/auto-accept-architect.md +31 -0
- aider/website/docs/recordings/dont-drop-original-read-files.md +35 -0
- aider/website/docs/recordings/index.md +21 -0
- aider/website/docs/recordings/model-accepts-settings.md +69 -0
- aider/website/docs/recordings/tree-sitter-language-pack.md +80 -0
- aider/website/docs/repomap.md +112 -0
- aider/website/docs/scripting.md +100 -0
- aider/website/docs/troubleshooting/aider-not-found.md +24 -0
- aider/website/docs/troubleshooting/edit-errors.md +76 -0
- aider/website/docs/troubleshooting/imports.md +62 -0
- aider/website/docs/troubleshooting/models-and-keys.md +54 -0
- aider/website/docs/troubleshooting/support.md +79 -0
- aider/website/docs/troubleshooting/token-limits.md +96 -0
- aider/website/docs/troubleshooting/warnings.md +12 -0
- aider/website/docs/troubleshooting.md +11 -0
- aider/website/docs/usage/browser.md +57 -0
- aider/website/docs/usage/caching.md +49 -0
- aider/website/docs/usage/commands.md +132 -0
- aider/website/docs/usage/conventions.md +119 -0
- aider/website/docs/usage/copypaste.md +121 -0
- aider/website/docs/usage/images-urls.md +48 -0
- aider/website/docs/usage/lint-test.md +118 -0
- aider/website/docs/usage/modes.md +211 -0
- aider/website/docs/usage/not-code.md +179 -0
- aider/website/docs/usage/notifications.md +87 -0
- aider/website/docs/usage/tips.md +79 -0
- aider/website/docs/usage/tutorials.md +30 -0
- aider/website/docs/usage/voice.md +121 -0
- aider/website/docs/usage/watch.md +294 -0
- aider/website/docs/usage.md +92 -0
- aider/website/share/index.md +101 -0
- chatmcp_cli-0.1.0.dist-info/METADATA +502 -0
- chatmcp_cli-0.1.0.dist-info/RECORD +228 -0
- chatmcp_cli-0.1.0.dist-info/WHEEL +5 -0
- chatmcp_cli-0.1.0.dist-info/entry_points.txt +3 -0
- chatmcp_cli-0.1.0.dist-info/licenses/LICENSE.txt +202 -0
- 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
|