Add support for ai-title and prefer it over legacy summary#136
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds AI-generated session title support: new AiTitleTranscriptEntry type, DB migration and persistence, pipeline handling (load, filter, DAG skip, dedup last-wins), cache extraction/loading, renderer/TUI preference for ai_title, and tests covering parsing, dedup, priority, persistence, and version-compatibility. ChangesAI-Generated Session Titles
Sequence DiagramsequenceDiagram
participant Parser
participant Converter
participant CacheManager
participant SQLiteDB
participant Renderer
participant TUI
Parser->>Converter: parse ai-title JSONL to AiTitleTranscriptEntry
Converter->>Converter: preserve ai-title through filtering
Converter->>Converter: deduplicate per session (last-wins)
Converter->>CacheManager: extract session_ai_titles and build SessionCacheData
CacheManager->>SQLiteDB: persist SessionCacheData including ai_title
SQLiteDB->>CacheManager: return rows with ai_title
CacheManager->>Renderer: provide SessionCacheData with ai_title
Renderer->>Renderer: merge ai_title into session summaries
Renderer->>TUI: pass session data with ai_title
TUI->>TUI: display ai_title in table and expanded view
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
test/test_ai_title.py (1)
32-154: ⚡ Quick winAdd one cache round-trip test for
ai_title.These tests cover parse/dedup/title priority, but the feature also depends on the cache schema and save/load path. A tiny
CacheManager.update_session_cache()→ reload assertion would protect the migration and would catch persistence bugs in this PR.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/test_ai_title.py` around lines 32 - 154, Add a small test that exercises the cache round-trip for ai_title: create or get a CacheManager, call CacheManager.update_session_cache(session_id="s1", data=SessionCacheData(..., ai_title="Saved AI title", ...)) (or the appropriate update API), then reload the cache (either by creating a new CacheManager or calling the cache load method) and assert the persisted SessionCacheData for "s1" contains ai_title == "Saved AI title" and that build_session_title("Project", "s1", reloaded_cache_entry) returns "Project: Saved AI title"; use SessionCacheData and build_session_title from the diff to construct/verify the entry so this will catch save/load schema regressions.claude_code_log/tui.py (1)
1746-1751: ⚡ Quick win
_escape_rich_markupduplicates the already-importedescape_markup; consolidate.
_escape_rich_markup(lines 1746–1751) does exactly whatrich.markup.escapedoes — both escape[and]. Sinceescape_markupis already imported at line 24 and used inpopulate_table, the hand-rolled method is redundant. The newai_titleblock in_update_expanded_content(and all pre-existing fields) could use the imported function instead.♻️ Proposed refactor
- def _escape_rich_markup(self, text: str) -> str: - """Escape Rich markup characters in text to prevent parsing errors.""" - if not text: - return text - # Escape square brackets which are used for Rich markup - return text.replace("[", "\\[").replace("]", "\\]") - def _update_expanded_content(self) -> None: ... # AI title (Claude Code's curated short title) - escape markup if session_data.ai_title: - escaped_title = self._escape_rich_markup(session_data.ai_title) + escaped_title = escape_markup(session_data.ai_title) content_parts.append(f"\n[bold]Title:[/bold] {escaped_title}") # Summary (if available) - escape markup if session_data.summary: - escaped_summary = self._escape_rich_markup(session_data.summary) + escaped_summary = escape_markup(session_data.summary) content_parts.append(f"\n[bold]Summary:[/bold] {escaped_summary}") # First user message - escape markup if session_data.first_user_message: - escaped_message = self._escape_rich_markup(session_data.first_user_message) + escaped_message = escape_markup(session_data.first_user_message) ... # Working directory (if available) - escape markup if session_data.cwd: - escaped_cwd = self._escape_rich_markup(session_data.cwd) + escaped_cwd = escape_markup(session_data.cwd) ...Also applies to: 1773-1776
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@claude_code_log/tui.py` around lines 1746 - 1751, Remove the duplicate escape helper and use the already-imported escape_markup from rich.markup: delete the _escape_rich_markup method and replace its usages (e.g., in populate_table and in _update_expanded_content where ai_title and other fields are escaped) with calls to escape_markup(text). Ensure import of escape_markup remains and update any references to _escape_rich_markup to call escape_markup so behavior is unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@claude_code_log/converter.py`:
- Around line 1081-1085: The pagination fallback in
_build_session_data_from_messages currently only fills ai_title via
session_ai_titles (from AiTitleTranscriptEntry) but does not reconstruct the
legacy summary, so paginated session titles skip the intended summary fallback;
update the pagination path (the same block that builds SessionCacheData) to
extract the legacy summary the same way
_update_cache_with_session_data()/_collect_project_sessions() do and pass that
summary into SessionCacheData so the title resolution order remains ai_title >
summary > preview > id (also apply the same fix to the analogous block around
lines 1163-1167).
- Around line 1751-1755: The ai_title write path is populating
SessionCacheData.ai_title and persisting immediately, but the INSERT in cache.py
(around the function handling cache refresh / session upserts) lists ai_title
twice and has fewer placeholders than bound values causing an exception; fix it
by editing the INSERT statement to include ai_title only once and adjust the
placeholders to match the exact number and order of bound SessionCacheData
fields, ensure the bound parameter list/order matches the INSERT column order,
and verify the converter.py logic that builds session_ai_titles (the loop
creating session_ai_titles and where SessionCacheData.ai_title is set) uses the
same field name so persistence and binding align.
---
Nitpick comments:
In `@claude_code_log/tui.py`:
- Around line 1746-1751: Remove the duplicate escape helper and use the
already-imported escape_markup from rich.markup: delete the _escape_rich_markup
method and replace its usages (e.g., in populate_table and in
_update_expanded_content where ai_title and other fields are escaped) with calls
to escape_markup(text). Ensure import of escape_markup remains and update any
references to _escape_rich_markup to call escape_markup so behavior is
unchanged.
In `@test/test_ai_title.py`:
- Around line 32-154: Add a small test that exercises the cache round-trip for
ai_title: create or get a CacheManager, call
CacheManager.update_session_cache(session_id="s1", data=SessionCacheData(...,
ai_title="Saved AI title", ...)) (or the appropriate update API), then reload
the cache (either by creating a new CacheManager or calling the cache load
method) and assert the persisted SessionCacheData for "s1" contains ai_title ==
"Saved AI title" and that build_session_title("Project", "s1",
reloaded_cache_entry) returns "Project: Saved AI title"; use SessionCacheData
and build_session_title from the diff to construct/verify the entry so this will
catch save/load schema regressions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5e16de3f-aa38-43cf-aa6a-1284e4e6fd8c
📒 Files selected for processing (12)
claude_code_log/cache.pyclaude_code_log/converter.pyclaude_code_log/dag.pyclaude_code_log/factories/transcript_factory.pyclaude_code_log/migrations/006_session_ai_title.sqlclaude_code_log/models.pyclaude_code_log/renderer.pyclaude_code_log/tui.pytest/test_ai_title.pytest/test_cache.pytest/test_dag_silent.pytest/test_html_regeneration.py
2d0d031 to
0aa9bdd
Compare
|
Rebased this and fixed the conflicts, I think it's good to merge. I've also been thinking we should cut a new release as there's been so many PRs getting merged lately! |
Sorry for the churn ;-)
Well, as I've started with "Monitor", I wanted to try to be complete with the other Speaking about this, have you tried Claude Code within Claude Desktop yet? Are the transcripts there compatible with the "regular" ones? There may be differences; we had a few with the Claude Code extension for Visual Code (it's been a while since the last time I've used it). And then, there's CoWork, same question... |
# Conflicts: # claude_code_log/cache.py # claude_code_log/dag.py
1265bb6 to
aaeab7a
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
claude_code_log/renderer.py (1)
946-959: ⚡ Quick winIgnore blank
ai-titlevalues before overriding summaries.If
aiTitleis empty/whitespace, this can replace a usable legacy summary with an empty value and degrade session labels to UUID fallback. Filter out blank titles here.Proposed patch
def prepare_session_ai_titles(messages: list[TranscriptEntry]) -> dict[str, str]: @@ out: dict[str, str] = {} for message in messages: if isinstance(message, AiTitleTranscriptEntry): - out[message.sessionId] = message.aiTitle + title = message.aiTitle.strip() + if title: + out[message.sessionId] = title return out🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@claude_code_log/renderer.py` around lines 946 - 959, The prepare_session_ai_titles function currently overwrites existing titles with blank/whitespace aiTitle values; change the loop that handles AiTitleTranscriptEntry so it only assigns out[message.sessionId] = message.aiTitle when message.aiTitle.strip() is non-empty (i.e., ignore blank/whitespace titles) so legacy summaries aren't replaced by empty strings; reference the prepare_session_ai_titles function and the AiTitleTranscriptEntry.message.aiTitle/sessionId fields when applying the check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@claude_code_log/renderer.py`:
- Around line 946-959: The prepare_session_ai_titles function currently
overwrites existing titles with blank/whitespace aiTitle values; change the loop
that handles AiTitleTranscriptEntry so it only assigns out[message.sessionId] =
message.aiTitle when message.aiTitle.strip() is non-empty (i.e., ignore
blank/whitespace titles) so legacy summaries aren't replaced by empty strings;
reference the prepare_session_ai_titles function and the
AiTitleTranscriptEntry.message.aiTitle/sessionId fields when applying the check.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1e6f8a97-e4fc-4951-a68b-f512c67f7f44
📒 Files selected for processing (11)
claude_code_log/cache.pyclaude_code_log/converter.pyclaude_code_log/dag.pyclaude_code_log/factories/transcript_factory.pyclaude_code_log/migrations/006_session_ai_title.sqlclaude_code_log/models.pyclaude_code_log/renderer.pyclaude_code_log/tui.pytest/test_ai_title.pytest/test_cache.pytest/test_html_regeneration.py
✅ Files skipped from review due to trivial changes (1)
- test/test_ai_title.py
🚧 Files skipped from review as they are similar to previous changes (9)
- claude_code_log/factories/transcript_factory.py
- claude_code_log/migrations/006_session_ai_title.sql
- test/test_cache.py
- test/test_html_regeneration.py
- claude_code_log/dag.py
- claude_code_log/cache.py
- claude_code_log/tui.py
- claude_code_log/models.py
- claude_code_log/converter.py
This needed a little migration, so also bumping required cache version.
To test locally, manually edit version in pyproject.yaml to 1.3.0 and generate with "just cli"
Summary by CodeRabbit
New Features
Chores
Tests