Skip to content

feat: add /sessions command to list sessions and switch to session#90

Merged
stdrc merged 8 commits intoMoonshotAI:mainfrom
YangLi-leo:feature/session-management
Dec 15, 2025
Merged

feat: add /sessions command to list sessions and switch to session#90
stdrc merged 8 commits intoMoonshotAI:mainfrom
YangLi-leo:feature/session-management

Conversation

@YangLi-leo
Copy link
Copy Markdown
Contributor

@YangLi-leo YangLi-leo commented Oct 26, 2025

Fix #83

@YangLi-leo YangLi-leo force-pushed the feature/session-management branch 5 times, most recently from 632771a to cfd30b6 Compare October 26, 2025 15:54
@stdrc
Copy link
Copy Markdown
Contributor

stdrc commented Oct 27, 2025

I just refactored the cli entrypoint today. Now the main branch is a bit off with this PR. Could you please have a look and resolve the conflict? BTW, I guess it would be way easier to raise a Reload exception to let the cli entrypoint "continue" another specified session.

@YangLi-leo
Copy link
Copy Markdown
Contributor Author

I tried using Reload exception as suggested, but it causes a UX issue: the shell restarts and clears the screen, so users can't see their previous conversation history. The direct state replacement approach provides better UX.

@YangLi-leo
Copy link
Copy Markdown
Contributor Author

And I resolve the conflict issue in the newest commit but still use the old logical

Signed-off-by: Richard Chien <stdrc@outlook.com>
Signed-off-by: Richard Chien <stdrc@outlook.com>
Signed-off-by: Richard Chien <stdrc@outlook.com>
Signed-off-by: Richard Chien <stdrc@outlook.com>
@stdrc stdrc force-pushed the feature/session-management branch from 3d76931 to 9966846 Compare December 15, 2025 05:45
Copilot AI review requested due to automatic review settings December 15, 2025 05:45
@stdrc stdrc changed the title feat: add /session command to list and switch between conversation se… feat: add /sessions command to list sessions and switch to session Dec 15, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements session management functionality with a new /sessions (alias /resume) meta command that allows users to list and interactively switch between conversation sessions. The feature includes proper session metadata extraction (titles from first user input, timestamps), interactive session selection UI, and full conversation history restoration upon switching.

Key Changes:

  • Added Session.refresh() method to extract session titles from wire files and update metadata
  • Implemented /sessions meta command with interactive selection UI using ChoiceInput
  • Refactored Reload exception to support session switching with optional session_id parameter

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/kimi_cli/session.py Made Session mutable, added refresh() method for title extraction from wire files, added wire_file property, refactored session creation/finding to use refresh
src/kimi_cli/ui/shell/metacmd.py Added list_sessions meta command to display sessions with previews and allow interactive switching
src/kimi_cli/cli.py Enhanced Reload exception with session_id parameter and modified main loop to support session switching
src/kimi_cli/utils/datetime.py Added format_relative_time() utility for human-friendly timestamp formatting
src/kimi_cli/soul/kimisoul.py Renamed wire_file_backend property to wire_file to match Session API
src/kimi_cli/ui/wire/init.py Updated to use renamed wire_file property
src/kimi_cli/ui/shell/init.py Updated to use renamed wire_file property
src/kimi_cli/ui/print/init.py Updated to use renamed wire_file property
src/kimi_cli/soul/init.py Updated parameter name from wire_file_backend to wire_file
src/kimi_cli/wire/serde.py Updated deserialize_wire_message type signature to accept Any for error handling tests
tests/test_session.py Added comprehensive tests for session creation, finding, listing, and title extraction
tests/test_wire_message.py Added test for invalid wire message deserialization error handling
Comments suppressed due to low confidence (1)

src/kimi_cli/session.py:38

  • The Session dataclass is modified from frozen=True to mutable (removing frozen), but the fields title and updated_at are marked as "refreshable metadata" while other fields are "static metadata". This creates potential for inconsistent state if the session object is shared or cached. Consider whether mutability is necessary, or if creating a new Session instance in refresh() would be safer. If mutability is intentional, document the thread-safety implications and expected usage patterns.
@dataclass(slots=True, kw_only=True)
class Session:
    """A session of a work directory."""

    # static metadata
    id: str
    """The session ID."""
    work_dir: KaosPath
    """The absolute path of the work directory."""
    work_dir_meta: WorkDirMeta
    """The metadata of the work directory."""
    context_file: Path
    """The absolute path to the file storing the message history."""

    # refreshable metadata
    title: str
    """The title of the session."""
    updated_at: float
    """The timestamp of the last update to the session."""

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +267 to +304
@meta_command(name="sessions", aliases=["resume"], kimi_soul_only=True)
async def list_sessions(app: Shell, args: list[str]):
"""List sessions and resume optionally"""
assert isinstance(app.soul, KimiSoul)

work_dir = app.soul._runtime.session.work_dir
current_session_id = app.soul._runtime.session.id
sessions = await Session.list(work_dir)

if not sessions:
console.print("[yellow]No sessions found.[/yellow]")
return

choices: list[tuple[str, str]] = []
for session in sessions:
time_str = format_relative_time(session.updated_at)
marker = " (current)" if session.id == current_session_id else ""
label = f"{session.title}, {time_str}{marker}"
choices.append((session.id, label))

try:
selection = await ChoiceInput(
message="Select a session to switch to (↑↓ navigate, Enter select, Ctrl+C cancel):",
options=choices,
default=choices[0][0],
).prompt_async()
except (EOFError, KeyboardInterrupt):
return

if not selection:
return

if selection == current_session_id:
console.print("[yellow]You are already in this session.[/yellow]")
return

console.print(f"[green]Switching to session {selection}...[/green]")
raise Reload(session_id=selection)
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new /sessions (alias /resume) metacommand lacks test coverage. The repository has comprehensive tests for meta commands (test_metacmd.py) and other functionality, but there are no tests verifying the behavior of the list_sessions command, including edge cases like empty session lists, current session selection, user cancellation, or session switching logic.

Copilot uses AI. Check for mistakes.
Signed-off-by: Richard Chien <stdrc@outlook.com>
Signed-off-by: Richard Chien <stdrc@outlook.com>
Signed-off-by: Richard Chien <stdrc@outlook.com>
@stdrc stdrc merged commit b0d3843 into MoonshotAI:main Dec 15, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Session Management Command

3 participants