Skip to content

Commit cfa4d2f

Browse files
committed
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.
1 parent c867082 commit cfa4d2f

5 files changed

Lines changed: 90 additions & 21 deletions

File tree

aider/args.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,10 @@ def get_parser(default_config_files, git_root):
309309
group.add_argument(
310310
"--preserve-todo-list",
311311
action="store_true",
312-
help="Preserve the existing .aider.todo.txt file on startup (default: False)",
312+
help=(
313+
"Deprecated: no longer needed because the todo list is saved and restored with"
314+
" sessions. This flag has no effect and will be removed."
315+
),
313316
default=False,
314317
)
315318
group.add_argument(

aider/coders/base_coder.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,12 @@ def __init__(
314314
self.auto_accept_architect = auto_accept_architect
315315
self.preserve_todo_list = preserve_todo_list
316316

317+
if self.preserve_todo_list:
318+
self.io.tool_warning(
319+
"--preserve-todo-list is deprecated; todo lists are now saved and restored with"
320+
" sessions. The flag will be removed in a future release."
321+
)
322+
317323
self.ignore_mentions = ignore_mentions
318324
if not self.ignore_mentions:
319325
self.ignore_mentions = set()
@@ -524,17 +530,16 @@ def __init__(
524530
self.auto_test = auto_test
525531
self.test_cmd = test_cmd
526532

527-
# Clean up todo list file on startup unless preserve_todo_list is True
528-
if not getattr(self, "preserve_todo_list", False):
529-
todo_file_path = ".aider.todo.txt"
530-
abs_path = self.abs_root_path(todo_file_path)
531-
if os.path.isfile(abs_path):
532-
try:
533-
os.remove(abs_path)
534-
if self.verbose:
535-
self.io.tool_output(f"Removed existing todo list file: {todo_file_path}")
536-
except Exception as e:
537-
self.io.tool_warning(f"Could not remove todo list file {todo_file_path}: {e}")
533+
# Clean up todo list file on startup; sessions will restore it when needed
534+
todo_file_path = ".aider.todo.txt"
535+
abs_path = self.abs_root_path(todo_file_path)
536+
if os.path.isfile(abs_path):
537+
try:
538+
os.remove(abs_path)
539+
if self.verbose:
540+
self.io.tool_output(f"Removed existing todo list file: {todo_file_path}")
541+
except Exception as e:
542+
self.io.tool_warning(f"Could not remove todo list file {todo_file_path}: {e}")
538543

539544
# validate the functions jsonschema
540545
if self.functions:

aider/sessions.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ def _build_session_data(self, session_name) -> Dict:
126126
for abs_fname in self.coder.abs_read_only_stubs_fnames
127127
]
128128

129+
# Capture todo list content so it can be restored with the session
130+
todo_content = None
131+
try:
132+
todo_path = self.coder.abs_root_path(".aider.todo.txt")
133+
if os.path.isfile(todo_path):
134+
todo_content = self.io.read_text(todo_path)
135+
if todo_content is None:
136+
todo_content = ""
137+
except Exception as e:
138+
self.io.tool_warning(f"Could not read todo list file: {e}")
139+
129140
return {
130141
"version": 1,
131142
"session_name": session_name,
@@ -148,6 +159,7 @@ def _build_session_data(self, session_name) -> Dict:
148159
"auto_lint": self.coder.auto_lint,
149160
"auto_test": self.coder.auto_test,
150161
},
162+
"todo_list": todo_content,
151163
}
152164

153165
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:
232244
if "auto_test" in settings:
233245
self.coder.auto_test = settings["auto_test"]
234246

247+
# Restore todo list content if present in the session
248+
if "todo_list" in session_data:
249+
todo_path = self.coder.abs_root_path(".aider.todo.txt")
250+
todo_content = session_data.get("todo_list")
251+
try:
252+
if todo_content is None:
253+
if os.path.exists(todo_path):
254+
os.remove(todo_path)
255+
else:
256+
self.io.write_text(todo_path, todo_content)
257+
except Exception as e:
258+
self.io.tool_warning(f"Could not restore todo list: {e}")
259+
235260
self.io.tool_output(
236261
f"Session loaded: {session_data.get('session_name', session_file.stem)}"
237262
)

aider/website/docs/sessions.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ When `--auto-save` is enabled, aider will automatically save your session as 'au
4242
- All files in the chat (editable, read-only, and read-only stubs)
4343
- Current model and edit format settings
4444
- Auto-commit, auto-lint, and auto-test settings
45+
- Todo list content from `.aider.todo.txt`
4546
- Session metadata (timestamp, version)
4647
4748
### `/load-session <name>`
@@ -56,6 +57,7 @@ Load a previously saved session by name or file path.
5657
- Restores chat history and file configurations
5758
- Recreates the exact session state
5859
- Preserves all settings and model configurations
60+
- Restores the todo list content saved in the session
5961
6062
### `/list-sessions`
6163
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
7880
7981
```json
8082
{
81-
"version": "1.0",
83+
"version": 1,
8284
"timestamp": 1700000000,
8385
"session_name": "my-session",
8486
"model": "gpt-4",
87+
"weak_model": "gpt-4o-mini",
88+
"editor_model": "gpt-4o",
89+
"editor_edit_format": "diff",
8590
"edit_format": "diff",
8691
"chat_history": {
8792
"done_messages": [...],
@@ -93,11 +98,11 @@ Sessions are stored as JSON files in the `.aider/sessions/` directory within you
9398
"read_only_stubs": []
9499
},
95100
"settings": {
96-
"root": "/path/to/project",
97101
"auto_commits": true,
98102
"auto_lint": false,
99103
"auto_test": false
100-
}
104+
},
105+
"todo_list": "- plan feature A\n- write tests\n"
101106
}
102107
```
103108

@@ -143,6 +148,7 @@ Sessions are stored as JSON files in the `.aider/sessions/` directory within you
143148
- Session files include all file paths, so they work best when project structure is stable
144149
- External files (outside the project root) are stored with absolute paths
145150
- Missing files are skipped with warnings during loading
151+
- 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
146152

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

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

177+
### Deprecated Options
178+
- `--preserve-todo-list` is deprecated. The todo list is cleared on startup and restored only when you load a session that contains it.
179+
170180
## Related Commands
171181
- `/reset` - Clear chat history and drop files (useful before loading a session)
172182

tests/basic/test_sessions.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ async def test_cmd_save_session_basic(self):
5757
{"role": "user", "content": "Can you help me?"},
5858
]
5959

