diff --git a/VERSION b/VERSION index 9ab8337..8fdcf38 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.1 +1.9.2 diff --git a/linux/claude-mode b/linux/claude-mode index 902c2fe..ab56cad 100755 --- a/linux/claude-mode +++ b/linux/claude-mode @@ -2212,9 +2212,34 @@ cm_apply_session_action() { # Transcript repair # --------------------------------------------------------------------------- -# ~/.claude/projects/ +# Claude Code files transcripts under ~/.claude/projects/, 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() { - 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