feat(agent): aggregate Claude usage from local transcripts (#49) #51

Merged
maximus merged 3 commits from issue-49-claude-usage-aggregate into main 2026-10-04 19:29:38 +00:00
2 changed files with 7 additions and 1 deletions
Showing only changes of commit 5cb9182911 - Show all commits

View file

@ -171,6 +171,9 @@ describe("mapProject", () => {
expect( expect(
mapProject(`${HOME}/claude-code/vps-health-api/.claude/worktrees/agent-x`, HOME), mapProject(`${HOME}/claude-code/vps-health-api/.claude/worktrees/agent-x`, HOME),
).toBe("vps-health-api"); ).toBe("vps-health-api");
// A doubled slash never yields an empty project name.
expect(mapProject(`${HOME}/claude-code//simpl-liste/app`, HOME)).toBe("simpl-liste");
expect(mapProject(`${HOME}//claude-code`, HOME)).toBe("(monorepo)");
}); });
it("rule 1: .spikes and .archive keep their second segment", () => { it("rule 1: .spikes and .archive keep their second segment", () => {

View file

@ -89,8 +89,11 @@ function escapeRe(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
} }
// Collapses repeated slashes and drops trailing ones, so "<home>/claude-code//x"
// cannot yield an empty project name.
function stripTrailingSlashes(p) { function stripTrailingSlashes(p) {
return p.length > 1 ? p.replace(/\/+$/, "") : p; const collapsed = p.replace(/\/{2,}/g, "/");
return collapsed.length > 1 ? collapsed.replace(/\/+$/, "") : collapsed;
} }
// Project name from a transcript line's `cwd`, rules applied in order: // Project name from a transcript line's `cwd`, rules applied in order: