aider-ce 0.87.2.dev9__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.
Potentially problematic release.
This version of aider-ce might be problematic. Click here for more details.
- aider/__init__.py +20 -0
- aider/__main__.py +4 -0
- aider/_version.py +34 -0
- aider/analytics.py +258 -0
- aider/args.py +1014 -0
- aider/args_formatter.py +228 -0
- aider/change_tracker.py +133 -0
- aider/coders/__init__.py +36 -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 +3013 -0
- aider/coders/base_prompts.py +87 -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 +177 -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 +9 -0
- aider/coders/editor_editblock_prompts.py +21 -0
- aider/coders/editor_whole_coder.py +9 -0
- aider/coders/editor_whole_prompts.py +12 -0
- aider/coders/help_coder.py +16 -0
- aider/coders/help_prompts.py +46 -0
- aider/coders/navigator_coder.py +2711 -0
- aider/coders/navigator_legacy_prompts.py +338 -0
- aider/coders/navigator_prompts.py +530 -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 +117 -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 +70 -0
- aider/commands.py +1946 -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 +178 -0
- aider/io.py +1257 -0
- aider/linter.py +304 -0
- aider/llm.py +47 -0
- aider/main.py +1297 -0
- aider/mcp/__init__.py +94 -0
- aider/mcp/server.py +119 -0
- aider/mdstream.py +243 -0
- aider/models.py +1344 -0
- aider/onboarding.py +428 -0
- aider/openrouter.py +129 -0
- aider/prompts.py +56 -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/clojure-tags.scm +7 -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/matlab-tags.scm +10 -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/matlab-tags.scm +10 -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 +621 -0
- aider/repomap.py +988 -0
- aider/report.py +200 -0
- aider/resources/__init__.py +3 -0
- aider/resources/model-metadata.json +699 -0
- aider/resources/model-settings.yml +2046 -0
- aider/run_cmd.py +132 -0
- aider/scrape.py +284 -0
- aider/sendchat.py +61 -0
- aider/special.py +203 -0
- aider/tools/__init__.py +26 -0
- aider/tools/command.py +58 -0
- aider/tools/command_interactive.py +53 -0
- aider/tools/delete_block.py +120 -0
- aider/tools/delete_line.py +112 -0
- aider/tools/delete_lines.py +137 -0
- aider/tools/extract_lines.py +276 -0
- aider/tools/grep.py +171 -0
- aider/tools/indent_lines.py +155 -0
- aider/tools/insert_block.py +211 -0
- aider/tools/list_changes.py +51 -0
- aider/tools/ls.py +49 -0
- aider/tools/make_editable.py +46 -0
- aider/tools/make_readonly.py +29 -0
- aider/tools/remove.py +48 -0
- aider/tools/replace_all.py +77 -0
- aider/tools/replace_line.py +125 -0
- aider/tools/replace_lines.py +160 -0
- aider/tools/replace_text.py +125 -0
- aider/tools/show_numbered_context.py +101 -0
- aider/tools/tool_utils.py +313 -0
- aider/tools/undo_change.py +60 -0
- aider/tools/view.py +13 -0
- aider/tools/view_files_at_glob.py +65 -0
- aider/tools/view_files_matching.py +103 -0
- aider/tools/view_files_with_symbol.py +121 -0
- aider/urls.py +17 -0
- aider/utils.py +454 -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 +2260 -0
- aider/website/docs/config/aider_conf.md +548 -0
- aider/website/docs/config/api-keys.md +90 -0
- aider/website/docs/config/dotenv.md +493 -0
- aider/website/docs/config/editor.md +127 -0
- aider/website/docs/config/mcp.md +95 -0
- aider/website/docs/config/model-aliases.md +104 -0
- aider/website/docs/config/options.md +890 -0
- aider/website/docs/config/reasoning.md +210 -0
- aider/website/docs/config.md +44 -0
- aider/website/docs/faq.md +384 -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 +111 -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 +111 -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 +127 -0
- aider/website/docs/more/edit-formats.md +116 -0
- aider/website/docs/more/infinite-output.md +159 -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 +133 -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 +102 -0
- aider/website/share/index.md +101 -0
- aider_ce-0.87.2.dev9.dist-info/METADATA +543 -0
- aider_ce-0.87.2.dev9.dist-info/RECORD +264 -0
- aider_ce-0.87.2.dev9.dist-info/WHEEL +5 -0
- aider_ce-0.87.2.dev9.dist-info/entry_points.txt +3 -0
- aider_ce-0.87.2.dev9.dist-info/licenses/LICENSE.txt +202 -0
- aider_ce-0.87.2.dev9.dist-info/top_level.txt +1 -0
aider/watch.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import threading
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from grep_ast import TreeContext
|
|
7
|
+
from pathspec import PathSpec
|
|
8
|
+
from pathspec.patterns import GitWildMatchPattern
|
|
9
|
+
from watchfiles import watch
|
|
10
|
+
|
|
11
|
+
from aider.dump import dump # noqa
|
|
12
|
+
from aider.watch_prompts import watch_ask_prompt, watch_code_prompt
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_gitignores(gitignore_paths: list[Path]) -> Optional[PathSpec]:
|
|
16
|
+
"""Load and parse multiple .gitignore files into a single PathSpec"""
|
|
17
|
+
if not gitignore_paths:
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
patterns = [
|
|
21
|
+
".aider*",
|
|
22
|
+
".git",
|
|
23
|
+
# Common editor backup/temp files
|
|
24
|
+
"*~", # Emacs/vim backup
|
|
25
|
+
"*.bak", # Generic backup
|
|
26
|
+
"*.swp", # Vim swap
|
|
27
|
+
"*.swo", # Vim swap
|
|
28
|
+
"\\#*\\#", # Emacs auto-save
|
|
29
|
+
".#*", # Emacs lock files
|
|
30
|
+
"*.tmp", # Generic temp files
|
|
31
|
+
"*.temp", # Generic temp files
|
|
32
|
+
"*.orig", # Merge conflict originals
|
|
33
|
+
"*.pyc", # Python bytecode
|
|
34
|
+
"__pycache__/", # Python cache dir
|
|
35
|
+
".DS_Store", # macOS metadata
|
|
36
|
+
"Thumbs.db", # Windows thumbnail cache
|
|
37
|
+
"*.svg",
|
|
38
|
+
"*.pdf",
|
|
39
|
+
# IDE files
|
|
40
|
+
".idea/", # JetBrains IDEs
|
|
41
|
+
".vscode/", # VS Code
|
|
42
|
+
"*.sublime-*", # Sublime Text
|
|
43
|
+
".project", # Eclipse
|
|
44
|
+
".settings/", # Eclipse
|
|
45
|
+
"*.code-workspace", # VS Code workspace
|
|
46
|
+
# Environment files
|
|
47
|
+
".env", # Environment variables
|
|
48
|
+
".venv/", # Python virtual environments
|
|
49
|
+
"node_modules/", # Node.js dependencies
|
|
50
|
+
"vendor/", # Various dependencies
|
|
51
|
+
# Logs and caches
|
|
52
|
+
"*.log", # Log files
|
|
53
|
+
".cache/", # Cache directories
|
|
54
|
+
".pytest_cache/", # Python test cache
|
|
55
|
+
"coverage/", # Code coverage reports
|
|
56
|
+
] # Always ignore
|
|
57
|
+
for path in gitignore_paths:
|
|
58
|
+
if path.exists():
|
|
59
|
+
with open(path) as f:
|
|
60
|
+
patterns.extend(f.readlines())
|
|
61
|
+
|
|
62
|
+
return PathSpec.from_lines(GitWildMatchPattern, patterns) if patterns else None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class FileWatcher:
|
|
66
|
+
"""Watches source files for changes and AI comments"""
|
|
67
|
+
|
|
68
|
+
# Compiled regex pattern for AI comments
|
|
69
|
+
ai_comment_pattern = re.compile(
|
|
70
|
+
r"(?:#|//|--|;+) *(ai\b.*|ai\b.*|.*\bai[?!]?) *$", re.IGNORECASE
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def __init__(self, coder, gitignores=None, verbose=False, analytics=None, root=None):
|
|
74
|
+
self.coder = coder
|
|
75
|
+
self.io = coder.io
|
|
76
|
+
self.root = Path(root) if root else Path(coder.root)
|
|
77
|
+
self.verbose = verbose
|
|
78
|
+
self.analytics = analytics
|
|
79
|
+
self.stop_event = None
|
|
80
|
+
self.watcher_thread = None
|
|
81
|
+
self.changed_files = set()
|
|
82
|
+
self.gitignores = gitignores
|
|
83
|
+
|
|
84
|
+
self.gitignore_spec = load_gitignores(
|
|
85
|
+
[Path(g) for g in self.gitignores] if self.gitignores else []
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
coder.io.file_watcher = self
|
|
89
|
+
|
|
90
|
+
def filter_func(self, change_type, path):
|
|
91
|
+
"""Filter function for the file watcher"""
|
|
92
|
+
path_obj = Path(path)
|
|
93
|
+
path_abs = path_obj.absolute()
|
|
94
|
+
|
|
95
|
+
if not path_abs.is_relative_to(self.root.absolute()):
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
rel_path = path_abs.relative_to(self.root)
|
|
99
|
+
if self.verbose:
|
|
100
|
+
print("Changed", rel_path)
|
|
101
|
+
|
|
102
|
+
if self.gitignore_spec and self.gitignore_spec.match_file(
|
|
103
|
+
rel_path.as_posix() + ("/" if path_abs.is_dir() else "")
|
|
104
|
+
):
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
# Check file size before reading content
|
|
108
|
+
if path_abs.is_file() and path_abs.stat().st_size > 1 * 1024 * 1024: # 1MB limit
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
if self.verbose:
|
|
112
|
+
print("Checking", rel_path)
|
|
113
|
+
|
|
114
|
+
# Check if file contains AI markers
|
|
115
|
+
try:
|
|
116
|
+
comments, _, _ = self.get_ai_comments(str(path_abs))
|
|
117
|
+
return bool(comments)
|
|
118
|
+
except Exception:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
def get_roots_to_watch(self):
|
|
122
|
+
"""Determine which root paths to watch based on gitignore rules"""
|
|
123
|
+
if self.gitignore_spec:
|
|
124
|
+
roots = [
|
|
125
|
+
str(path)
|
|
126
|
+
for path in self.root.iterdir()
|
|
127
|
+
if not self.gitignore_spec.match_file(
|
|
128
|
+
path.relative_to(self.root).as_posix() + ("/" if path.is_dir() else "")
|
|
129
|
+
)
|
|
130
|
+
]
|
|
131
|
+
# Fallback to watching root if all top-level items are filtered out
|
|
132
|
+
return roots if roots else [str(self.root)]
|
|
133
|
+
return [str(self.root)]
|
|
134
|
+
|
|
135
|
+
def handle_changes(self, changes):
|
|
136
|
+
"""Process the detected changes and update state"""
|
|
137
|
+
if not changes:
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
changed_files = {str(Path(change[1])) for change in changes}
|
|
141
|
+
self.changed_files.update(changed_files)
|
|
142
|
+
self.io.interrupt_input()
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
def watch_files(self):
|
|
146
|
+
"""Watch for file changes and process them"""
|
|
147
|
+
try:
|
|
148
|
+
roots_to_watch = self.get_roots_to_watch()
|
|
149
|
+
|
|
150
|
+
for changes in watch(
|
|
151
|
+
*roots_to_watch,
|
|
152
|
+
watch_filter=self.filter_func,
|
|
153
|
+
stop_event=self.stop_event,
|
|
154
|
+
ignore_permission_denied=True,
|
|
155
|
+
):
|
|
156
|
+
if self.handle_changes(changes):
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
except Exception as e:
|
|
160
|
+
if self.verbose:
|
|
161
|
+
dump(f"File watcher error: {e}")
|
|
162
|
+
raise e
|
|
163
|
+
|
|
164
|
+
def start(self):
|
|
165
|
+
"""Start watching for file changes"""
|
|
166
|
+
self.stop_event = threading.Event()
|
|
167
|
+
self.changed_files = set()
|
|
168
|
+
|
|
169
|
+
self.watcher_thread = threading.Thread(target=self.watch_files, daemon=True)
|
|
170
|
+
self.watcher_thread.start()
|
|
171
|
+
|
|
172
|
+
def stop(self):
|
|
173
|
+
"""Stop watching for file changes"""
|
|
174
|
+
if self.stop_event:
|
|
175
|
+
self.stop_event.set()
|
|
176
|
+
if self.watcher_thread:
|
|
177
|
+
self.watcher_thread.join()
|
|
178
|
+
self.watcher_thread = None
|
|
179
|
+
self.stop_event = None
|
|
180
|
+
|
|
181
|
+
def process_changes(self):
|
|
182
|
+
"""Get any detected file changes"""
|
|
183
|
+
|
|
184
|
+
has_action = None
|
|
185
|
+
added = False
|
|
186
|
+
for fname in self.changed_files:
|
|
187
|
+
_, _, action = self.get_ai_comments(fname)
|
|
188
|
+
if action in ("!", "?"):
|
|
189
|
+
has_action = action
|
|
190
|
+
|
|
191
|
+
if fname in self.coder.abs_fnames:
|
|
192
|
+
continue
|
|
193
|
+
if self.analytics:
|
|
194
|
+
self.analytics.event("ai-comments file-add")
|
|
195
|
+
self.coder.abs_fnames.add(fname)
|
|
196
|
+
rel_fname = self.coder.get_rel_fname(fname)
|
|
197
|
+
if not added:
|
|
198
|
+
self.io.tool_output()
|
|
199
|
+
added = True
|
|
200
|
+
self.io.tool_output(f"Added {rel_fname} to the chat")
|
|
201
|
+
|
|
202
|
+
if not has_action:
|
|
203
|
+
if added:
|
|
204
|
+
self.io.tool_output(
|
|
205
|
+
"End your comment with AI! to request changes or AI? to ask questions"
|
|
206
|
+
)
|
|
207
|
+
return ""
|
|
208
|
+
|
|
209
|
+
if self.analytics:
|
|
210
|
+
self.analytics.event("ai-comments execute")
|
|
211
|
+
self.io.tool_output("Processing your request...")
|
|
212
|
+
|
|
213
|
+
if has_action == "!":
|
|
214
|
+
res = watch_code_prompt
|
|
215
|
+
elif has_action == "?":
|
|
216
|
+
res = watch_ask_prompt
|
|
217
|
+
|
|
218
|
+
# Refresh all AI comments from tracked files
|
|
219
|
+
for fname in self.coder.abs_fnames:
|
|
220
|
+
line_nums, comments, _action = self.get_ai_comments(fname)
|
|
221
|
+
if not line_nums:
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
code = self.io.read_text(fname)
|
|
225
|
+
if not code:
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
rel_fname = self.coder.get_rel_fname(fname)
|
|
229
|
+
res += f"\n{rel_fname}:\n"
|
|
230
|
+
|
|
231
|
+
# Convert comment line numbers to line indices (0-based)
|
|
232
|
+
lois = [ln - 1 for ln, _ in zip(line_nums, comments) if ln > 0]
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
context = TreeContext(
|
|
236
|
+
rel_fname,
|
|
237
|
+
code,
|
|
238
|
+
color=False,
|
|
239
|
+
line_number=False,
|
|
240
|
+
child_context=False,
|
|
241
|
+
last_line=False,
|
|
242
|
+
margin=0,
|
|
243
|
+
mark_lois=True,
|
|
244
|
+
loi_pad=3,
|
|
245
|
+
show_top_of_file_parent_scope=False,
|
|
246
|
+
)
|
|
247
|
+
context.lines_of_interest = set()
|
|
248
|
+
context.add_lines_of_interest(lois)
|
|
249
|
+
context.add_context()
|
|
250
|
+
res += context.format()
|
|
251
|
+
except ValueError:
|
|
252
|
+
for ln, comment in zip(line_nums, comments):
|
|
253
|
+
res += f" Line {ln}: {comment}\n"
|
|
254
|
+
|
|
255
|
+
return res
|
|
256
|
+
|
|
257
|
+
def get_ai_comments(self, filepath):
|
|
258
|
+
"""Extract AI comment line numbers, comments and action status from a file"""
|
|
259
|
+
line_nums = []
|
|
260
|
+
comments = []
|
|
261
|
+
has_action = None # None, "!" or "?"
|
|
262
|
+
content = self.io.read_text(filepath, silent=True)
|
|
263
|
+
if not content:
|
|
264
|
+
return None, None, None
|
|
265
|
+
|
|
266
|
+
for i, line in enumerate(content.splitlines(), 1):
|
|
267
|
+
if match := self.ai_comment_pattern.search(line):
|
|
268
|
+
comment = match.group(0).strip()
|
|
269
|
+
if comment:
|
|
270
|
+
line_nums.append(i)
|
|
271
|
+
comments.append(comment)
|
|
272
|
+
comment = comment.lower()
|
|
273
|
+
comment = comment.lstrip("/#-;") # Added semicolon for Lisp comments
|
|
274
|
+
comment = comment.strip()
|
|
275
|
+
if comment.startswith("ai!") or comment.endswith("ai!"):
|
|
276
|
+
has_action = "!"
|
|
277
|
+
elif comment.startswith("ai?") or comment.endswith("ai?"):
|
|
278
|
+
has_action = "?"
|
|
279
|
+
if not line_nums:
|
|
280
|
+
return None, None, None
|
|
281
|
+
return line_nums, comments, has_action
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def main():
|
|
285
|
+
"""Example usage of the file watcher"""
|
|
286
|
+
import argparse
|
|
287
|
+
|
|
288
|
+
parser = argparse.ArgumentParser(description="Watch source files for changes")
|
|
289
|
+
parser.add_argument("directory", help="Directory to watch")
|
|
290
|
+
parser.add_argument(
|
|
291
|
+
"--gitignore",
|
|
292
|
+
action="append",
|
|
293
|
+
help="Path to .gitignore file (can be specified multiple times)",
|
|
294
|
+
)
|
|
295
|
+
args = parser.parse_args()
|
|
296
|
+
|
|
297
|
+
directory = args.directory
|
|
298
|
+
print(f"Watching source files in {directory}...")
|
|
299
|
+
|
|
300
|
+
# Example ignore function that ignores files with "test" in the name
|
|
301
|
+
def ignore_test_files(path):
|
|
302
|
+
return "test" in path.name.lower()
|
|
303
|
+
|
|
304
|
+
watcher = FileWatcher(directory, gitignores=args.gitignore)
|
|
305
|
+
try:
|
|
306
|
+
watcher.start()
|
|
307
|
+
while True:
|
|
308
|
+
if changes := watcher.get_changes():
|
|
309
|
+
for file in sorted(changes.keys()):
|
|
310
|
+
print(file)
|
|
311
|
+
watcher.changed_files = None
|
|
312
|
+
except KeyboardInterrupt:
|
|
313
|
+
print("\nStopped watching files")
|
|
314
|
+
watcher.stop()
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
if __name__ == "__main__":
|
|
318
|
+
main()
|
aider/watch_prompts.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
watch_code_prompt = """
|
|
2
|
+
I've written your instructions in comments in the code and marked them with "ai"
|
|
3
|
+
You can see the "AI" comments shown below (marked with █).
|
|
4
|
+
Find them in the code files I've shared with you, and follow their instructions.
|
|
5
|
+
|
|
6
|
+
After completing those instructions, also be sure to remove all the "AI" comments from the code too.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
watch_ask_prompt = """/ask
|
|
10
|
+
Find the "AI" comments below (marked with █) in the code files I've shared with you.
|
|
11
|
+
They contain my questions that I need you to answer and other instructions for you.
|
|
12
|
+
"""
|
aider/website/Gemfile
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<div class="chart-container">
|
|
2
|
+
<canvas id="blameChart" style="margin-top: 20px"></canvas>
|
|
3
|
+
</div>
|
|
4
|
+
<div class="chart-container">
|
|
5
|
+
<canvas id="linesChart" style="margin-top: 20px"></canvas>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<style>
|
|
9
|
+
.chart-container {
|
|
10
|
+
position: relative;
|
|
11
|
+
width: 100%;
|
|
12
|
+
height: 300px;
|
|
13
|
+
}
|
|
14
|
+
</style>
|
|
15
|
+
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/moment"></script>
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment"></script>
|
|
19
|
+
<script>
|
|
20
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
21
|
+
var blameCtx = document.getElementById('blameChart').getContext('2d');
|
|
22
|
+
var linesCtx = document.getElementById('linesChart').getContext('2d');
|
|
23
|
+
|
|
24
|
+
var labels = [{% for row in site.data.blame %}'{{ row.end_tag }}',{% endfor %}];
|
|
25
|
+
|
|
26
|
+
var blameData = {
|
|
27
|
+
labels: labels,
|
|
28
|
+
datasets: [{
|
|
29
|
+
label: 'Aider\'s percent of new code by release',
|
|
30
|
+
data: [{% for row in site.data.blame %}{ x: '{{ row.end_tag }}', y: {{ row.aider_percentage }}, lines: {{ row.aider_total }}, end_date: '{{ row.end_date }}' },{% endfor %}],
|
|
31
|
+
backgroundColor: 'rgba(54, 162, 235, 0.8)',
|
|
32
|
+
borderColor: 'rgba(54, 162, 235, 1)',
|
|
33
|
+
borderWidth: 1
|
|
34
|
+
}]
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
var linesData = {
|
|
38
|
+
labels: labels,
|
|
39
|
+
datasets: [{
|
|
40
|
+
label: 'Aider',
|
|
41
|
+
data: [{% for row in site.data.blame %}{ x: '{{ row.end_tag }}', y: {{ row.aider_total }} },{% endfor %}],
|
|
42
|
+
backgroundColor: 'rgba(54, 162, 235, 0.8)',
|
|
43
|
+
borderColor: 'rgba(54, 162, 235, 1)',
|
|
44
|
+
borderWidth: 1
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: 'Human',
|
|
48
|
+
data: [{% for row in site.data.blame %}{ x: '{{ row.end_tag }}', y: {{ row.total_lines | minus: row.aider_total }} },{% endfor %}],
|
|
49
|
+
backgroundColor: 'rgba(200, 200, 200, 0.8)',
|
|
50
|
+
borderColor: 'rgba(200, 200, 200, 1)',
|
|
51
|
+
borderWidth: 1
|
|
52
|
+
}]
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
var blameChart = new Chart(blameCtx, {
|
|
56
|
+
type: 'bar',
|
|
57
|
+
data: blameData,
|
|
58
|
+
options: {
|
|
59
|
+
maintainAspectRatio: false,
|
|
60
|
+
scales: {
|
|
61
|
+
x: {
|
|
62
|
+
type: 'category',
|
|
63
|
+
title: {
|
|
64
|
+
display: true,
|
|
65
|
+
text: 'Version'
|
|
66
|
+
},
|
|
67
|
+
ticks: {
|
|
68
|
+
maxRotation: 45,
|
|
69
|
+
minRotation: 45
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
y: {
|
|
73
|
+
title: {
|
|
74
|
+
display: true,
|
|
75
|
+
text: 'Percent of new code'
|
|
76
|
+
},
|
|
77
|
+
beginAtZero: true
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
plugins: {
|
|
81
|
+
legend: {
|
|
82
|
+
display: false
|
|
83
|
+
},
|
|
84
|
+
tooltip: {
|
|
85
|
+
callbacks: {
|
|
86
|
+
label: function(context) {
|
|
87
|
+
var label = 'Aider\'s contribution';
|
|
88
|
+
var value = context.parsed.y || 0;
|
|
89
|
+
var lines = context.raw.lines || 0;
|
|
90
|
+
return `${label}: ${Math.round(value)}% (${lines} lines)`;
|
|
91
|
+
},
|
|
92
|
+
afterLabel: function(context) {
|
|
93
|
+
let date = context.raw.end_date || 'n/a';
|
|
94
|
+
return `Date: ` + date;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
title: {
|
|
99
|
+
display: true,
|
|
100
|
+
text: 'Percent of new code written by aider, by release',
|
|
101
|
+
font: {
|
|
102
|
+
size: 16
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
var linesChart = new Chart(linesCtx, {
|
|
110
|
+
type: 'bar',
|
|
111
|
+
data: linesData,
|
|
112
|
+
options: {
|
|
113
|
+
maintainAspectRatio: false,
|
|
114
|
+
scales: {
|
|
115
|
+
x: {
|
|
116
|
+
type: 'category',
|
|
117
|
+
stacked: true,
|
|
118
|
+
title: {
|
|
119
|
+
display: true,
|
|
120
|
+
text: 'Version'
|
|
121
|
+
},
|
|
122
|
+
ticks: {
|
|
123
|
+
maxRotation: 45,
|
|
124
|
+
minRotation: 45
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
y: {
|
|
128
|
+
stacked: true,
|
|
129
|
+
title: {
|
|
130
|
+
display: true,
|
|
131
|
+
text: 'Lines of new code'
|
|
132
|
+
},
|
|
133
|
+
beginAtZero: true
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
plugins: {
|
|
137
|
+
legend: {
|
|
138
|
+
display: true,
|
|
139
|
+
position: 'chartArea',
|
|
140
|
+
reverse: true
|
|
141
|
+
},
|
|
142
|
+
tooltip: {
|
|
143
|
+
callbacks: {
|
|
144
|
+
label: function(context) {
|
|
145
|
+
var label = context.dataset.label;
|
|
146
|
+
var value = context.parsed.y || 0;
|
|
147
|
+
return `${label}: ${value}`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
title: {
|
|
152
|
+
display: true,
|
|
153
|
+
text: 'Lines of new code, by release',
|
|
154
|
+
font: {
|
|
155
|
+
size: 16
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
If you already have python 3.8-3.13 installed, you can get started quickly like this.
|
|
3
|
+
|
|
4
|
+
First, install aider:
|
|
5
|
+
|
|
6
|
+
{% include install.md %}
|
|
7
|
+
|
|
8
|
+
Start working with aider on your codebase:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# Change directory into your codebase
|
|
12
|
+
cd /to/your/project
|
|
13
|
+
|
|
14
|
+
# DeepSeek
|
|
15
|
+
aider --model deepseek --api-key deepseek=<key>
|
|
16
|
+
|
|
17
|
+
# Claude 3.7 Sonnet
|
|
18
|
+
aider --model sonnet --api-key anthropic=<key>
|
|
19
|
+
|
|
20
|
+
# o3-mini
|
|
21
|
+
aider --model o3-mini --api-key openai=<key>
|
|
22
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
If you need more help, please check our
|
|
2
|
+
[GitHub issues](https://github.com/Aider-AI/aider/issues)
|
|
3
|
+
and file a new issue if your problem isn't discussed.
|
|
4
|
+
Or drop into our
|
|
5
|
+
[Discord](https://discord.gg/Y7X7bhMQFV)
|
|
6
|
+
to chat with us.
|
|
7
|
+
|
|
8
|
+
When reporting problems, it is very helpful if you can provide:
|
|
9
|
+
|
|
10
|
+
- Aider version
|
|
11
|
+
- LLM model you are using
|
|
12
|
+
|
|
13
|
+
Including the "announcement" lines that
|
|
14
|
+
aider prints at startup
|
|
15
|
+
is an easy way to share this helpful info.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Aider v0.37.1-dev
|
|
19
|
+
Models: gpt-4o with diff edit format, weak model gpt-3.5-turbo
|
|
20
|
+
Git repo: .git with 243 files
|
|
21
|
+
Repo-map: using 1024 tokens
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
{% include help-tip.md %}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
## Unknown context window size and token costs
|
|
3
|
+
|
|
4
|
+
```
|
|
5
|
+
Model foobar: Unknown context window size and costs, using sane defaults.
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
If you specify a model that aider has never heard of, you will get
|
|
9
|
+
this warning.
|
|
10
|
+
This means aider doesn't know the context window size and token costs
|
|
11
|
+
for that model.
|
|
12
|
+
Aider will use an unlimited context window and assume the model is free,
|
|
13
|
+
so this is not usually a significant problem.
|
|
14
|
+
|
|
15
|
+
See the docs on
|
|
16
|
+
[configuring advanced model settings](/docs/config/adv-model-settings.html)
|
|
17
|
+
for details on how to remove this warning.
|
|
18
|
+
|
|
19
|
+
{: .tip }
|
|
20
|
+
You can probably ignore the unknown context window size and token costs warning.
|
|
21
|
+
|
|
22
|
+
## Did you mean?
|
|
23
|
+
|
|
24
|
+
If aider isn't familiar with the model you've specified,
|
|
25
|
+
it will suggest similarly named models.
|
|
26
|
+
This helps
|
|
27
|
+
in the case where you made a typo or mistake when specifying the model name.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Model gpt-5o: Unknown context window size and costs, using sane defaults.
|
|
31
|
+
Did you mean one of these?
|
|
32
|
+
- gpt-4o
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Missing environment variables
|
|
36
|
+
|
|
37
|
+
You need to set the listed environment variables.
|
|
38
|
+
Otherwise you will get error messages when you start chatting with the model.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Model azure/gpt-4-turbo: Missing these environment variables:
|
|
42
|
+
- AZURE_API_BASE
|
|
43
|
+
- AZURE_API_VERSION
|
|
44
|
+
- AZURE_API_KEY
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
{: .tip }
|
|
48
|
+
On Windows,
|
|
49
|
+
if you just set these environment variables using `setx` you may need to restart your terminal or
|
|
50
|
+
command prompt for the changes to take effect.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Unknown which environment variables are required
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Model gpt-5: Unknown which environment variables are required.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Aider is unable verify the environment because it doesn't know
|
|
60
|
+
which variables are required for the model.
|
|
61
|
+
If required variables are missing,
|
|
62
|
+
you may get errors when you attempt to chat with the model.
|
|
63
|
+
You can look in the [aider's LLM documentation](/docs/llms.html)
|
|
64
|
+
or the
|
|
65
|
+
[litellm documentation](https://docs.litellm.ai/docs/providers)
|
|
66
|
+
to see if the required variables are listed there.
|
|
67
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
You can send long, multi-line messages in the chat in a few ways:
|
|
2
|
+
- Paste a multi-line message directly into the chat.
|
|
3
|
+
- Enter `{` alone on the first line to start a multiline message and `}` alone on the last line to end it.
|
|
4
|
+
- Or, start with `{tag` (where "tag" is any sequence of letters/numbers) and end with `tag}`. This is useful when you need to include closing braces `}` in your message.
|
|
5
|
+
- Use Meta-ENTER to start a new line without sending the message (Esc+ENTER in some environments).
|
|
6
|
+
- Use `/paste` to paste text from the clipboard into the chat.
|
|
7
|
+
- Use the `/editor` command (or press `Ctrl-X Ctrl-E` if your terminal allows) to open your editor to create the next chat message. See [editor configuration docs](/docs/config/editor.html) for more info.
|
|
8
|
+
- Use multiline-mode, which swaps the function of Meta-Enter and Enter, so that Enter inserts a newline, and Meta-Enter submits your command. To enable multiline mode:
|
|
9
|
+
- Use the `/multiline-mode` command to toggle it during a session.
|
|
10
|
+
- Use the `--multiline` switch.
|
|
11
|
+
|
|
12
|
+
Example with a tag:
|
|
13
|
+
```
|
|
14
|
+
{python
|
|
15
|
+
def hello():
|
|
16
|
+
print("Hello}") # Note: contains a brace
|
|
17
|
+
python}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
{: .note }
|
|
21
|
+
People often ask for SHIFT-ENTER to be a soft-newline.
|
|
22
|
+
Unfortunately there is no portable way to detect that keystroke in terminals.
|