Point transcript lookup at the project directory Claude Code actually uses

Claude Code's project slug is not "slashes become dashes". Every
non-alphanumeric character becomes one dash, nothing collapsed - verified
against 2.1.269 by running a session in a directory named `slug._test x`,
which was filed as `-tmp-cmtest-slug--test-x`: the dot, the underscore and
the space each became a dash of their own. Paths with dots are ordinary on
macOS (iCloud Drive lives under `Mobile Documents`), and the old slash-only
rule pointed at a directory that does not exist, so repair-session could
not find a single transcript there.

It is also the physical directory that gets slugged. Claude Code asks the
OS for its working directory, and symlinks come back resolved: a session
started in /tmp/x is filed under -private-tmp-x on macOS, where /tmp is a
symlink, while the shell's $PWD still reads /tmp/x. Verified the same way -
a session started in /tmp/cmtest/linkdir landed under -tmp-cmtest-realdir.

cm_project_dir now applies the real rule and falls back to the resolved
path, which also covers the walk-up search in repair-session. Both cases
were re-tested end to end afterwards: from the dotted-and-spaced directory
and from the symlinked one, the transcripts are found.

The bar widget still reverses a slug for display ("-home-smoido-Work" ->
"home/smoido/Work"), which is lossy in the same way but is only a label,
and only ever sees Linux paths.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
smoido
2026-09-12 22:34:44 +03:00
co-authored by Claude Code
parent 5a1af9f7e6
commit d86a2ffe77
2 changed files with 28 additions and 3 deletions
+1 -1
View File
@@ -1 +1 @@
1.9.1 1.9.2
+27 -2
View File
@@ -2212,9 +2212,34 @@ cm_apply_session_action() {
# Transcript repair # Transcript repair
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# ~/.claude/projects/<cwd with every slash turned into a dash> # Claude Code files transcripts under ~/.claude/projects/<slug>, and the slug
# is not "slashes to dashes": *every* non-alphanumeric character becomes one
# dash, nothing collapsed. Verified against 2.1.269 - a directory named
# `slug._test x` was filed as `-tmp-cmtest-slug--test-x`, so the dot, the
# underscore and the space each became a dash of their own. Paths with a dot in
# them are ordinary on macOS (iCloud Drive sits under `Mobile Documents`), and
# a slash-only rule points at a directory that does not exist.
cm_project_slug() {
printf '%s' "$1" | sed 's|[^a-zA-Z0-9]|-|g'
}
# It is also the *physical* directory that gets slugged. Claude Code asks the OS
# for its working directory and symlinks come back resolved, so a session
# started in /tmp/x is filed under -private-tmp-x on macOS (where /tmp is a
# symlink) while the shell's $PWD still reads /tmp/x and looks in -tmp-x.
# The logical path is tried first, since that is what a user types and what a
# plain project looks like; the resolved one is the fallback.
cm_project_dir() { cm_project_dir() {
printf '%s/projects/%s' "$CM_SETTINGS_DIR" "$(printf '%s' "${1:-$PWD}" | sed 's|/|-|g')" local path="${1:-$PWD}" slug phys
slug="$(cm_project_slug "$path")"
if [ -d "$CM_SETTINGS_DIR/projects/$slug" ]; then
printf '%s/projects/%s' "$CM_SETTINGS_DIR" "$slug"; return 0
fi
phys="$(cd "$path" 2>/dev/null && pwd -P)" || phys=''
if [ -n "$phys" ] && [ "$phys" != "$path" ]; then
slug="$(cm_project_slug "$phys")"
fi
printf '%s/projects/%s' "$CM_SETTINGS_DIR" "$slug"
} }
# Modification time as an epoch second. GNU stat spells it -c %Y, BSD/macOS # Modification time as an epoch second. GNU stat spells it -c %Y, BSD/macOS