Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion aider/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,10 @@ def get_parser(default_config_files, git_root):
group.add_argument(
"--preserve-todo-list",
action="store_true",
help="Preserve the existing .aider.todo.txt file on startup (default: False)",
help=(
"Deprecated: no longer needed because the todo list is saved and restored with"
" sessions. This flag has no effect and will be removed."
),
default=False,
)
group.add_argument(
Expand Down
27 changes: 16 additions & 11 deletions aider/coders/base_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,12 @@ def __init__(
self.auto_accept_architect = auto_accept_architect
self.preserve_todo_list = preserve_todo_list

if self.preserve_todo_list:
self.io.tool_warning(
"--preserve-todo-list is deprecated; todo lists are now saved and restored with"
" sessions. The flag will be removed in a future release."
)

self.ignore_mentions = ignore_mentions
if not self.ignore_mentions:
self.ignore_mentions = set()
Expand Down Expand Up @@ -524,17 +530,16 @@ def __init__(
self.auto_test = auto_test
self.test_cmd = test_cmd

# Clean up todo list file on startup unless preserve_todo_list is True
if not getattr(self, "preserve_todo_list", False):
todo_file_path = ".aider.todo.txt"
abs_path = self.abs_root_path(todo_file_path)
if os.path.isfile(abs_path):
try:
os.remove(abs_path)
if self.verbose:
self.io.tool_output(f"Removed existing todo list file: {todo_file_path}")
except Exception as e:
self.io.tool_warning(f"Could not remove todo list file {todo_file_path}: {e}")
# Clean up todo list file on startup; sessions will restore it when needed
todo_file_path = ".aider.todo.txt"
abs_path = self.abs_root_path(todo_file_path)
if os.path.isfile(abs_path):
try:
os.remove(abs_path)
if self.verbose:
self.io.tool_output(f"Removed existing todo list file: {todo_file_path}")
except Exception as e:
self.io.tool_warning(f"Could not remove todo list file {todo_file_path}: {e}")

# validate the functions jsonschema
if self.functions:
Expand Down
25 changes: 25 additions & 0 deletions aider/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ def _build_session_data(self, session_name) -> Dict:
for abs_fname in self.coder.abs_read_only_stubs_fnames
]

# Capture todo list content so it can be restored with the session
todo_content = None
try:
todo_path = self.coder.abs_root_path(".aider.todo.txt")
if os.path.isfile(todo_path):
todo_content = self.io.read_text(todo_path)
if todo_content is None:
todo_content = ""
except Exception as e:
self.io.tool_warning(f"Could not read todo list file: {e}")

return {
"version": 1,
"session_name": session_name,
Expand All @@ -148,6 +159,7 @@ def _build_session_data(self, session_name) -> Dict:
"auto_lint": self.coder.auto_lint,
"auto_test": self.coder.auto_test,
},
"todo_list": todo_content,
}

def _find_session_file(self, session_identifier: str) -> Optional[Path]:
Expand Down Expand Up @@ -232,6 +244,19 @@ def _apply_session_data(self, session_data: Dict, session_file: Path) -> bool:
if "auto_test" in settings:
self.coder.auto_test = settings["auto_test"]

# Restore todo list content if present in the session
if "todo_list" in session_data:
todo_path = self.coder.abs_root_path(".aider.todo.txt")
todo_content = session_data.get("todo_list")
try:
if todo_content is None:
if os.path.exists(todo_path):
os.remove(todo_path)
else:
self.io.write_text(todo_path, todo_content)
except Exception as e:
self.io.tool_warning(f"Could not restore todo list: {e}")

self.io.tool_output(
f"Session loaded: {session_data.get('session_name', session_file.stem)}"
)
Expand Down
5 changes: 1 addition & 4 deletions aider/website/docs/config/agent-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,9 @@ When `include_context_blocks` is specified, only the listed blocks will be inclu

#### Other Aider-CE CLI/Config Options for Agent Mode

- `preserve-todo-list` - Preserve todo list across sessions
- `use-enhanced-map` - Use enhanced repo map that takes into account import relationships between files

```yaml
preserve-todo-list: true
use-enhanced-map: true
```

Expand Down Expand Up @@ -240,7 +238,6 @@ agent-config:
skills_excludelist: ["legacy-tools"] # Optional: Blacklist of skills to exclude

# Other Agent Mode options
preserve-todo-list: true # Preserve todo list across sessions
use-enhanced-map: true # Use enhanced repo map with import relationships
```

Expand All @@ -260,4 +257,4 @@ For complete documentation on creating and using skills, including skill directo
- **Scalable exploration**: Can handle large codebases through strategic context management
- **Recovery mechanisms**: Built-in undo and safety features

Agent Mode represents a significant evolution in aider's capabilities, enabling more sophisticated and autonomous codebase manipulation while maintaining safety and control through the tool-based architecture.
Agent Mode represents a significant evolution in aider's capabilities, enabling more sophisticated and autonomous codebase manipulation while maintaining safety and control through the tool-based architecture.
16 changes: 13 additions & 3 deletions aider/website/docs/sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ When `--auto-save` is enabled, aider will automatically save your session as 'au
- All files in the chat (editable, read-only, and read-only stubs)
- Current model and edit format settings
- Auto-commit, auto-lint, and auto-test settings
- Todo list content from `.aider.todo.txt`
- Session metadata (timestamp, version)

### `/load-session <name>`
Expand All @@ -56,6 +57,7 @@ Load a previously saved session by name or file path.
- Restores chat history and file configurations
- Recreates the exact session state
- Preserves all settings and model configurations
- Restores the todo list content saved in the session

### `/list-sessions`
List all available saved sessions in `.aider/sessions/`.
Expand All @@ -78,10 +80,13 @@ Sessions are stored as JSON files in the `.aider/sessions/` directory within you

```json
{
"version": "1.0",
"version": 1,
"timestamp": 1700000000,
"session_name": "my-session",
"model": "gpt-4",
"weak_model": "gpt-4o-mini",
"editor_model": "gpt-4o",
"editor_edit_format": "diff",
"edit_format": "diff",
"chat_history": {
"done_messages": [...],
Expand All @@ -93,11 +98,11 @@ Sessions are stored as JSON files in the `.aider/sessions/` directory within you
"read_only_stubs": []
},
"settings": {
"root": "/path/to/project",
"auto_commits": true,
"auto_lint": false,
"auto_test": false
}
},
"todo_list": "- plan feature A\n- write tests\n"
}
```

Expand Down Expand Up @@ -143,6 +148,7 @@ Sessions are stored as JSON files in the `.aider/sessions/` directory within you
- Session files include all file paths, so they work best when project structure is stable
- External files (outside the project root) are stored with absolute paths
- Missing files are skipped with warnings during loading
- The todo list file (`.aider.todo.txt`) is cleared on startup; it is restored when you load a session or when you update it during a run

### Version Control
- Consider adding `.aider/sessions/` to your `.gitignore` if sessions contain sensitive information
Expand All @@ -160,13 +166,17 @@ If files are reported as missing during loading:
- The files may have been moved or deleted
- Session files store relative paths, so directory structure changes can affect this
- External files must exist at their original locations
- The todo list (`.aider.todo.txt`) is cleared on startup unless restored from a loaded session

### Corrupted Sessions
If a session fails to load:
- Check the session file is valid JSON
- Verify the session version is compatible
- Try creating a new session and compare file structures

### Deprecated Options
- `--preserve-todo-list` is deprecated. The todo list is cleared on startup and restored only when you load a session that contains it.

## Related Commands
- `/reset` - Clear chat history and drop files (useful before loading a session)

Expand Down
38 changes: 32 additions & 6 deletions tests/basic/test_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ async def test_cmd_save_session_basic(self):
{"role": "user", "content": "Can you help me?"},
]

# Add a todo list
todo_content = "Task 1\nTask 2"
Path(".aider.todo.txt").write_text(todo_content, encoding="utf-8")

# Save session
session_name = "test_session"
commands.cmd_save_session(session_name)
Expand All @@ -69,7 +73,7 @@ async def test_cmd_save_session_basic(self):
with open(session_file, "r", encoding="utf-8") as f:
session_data = json.load(f)

self.assertEqual(session_data["version"], "1.0")
self.assertEqual(session_data["version"], 1)
self.assertEqual(session_data["session_name"], session_name)
self.assertEqual(session_data["model"], self.GPT35.name)
self.assertEqual(session_data["edit_format"], coder.edit_format)
Expand All @@ -87,11 +91,13 @@ async def test_cmd_save_session_basic(self):

# Verify settings
settings = session_data["settings"]
self.assertEqual(settings["root"], coder.root)
self.assertEqual(settings["auto_commits"], coder.auto_commits)
self.assertEqual(settings["auto_lint"], coder.auto_lint)
self.assertEqual(settings["auto_test"], coder.auto_test)

# Verify todo list persisted
self.assertEqual(session_data["todo_list"], todo_content)

async def test_cmd_load_session_basic(self):
"""Test basic session load functionality"""
with GitTemporaryDirectory() as repo_dir:
Expand All @@ -113,7 +119,7 @@ async def test_cmd_load_session_basic(self):

# Create a session file manually
session_data = {
"version": "1.0",
"version": 1,
"timestamp": time.time(),
"session_name": "test_session",
"model": self.GPT35.name,
Expand All @@ -133,11 +139,11 @@ async def test_cmd_load_session_basic(self):
"read_only_stubs": [],
},
"settings": {
"root": str(repo_dir),
"auto_commits": True,
"auto_lint": False,
"auto_test": False,
},
"todo_list": "Restored tasks\n- item",
}

# Save session file
Expand Down Expand Up @@ -166,6 +172,11 @@ async def test_cmd_load_session_basic(self):
self.assertEqual(coder.auto_lint, False)
self.assertEqual(coder.auto_test, False)

# Verify todo list restored
todo_file = Path(".aider.todo.txt")
self.assertTrue(todo_file.exists())
self.assertEqual(todo_file.read_text(encoding="utf-8"), session_data["todo_list"])

async def test_cmd_list_sessions_basic(self):
"""Test basic session list functionality"""
with GitTemporaryDirectory():
Expand All @@ -176,7 +187,7 @@ async def test_cmd_list_sessions_basic(self):
# Create multiple session files
sessions_data = [
{
"version": "1.0",
"version": 1,
"timestamp": time.time() - 3600, # 1 hour ago
"session_name": "session1",
"model": "gpt-3.5-turbo",
Expand All @@ -191,7 +202,7 @@ async def test_cmd_list_sessions_basic(self):
},
},
{
"version": "1.0",
"version": 1,
"timestamp": time.time(), # current time
"session_name": "session2",
"model": "gpt-4",
Expand Down Expand Up @@ -232,3 +243,18 @@ async def test_cmd_list_sessions_basic(self):
self.assertIn("session2", output_text)
self.assertIn("gpt-3.5-turbo", output_text)
self.assertIn("gpt-4", output_text)

async def test_preserve_todo_list_deprecated(self):
"""Ensure preserve-todo-list flag is deprecated and todo is cleared on startup"""
with GitTemporaryDirectory():
todo_path = Path(".aider.todo.txt")
todo_path.write_text("keep me", encoding="utf-8")

io = InputOutput(pretty=False, fancy_input=False, yes=True)
with mock.patch.object(io, "tool_warning") as mock_tool_warning:
await Coder.create(self.GPT35, None, io, preserve_todo_list=True)

self.assertFalse(todo_path.exists())
self.assertTrue(
any("deprecated" in call[0][0] for call in mock_tool_warning.call_args_list)
)
Loading