From cfa4d2f809fa7e45bfc6f474d38bd617d8ad528b Mon Sep 17 00:00:00 2001 From: James Williams Date: Thu, 18 Dec 2025 12:43:52 -0300 Subject: [PATCH 1/2] Integrate todo list with session management. This adds the current todo list content into the session save/load payload. The flag --preserve-todo-list no longer has any effect and is marked as deprecated. --- aider/args.py | 5 ++++- aider/coders/base_coder.py | 27 ++++++++++++++---------- aider/sessions.py | 25 ++++++++++++++++++++++ aider/website/docs/sessions.md | 16 +++++++++++--- tests/basic/test_sessions.py | 38 ++++++++++++++++++++++++++++------ 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/aider/args.py b/aider/args.py index e501d6d5506..43240ee55bd 100644 --- a/aider/args.py +++ b/aider/args.py @@ -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( diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index b97d0300f8b..f4a311878be 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -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() @@ -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: diff --git a/aider/sessions.py b/aider/sessions.py index f4a29771981..bbc0b2c807e 100644 --- a/aider/sessions.py +++ b/aider/sessions.py @@ -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, @@ -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]: @@ -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)}" ) diff --git a/aider/website/docs/sessions.md b/aider/website/docs/sessions.md index 8ca925fba2b..1afdb45ec61 100644 --- a/aider/website/docs/sessions.md +++ b/aider/website/docs/sessions.md @@ -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 ` @@ -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/`. @@ -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": [...], @@ -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" } ``` @@ -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 @@ -160,6 +166,7 @@ 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: @@ -167,6 +174,9 @@ If a session fails to load: - 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) diff --git a/tests/basic/test_sessions.py b/tests/basic/test_sessions.py index 88367e49b9a..2b24607e831 100644 --- a/tests/basic/test_sessions.py +++ b/tests/basic/test_sessions.py @@ -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) @@ -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) @@ -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: @@ -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, @@ -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 @@ -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(): @@ -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", @@ -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", @@ -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) + ) From 89482a95cf5238c7b88e8580ecc244421241db55 Mon Sep 17 00:00:00 2001 From: James Williams Date: Thu, 18 Dec 2025 17:53:31 -0300 Subject: [PATCH 2/2] Remove references to --preserve-todo-list elsewhere in code. --- aider/website/docs/config/agent-mode.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/aider/website/docs/config/agent-mode.md b/aider/website/docs/config/agent-mode.md index c0953b755e0..2300406a053 100644 --- a/aider/website/docs/config/agent-mode.md +++ b/aider/website/docs/config/agent-mode.md @@ -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 ``` @@ -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 ``` @@ -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. \ No newline at end of file +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.