aider-ce 0.88.20__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 +34 -0
- aider/analytics.py +258 -0
- aider/args.py +1056 -0
- aider/args_formatter.py +228 -0
- aider/change_tracker.py +133 -0
- aider/coders/__init__.py +36 -0
- aider/coders/agent_coder.py +2166 -0
- aider/coders/agent_prompts.py +104 -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 +3613 -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 +175 -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/patch_coder.py +706 -0
- aider/coders/patch_prompts.py +159 -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 +65 -0
- aider/commands.py +2173 -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 +115 -0
- aider/format_settings.py +26 -0
- aider/gui.py +545 -0
- aider/help.py +163 -0
- aider/help_pats.py +19 -0
- aider/helpers/__init__.py +9 -0
- aider/helpers/similarity.py +98 -0
- aider/history.py +180 -0
- aider/io.py +1608 -0
- aider/linter.py +304 -0
- aider/llm.py +55 -0
- aider/main.py +1415 -0
- aider/mcp/__init__.py +174 -0
- aider/mcp/server.py +149 -0
- aider/mdstream.py +243 -0
- aider/models.py +1313 -0
- aider/onboarding.py +429 -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 +24 -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/fortran-tags.scm +15 -0
- aider/queries/tree-sitter-languages/go-tags.scm +30 -0
- aider/queries/tree-sitter-languages/haskell-tags.scm +3 -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/julia-tags.scm +60 -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/queries/tree-sitter-languages/zig-tags.scm +3 -0
- aider/reasoning_tags.py +82 -0
- aider/repo.py +621 -0
- aider/repomap.py +1174 -0
- aider/report.py +260 -0
- aider/resources/__init__.py +3 -0
- aider/resources/model-metadata.json +776 -0
- aider/resources/model-settings.yml +2068 -0
- aider/run_cmd.py +133 -0
- aider/scrape.py +293 -0
- aider/sendchat.py +242 -0
- aider/sessions.py +256 -0
- aider/special.py +203 -0
- aider/tools/__init__.py +72 -0
- aider/tools/command.py +105 -0
- aider/tools/command_interactive.py +122 -0
- aider/tools/delete_block.py +182 -0
- aider/tools/delete_line.py +155 -0
- aider/tools/delete_lines.py +184 -0
- aider/tools/extract_lines.py +341 -0
- aider/tools/finished.py +48 -0
- aider/tools/git_branch.py +129 -0
- aider/tools/git_diff.py +60 -0
- aider/tools/git_log.py +57 -0
- aider/tools/git_remote.py +53 -0
- aider/tools/git_show.py +51 -0
- aider/tools/git_status.py +46 -0
- aider/tools/grep.py +256 -0
- aider/tools/indent_lines.py +221 -0
- aider/tools/insert_block.py +288 -0
- aider/tools/list_changes.py +86 -0
- aider/tools/ls.py +93 -0
- aider/tools/make_editable.py +85 -0
- aider/tools/make_readonly.py +69 -0
- aider/tools/remove.py +91 -0
- aider/tools/replace_all.py +126 -0
- aider/tools/replace_line.py +173 -0
- aider/tools/replace_lines.py +217 -0
- aider/tools/replace_text.py +187 -0
- aider/tools/show_numbered_context.py +147 -0
- aider/tools/tool_utils.py +313 -0
- aider/tools/undo_change.py +95 -0
- aider/tools/update_todo_list.py +156 -0
- aider/tools/view.py +57 -0
- aider/tools/view_files_matching.py +141 -0
- aider/tools/view_files_with_symbol.py +129 -0
- aider/urls.py +17 -0
- aider/utils.py +456 -0
- aider/versioncheck.py +113 -0
- aider/voice.py +205 -0
- aider/waiting.py +38 -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 +2261 -0
- aider/website/docs/config/agent-mode.md +194 -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 +117 -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 +165 -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/sessions.md +203 -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.88.20.dist-info/METADATA +187 -0
- aider_ce-0.88.20.dist-info/RECORD +279 -0
- aider_ce-0.88.20.dist-info/WHEEL +5 -0
- aider_ce-0.88.20.dist-info/entry_points.txt +2 -0
- aider_ce-0.88.20.dist-info/licenses/LICENSE.txt +202 -0
- aider_ce-0.88.20.dist-info/top_level.txt +1 -0
aider/sessions.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Session management utilities for Aider."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from aider import models
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SessionManager:
|
|
12
|
+
"""Manages chat session saving, listing, and loading."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, coder, io):
|
|
15
|
+
self.coder = coder
|
|
16
|
+
self.io = io
|
|
17
|
+
|
|
18
|
+
def _get_session_directory(self) -> Path:
|
|
19
|
+
"""Get the session directory, creating it if necessary."""
|
|
20
|
+
session_dir = Path(self.coder.abs_root_path(".aider/sessions"))
|
|
21
|
+
os.makedirs(session_dir, exist_ok=True)
|
|
22
|
+
return session_dir
|
|
23
|
+
|
|
24
|
+
def save_session(self, session_name: str, output=True) -> bool:
|
|
25
|
+
"""Save the current chat session to a named file."""
|
|
26
|
+
if not session_name:
|
|
27
|
+
if output:
|
|
28
|
+
self.io.tool_error("Please provide a session name.")
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
session_name = session_name.replace(".json", "")
|
|
32
|
+
session_dir = self._get_session_directory()
|
|
33
|
+
session_file = session_dir / f"{session_name}.json"
|
|
34
|
+
|
|
35
|
+
if session_file.exists():
|
|
36
|
+
if output:
|
|
37
|
+
self.io.tool_warning(f"Session '{session_name}' already exists. Overwriting.")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
session_data = self._build_session_data(session_name)
|
|
41
|
+
with open(session_file, "w", encoding="utf-8") as f:
|
|
42
|
+
json.dump(session_data, f, indent=2)
|
|
43
|
+
|
|
44
|
+
if output:
|
|
45
|
+
self.io.tool_output(f"Session saved: {session_file}")
|
|
46
|
+
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
except Exception as e:
|
|
50
|
+
self.io.tool_error(f"Error saving session: {e}")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
def list_sessions(self) -> List[Dict]:
|
|
54
|
+
"""List all saved sessions with metadata."""
|
|
55
|
+
session_dir = self._get_session_directory()
|
|
56
|
+
session_files = list(session_dir.glob("*.json"))
|
|
57
|
+
|
|
58
|
+
if not session_files:
|
|
59
|
+
self.io.tool_output("No saved sessions found.")
|
|
60
|
+
return []
|
|
61
|
+
|
|
62
|
+
sessions = []
|
|
63
|
+
for session_file in sorted(session_files, key=lambda x: x.stat().st_mtime, reverse=True):
|
|
64
|
+
try:
|
|
65
|
+
with open(session_file, "r", encoding="utf-8") as f:
|
|
66
|
+
session_data = json.load(f)
|
|
67
|
+
|
|
68
|
+
session_info = {
|
|
69
|
+
"name": session_file.stem,
|
|
70
|
+
"file": session_file,
|
|
71
|
+
"model": session_data.get("model", "unknown"),
|
|
72
|
+
"edit_format": session_data.get("edit_format", "unknown"),
|
|
73
|
+
"num_messages": len(
|
|
74
|
+
session_data.get("chat_history", {}).get("done_messages", [])
|
|
75
|
+
) + len(session_data.get("chat_history", {}).get("cur_messages", [])),
|
|
76
|
+
"num_files": (
|
|
77
|
+
len(session_data.get("files", {}).get("editable", []))
|
|
78
|
+
+ len(session_data.get("files", {}).get("read_only", []))
|
|
79
|
+
+ len(session_data.get("files", {}).get("read_only_stubs", []))
|
|
80
|
+
),
|
|
81
|
+
}
|
|
82
|
+
sessions.append(session_info)
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
self.io.tool_output(f" {session_file.stem} [error reading: {e}]")
|
|
86
|
+
|
|
87
|
+
return sessions
|
|
88
|
+
|
|
89
|
+
def load_session(self, session_identifier: str) -> bool:
|
|
90
|
+
"""Load a saved session by name or file path."""
|
|
91
|
+
if not session_identifier:
|
|
92
|
+
self.io.tool_error("Please provide a session name or file path.")
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
# Try to find the session file
|
|
96
|
+
session_file = self._find_session_file(session_identifier)
|
|
97
|
+
if not session_file:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
with open(session_file, "r", encoding="utf-8") as f:
|
|
102
|
+
session_data = json.load(f)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
self.io.tool_error(f"Error loading session: {e}")
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
# Verify session format
|
|
108
|
+
if not isinstance(session_data, dict) or "version" not in session_data:
|
|
109
|
+
self.io.tool_error("Invalid session format.")
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
# Apply session data
|
|
113
|
+
return self._apply_session_data(session_data, session_file)
|
|
114
|
+
|
|
115
|
+
def _build_session_data(self, session_name) -> Dict:
|
|
116
|
+
"""Build session data dictionary from current coder state."""
|
|
117
|
+
# Get relative paths for all files
|
|
118
|
+
editable_files = [
|
|
119
|
+
self.coder.get_rel_fname(abs_fname) for abs_fname in self.coder.abs_fnames
|
|
120
|
+
]
|
|
121
|
+
read_only_files = [
|
|
122
|
+
self.coder.get_rel_fname(abs_fname) for abs_fname in self.coder.abs_read_only_fnames
|
|
123
|
+
]
|
|
124
|
+
read_only_stubs_files = [
|
|
125
|
+
self.coder.get_rel_fname(abs_fname)
|
|
126
|
+
for abs_fname in self.coder.abs_read_only_stubs_fnames
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
"version": 1,
|
|
131
|
+
"session_name": session_name,
|
|
132
|
+
"model": self.coder.main_model.name,
|
|
133
|
+
"weak_model": self.coder.main_model.weak_model.name,
|
|
134
|
+
"editor_model": self.coder.main_model.editor_model.name,
|
|
135
|
+
"editor_edit_format": self.coder.main_model.editor_edit_format,
|
|
136
|
+
"edit_format": self.coder.edit_format,
|
|
137
|
+
"chat_history": {
|
|
138
|
+
"done_messages": self.coder.done_messages,
|
|
139
|
+
"cur_messages": self.coder.cur_messages,
|
|
140
|
+
},
|
|
141
|
+
"files": {
|
|
142
|
+
"editable": editable_files,
|
|
143
|
+
"read_only": read_only_files,
|
|
144
|
+
"read_only_stubs": read_only_stubs_files,
|
|
145
|
+
},
|
|
146
|
+
"settings": {
|
|
147
|
+
"auto_commits": self.coder.auto_commits,
|
|
148
|
+
"auto_lint": self.coder.auto_lint,
|
|
149
|
+
"auto_test": self.coder.auto_test,
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
def _find_session_file(self, session_identifier: str) -> Optional[Path]:
|
|
154
|
+
"""Find session file by name or path."""
|
|
155
|
+
# Check if it's a direct file path
|
|
156
|
+
session_file = Path(session_identifier)
|
|
157
|
+
if session_file.exists():
|
|
158
|
+
return session_file
|
|
159
|
+
|
|
160
|
+
# Check if it's a session name in the sessions directory
|
|
161
|
+
session_dir = self._get_session_directory()
|
|
162
|
+
|
|
163
|
+
# Try with .json extension
|
|
164
|
+
if not session_identifier.endswith(".json"):
|
|
165
|
+
session_file = session_dir / f"{session_identifier}.json"
|
|
166
|
+
if session_file.exists():
|
|
167
|
+
return session_file
|
|
168
|
+
|
|
169
|
+
session_file = session_dir / f"{session_identifier}"
|
|
170
|
+
if session_file.exists():
|
|
171
|
+
return session_file
|
|
172
|
+
|
|
173
|
+
self.io.tool_error(f"Session not found: {session_identifier}")
|
|
174
|
+
self.io.tool_output("Use /list-sessions to see available sessions.")
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
def _apply_session_data(self, session_data: Dict, session_file: Path) -> bool:
|
|
178
|
+
"""Apply session data to current coder state."""
|
|
179
|
+
try:
|
|
180
|
+
# Clear current state
|
|
181
|
+
self.coder.abs_fnames = set()
|
|
182
|
+
self.coder.abs_read_only_fnames = set()
|
|
183
|
+
self.coder.abs_read_only_stubs_fnames = set()
|
|
184
|
+
self.coder.done_messages = []
|
|
185
|
+
self.coder.cur_messages = []
|
|
186
|
+
|
|
187
|
+
# Load chat history
|
|
188
|
+
chat_history = session_data.get("chat_history", {})
|
|
189
|
+
self.coder.done_messages = chat_history.get("done_messages", [])
|
|
190
|
+
self.coder.cur_messages = chat_history.get("cur_messages", [])
|
|
191
|
+
|
|
192
|
+
# Load files
|
|
193
|
+
files = session_data.get("files", {})
|
|
194
|
+
for rel_fname in files.get("editable", []):
|
|
195
|
+
abs_fname = self.coder.abs_root_path(rel_fname)
|
|
196
|
+
if os.path.exists(abs_fname):
|
|
197
|
+
self.coder.abs_fnames.add(abs_fname)
|
|
198
|
+
else:
|
|
199
|
+
self.io.tool_warning(f"File not found, skipping: {rel_fname}")
|
|
200
|
+
|
|
201
|
+
for rel_fname in files.get("read_only", []):
|
|
202
|
+
abs_fname = self.coder.abs_root_path(rel_fname)
|
|
203
|
+
if os.path.exists(abs_fname):
|
|
204
|
+
self.coder.abs_read_only_fnames.add(abs_fname)
|
|
205
|
+
else:
|
|
206
|
+
self.io.tool_warning(f"File not found, skipping: {rel_fname}")
|
|
207
|
+
|
|
208
|
+
for rel_fname in files.get("read_only_stubs", []):
|
|
209
|
+
abs_fname = self.coder.abs_root_path(rel_fname)
|
|
210
|
+
if os.path.exists(abs_fname):
|
|
211
|
+
self.coder.abs_read_only_stubs_fnames.add(abs_fname)
|
|
212
|
+
else:
|
|
213
|
+
self.io.tool_warning(f"File not found, skipping: {rel_fname}")
|
|
214
|
+
|
|
215
|
+
if session_data.get("model"):
|
|
216
|
+
self.coder.main_model = models.Model(
|
|
217
|
+
session_data.get("model", self.coder.args.model),
|
|
218
|
+
weak_model=session_data.get("weak_model", self.coder.args.weak_model),
|
|
219
|
+
editor_model=session_data.get("editor_model", self.coder.args.editor_model),
|
|
220
|
+
editor_edit_format=session_data.get(
|
|
221
|
+
"editor_edit_format", self.coder.args.editor_edit_format
|
|
222
|
+
),
|
|
223
|
+
verbose=self.coder.args.verbose,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Load settings
|
|
227
|
+
settings = session_data.get("settings", {})
|
|
228
|
+
if "auto_commits" in settings:
|
|
229
|
+
self.coder.auto_commits = settings["auto_commits"]
|
|
230
|
+
if "auto_lint" in settings:
|
|
231
|
+
self.coder.auto_lint = settings["auto_lint"]
|
|
232
|
+
if "auto_test" in settings:
|
|
233
|
+
self.coder.auto_test = settings["auto_test"]
|
|
234
|
+
|
|
235
|
+
self.io.tool_output(
|
|
236
|
+
f"Session loaded: {session_data.get('session_name', session_file.stem)}"
|
|
237
|
+
)
|
|
238
|
+
self.io.tool_output(
|
|
239
|
+
f"Model: {session_data.get('model', 'unknown')}, Edit format:"
|
|
240
|
+
f" {session_data.get('edit_format', 'unknown')}"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Show summary
|
|
244
|
+
num_messages = len(self.coder.done_messages) + len(self.coder.cur_messages)
|
|
245
|
+
num_files = (
|
|
246
|
+
len(self.coder.abs_fnames)
|
|
247
|
+
+ len(self.coder.abs_read_only_fnames)
|
|
248
|
+
+ len(self.coder.abs_read_only_stubs_fnames)
|
|
249
|
+
)
|
|
250
|
+
self.io.tool_output(f"Loaded {num_messages} messages and {num_files} files")
|
|
251
|
+
|
|
252
|
+
return True
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
self.io.tool_error(f"Error applying session data: {e}")
|
|
256
|
+
return False
|
aider/special.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
ROOT_IMPORTANT_FILES = [
|
|
4
|
+
# Version Control
|
|
5
|
+
".gitignore",
|
|
6
|
+
".gitattributes",
|
|
7
|
+
# Documentation
|
|
8
|
+
"README",
|
|
9
|
+
"README.md",
|
|
10
|
+
"README.txt",
|
|
11
|
+
"README.rst",
|
|
12
|
+
"CONTRIBUTING",
|
|
13
|
+
"CONTRIBUTING.md",
|
|
14
|
+
"CONTRIBUTING.txt",
|
|
15
|
+
"CONTRIBUTING.rst",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"LICENSE.md",
|
|
18
|
+
"LICENSE.txt",
|
|
19
|
+
"CHANGELOG",
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"CHANGELOG.txt",
|
|
22
|
+
"CHANGELOG.rst",
|
|
23
|
+
"SECURITY",
|
|
24
|
+
"SECURITY.md",
|
|
25
|
+
"SECURITY.txt",
|
|
26
|
+
"CODEOWNERS",
|
|
27
|
+
# Package Management and Dependencies
|
|
28
|
+
"requirements.txt",
|
|
29
|
+
"Pipfile",
|
|
30
|
+
"Pipfile.lock",
|
|
31
|
+
"pyproject.toml",
|
|
32
|
+
"setup.py",
|
|
33
|
+
"setup.cfg",
|
|
34
|
+
"package.json",
|
|
35
|
+
"package-lock.json",
|
|
36
|
+
"yarn.lock",
|
|
37
|
+
"npm-shrinkwrap.json",
|
|
38
|
+
"Gemfile",
|
|
39
|
+
"Gemfile.lock",
|
|
40
|
+
"composer.json",
|
|
41
|
+
"composer.lock",
|
|
42
|
+
"pom.xml",
|
|
43
|
+
"build.gradle",
|
|
44
|
+
"build.gradle.kts",
|
|
45
|
+
"build.sbt",
|
|
46
|
+
"go.mod",
|
|
47
|
+
"go.sum",
|
|
48
|
+
"Cargo.toml",
|
|
49
|
+
"Cargo.lock",
|
|
50
|
+
"mix.exs",
|
|
51
|
+
"rebar.config",
|
|
52
|
+
"project.clj",
|
|
53
|
+
"Podfile",
|
|
54
|
+
"Cartfile",
|
|
55
|
+
"dub.json",
|
|
56
|
+
"dub.sdl",
|
|
57
|
+
# Configuration and Settings
|
|
58
|
+
".env",
|
|
59
|
+
".env.example",
|
|
60
|
+
".editorconfig",
|
|
61
|
+
"tsconfig.json",
|
|
62
|
+
"jsconfig.json",
|
|
63
|
+
".babelrc",
|
|
64
|
+
"babel.config.js",
|
|
65
|
+
".eslintrc",
|
|
66
|
+
".eslintignore",
|
|
67
|
+
".prettierrc",
|
|
68
|
+
".stylelintrc",
|
|
69
|
+
"tslint.json",
|
|
70
|
+
".pylintrc",
|
|
71
|
+
".flake8",
|
|
72
|
+
".rubocop.yml",
|
|
73
|
+
".scalafmt.conf",
|
|
74
|
+
".dockerignore",
|
|
75
|
+
".gitpod.yml",
|
|
76
|
+
"sonar-project.properties",
|
|
77
|
+
"renovate.json",
|
|
78
|
+
"dependabot.yml",
|
|
79
|
+
".pre-commit-config.yaml",
|
|
80
|
+
"mypy.ini",
|
|
81
|
+
"tox.ini",
|
|
82
|
+
".yamllint",
|
|
83
|
+
"pyrightconfig.json",
|
|
84
|
+
# Build and Compilation
|
|
85
|
+
"webpack.config.js",
|
|
86
|
+
"rollup.config.js",
|
|
87
|
+
"parcel.config.js",
|
|
88
|
+
"gulpfile.js",
|
|
89
|
+
"Gruntfile.js",
|
|
90
|
+
"build.xml",
|
|
91
|
+
"build.boot",
|
|
92
|
+
"project.json",
|
|
93
|
+
"build.cake",
|
|
94
|
+
"MANIFEST.in",
|
|
95
|
+
# Testing
|
|
96
|
+
"pytest.ini",
|
|
97
|
+
"phpunit.xml",
|
|
98
|
+
"karma.conf.js",
|
|
99
|
+
"jest.config.js",
|
|
100
|
+
"cypress.json",
|
|
101
|
+
".nycrc",
|
|
102
|
+
".nycrc.json",
|
|
103
|
+
# CI/CD
|
|
104
|
+
".travis.yml",
|
|
105
|
+
".gitlab-ci.yml",
|
|
106
|
+
"Jenkinsfile",
|
|
107
|
+
"azure-pipelines.yml",
|
|
108
|
+
"bitbucket-pipelines.yml",
|
|
109
|
+
"appveyor.yml",
|
|
110
|
+
"circle.yml",
|
|
111
|
+
".circleci/config.yml",
|
|
112
|
+
".github/dependabot.yml",
|
|
113
|
+
"codecov.yml",
|
|
114
|
+
".coveragerc",
|
|
115
|
+
# Docker and Containers
|
|
116
|
+
"Dockerfile",
|
|
117
|
+
"docker-compose.yml",
|
|
118
|
+
"docker-compose.override.yml",
|
|
119
|
+
# Cloud and Serverless
|
|
120
|
+
"serverless.yml",
|
|
121
|
+
"firebase.json",
|
|
122
|
+
"now.json",
|
|
123
|
+
"netlify.toml",
|
|
124
|
+
"vercel.json",
|
|
125
|
+
"app.yaml",
|
|
126
|
+
"terraform.tf",
|
|
127
|
+
"main.tf",
|
|
128
|
+
"cloudformation.yaml",
|
|
129
|
+
"cloudformation.json",
|
|
130
|
+
"ansible.cfg",
|
|
131
|
+
"kubernetes.yaml",
|
|
132
|
+
"k8s.yaml",
|
|
133
|
+
# Database
|
|
134
|
+
"schema.sql",
|
|
135
|
+
"liquibase.properties",
|
|
136
|
+
"flyway.conf",
|
|
137
|
+
# Framework-specific
|
|
138
|
+
"next.config.js",
|
|
139
|
+
"nuxt.config.js",
|
|
140
|
+
"vue.config.js",
|
|
141
|
+
"angular.json",
|
|
142
|
+
"gatsby-config.js",
|
|
143
|
+
"gridsome.config.js",
|
|
144
|
+
# API Documentation
|
|
145
|
+
"swagger.yaml",
|
|
146
|
+
"swagger.json",
|
|
147
|
+
"openapi.yaml",
|
|
148
|
+
"openapi.json",
|
|
149
|
+
# Development environment
|
|
150
|
+
".nvmrc",
|
|
151
|
+
".ruby-version",
|
|
152
|
+
".python-version",
|
|
153
|
+
"Vagrantfile",
|
|
154
|
+
# Quality and metrics
|
|
155
|
+
".codeclimate.yml",
|
|
156
|
+
"codecov.yml",
|
|
157
|
+
# Documentation
|
|
158
|
+
"mkdocs.yml",
|
|
159
|
+
"_config.yml",
|
|
160
|
+
"book.toml",
|
|
161
|
+
"readthedocs.yml",
|
|
162
|
+
".readthedocs.yaml",
|
|
163
|
+
# Package registries
|
|
164
|
+
".npmrc",
|
|
165
|
+
".yarnrc",
|
|
166
|
+
# Linting and formatting
|
|
167
|
+
".isort.cfg",
|
|
168
|
+
".markdownlint.json",
|
|
169
|
+
".markdownlint.yaml",
|
|
170
|
+
# Security
|
|
171
|
+
".bandit",
|
|
172
|
+
".secrets.baseline",
|
|
173
|
+
# Misc
|
|
174
|
+
".pypirc",
|
|
175
|
+
".gitkeep",
|
|
176
|
+
".npmignore",
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# Normalize the lists once
|
|
181
|
+
NORMALIZED_ROOT_IMPORTANT_FILES = set(os.path.normpath(path) for path in ROOT_IMPORTANT_FILES)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def is_important(file_path):
|
|
185
|
+
file_name = os.path.basename(file_path)
|
|
186
|
+
dir_name = os.path.normpath(os.path.dirname(file_path))
|
|
187
|
+
normalized_path = os.path.normpath(file_path)
|
|
188
|
+
|
|
189
|
+
# Check for GitHub Actions workflow files
|
|
190
|
+
if dir_name == os.path.normpath(".github/workflows") and file_name.endswith(".yml"):
|
|
191
|
+
return True
|
|
192
|
+
|
|
193
|
+
return normalized_path in NORMALIZED_ROOT_IMPORTANT_FILES
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def filter_important_files(file_paths):
|
|
197
|
+
"""
|
|
198
|
+
Filter a list of file paths to return only those that are commonly important in codebases.
|
|
199
|
+
|
|
200
|
+
:param file_paths: List of file paths to check
|
|
201
|
+
:return: List of file paths that match important file patterns
|
|
202
|
+
"""
|
|
203
|
+
return list(filter(is_important, file_paths))
|
aider/tools/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# flake8: noqa: F401
|
|
2
|
+
# Import tool modules into the aider.tools namespace
|
|
3
|
+
|
|
4
|
+
# Import all tool modules
|
|
5
|
+
from . import (
|
|
6
|
+
command,
|
|
7
|
+
command_interactive,
|
|
8
|
+
delete_block,
|
|
9
|
+
delete_line,
|
|
10
|
+
delete_lines,
|
|
11
|
+
extract_lines,
|
|
12
|
+
finished,
|
|
13
|
+
git_branch,
|
|
14
|
+
git_diff,
|
|
15
|
+
git_log,
|
|
16
|
+
git_remote,
|
|
17
|
+
git_show,
|
|
18
|
+
git_status,
|
|
19
|
+
grep,
|
|
20
|
+
indent_lines,
|
|
21
|
+
insert_block,
|
|
22
|
+
list_changes,
|
|
23
|
+
ls,
|
|
24
|
+
make_editable,
|
|
25
|
+
make_readonly,
|
|
26
|
+
remove,
|
|
27
|
+
replace_all,
|
|
28
|
+
replace_line,
|
|
29
|
+
replace_lines,
|
|
30
|
+
replace_text,
|
|
31
|
+
show_numbered_context,
|
|
32
|
+
undo_change,
|
|
33
|
+
update_todo_list,
|
|
34
|
+
view,
|
|
35
|
+
view_files_matching,
|
|
36
|
+
view_files_with_symbol,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# List of all available tool modules for dynamic discovery
|
|
40
|
+
TOOL_MODULES = [
|
|
41
|
+
command,
|
|
42
|
+
command_interactive,
|
|
43
|
+
delete_block,
|
|
44
|
+
delete_line,
|
|
45
|
+
delete_lines,
|
|
46
|
+
extract_lines,
|
|
47
|
+
finished,
|
|
48
|
+
git_branch,
|
|
49
|
+
git_diff,
|
|
50
|
+
git_log,
|
|
51
|
+
git_remote,
|
|
52
|
+
git_show,
|
|
53
|
+
git_status,
|
|
54
|
+
grep,
|
|
55
|
+
indent_lines,
|
|
56
|
+
insert_block,
|
|
57
|
+
list_changes,
|
|
58
|
+
ls,
|
|
59
|
+
make_editable,
|
|
60
|
+
make_readonly,
|
|
61
|
+
remove,
|
|
62
|
+
replace_all,
|
|
63
|
+
replace_line,
|
|
64
|
+
replace_lines,
|
|
65
|
+
replace_text,
|
|
66
|
+
show_numbered_context,
|
|
67
|
+
undo_change,
|
|
68
|
+
update_todo_list,
|
|
69
|
+
view,
|
|
70
|
+
view_files_matching,
|
|
71
|
+
view_files_with_symbol,
|
|
72
|
+
]
|
aider/tools/command.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Import necessary functions
|
|
2
|
+
from aider.run_cmd import run_cmd_subprocess
|
|
3
|
+
|
|
4
|
+
schema = {
|
|
5
|
+
"type": "function",
|
|
6
|
+
"function": {
|
|
7
|
+
"name": "Command",
|
|
8
|
+
"description": "Execute a shell command.",
|
|
9
|
+
"parameters": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"command_string": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "The shell command to execute.",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
"required": ["command_string"],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# Normalized tool name for lookup
|
|
23
|
+
NORM_NAME = "command"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def _execute_command(coder, command_string):
|
|
27
|
+
"""
|
|
28
|
+
Execute a non-interactive shell command after user confirmation.
|
|
29
|
+
"""
|
|
30
|
+
try:
|
|
31
|
+
# Ask for confirmation before executing.
|
|
32
|
+
# allow_never=True enables the 'Always' option.
|
|
33
|
+
# confirm_ask handles remembering the 'Always' choice based on the subject.
|
|
34
|
+
|
|
35
|
+
confirmed = (
|
|
36
|
+
True
|
|
37
|
+
if coder.skip_cli_confirmations
|
|
38
|
+
else await coder.io.confirm_ask(
|
|
39
|
+
"Allow execution of this command?",
|
|
40
|
+
subject=command_string,
|
|
41
|
+
explicit_yes_required=True, # Require explicit 'yes' or 'always'
|
|
42
|
+
allow_never=True, # Enable the 'Always' option
|
|
43
|
+
group_response="Command Tool",
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
await coder.io.recreate_input()
|
|
48
|
+
|
|
49
|
+
if not confirmed:
|
|
50
|
+
# This happens if the user explicitly says 'no' this time.
|
|
51
|
+
# If 'Always' was chosen previously, confirm_ask returns True directly.
|
|
52
|
+
coder.io.tool_output(f"Skipped execution of shell command: {command_string}")
|
|
53
|
+
return "Shell command execution skipped by user."
|
|
54
|
+
|
|
55
|
+
# Proceed with execution if confirmed is True
|
|
56
|
+
coder.io.tool_output(f"⚙️ Executing non-interactive shell command: {command_string}")
|
|
57
|
+
|
|
58
|
+
# Use run_cmd_subprocess for non-interactive execution
|
|
59
|
+
exit_status, combined_output = run_cmd_subprocess(
|
|
60
|
+
command_string, verbose=coder.verbose, cwd=coder.root # Execute in the project root
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Format the output for the result message, include more content
|
|
64
|
+
output_content = combined_output or ""
|
|
65
|
+
# Use the existing token threshold constant as the character limit for truncation
|
|
66
|
+
output_limit = coder.large_file_token_threshold
|
|
67
|
+
if len(output_content) > output_limit:
|
|
68
|
+
# Truncate and add a clear message using the constant value
|
|
69
|
+
output_content = (
|
|
70
|
+
output_content[:output_limit]
|
|
71
|
+
+ f"\n... (output truncated at {output_limit} characters, based on"
|
|
72
|
+
" large_file_token_threshold)"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if exit_status == 0:
|
|
76
|
+
return f"Shell command executed successfully (exit code 0). Output:\n{output_content}"
|
|
77
|
+
else:
|
|
78
|
+
return f"Shell command failed with exit code {exit_status}. Output:\n{output_content}"
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
coder.io.tool_error(
|
|
82
|
+
f"Error executing non-interactive shell command '{command_string}': {str(e)}"
|
|
83
|
+
)
|
|
84
|
+
# Optionally include traceback for debugging if verbose
|
|
85
|
+
# if coder.verbose:
|
|
86
|
+
# coder.io.tool_error(traceback.format_exc())
|
|
87
|
+
return f"Error executing command: {str(e)}"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def process_response(coder, params):
|
|
91
|
+
"""
|
|
92
|
+
Process the Command tool response.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
coder: The Coder instance
|
|
96
|
+
params: Dictionary of parameters
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
str: Result message
|
|
100
|
+
"""
|
|
101
|
+
command_string = params.get("command_string")
|
|
102
|
+
if command_string is not None:
|
|
103
|
+
return await _execute_command(coder, command_string)
|
|
104
|
+
else:
|
|
105
|
+
return "Error: Missing 'command_string' parameter for Command"
|