diff --git a/main.swift b/main.swift index 2da6eb1..37e29aa 100644 --- a/main.swift +++ b/main.swift @@ -3,6 +3,15 @@ import ApplicationServices // MARK: - Model +/// Claude Code names a project dir after its cwd with every non-alphanumeric +/// character replaced by "-", so /Users/me/src/github.com/a_b becomes +/// -Users-me-src-github-com-a-b. Replacing only "/" silently fails to match +/// any path containing a dot or underscore. +func encodedProjectDir(_ path: String) -> String { + // ASCII-only on purpose: a non-ASCII letter is not [a-zA-Z0-9] either. + String(path.map { $0.isASCII && ($0.isLetter || $0.isNumber) ? $0 : "-" }) +} + enum AgentKind: String { case claude = "Claude Code", codex = "Codex" } struct AgentSession { @@ -143,7 +152,7 @@ final class ProcessDiscovery { // a claude process can hold several project transcripts open; prefer // the one whose encoded project dir matches the process cwd if all.count > 1, let cwd { - let encoded = cwd.replacingOccurrences(of: "/", with: "-") + let encoded = encodedProjectDir(cwd) if let preferred = all.first(where: { $0.contains(encoded) }) { return preferred } } return all.first @@ -1094,7 +1103,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { if let path = snap.transcriptPath { seen.insert(path) } else if snap.kind == .claude, let cwd = snap.cwd { - let encoded = cwd.replacingOccurrences(of: "/", with: "-") + let encoded = encodedProjectDir(cwd) let i = cwdIndex[encoded, default: 0] cwdIndex[encoded] = i + 1 seen.insert("cwd#\(encoded)#\(i)") @@ -1167,7 +1176,7 @@ if CommandLine.arguments.contains("--scan") { var cwdCounts: [String: Int] = [:] for s in snaps { if let p = s.transcriptPath { live.insert(p) } - else if s.kind == .claude, let c = s.cwd { cwdCounts[c.replacingOccurrences(of: "/", with: "-"), default: 0] += 1 } + else if s.kind == .claude, let c = s.cwd { cwdCounts[encodedProjectDir(c), default: 0] += 1 } } print("== sessions ==") for s in SessionScanner().scan(live: live, claudeCwdCounts: cwdCounts) {