Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions __tests__/stats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
28 changes: 17 additions & 11 deletions src/lib/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,13 @@ export function computeStats(data: ParsedData): ComputedStats {
const projectSet = new Set<string>();
const branchSet = new Set<string>();
const projectActivity: Record<string, number> = {};
// 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);
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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];
}
}
Expand Down
Loading