Bug Description
resolveEncodedProjectPath in scanner.mjs fails to resolve many project directories, causing them to silently disappear from the sidebar. On a machine with 45 project directories in ~/.claude/projects/, only 9 were displayed.
Root Cause
Claude Code's path encoding replaces both / (path separators) and _ (underscores) with - — but inconsistently across versions. Some older project dirs preserve underscores, newer ones replace them with hyphens. This makes the encoding lossy and ambiguous.
The current greedy resolver (scanner.mjs:161) splits the encoded name on - and tries to reconstruct the path by joining segments with hyphens and checking exists(). This fails in two ways:
1. Underscore directories never match
A directory named My_Projects gets encoded as My-Projects. The resolver tries My-Projects against the filesystem — but the actual directory is My_Projects. No match, resolution fails.
2. Greedy matching dead-ends without backtracking
For a path like DriveRoot/Parent_Dir/my-repo, encoded as X--DriveRoot-Parent-Dir-my-repo:
- Segments:
["DriveRoot", "Parent", "Dir", "my", "repo"]
- Resolver matches
DriveRoot/ at level 1 ✓
- At level 2, tries
Parent-Dir-my-repo, Parent-Dir-my, Parent-Dir — none exist (actual name is Parent_Dir)
- Falls back to single segment
Parent — doesn't exist either
- Path reconstruction fails,
exists() returns false, project is hidden
Suggested Fix
Replace the greedy algorithm with one that:
- Lists actual directory entries at each level instead of guessing paths
- Normalizes both candidate and directory names (replace
_ with -, case-insensitive) before comparing
- Uses DFS with backtracking so ambiguous splits don't dead-end
Here's a working replacement for resolveEncodedProjectPath:
async function resolveEncodedProjectPath(encoded) {
const segments = encoded.replace(/^-/, "").split("-");
let rootPath = "/";
let startIdx = 0;
if (platform() === "win32" && segments.length >= 2 && segments[0].length === 1 && segments[1] === "") {
rootPath = segments[0].toUpperCase() + ":\\";
startIdx = 2;
}
// Normalize for comparison: lowercase, replace _ with -
const norm = (s) => s.toLowerCase().replace(/_/g, "-");
// DFS resolver with backtracking
async function resolve(currentPath, i) {
if (i >= segments.length) {
return (await exists(currentPath)) ? currentPath : null;
}
let entries;
try {
entries = await readdir(currentPath, { withFileTypes: true });
entries = entries.filter(e => e.isDirectory());
} catch {
return null;
}
// Map normalized names → actual names
const entryMap = new Map();
for (const e of entries) {
const key = norm(e.name);
if (!entryMap.has(key)) entryMap.set(key, []);
entryMap.get(key).push(e.name);
}
// Try longest match first, backtrack on failure
for (let end = segments.length; end > i; end--) {
const candidate = norm(segments.slice(i, end).join("-"));
const matches = entryMap.get(candidate);
if (matches) {
for (const actualName of matches) {
const nextPath = join(currentPath, actualName);
const result = await resolve(nextPath, end);
if (result) return result;
}
}
}
return null;
}
return resolve(rootPath, startIdx);
}
This resolves all 45 project directories correctly on the affected machine. The readdir calls add minimal overhead since project paths are typically only 3-5 levels deep.
Environment
- OS: Windows 11
- CCO version: 0.10.3
- Node: v20.19.0
Reproduction
Any Windows user whose repositories live in directories containing underscores (e.g., My_Projects, CORE_REPOS) or hyphens in folder names will see those projects silently missing from the sidebar. The issue also affects Linux/macOS for the same encoding ambiguity.
Bug Description
resolveEncodedProjectPathinscanner.mjsfails to resolve many project directories, causing them to silently disappear from the sidebar. On a machine with 45 project directories in~/.claude/projects/, only 9 were displayed.Root Cause
Claude Code's path encoding replaces both
/(path separators) and_(underscores) with-— but inconsistently across versions. Some older project dirs preserve underscores, newer ones replace them with hyphens. This makes the encoding lossy and ambiguous.The current greedy resolver (
scanner.mjs:161) splits the encoded name on-and tries to reconstruct the path by joining segments with hyphens and checkingexists(). This fails in two ways:1. Underscore directories never match
A directory named
My_Projectsgets encoded asMy-Projects. The resolver triesMy-Projectsagainst the filesystem — but the actual directory isMy_Projects. No match, resolution fails.2. Greedy matching dead-ends without backtracking
For a path like
DriveRoot/Parent_Dir/my-repo, encoded asX--DriveRoot-Parent-Dir-my-repo:["DriveRoot", "Parent", "Dir", "my", "repo"]DriveRoot/at level 1 ✓Parent-Dir-my-repo,Parent-Dir-my,Parent-Dir— none exist (actual name isParent_Dir)Parent— doesn't exist eitherexists()returns false, project is hiddenSuggested Fix
Replace the greedy algorithm with one that:
_with-, case-insensitive) before comparingHere's a working replacement for
resolveEncodedProjectPath:This resolves all 45 project directories correctly on the affected machine. The
readdircalls add minimal overhead since project paths are typically only 3-5 levels deep.Environment
Reproduction
Any Windows user whose repositories live in directories containing underscores (e.g.,
My_Projects,CORE_REPOS) or hyphens in folder names will see those projects silently missing from the sidebar. The issue also affects Linux/macOS for the same encoding ambiguity.