60+
# Add a todo list
61+
todo_content = "Task 1\nTask 2"
62+
Path(".aider.todo.txt").write_text(todo_content, encoding="utf-8")
63+
6064
# Save session
6165
session_name = "test_session"
6266
commands.cmd_save_session(session_name)
@@ -69,7 +73,7 @@ async def test_cmd_save_session_basic(self):
6973
with open(session_file, "r", encoding="utf-8") as f:
7074
session_data = json.load(f)
7175

72-
self.assertEqual(session_data["version"], "1.0")
76+
self.assertEqual(session_data["version"], 1)
7377
self.assertEqual(session_data["session_name"], session_name)
7478
self.assertEqual(session_data["model"], self.GPT35.name)
7579
self.assertEqual(session_data["edit_format"], coder.edit_format)
@@ -87,11 +91,13 @@ async def test_cmd_save_session_basic(self):
8791

8892
# Verify settings
8993
settings = session_data["settings"]
90-
self.assertEqual(settings["root"], coder.root)
9194
self.assertEqual(settings["auto_commits"], coder.auto_commits)
9295
self.assertEqual(settings["auto_lint"], coder.auto_lint)
9396
self.assertEqual(settings["auto_test"], coder.auto_test)
9497

98+
# Verify todo list persisted
99+
self.assertEqual(session_data["todo_list"], todo_content)
100+
95101
async def test_cmd_load_session_basic(self):
96102
"""Test basic session load functionality"""
97103
with GitTemporaryDirectory() as repo_dir:
@@ -113,7 +119,7 @@ async def test_cmd_load_session_basic(self):
113119

114120
# Create a session file manually
115121
session_data = {
116-
"version": "1.0",
122+
"version": 1,
117123
"timestamp": time.time(),
118124
"session_name": "test_session",
119125
"model": self.GPT35.name,
@@ -133,11 +139,11 @@ async def test_cmd_load_session_basic(self):
133139
"read_only_stubs": [],
134140
},
135141
"settings": {
136-
"root": str(repo_dir),
137142
"auto_commits": True,
138143
"auto_lint": False,
139144
"auto_test": False,
140145
},
146+
"todo_list": "Restored tasks\n- item",
141147
}
142148

143149
# Save session file
@@ -166,6 +172,11 @@ async def test_cmd_load_session_basic(self):
166172
self.assertEqual(coder.auto_lint, False)
167173
self.assertEqual(coder.auto_test, False)
168174

175+
# Verify todo list restored
176+
todo_file = Path(".aider.todo.txt")
177+
self.assertTrue(todo_file.exists())
178+
self.assertEqual(todo_file.read_text(encoding="utf-8"), session_data["todo_list"])
179+
169180
async def test_cmd_list_sessions_basic(self):
170181
"""Test basic session list functionality"""
171182
with GitTemporaryDirectory():
@@ -176,7 +187,7 @@ async def test_cmd_list_sessions_basic(self):
176187
# Create multiple session files
177188
sessions_data = [
178189
{
179-
"version": "1.0",
190+
"version": 1,
180191
"timestamp": time.time() - 3600, # 1 hour ago
181192
"session_name": "session1",
182193
"model": "gpt-3.5-turbo",
@@ -191,7 +202,7 @@ async def test_cmd_list_sessions_basic(self):
191202
},
192203
},
193204
{
194-
"version": "1.0",
205+
"version": 1,
195206
"timestamp": time.time(), # current time
196207
"session_name": "session2",
197208
"model": "gpt-4",
@@ -232,3 +243,18 @@ async def test_cmd_list_sessions_basic(self):
232243
self.assertIn("session2", output_text)
233244
self.assertIn("gpt-3.5-turbo", output_text)
234245
self.assertIn("gpt-4", output_text)
246+
247+
async def test_preserve_todo_list_deprecated(self):
248+
"""Ensure preserve-todo-list flag is deprecated and todo is cleared on startup"""
249+
with GitTemporaryDirectory():
250+
todo_path = Path(".aider.todo.txt")
251+
todo_path.write_text("keep me", encoding="utf-8")
252+
253+
io = InputOutput(pretty=False, fancy_input=False, yes=True)
254+
with mock.patch.object(io, "tool_warning") as mock_tool_warning:
255+
await Coder.create(self.GPT35, None, io, preserve_todo_list=True)
256+
257+
self.assertFalse(todo_path.exists())
258+
self.assertTrue(
259+
any("deprecated" in call[0][0] for call in mock_tool_warning.call_args_list)
260+
)

0 commit comments

Comments
 (0)