Repository navigation
Clean up Claude session titles - #3211
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughRefactors Claude session discovery and title handling and adds localized label for local commands. Session enumeration now scans multiple Claude config roots, applies per-candidate ripgrep prefiltering, and builds resume CLI invocations that inject a per-session CLAUDE_CONFIG_DIR. Tests and a localization string were added. Changes
Sequence Diagram(s)sequenceDiagram
participant Indexer as SessionIndexStore
participant FS as FileSystem
participant RG as Ripgrep
participant Parser as TitleParser
participant Shell as CLI Builder
participant L10n as Localizable
Indexer->>FS: enumerate roots (env, ~/.codex-accounts/claude/*, ~/.claude)
FS-->>Indexer: list of project candidate dirs
Indexer->>RG: prefilter candidates (cwdFilter / ripgrep)
RG-->>Indexer: prefilter result per-candidate
Indexer->>Parser: extract claude metadata & isMeta flags
Parser-->>Indexer: parsed title components (command-name/message/args)
Indexer->>L10n: request "Local command" translation for local-command envelopes
L10n-->>Indexer: localized label
Indexer->>Shell: build resume command with env CLAUDE_CONFIG_DIR (shell-quoted)
Shell-->>Indexer: resume command string
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 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)
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 |
Greptile SummaryThis PR filters out Claude's synthetic markup envelopes ( Confidence Score: 5/5Safe to merge — logic is correct, regression tests follow the two-commit policy, and all new user-facing strings are properly localized. No P0 or P1 issues found. The title-parsing helpers handle all edge cases correctly (empty strings, synthetic envelopes, nested tag absence, isMeta scoped per JSONL line). The "Local command" path in displayTitle is intentionally a backward-compat escape hatch for cached sessions (per the PR description), not a bug. Localization is complete for both supported languages. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[displayTitle called] --> B{agent == .claude?}
B -- No --> Z[generic empty/trimmed fallback]
B -- Yes --> C[claudeDisplayTitle from trimmed]
C --> D{Returns non-nil?}
D -- Yes --> E[Return cleaned title\ne.g. /plugin install args]
D -- No --> F{isClaudeLocalCommandEnvelope?}
F -- Yes --> G[Return 'Local command'\n sessionIndex.localCommand]
F -- No --> H{isClaudeSyntheticEnvelope?\ne.g. system-reminder}
H -- Yes --> I[Return 'Untitled session'\n sessionIndex.untitled]
H -- No --> Z
Z --> J{trimmed.isEmpty?}
J -- Yes --> I
J -- No --> K[Return trimmed]
subgraph claudeDisplayTitle
L[Trim whitespace] --> M{Empty?}
M -- Yes --> N[return nil]
M -- No --> O[claudeSlashCommandTitle?]
O -- Found --> P[Return /cmd args]
O -- Not found --> Q{isMeta or SyntheticEnvelope?}
Q -- Yes --> N
Q -- No --> R[Return trimmed text]
end
Reviews (1): Last reviewed commit: "Clean up Claude session titles" | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmuxTests/SessionIndexViewTests.swift`:
- Around line 14-20: The test testClaudeLocalCommandCaveatDisplaysReadableTitle
hardcodes the English string "Local command" while entry.displayTitle is
produced from String(localized:), making the assertion locale-sensitive; update
the assertion to compare entry.displayTitle against the same localization lookup
used by the production code (e.g., use String(localized: <the displayTitle's
localization key> or the same NSLocalizedString key/initializer the displayTitle
implementation uses) rather than the English literal, ensuring the test remains
correct across locales and referencing displayTitle and makeEntry to locate the
relevant code.
In `@Sources/SessionIndexStore.swift`:
- Around line 130-137: The function claudeDisplayTitle(from:isMeta:) checks for
slash-command titles before honoring the isMeta flag, so move the isMeta (and
isClaudeSyntheticEnvelope) check to occur before calling
claudeSlashCommandTitle; specifically, in claudeDisplayTitle ensure you
immediately return nil when isMeta is true (or when
isClaudeSyntheticEnvelope(trimmed) is true) and only then attempt to extract a
claudeSlashCommandTitle from trimmed text.
🪄 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: 770b125d-39c1-4227-91da-3ff4df9fd1d3
📒 Files selected for processing (3)
Resources/Localizable.xcstringsSources/SessionIndexStore.swiftcmuxTests/SessionIndexViewTests.swift
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4a1bce5e60
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
Sources/SessionIndexStore.swift (1)
159-166:⚠️ Potential issue | 🟠 MajorHonor
isMeta/synthetic guards before slash-command parsing.Line 162 still returns
claudeSlashCommandTitle(...)before Line 165 checksisMetaand synthetic envelopes, so meta<local-command-*>/<system-reminder>payloads can still become sidebar titles instead of being skipped.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/SessionIndexStore.swift` around lines 159 - 166, In claudeDisplayTitle(from:isMeta:), the function calls claudeSlashCommandTitle(from:) before checking isMeta or isClaudeSyntheticEnvelope, allowing meta/synthetic payloads to be treated as slash-command titles; reorder the logic so the guard for isMeta || isClaudeSyntheticEnvelope(trimmed) runs before attempting claudeSlashCommandTitle, returning nil for meta/synthetic envelopes and only calling claudeSlashCommandTitle when those guards pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/SessionIndexStore.swift`:
- Around line 1308-1372: loadClaudeEntries currently skips an entire root when
ripgrepMatchingPaths returns nil for that root, causing incomplete results for
non-empty needles; modify the needle branch so that for each root, if
ripgrepMatchingPaths returns nil you fall back to the same directory enumeration
used in the else branch (enumerate root.projectsRoot project dirs or the
specific cwd-encoded dir and append *.jsonl files to candidates), and only set
rgFiltered = true when ripgrepMatchingPaths actually returns results; reference
loadClaudeEntries, ripgrepMatchingPaths, candidates and rgFiltered when making
this change.
---
Duplicate comments:
In `@Sources/SessionIndexStore.swift`:
- Around line 159-166: In claudeDisplayTitle(from:isMeta:), the function calls
claudeSlashCommandTitle(from:) before checking isMeta or
isClaudeSyntheticEnvelope, allowing meta/synthetic payloads to be treated as
slash-command titles; reorder the logic so the guard for isMeta ||
isClaudeSyntheticEnvelope(trimmed) runs before attempting
claudeSlashCommandTitle, returning nil for meta/synthetic envelopes and only
calling claudeSlashCommandTitle when those guards pass.
🪄 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: 6b867149-db0f-4ea9-9072-a425bbdf57c8
📒 Files selected for processing (2)
Sources/SessionIndexStore.swiftcmuxTests/SessionIndexViewTests.swift
🚧 Files skipped from review as they are similar to previous changes (1)
- cmuxTests/SessionIndexViewTests.swift
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
Sources/SessionIndexStore.swift (2)
159-166:⚠️ Potential issue | 🟠 MajorReject meta and synthetic envelopes before slash-command parsing.
claudeSlashCommandTitle(...)still runs before theisMeta/ synthetic-envelope check, so meta user records and<local-command-*>/<system-reminder>envelopes can still win the session title and bypass the localized fallback.Suggested fix
static func claudeDisplayTitle(from raw: String, isMeta: Bool = false) -> String? { let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return nil } - if let commandTitle = claudeSlashCommandTitle(from: trimmed) { - return commandTitle - } if isMeta || isClaudeSyntheticEnvelope(trimmed) { return nil } + if let commandTitle = claudeSlashCommandTitle(from: trimmed) { + return commandTitle + } return trimmed }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/SessionIndexStore.swift` around lines 159 - 166, In claudeDisplayTitle(from:isMeta:), reject meta or synthetic envelopes before attempting slash-command parsing: first trim and guard non-empty, then check if isMeta || isClaudeSyntheticEnvelope(trimmed) and return nil if true, and only after that call claudeSlashCommandTitle(from:) so meta user records and <local-command-*> / <system-reminder> envelopes cannot win the session title.
1320-1336:⚠️ Potential issue | 🟠 MajorKeep the non-
rgfallback on a per-root basis.When
ripgrepMatchingPaths(...)returnsnilfor one Claude root, this branch justcontinues and drops that root entirely. That means non-empty Claude searches can be partial, and if every root hitsnilthey return no results instead of falling back to the directory/head/tail scan.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/SessionIndexStore.swift` around lines 1320 - 1336, The loop currently drops an entire Claude root when ripgrepMatchingPaths(...) returns nil (the guard uses continue), causing the non-rg fallback to be skipped for that root; instead, when ripgrepMatchingPaths returns nil for a given root you should not continue but invoke the non-rg fallback scan for that root (the same directory/head/tail scan that populates candidates when ripgrep is unavailable), leaving rgFiltered unset for that root; update the for root in roots loop to branch: if ripgrepMatchingPaths(...) yields paths use them to append candidates (setting rgFiltered = true), otherwise run the existing fallback logic that inspects files, uses fm.attributesOfItem(atPath:), extracts mtime and dirName via claudeProjectDirName(for:projectsRoot:) and appends to candidates so each root is independently handled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/SessionIndexStore.swift`:
- Around line 105-114: The code in claudeConfigDirectoryForResume uses
fileURL.pathComponents.firstIndex(of: "projects") which picks the first
"projects" component and breaks custom Claude roots; change this to find the
last occurrence (use fileURL.pathComponents.lastIndex(of: "projects") or
lastIndex(where:) ) so projectsIndex refers to the final "projects" segment,
then reconstruct configComponents and configDir the same way and keep the
existing empty-check logic.
- Around line 171-179: The claudeSlashCommandTitle function currently selects
either "command-name" or "command-message" and drops the other; change it to
read both via claudeTagValue("command-name", in: raw) and
claudeTagValue("command-message", in: raw) and concatenate them in the intended
order (command-name followed by command-message) when both exist, and then
append command-args if present; update claudeSlashCommandTitle to return the
joined components (skipping any nil/empty pieces) so a payload with all three
tags renders "command-name command-message command-args".
---
Duplicate comments:
In `@Sources/SessionIndexStore.swift`:
- Around line 159-166: In claudeDisplayTitle(from:isMeta:), reject meta or
synthetic envelopes before attempting slash-command parsing: first trim and
guard non-empty, then check if isMeta || isClaudeSyntheticEnvelope(trimmed) and
return nil if true, and only after that call claudeSlashCommandTitle(from:) so
meta user records and <local-command-*> / <system-reminder> envelopes cannot win
the session title.
- Around line 1320-1336: The loop currently drops an entire Claude root when
ripgrepMatchingPaths(...) returns nil (the guard uses continue), causing the
non-rg fallback to be skipped for that root; instead, when ripgrepMatchingPaths
returns nil for a given root you should not continue but invoke the non-rg
fallback scan for that root (the same directory/head/tail scan that populates
candidates when ripgrep is unavailable), leaving rgFiltered unset for that root;
update the for root in roots loop to branch: if ripgrepMatchingPaths(...) yields
paths use them to append candidates (setting rgFiltered = true), otherwise run
the existing fallback logic that inspects files, uses
fm.attributesOfItem(atPath:), extracts mtime and dirName via
claudeProjectDirName(for:projectsRoot:) and appends to candidates so each root
is independently handled.
🪄 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: 7128b8be-4192-4004-921f-a99a806a169b
📒 Files selected for processing (1)
Sources/SessionIndexStore.swift
Summary
Testing
jq empty Resources/Localizable.xcstrings./scripts/reload.sh --tag cctitleIssues
<local-command-caveat>titles in the Sessions sidebar.Summary by cubic
Cleaned up Claude session titles in the Sessions sidebar and made resume commands run in the correct config root. Session discovery now scans multiple Claude roots.
Bug Fixes
<local-command-*>,<system-reminder>) and show localized “Local command” (en/ja); adds regression tests.env CLAUDE_CONFIG_DIR=<root>derived from the session file path so actions run in the right config; adds tests.New Features
<command-name>, distinct<command-message>, and<command-args>(e.g., "/plugin install codex@openai-codex").CLAUDE_CONFIG_DIR, validated~/.codex-accounts/claude/*, and default~/.claude.Written for commit 2d959d3. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
Improvements
Localization
Tests