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/copypaste.py
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
import threading
|
2
|
+
import time
|
3
|
+
|
4
|
+
import pyperclip
|
5
|
+
|
6
|
+
|
7
|
+
class ClipboardWatcher:
|
8
|
+
"""Watches clipboard for changes and updates IO placeholder"""
|
9
|
+
|
10
|
+
def __init__(self, io, verbose=False):
|
11
|
+
self.io = io
|
12
|
+
self.verbose = verbose
|
13
|
+
self.stop_event = None
|
14
|
+
self.watcher_thread = None
|
15
|
+
self.last_clipboard = None
|
16
|
+
self.io.clipboard_watcher = self
|
17
|
+
|
18
|
+
def start(self):
|
19
|
+
"""Start watching clipboard for changes"""
|
20
|
+
self.stop_event = threading.Event()
|
21
|
+
self.last_clipboard = pyperclip.paste()
|
22
|
+
|
23
|
+
def watch_clipboard():
|
24
|
+
while not self.stop_event.is_set():
|
25
|
+
try:
|
26
|
+
current = pyperclip.paste()
|
27
|
+
if current != self.last_clipboard:
|
28
|
+
self.last_clipboard = current
|
29
|
+
self.io.interrupt_input()
|
30
|
+
self.io.placeholder = current
|
31
|
+
if len(current.splitlines()) > 1:
|
32
|
+
self.io.placeholder = "\n" + self.io.placeholder + "\n"
|
33
|
+
|
34
|
+
time.sleep(0.5)
|
35
|
+
except Exception as e:
|
36
|
+
if self.verbose:
|
37
|
+
from aider.dump import dump
|
38
|
+
|
39
|
+
dump(f"Clipboard watcher error: {e}")
|
40
|
+
continue
|
41
|
+
|
42
|
+
self.watcher_thread = threading.Thread(target=watch_clipboard, daemon=True)
|
43
|
+
self.watcher_thread.start()
|
44
|
+
|
45
|
+
def stop(self):
|
46
|
+
"""Stop watching clipboard for changes"""
|
47
|
+
if self.stop_event:
|
48
|
+
self.stop_event.set()
|
49
|
+
if self.watcher_thread:
|
50
|
+
self.watcher_thread.join()
|
51
|
+
self.watcher_thread = None
|
52
|
+
self.stop_event = None
|
53
|
+
|
54
|
+
|
55
|
+
def main():
|
56
|
+
"""Example usage of the clipboard watcher"""
|
57
|
+
from aider.io import InputOutput
|
58
|
+
|
59
|
+
io = InputOutput()
|
60
|
+
watcher = ClipboardWatcher(io, verbose=True)
|
61
|
+
|
62
|
+
try:
|
63
|
+
watcher.start()
|
64
|
+
while True:
|
65
|
+
time.sleep(1)
|
66
|
+
except KeyboardInterrupt:
|
67
|
+
print("\nStopped watching clipboard")
|
68
|
+
watcher.stop()
|
69
|
+
|
70
|
+
|
71
|
+
if __name__ == "__main__":
|
72
|
+
main()
|
aider/deprecated.py
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
def add_deprecated_model_args(parser, group):
|
2
|
+
"""Add deprecated model shortcut arguments to the argparse parser."""
|
3
|
+
opus_model = "claude-3-opus-20240229"
|
4
|
+
group.add_argument(
|
5
|
+
"--opus",
|
6
|
+
action="store_true",
|
7
|
+
help=f"Use {opus_model} model for the main chat (deprecated, use --model)",
|
8
|
+
default=False,
|
9
|
+
)
|
10
|
+
sonnet_model = "anthropic/claude-3-7-sonnet-20250219"
|
11
|
+
group.add_argument(
|
12
|
+
"--sonnet",
|
13
|
+
action="store_true",
|
14
|
+
help=f"Use {sonnet_model} model for the main chat (deprecated, use --model)",
|
15
|
+
default=False,
|
16
|
+
)
|
17
|
+
haiku_model = "claude-3-5-haiku-20241022"
|
18
|
+
group.add_argument(
|
19
|
+
"--haiku",
|
20
|
+
action="store_true",
|
21
|
+
help=f"Use {haiku_model} model for the main chat (deprecated, use --model)",
|
22
|
+
default=False,
|
23
|
+
)
|
24
|
+
gpt_4_model = "gpt-4-0613"
|
25
|
+
group.add_argument(
|
26
|
+
"--4",
|
27
|
+
"-4",
|
28
|
+
action="store_true",
|
29
|
+
help=f"Use {gpt_4_model} model for the main chat (deprecated, use --model)",
|
30
|
+
default=False,
|
31
|
+
)
|
32
|
+
gpt_4o_model = "gpt-4o"
|
33
|
+
group.add_argument(
|
34
|
+
"--4o",
|
35
|
+
action="store_true",
|
36
|
+
help=f"Use {gpt_4o_model} model for the main chat (deprecated, use --model)",
|
37
|
+
default=False,
|
38
|
+
)
|
39
|
+
gpt_4o_mini_model = "gpt-4o-mini"
|
40
|
+
group.add_argument(
|
41
|
+
"--mini",
|
42
|
+
action="store_true",
|
43
|
+
help=f"Use {gpt_4o_mini_model} model for the main chat (deprecated, use --model)",
|
44
|
+
default=False,
|
45
|
+
)
|
46
|
+
gpt_4_turbo_model = "gpt-4-1106-preview"
|
47
|
+
group.add_argument(
|
48
|
+
"--4-turbo",
|
49
|
+
action="store_true",
|
50
|
+
help=f"Use {gpt_4_turbo_model} model for the main chat (deprecated, use --model)",
|
51
|
+
default=False,
|
52
|
+
)
|
53
|
+
gpt_3_model_name = "gpt-3.5-turbo"
|
54
|
+
group.add_argument(
|
55
|
+
"--35turbo",
|
56
|
+
"--35-turbo",
|
57
|
+
"--3",
|
58
|
+
"-3",
|
59
|
+
action="store_true",
|
60
|
+
help=f"Use {gpt_3_model_name} model for the main chat (deprecated, use --model)",
|
61
|
+
default=False,
|
62
|
+
)
|
63
|
+
deepseek_model = "deepseek/deepseek-chat"
|
64
|
+
group.add_argument(
|
65
|
+
"--deepseek",
|
66
|
+
action="store_true",
|
67
|
+
help=f"Use {deepseek_model} model for the main chat (deprecated, use --model)",
|
68
|
+
default=False,
|
69
|
+
)
|
70
|
+
o1_mini_model = "o1-mini"
|
71
|
+
group.add_argument(
|
72
|
+
"--o1-mini",
|
73
|
+
action="store_true",
|
74
|
+
help=f"Use {o1_mini_model} model for the main chat (deprecated, use --model)",
|
75
|
+
default=False,
|
76
|
+
)
|
77
|
+
o1_preview_model = "o1-preview"
|
78
|
+
group.add_argument(
|
79
|
+
"--o1-preview",
|
80
|
+
action="store_true",
|
81
|
+
help=f"Use {o1_preview_model} model for the main chat (deprecated, use --model)",
|
82
|
+
default=False,
|
83
|
+
)
|
84
|
+
|
85
|
+
|
86
|
+
def handle_deprecated_model_args(args, io):
|
87
|
+
"""Handle deprecated model shortcut arguments and provide appropriate warnings."""
|
88
|
+
# Define model mapping
|
89
|
+
model_map = {
|
90
|
+
"opus": "claude-3-opus-20240229",
|
91
|
+
"sonnet": "anthropic/claude-3-7-sonnet-20250219",
|
92
|
+
"haiku": "claude-3-5-haiku-20241022",
|
93
|
+
"4": "gpt-4-0613",
|
94
|
+
"4o": "gpt-4o",
|
95
|
+
"mini": "gpt-4o-mini",
|
96
|
+
"4_turbo": "gpt-4-1106-preview",
|
97
|
+
"35turbo": "gpt-3.5-turbo",
|
98
|
+
"deepseek": "deepseek/deepseek-chat",
|
99
|
+
"o1_mini": "o1-mini",
|
100
|
+
"o1_preview": "o1-preview",
|
101
|
+
}
|
102
|
+
|
103
|
+
# Check if any deprecated args are used
|
104
|
+
for arg_name, model_name in model_map.items():
|
105
|
+
arg_name_clean = arg_name.replace("-", "_")
|
106
|
+
if hasattr(args, arg_name_clean) and getattr(args, arg_name_clean):
|
107
|
+
# Find preferred name to display in warning
|
108
|
+
from aider.models import MODEL_ALIASES
|
109
|
+
|
110
|
+
display_name = model_name
|
111
|
+
# Check if there's a shorter alias for this model
|
112
|
+
for alias, full_name in MODEL_ALIASES.items():
|
113
|
+
if full_name == model_name:
|
114
|
+
display_name = alias
|
115
|
+
break
|
116
|
+
|
117
|
+
# Show the warning
|
118
|
+
io.tool_warning(
|
119
|
+
f"The --{arg_name.replace('_', '-')} flag is deprecated and will be removed in a"
|
120
|
+
f" future version. Please use --model {display_name} instead."
|
121
|
+
)
|
122
|
+
|
123
|
+
# Set the model
|
124
|
+
if not args.model:
|
125
|
+
args.model = model_name
|
126
|
+
break
|
aider/diffs.py
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
import difflib
|
2
|
+
import sys
|
3
|
+
|
4
|
+
from .dump import dump # noqa: F401
|
5
|
+
|
6
|
+
|
7
|
+
def main():
|
8
|
+
if len(sys.argv) != 3:
|
9
|
+
print("Usage: python diffs.py file1 file")
|
10
|
+
sys.exit(1)
|
11
|
+
|
12
|
+
file_orig, file_updated = sys.argv[1], sys.argv[2]
|
13
|
+
|
14
|
+
with open(file_orig, "r", encoding="utf-8") as f:
|
15
|
+
lines_orig = f.readlines()
|
16
|
+
|
17
|
+
with open(file_updated, "r", encoding="utf-8") as f:
|
18
|
+
lines_updated = f.readlines()
|
19
|
+
|
20
|
+
for i in range(len(file_updated)):
|
21
|
+
res = diff_partial_update(lines_orig, lines_updated[:i])
|
22
|
+
print(res)
|
23
|
+
input()
|
24
|
+
|
25
|
+
|
26
|
+
def create_progress_bar(percentage):
|
27
|
+
block = "█"
|
28
|
+
empty = "░"
|
29
|
+
total_blocks = 30
|
30
|
+
filled_blocks = int(total_blocks * percentage // 100)
|
31
|
+
empty_blocks = total_blocks - filled_blocks
|
32
|
+
bar = block * filled_blocks + empty * empty_blocks
|
33
|
+
return bar
|
34
|
+
|
35
|
+
|
36
|
+
def assert_newlines(lines):
|
37
|
+
if not lines:
|
38
|
+
return
|
39
|
+
for line in lines[:-1]:
|
40
|
+
assert line and line[-1] == "\n", line
|
41
|
+
|
42
|
+
|
43
|
+
def diff_partial_update(lines_orig, lines_updated, final=False, fname=None):
|
44
|
+
"""
|
45
|
+
Given only the first part of an updated file, show the diff while
|
46
|
+
ignoring the block of "deleted" lines that are past the end of the
|
47
|
+
partially complete update.
|
48
|
+
"""
|
49
|
+
|
50
|
+
# dump(lines_orig)
|
51
|
+
# dump(lines_updated)
|
52
|
+
|
53
|
+
assert_newlines(lines_orig)
|
54
|
+
|
55
|
+
num_orig_lines = len(lines_orig)
|
56
|
+
|
57
|
+
if final:
|
58
|
+
last_non_deleted = num_orig_lines
|
59
|
+
else:
|
60
|
+
last_non_deleted = find_last_non_deleted(lines_orig, lines_updated)
|
61
|
+
|
62
|
+
# dump(last_non_deleted)
|
63
|
+
if last_non_deleted is None:
|
64
|
+
return ""
|
65
|
+
|
66
|
+
if num_orig_lines:
|
67
|
+
pct = last_non_deleted * 100 / num_orig_lines
|
68
|
+
else:
|
69
|
+
pct = 50
|
70
|
+
bar = create_progress_bar(pct)
|
71
|
+
bar = f" {last_non_deleted:3d} / {num_orig_lines:3d} lines [{bar}] {pct:3.0f}%\n"
|
72
|
+
|
73
|
+
lines_orig = lines_orig[:last_non_deleted]
|
74
|
+
|
75
|
+
if not final:
|
76
|
+
lines_updated = lines_updated[:-1] + [bar]
|
77
|
+
|
78
|
+
diff = difflib.unified_diff(lines_orig, lines_updated, n=5)
|
79
|
+
|
80
|
+
diff = list(diff)[2:]
|
81
|
+
|
82
|
+
diff = "".join(diff)
|
83
|
+
if not diff.endswith("\n"):
|
84
|
+
diff += "\n"
|
85
|
+
|
86
|
+
for i in range(3, 10):
|
87
|
+
backticks = "`" * i
|
88
|
+
if backticks not in diff:
|
89
|
+
break
|
90
|
+
|
91
|
+
show = f"{backticks}diff\n"
|
92
|
+
if fname:
|
93
|
+
show += f"--- {fname} original\n"
|
94
|
+
show += f"+++ {fname} updated\n"
|
95
|
+
|
96
|
+
show += diff
|
97
|
+
|
98
|
+
show += f"{backticks}\n\n"
|
99
|
+
|
100
|
+
# print(diff)
|
101
|
+
|
102
|
+
return show
|
103
|
+
|
104
|
+
|
105
|
+
def find_last_non_deleted(lines_orig, lines_updated):
|
106
|
+
diff = list(difflib.ndiff(lines_orig, lines_updated))
|
107
|
+
|
108
|
+
num_orig = 0
|
109
|
+
last_non_deleted_orig = None
|
110
|
+
|
111
|
+
for line in diff:
|
112
|
+
# print(f"{num_orig:2d} {num_updated:2d} {line}", end="")
|
113
|
+
code = line[0]
|
114
|
+
if code == " ":
|
115
|
+
num_orig += 1
|
116
|
+
last_non_deleted_orig = num_orig
|
117
|
+
elif code == "-":
|
118
|
+
# line only in orig
|
119
|
+
num_orig += 1
|
120
|
+
elif code == "+":
|
121
|
+
# line only in updated
|
122
|
+
pass
|
123
|
+
|
124
|
+
return last_non_deleted_orig
|
125
|
+
|
126
|
+
|
127
|
+
if __name__ == "__main__":
|
128
|
+
main()
|
aider/dump.py
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
import json
|
2
|
+
import traceback
|
3
|
+
|
4
|
+
|
5
|
+
def cvt(s):
|
6
|
+
if isinstance(s, str):
|
7
|
+
return s
|
8
|
+
try:
|
9
|
+
return json.dumps(s, indent=4)
|
10
|
+
except TypeError:
|
11
|
+
return str(s)
|
12
|
+
|
13
|
+
|
14
|
+
def dump(*vals):
|
15
|
+
# http://docs.python.org/library/traceback.html
|
16
|
+
stack = traceback.extract_stack()
|
17
|
+
vars = stack[-2][3]
|
18
|
+
|
19
|
+
# strip away the call to dump()
|
20
|
+
vars = "(".join(vars.split("(")[1:])
|
21
|
+
vars = ")".join(vars.split(")")[:-1])
|
22
|
+
|
23
|
+
vals = [cvt(v) for v in vals]
|
24
|
+
has_newline = sum(1 for v in vals if "\n" in v)
|
25
|
+
if has_newline:
|
26
|
+
print("%s:" % vars)
|
27
|
+
print(", ".join(vals))
|
28
|
+
else:
|
29
|
+
print("%s:" % vars, ", ".join(vals))
|
aider/editor.py
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
"""
|
2
|
+
Editor module for handling system text editor interactions.
|
3
|
+
|
4
|
+
This module provides functionality to:
|
5
|
+
- Discover and launch the system's configured text editor
|
6
|
+
- Create and manage temporary files for editing
|
7
|
+
- Handle editor preferences from environment variables
|
8
|
+
- Support cross-platform editor operations
|
9
|
+
"""
|
10
|
+
|
11
|
+
import os
|
12
|
+
import platform
|
13
|
+
import subprocess
|
14
|
+
import tempfile
|
15
|
+
|
16
|
+
from rich.console import Console
|
17
|
+
|
18
|
+
from aider.dump import dump # noqa
|
19
|
+
|
20
|
+
DEFAULT_EDITOR_NIX = "vi"
|
21
|
+
DEFAULT_EDITOR_OS_X = "vim"
|
22
|
+
DEFAULT_EDITOR_WINDOWS = "notepad"
|
23
|
+
|
24
|
+
console = Console()
|
25
|
+
|
26
|
+
|
27
|
+
def print_status_message(success, message, style=None):
|
28
|
+
"""
|
29
|
+
Print a status message with appropriate styling.
|
30
|
+
|
31
|
+
:param success: Whether the operation was successful
|
32
|
+
:param message: The message to display
|
33
|
+
:param style: Optional style override. If None, uses green for success and red for failure
|
34
|
+
"""
|
35
|
+
if style is None:
|
36
|
+
style = "bold green" if success else "bold red"
|
37
|
+
console.print(message, style=style)
|
38
|
+
print("")
|
39
|
+
|
40
|
+
|
41
|
+
def write_temp_file(
|
42
|
+
input_data="",
|
43
|
+
suffix=None,
|
44
|
+
prefix=None,
|
45
|
+
dir=None,
|
46
|
+
):
|
47
|
+
"""
|
48
|
+
Create a temporary file with the given input data.
|
49
|
+
|
50
|
+
:param input_data: Content to write to the temporary file
|
51
|
+
:param suffix: Optional file extension (without the dot)
|
52
|
+
:param prefix: Optional prefix for the temporary filename
|
53
|
+
:param dir: Optional directory to create the file in
|
54
|
+
:return: Path to the created temporary file
|
55
|
+
:raises: OSError if file creation or writing fails
|
56
|
+
"""
|
57
|
+
kwargs = {"prefix": prefix, "dir": dir}
|
58
|
+
if suffix:
|
59
|
+
kwargs["suffix"] = f".{suffix}"
|
60
|
+
fd, filepath = tempfile.mkstemp(**kwargs)
|
61
|
+
try:
|
62
|
+
with os.fdopen(fd, "w") as f:
|
63
|
+
f.write(input_data)
|
64
|
+
except Exception:
|
65
|
+
os.close(fd)
|
66
|
+
raise
|
67
|
+
return filepath
|
68
|
+
|
69
|
+
|
70
|
+
def get_environment_editor(default=None):
|
71
|
+
"""
|
72
|
+
Fetches the preferred editor from the environment variables.
|
73
|
+
|
74
|
+
This function checks the following environment variables in order to
|
75
|
+
determine the user's preferred editor:
|
76
|
+
|
77
|
+
- VISUAL
|
78
|
+
- EDITOR
|
79
|
+
|
80
|
+
:param default: The default editor to return if no environment variable is set.
|
81
|
+
:type default: str or None
|
82
|
+
:return: The preferred editor as specified by environment variables or the default value.
|
83
|
+
:rtype: str or None
|
84
|
+
"""
|
85
|
+
editor = os.environ.get("VISUAL", os.environ.get("EDITOR", default))
|
86
|
+
return editor
|
87
|
+
|
88
|
+
|
89
|
+
def discover_editor(editor_override=None):
|
90
|
+
"""
|
91
|
+
Discovers and returns the appropriate editor command.
|
92
|
+
|
93
|
+
Handles cases where the editor command includes arguments, including quoted arguments
|
94
|
+
with spaces (e.g. 'vim -c "set noswapfile"').
|
95
|
+
|
96
|
+
:return: The editor command as a string
|
97
|
+
:rtype: str
|
98
|
+
"""
|
99
|
+
system = platform.system()
|
100
|
+
if system == "Windows":
|
101
|
+
default_editor = DEFAULT_EDITOR_WINDOWS
|
102
|
+
elif system == "Darwin":
|
103
|
+
default_editor = DEFAULT_EDITOR_OS_X
|
104
|
+
else:
|
105
|
+
default_editor = DEFAULT_EDITOR_NIX
|
106
|
+
|
107
|
+
if editor_override:
|
108
|
+
editor = editor_override
|
109
|
+
else:
|
110
|
+
editor = get_environment_editor(default_editor)
|
111
|
+
|
112
|
+
return editor
|
113
|
+
|
114
|
+
|
115
|
+
def pipe_editor(input_data="", suffix=None, editor=None):
|
116
|
+
"""
|
117
|
+
Opens the system editor with optional input data and returns the edited content.
|
118
|
+
|
119
|
+
This function creates a temporary file with the provided input data, opens it in
|
120
|
+
the system editor, waits for the user to make changes and close the editor, then
|
121
|
+
reads and returns the modified content. The temporary file is deleted afterwards.
|
122
|
+
|
123
|
+
:param input_data: Initial content to populate the editor with
|
124
|
+
:type input_data: str
|
125
|
+
:param suffix: Optional file extension for the temporary file (e.g. '.txt', '.md')
|
126
|
+
:type suffix: str or None
|
127
|
+
:return: The edited content after the editor is closed
|
128
|
+
:rtype: str
|
129
|
+
"""
|
130
|
+
filepath = write_temp_file(input_data, suffix)
|
131
|
+
command_str = discover_editor(editor)
|
132
|
+
command_str += " " + filepath
|
133
|
+
|
134
|
+
subprocess.call(command_str, shell=True)
|
135
|
+
with open(filepath, "r") as f:
|
136
|
+
output_data = f.read()
|
137
|
+
try:
|
138
|
+
os.remove(filepath)
|
139
|
+
except PermissionError:
|
140
|
+
print_status_message(
|
141
|
+
False,
|
142
|
+
(
|
143
|
+
f"WARNING: Unable to delete temporary file {filepath!r}. You may need to delete it"
|
144
|
+
" manually."
|
145
|
+
),
|
146
|
+
)
|
147
|
+
return output_data
|
aider/exceptions.py
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
|
3
|
+
from aider.dump import dump # noqa: F401
|
4
|
+
|
5
|
+
|
6
|
+
@dataclass
|
7
|
+
class ExInfo:
|
8
|
+
name: str
|
9
|
+
retry: bool
|
10
|
+
description: str
|
11
|
+
|
12
|
+
|
13
|
+
EXCEPTIONS = [
|
14
|
+
ExInfo("APIConnectionError", True, None),
|
15
|
+
ExInfo("APIError", True, None),
|
16
|
+
ExInfo("APIResponseValidationError", True, None),
|
17
|
+
ExInfo(
|
18
|
+
"AuthenticationError",
|
19
|
+
False,
|
20
|
+
"The API provider is not able to authenticate you. Check your API key.",
|
21
|
+
),
|
22
|
+
ExInfo("AzureOpenAIError", True, None),
|
23
|
+
ExInfo("BadRequestError", False, None),
|
24
|
+
ExInfo("BudgetExceededError", True, None),
|
25
|
+
ExInfo(
|
26
|
+
"ContentPolicyViolationError",
|
27
|
+
True,
|
28
|
+
"The API provider has refused the request due to a safety policy about the content.",
|
29
|
+
),
|
30
|
+
ExInfo("ContextWindowExceededError", False, None), # special case handled in base_coder
|
31
|
+
ExInfo("InternalServerError", True, "The API provider's servers are down or overloaded."),
|
32
|
+
ExInfo("InvalidRequestError", True, None),
|
33
|
+
ExInfo("JSONSchemaValidationError", True, None),
|
34
|
+
ExInfo("NotFoundError", False, None),
|
35
|
+
ExInfo("OpenAIError", True, None),
|
36
|
+
ExInfo(
|
37
|
+
"RateLimitError",
|
38
|
+
True,
|
39
|
+
"The API provider has rate limited you. Try again later or check your quotas.",
|
40
|
+
),
|
41
|
+
ExInfo("RouterRateLimitError", True, None),
|
42
|
+
ExInfo("ServiceUnavailableError", True, "The API provider's servers are down or overloaded."),
|
43
|
+
ExInfo("UnprocessableEntityError", True, None),
|
44
|
+
ExInfo("UnsupportedParamsError", True, None),
|
45
|
+
ExInfo(
|
46
|
+
"Timeout",
|
47
|
+
True,
|
48
|
+
"The API provider timed out without returning a response. They may be down or overloaded.",
|
49
|
+
),
|
50
|
+
]
|
51
|
+
|
52
|
+
|
53
|
+
class LiteLLMExceptions:
|
54
|
+
exceptions = dict()
|
55
|
+
exception_info = {exi.name: exi for exi in EXCEPTIONS}
|
56
|
+
|
57
|
+
def __init__(self):
|
58
|
+
self._load()
|
59
|
+
|
60
|
+
def _load(self, strict=False):
|
61
|
+
import litellm
|
62
|
+
|
63
|
+
for var in dir(litellm):
|
64
|
+
if var.endswith("Error"):
|
65
|
+
if var not in self.exception_info:
|
66
|
+
raise ValueError(f"{var} is in litellm but not in aider's exceptions list")
|
67
|
+
|
68
|
+
for var in self.exception_info:
|
69
|
+
ex = getattr(litellm, var)
|
70
|
+
self.exceptions[ex] = self.exception_info[var]
|
71
|
+
|
72
|
+
def exceptions_tuple(self):
|
73
|
+
return tuple(self.exceptions)
|
74
|
+
|
75
|
+
def get_ex_info(self, ex):
|
76
|
+
"""Return the ExInfo for a given exception instance"""
|
77
|
+
import litellm
|
78
|
+
|
79
|
+
if ex.__class__ is litellm.APIConnectionError:
|
80
|
+
if "google.auth" in str(ex):
|
81
|
+
return ExInfo(
|
82
|
+
"APIConnectionError", False, "You need to: pip install google-generativeai"
|
83
|
+
)
|
84
|
+
if "boto3" in str(ex):
|
85
|
+
return ExInfo("APIConnectionError", False, "You need to: pip install boto3")
|
86
|
+
if "OpenrouterException" in str(ex) and "'choices'" in str(ex):
|
87
|
+
return ExInfo(
|
88
|
+
"APIConnectionError",
|
89
|
+
True,
|
90
|
+
(
|
91
|
+
"OpenRouter or the upstream API provider is down, overloaded or rate"
|
92
|
+
" limiting your requests."
|
93
|
+
),
|
94
|
+
)
|
95
|
+
|
96
|
+
# Check for specific non-retryable APIError cases like insufficient credits
|
97
|
+
if ex.__class__ is litellm.APIError:
|
98
|
+
err_str = str(ex).lower()
|
99
|
+
if "insufficient credits" in err_str and '"code":402' in err_str:
|
100
|
+
return ExInfo(
|
101
|
+
"APIError",
|
102
|
+
False,
|
103
|
+
"Insufficient credits with the API provider. Please add credits.",
|
104
|
+
)
|
105
|
+
# Fall through to default APIError handling if not the specific credits error
|
106
|
+
|
107
|
+
return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
|
aider/format_settings.py
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
def scrub_sensitive_info(args, text):
|
2
|
+
# Replace sensitive information with last 4 characters
|
3
|
+
if text and args.openai_api_key:
|
4
|
+
last_4 = args.openai_api_key[-4:]
|
5
|
+
text = text.replace(args.openai_api_key, f"...{last_4}")
|
6
|
+
if text and args.anthropic_api_key:
|
7
|
+
last_4 = args.anthropic_api_key[-4:]
|
8
|
+
text = text.replace(args.anthropic_api_key, f"...{last_4}")
|
9
|
+
return text
|
10
|
+
|
11
|
+
|
12
|
+
def format_settings(parser, args):
|
13
|
+
show = scrub_sensitive_info(args, parser.format_values())
|
14
|
+
# clean up the headings for consistency w/ new lines
|
15
|
+
heading_env = "Environment Variables:"
|
16
|
+
heading_defaults = "Defaults:"
|
17
|
+
if heading_env in show:
|
18
|
+
show = show.replace(heading_env, "\n" + heading_env)
|
19
|
+
show = show.replace(heading_defaults, "\n" + heading_defaults)
|
20
|
+
show += "\n"
|
21
|
+
show += "Option settings:\n"
|
22
|
+
for arg, val in sorted(vars(args).items()):
|
23
|
+
if val:
|
24
|
+
val = scrub_sensitive_info(args, str(val))
|
25
|
+
show += f" - {arg}: {val}\n" # noqa: E221
|
26
|
+
return show
|