Skip to content

feat: add --sessions/--list-sessions CLI options & fix CJK shorten#1376

Open
DRunkPiano114 wants to merge 3 commits intoMoonshotAI:mainfrom
DRunkPiano114:feat/session-argument
Open

feat: add --sessions/--list-sessions CLI options & fix CJK shorten#1376
DRunkPiano114 wants to merge 3 commits intoMoonshotAI:mainfrom
DRunkPiano114:feat/session-argument

Conversation

@DRunkPiano114
Copy link

@DRunkPiano114 DRunkPiano114 commented Mar 9, 2026

Related Issue

Resolves #1366

Description

  • Add --sessions / --list-sessions CLI options for interactive session selection and listing, see the result below.
    Screenshot 2026-03-10 at 02 07 24

  • Replace textwrap.shorten with a custom implementation that hard-truncates instead of word-boundary breaking, preventing CJK text without spaces from collapsing to just the placeholder.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked the related issue, if any.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have run make gen-changelog to update the changelog.
  • I have run make gen-docs to update the user documentation.

Open with Devin

…oonshotAI#1366)

Replace textwrap.shorten with a custom implementation that hard-truncates instead of word-boundary breaking, preventing CJK text without spaces from collapsing to just the placeholder.
Copilot AI review requested due to automatic review settings March 9, 2026 15:48
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

Copy link
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

Adds CLI affordances for working with historical sessions (interactive selection and listing) and replaces textwrap.shorten usages with a custom truncation helper to avoid CJK-without-spaces collapsing to just the placeholder.

Changes:

  • Add --sessions (interactive picker) and --list-sessions (print and exit) to the main CLI entrypoint, with conflict/validation and e2e coverage.
  • Introduce kimi_cli.utils.string.shorten() and migrate session-title/export/title-generation callsites to use it.
  • Update English/Chinese docs and changelogs to reflect the new options and truncation behavior.

Reviewed changes

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

Show a summary per file
File Description
tests/e2e/test_cli_error_output.py Adds e2e assertions for new flag conflicts, shell-only validation, and empty-session listing output.
src/kimi_cli/cli/init.py Implements --sessions picker and --list-sessions output path; adds mutual-exclusion checks.
src/kimi_cli/utils/string.py Adds custom shorten() helper intended to hard-truncate and be CJK-safe.
src/kimi_cli/session.py Switches session title truncation to the new shorten() and adjusts truncation width.
src/kimi_cli/utils/export.py Uses custom shorten() for export overview/hints truncation.
src/kimi_cli/web/api/sessions.py Uses custom shorten() for session title fallback/generated title truncation.
src/kimi_cli/web/store/sessions.py Uses custom shorten() when deriving titles from wire logs.
docs/en/reference/kimi-command.md Documents new CLI flags and updated mutual-exclusion rule.
docs/zh/reference/kimi-command.md Documents new CLI flags and updated mutual-exclusion rule (ZH).
docs/en/release-notes/changelog.md Notes new CLI flags and CJK truncation improvement.
docs/zh/release-notes/changelog.md Notes new CLI flags and CJK truncation improvement (ZH).
CHANGELOG.md Adds release notes entries for the new flags and truncation change.

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

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +10 to +24
def shorten(text: str, *, width: int, placeholder: str = "…") -> str:
"""Shorten text to at most width characters.

This always hard-truncates instead of
trying word-boundary breaking, so CJK text without spaces won't
collapse to just the placeholder.
"""
text = " ".join(text.split())
if len(text) <= width:
return text
cut = width - len(placeholder)
space = text.rfind(" ", 0, cut + 1)
if space > 0:
cut = space
return text[:cut].rstrip() + placeholder
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

shorten() claims to return at most width characters, but when width <= len(placeholder) (or if a longer placeholder is passed), cut becomes 0/negative and the returned string can exceed width (e.g., slicing with a negative index + placeholder). Consider guarding this case explicitly (e.g., return placeholder[:width] or raise) to preserve the function contract.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +23
"""Shorten text to at most width characters.

This always hard-truncates instead of
trying word-boundary breaking, so CJK text without spaces won't
collapse to just the placeholder.
"""
text = " ".join(text.split())
if len(text) <= width:
return text
cut = width - len(placeholder)
space = text.rfind(" ", 0, cut + 1)
if space > 0:
cut = space
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The docstring says this function “always hard-truncates instead of trying word-boundary breaking”, but the implementation still prefers truncating at the last space before cut (rfind(" ")). Either update the docstring to match the behavior, or remove the word-boundary logic if you want truly hard truncation.

Copilot uses AI. Check for mistakes.
Comment on lines +515 to +517
for s in all_sessions:
name = s.title.rsplit(f" ({s.id})", 1)[0] if s.title.endswith(f"({s.id})") else s.title
table.add_row(s.id, name, format_relative_time(s.updated_at))
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The suffix check and split use different strings: endswith(f"({s.id})") vs rsplit(f" ({s.id})", 1). Using the same suffix value for both (e.g., suffix = f" ({s.id})" then endswith(suffix)) avoids subtle mismatches if the title format ever changes.

Copilot uses AI. Check for mistakes.
Comment on lines +681 to +684
# s.title is "{content} ({full_id})" – strip the id suffix for display
suffix = f" ({s.id})"
name = s.title.rsplit(suffix, 1)[0] if s.title.endswith(f"({s.id})") else s.title
label = f"{name} ({short_id}), {time_str}"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

Same as the --list-sessions path: endswith(f"({s.id})") is less strict than the suffix = f" ({s.id})" used for rsplit. Reusing suffix in the endswith check makes the stripping logic consistent and easier to maintain.

Copilot uses AI. Check for mistakes.
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.

Add arguments for cli to select the history session

2 participants