Skip to content

Commit d158375

Browse files
committed
fix(hooks): derive project wing from non-macOS transcript paths (#1145)
_wing_from_transcript_path only matched '-Projects-<name>' segments, so Linux users with code under ~/dev/, ~/code/, or ~/src/ fell through to the wing_sessions fallback and lost the per-project diary scoping introduced in #659. Broaden the heuristic to derive the project from the final dash-separated token of the encoded project-folder name under .claude/projects/. Keeps the legacy -Projects- regex as a secondary match for transcripts living outside the standard Claude Code path. Covers macOS Users layout, Linux dev/code layouts, and deeper nested source paths while preserving existing Projects/ behavior.
1 parent 6d252a0 commit d158375

2 files changed

Lines changed: 37 additions & 4 deletions

File tree

mempalace/hooks_cli.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,29 @@ def _parse_harness_input(data: dict, harness: str) -> dict:
490490
def _wing_from_transcript_path(transcript_path: str) -> str:
491491
"""Derive a project wing name from a Claude Code transcript path.
492492
493-
Claude Code stores transcripts at:
493+
Claude Code encodes the project's source directory by replacing path
494+
separators with dashes, producing folders like:
494495
~/.claude/projects/-home-<user>-Projects-<project>/session.jsonl
495-
We extract <project> and return ``wing_<project>`` to match the
496-
AAAK_SPEC convention (``wing_user``, ``wing_agent``, ``wing_code``,
497-
``wing_<project>``…). Falls back to ``wing_sessions``.
496+
~/.claude/projects/-home-<user>-dev-<parent>-<project>/session.jsonl
497+
~/.claude/projects/-Users-<user>-<folder>-<project>/session.jsonl
498+
499+
The project directory name is the final dash-separated token of the
500+
encoded folder. Returns ``wing_<project>`` (lowercased, spaces → ``_``).
501+
Falls back to ``wing_sessions`` if the path does not match a Claude Code
502+
project-folder layout.
498503
"""
499504
# Normalize path separators for cross-platform (Windows backslashes)
500505
normalized = transcript_path.replace("\\", "/")
506+
# Primary: pull the encoded project folder out of ``.claude/projects/``
507+
# and take its last dash-separated token.
508+
match = re.search(r"/\.claude/projects/-([^/]+)", normalized)
509+
if match:
510+
encoded = match.group(1)
511+
project = encoded.rsplit("-", 1)[-1]
512+
if project:
513+
return f"wing_{project.lower().replace(' ', '_')}"
514+
# Legacy fallback: explicit ``-Projects-<name>`` segment, useful for
515+
# transcripts not under the standard Claude Code projects dir.
501516
match = re.search(r"-Projects-([^/]+?)(?:/|$)", normalized)
502517
if match:
503518
project = match.group(1).lower().replace(" ", "_")

tests/test_hooks_cli.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,24 @@ def test_wing_from_transcript_path_lowercases():
324324
assert _wing_from_transcript_path(path) == "wing_myproject"
325325

326326

327+
def test_wing_from_transcript_path_non_projects_layout():
328+
# Linux users with code under ~/dev/, ~/src/, ~/code/ — no -Projects- segment.
329+
# Project name is the final dash-separated token of the encoded folder.
330+
path = "/home/igor/.claude/projects/-home-igor-dev-MemPalace-mempalace/session.jsonl"
331+
assert _wing_from_transcript_path(path) == "wing_mempalace"
332+
333+
334+
def test_wing_from_transcript_path_macos_users_layout():
335+
# macOS ~/ layout without a Projects/ segment.
336+
path = "/Users/alice/.claude/projects/-Users-alice-code-MyApp/session.jsonl"
337+
assert _wing_from_transcript_path(path) == "wing_myapp"
338+
339+
340+
def test_wing_from_transcript_path_nested_deep():
341+
path = "/home/bob/.claude/projects/-home-bob-work-clients-acme-frontend/session.jsonl"
342+
assert _wing_from_transcript_path(path) == "wing_frontend"
343+
344+
327345
# --- _log ---
328346

329347

0 commit comments

Comments
 (0)