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
60 changes: 48 additions & 12 deletions devagent/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,42 @@ def session_compress(
console.print(f"[dim]Summary length: {len(result.summary)} chars.[/dim]")


# ---------------------------------------------------------------------------
# Primary agent run command
# ---------------------------------------------------------------------------

def _pick_session(sessions: list[dict]) -> str | None:
"""Interactively choose a session with arrow keys when possible."""
options = [
(item["id"], item.get("title") or "Untitled session")
for item in sessions
]
options.append(("", "Start new session"))

try:
from prompt_toolkit.shortcuts import radiolist_dialog

selected = radiolist_dialog(
title="Recent sessions",
text="Use ↑/↓ to select and Enter to confirm.",
values=options,
).run()
return selected or None
except (ImportError, EOFError, KeyboardInterrupt):
console.print("[bold]Recent sessions[/bold]")
for index, item in enumerate(sessions, 1):
title = item.get("title") or "Untitled session"
console.print(f" [cyan]{index}[/cyan]. {title} ({item['id'][:8]})")
new_index = len(sessions) + 1
console.print(f" [cyan]{new_index}[/cyan]. Start new session")
choice = console.input("Select a session [default: new]: ").strip()
if choice.isdigit():
selected = int(choice)
if 1 <= selected <= len(sessions):
return sessions[selected - 1]["id"]
return None


# ---------------------------------------------------------------------------
# Primary agent run command
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -1598,6 +1634,11 @@ def run(
diff_preview: bool = typer.Option(
False, "--diff-preview", help="Show a diff and ask for confirmation before each file write/edit"
),
session_picker: bool = typer.Option(
True,
"--session-picker/--no-session-picker",
help="Use arrow keys to select a recent session interactively",
),
) -> None:
"""Start an interactive agent session (the main DevAgent command)."""
from devagent.agent.flows import DevAgentSession
Expand All @@ -1615,22 +1656,17 @@ def run(
tools_list = [t.strip() for t in allow_tools.split(",")] if allow_tools else None

selected_resume = resume
if selected_resume is None and sys.stdin.isatty() and sys.stdout.isatty():
if (
selected_resume is None
and session_picker
and sys.stdin.isatty()
and sys.stdout.isatty()
):
from devagent.session.manager import SessionManager

sessions = SessionManager().list_by_project(str(project_root), limit=10)
if sessions:
console.print("[bold]Recent sessions[/bold]")
for index, item in enumerate(sessions, 1):
title = item.get("title") or "Untitled session"
console.print(f" [cyan]{index}[/cyan]. {title} ({item['id'][:8]})")
new_index = len(sessions) + 1
console.print(f" [cyan]{new_index}[/cyan]. Start new session")
choice = console.input("Select a session [default: new]: ").strip()
if choice and choice.isdigit():
selected = int(choice)
if 1 <= selected <= len(sessions):
selected_resume = sessions[selected - 1]["id"]
selected_resume = _pick_session(sessions)

try:
session = DevAgentSession(
Expand Down
34 changes: 34 additions & 0 deletions tests/test_cli_session_picker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from __future__ import annotations

from unittest.mock import Mock

from devagent import cli


def test_pick_session_uses_arrow_picker(monkeypatch) -> None:
sessions = [{"id": "session-one", "title": "First session"}]
dialog = Mock()
dialog.run.return_value = "session-one"
picker = Mock(return_value=dialog)
monkeypatch.setattr("prompt_toolkit.shortcuts.radiolist_dialog", picker)

assert cli._pick_session(sessions) == "session-one"
picker.assert_called_once()
assert picker.call_args.kwargs["values"] == [
("session-one", "First session"),
("", "Start new session"),
]


def test_pick_session_numeric_fallback(monkeypatch) -> None:
sessions = [
{"id": "session-one", "title": "First session"},
{"id": "session-two", "title": "Second session"},
]
monkeypatch.setattr(
"prompt_toolkit.shortcuts.radiolist_dialog",
Mock(side_effect=ImportError),
)
monkeypatch.setattr(cli.console, "input", lambda _: "2")

assert cli._pick_session(sessions) == "session-two"
Loading