diff --git a/README.md b/README.md index 41bcf1b..d2b170e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +hi + # Claude Code Rewind > There's no way to see your Claude Code usage. We changed that. Your developer archetype. Your token costs. Your Claude ELO. Nobody else has touched this data. Find out where you actually stand. diff --git a/__tests__/stats.test.ts b/__tests__/stats.test.ts index 34599df..a9d22fc 100644 --- a/__tests__/stats.test.ts +++ b/__tests__/stats.test.ts @@ -157,9 +157,9 @@ describe("computeStats", () => { it("resolves session slug paths to real project paths", () => { const stats = computeStats(richData); - // -home-user-project-a should resolve to /home/user/project-a + // -home-user-project-a should resolve to /home/user/project-a, displayed as basename const projectNames = stats.topProjectStats.map((p) => p.name); - expect(projectNames).toContain("/home/user/project-a"); + expect(projectNames).toContain("project-a"); }); it("counts tool calls from session messages", () => { diff --git a/src/lib/stats.ts b/src/lib/stats.ts index d778c22..3be54e1 100644 --- a/src/lib/stats.ts +++ b/src/lib/stats.ts @@ -195,11 +195,13 @@ export function computeStats(data: ParsedData): ComputedStats { const projectSet = new Set(); const branchSet = new Set(); const projectActivity: Record = {}; - // Build slug→realPath from history so we can resolve session folder names + // Build slug→realPath from history so we can resolve session folder names. + // Always replace \, / and : so Windows drive letters (C:\ or C:/) slug correctly + // regardless of whether history.jsonl stores backslashes or forward slashes. + const pathToSlug = (p: string) => p.replaceAll("\\", "-").replaceAll("/", "-").replaceAll(":", "-"); for (const entry of history) { if (entry.project) { - const slug = entry.project.replaceAll("/", "-"); - slugToPath[slug] = entry.project; + slugToPath[pathToSlug(entry.project)] = entry.project; projectSet.add(entry.project); } } @@ -229,11 +231,15 @@ export function computeStats(data: ParsedData): ComputedStats { } } const topProjectStats = Array.from(projectSet) - .map((name) => ({ - name, - messages: projectActivity[name] || 0, - tokens: projectTokens[name] || 0, - sessions: projectSessions[name] || 0, + .map((fullPath) => ({ + name: + fullPath + .split(fullPath.includes("\\") ? "\\" : "/") + .filter(Boolean) + .at(-1) ?? fullPath, + messages: projectActivity[fullPath] || 0, + tokens: projectTokens[fullPath] || 0, + sessions: projectSessions[fullPath] || 0, })) .sort((a, b) => b.messages - a.messages) .slice(0, 5); @@ -434,15 +440,15 @@ function extractUsername(history: HistoryEntry[], sessions: { messages: { cwd?: for (const session of sessions) { for (const msg of session.messages) { if (msg.cwd) { - // /home/username/... or /Users/username/... - const match = msg.cwd.match(/^\/(?:home|Users)\/([^/]+)/); + // /home/username/... or /Users/username/... or C:\Users\username\... + const match = msg.cwd.match(/^(?:\/(?:home|Users)\/|[A-Za-z]:\\[Uu]sers\\)([^/\\]+)/); if (match) return match[1]; } } } for (const entry of history) { if (entry.project) { - const match = entry.project.match(/^\/(?:home|Users)\/([^/]+)/); + const match = entry.project.match(/^(?:\/(?:home|Users)\/|[A-Za-z]:\\[Uu]sers\\)([^/\\]+)/); if (match) return match[1]; } }