Repository navigation
Fix: resolve real project paths for paths containing _, - or . (#465) - #487
Merged
Merged
Conversation
Newer Claude Code session files start with entries (last-prompt, permission-mode) that carry no cwd. get_project_path_from_sessions only read the first 10 lines of each file, so it often gave up and fell back to the lossy folder-name decoder, producing paths like /home/michal/mikes/projects and "Failed to send prompt" (winfunc#465). - Stream up to 200 lines per file and try up to 5 files per project - Skip lines over 1 MiB and non-UTF8 lines without buffering them - Pre-filter on the "cwd" key to avoid JSON-parsing irrelevant lines - Add regression tests and an ignored baseline benchmark
Claude Code names project folders by replacing every non-alphanumeric character (including _, -, ., \ and :) with '-', per UTF-16 code unit. The existing call sites only replaced '/', so they could not locate folders for paths containing '_' or '.' (winfunc#465). Add a single shared, allocation-light helper that mirrors the real rule; call sites are switched over in follow-up commits.
decode_project_path turned every '-' into '/', so folder names for paths containing '_', '-' or '.' decoded to non-existent directories such as /home/michal/mikes/projects (winfunc#465). Resolve the name by walking down from the root and keeping only child directories whose encoded name matches the front of the remaining text. This needs one directory listing per path level (no exponential search), allocates nothing per entry, backtracks out of dead ends and picks deterministically (name order) when layouts encode identically. The old '-' -> '/' guess remains only as a last resort. The filesystem is behind a small PathProbe trait so Windows-style layouts can be tested on any host.
Both commands fell back to the lossy '-' -> '/' decoder whenever the session
files had no cwd, handing a non-existent directory to the spawned claude
process ("Failed to send prompt", winfunc#465).
Add resolve_project_path: session cwd first, then the folder name resolved
against the real filesystem, then a best-effort guess. Results from the first
two are cached per project folder and invalidated by the folder's mtime, so
repeated listings skip all file reads (200 projects: ~8 ms cold, ~0.4 ms
cached). The blocking filesystem work now runs in spawn_blocking so it no
longer stalls the async runtime.
…puted
create_project, read_session_jsonl and the session file monitor encoded the
project path with replace('/', "-"), which leaves '_' and '.' untouched, so
they looked for (or created) folders that Claude Code never created for such
paths (winfunc#465). Use the shared encode_project_path helper, and add
session_jsonl_path so the two agents.rs lookups share one tested
implementation.
slash_commands.rs builds an internal command ID, not a project folder name,
so it is intentionally unchanged.
When the project directory does not exist, spawning claude failed with an opaque OS error and the UI only showed "Failed to send prompt", which made winfunc#465 hard to diagnose. - Validate the working directory before spawning and return "Project directory not found: <path>" (or "Project path is empty") - Show the backend's message in the chat error instead of discarding it
Chat tabs restored their project path from localStorage, so a path saved by an older build (e.g. /home/michal/mikes/p instead of /home/michal_mikes/p) kept breaking prompts across restarts (winfunc#465) until the user edited localStorage by hand. On restore, look the path up by project id in the backend's current project list (one listing call, only when a chat tab needs restoring), use it, and repair the saved record. Falls back to the saved path if the project is unknown or the listing fails.
Claude Code names a Windows project folder by replacing every non-alphanumeric character, so C:\Users\john_doe\app becomes C--Users-john-doe-app. The resolver only understood Unix names rooted at '/', so on Windows the same lossy '-' -> '/' guess broke prompts (winfunc#465). - Recognise a leading '<drive>--' as the root '<drive>:\' and walk down with '\' separators; UNC paths are unsupported and fall back to the guess - Make PathProbe string-based so Windows layouts are unit-tested on any host - Keep the last-resort guess platform-shaped (C:\a\b instead of C://a/b) The encoder already maps '\' and ':' to '-', so no change was needed there.
Name the project path cache type and use iter::repeat_n in a test. No behaviour change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Title: Fix: resolve real project paths for paths containing _, - or . (#465)
Problem
Claude Code names project folders under
~/.claude/projectsby replacing every non-alphanumeric character with-, so/home/michal_mikes/my-appand/home/michal/mikes/my/appproduce the same folder name. opcode reversed this withreplace('-', "/")and handed the resulting, non-existent directory to theclaudeprocess, so every prompt failed with a generic "Failed to send prompt" (#465, also reported for hyphenated and dot-folder paths in the issue comments).Three defects combined:
get_project_path_from_sessionsread only the first 10 lines of each session file; newer session files start with entries withoutcwd, so opcode fell back to the lossy decoder.memory/).replace('/', "-"), which does not match Claude Code for_/.; wrong paths were also saved in localStorage and reused after restart.Approach
get_project_path_from_sessions): stream up to 200 lines per file and up to 5 files per project until a non-emptycwdis found; lines over 1 MiB and non-UTF-8 lines are skipped without buffering; a cheap"cwd"pre-filter avoids JSON-parsing irrelevant lines.C--Users-...); UNC paths fall back to the previous best-effort guess.encode_project_pathhelper mirroring Claude Code's naming, used bycreate_projectand the two session-file lookups inagents.rs(viasession_jsonl_path).resolve_project_path: session cwd → filesystem match → guess, with a per-project cache invalidated by the folder's mtime.list_projects/get_project_sessionsrun their blocking filesystem work inspawn_blocking.list_projectsby project id and repair the saved record.Failed to send prompt: Project directory not found: <path>.Relation to #471
#471 also lifts the 10-line limit in
get_project_path_from_sessions(thanks @chaochaoweb3). This PR covers that part with a bounded scan (so a session file without anycwdis never read to the end) and additionally fixes the cases #471 cannot reach: projects with no session files at all, the encoders, persisted bad paths, the error message and Windows names. Happy to rebase onto #471 if it is merged first.Performance
Release-mode benchmark (included as an
#[ignore]d test, run withcargo test --release -- --ignored --nocapture bench_):Testing
cargo test: 32 tests pass (23 new): underscore / hyphen / dot-folder / mixed paths, cwd after many lines, bounded scan, oversized and invalid lines, ambiguity, backtracking, Windows layouts (in-memory filesystem), caching, encoder rules, session-file paths, directory validation.cargo fmt --checkclean; no new clippy warnings beyonddead_codein theopcode-webbinary (same as existing command helpers).main(same data, every field returned bylist_projects,get_project_sessions,read_session_jsonl,create_projectcompared): all cases that worked before are identical; only the broken cases changed (e.g.…/new/project→…/new_project,…//hidden/dot/app→…/.hidden/dot-app).~/Desktop/Office-Personal/Mob-Auto(folder contains onlymemory/, no session files) was listed as/Users/…/Office/Personal/Mob/Autoonmainand failed to start; with this PR it resolves correctly and a prompt runs.Documentation
All new functions have
///docs. No README change needed (no user-facing configuration changed). No new dependencies.Limitations / side effects
-home-a_b) are left in place and still show a best-effort path; nothing is deleted.require()); not changed here.src/lib/analytics/index.tshas an existingtscerror unrelated to this change.Fixes #465