claude-agent-ledger 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +1 -0
- package/bin/agent-ledger.ts +29 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ All notable changes to `claude-agent-ledger` will be documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.1] — 2026-04-22
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `--anonymize` flag — replaces project paths with `~/repo-A`, `~/repo-B`,
|
|
12
|
+
... and session ids with `sess-A`, `sess-B`, ... ordered by spend descending.
|
|
13
|
+
Use this when you want to share your output publicly (HN / Bluesky /
|
|
14
|
+
presentation) without leaking employer names or internal repo names.
|
|
15
|
+
Subagent / model / day labels stay untouched (those are public info).
|
|
16
|
+
|
|
8
17
|
## [0.4.0] — 2026-04-22
|
|
9
18
|
|
|
10
19
|
### Added
|
package/README.md
CHANGED
|
@@ -180,6 +180,7 @@ If Anthropic's actual rate differs (or web_fetch has its own price), open an iss
|
|
|
180
180
|
|---|---|
|
|
181
181
|
| `--summary` | One-screen dashboard with all the headline numbers |
|
|
182
182
|
| `--by <subagent\|model\|day\|project\|session>` | Group rows. Default: `subagent`. `day` renders as ASCII bar chart. |
|
|
183
|
+
| `--anonymize` | Replace project paths and session ids with `~/repo-A`, `sess-A`, ... so you can share screenshots without doxing your employer / private repos. |
|
|
183
184
|
| `--md` | Markdown table output (good for committing or sharing) |
|
|
184
185
|
| `--json` | Raw JSON output (pipe into jq) |
|
|
185
186
|
| `--plan pro\|max` | Hide dollar columns; show token utilization only |
|
package/bin/agent-ledger.ts
CHANGED
|
@@ -14,6 +14,7 @@ interface Args {
|
|
|
14
14
|
plan: Plan;
|
|
15
15
|
by: GroupKey;
|
|
16
16
|
summary: boolean;
|
|
17
|
+
anonymize: boolean;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
function periodRange(period: Period): { from: Date; to: Date } {
|
|
@@ -47,6 +48,9 @@ Flags:
|
|
|
47
48
|
--md Markdown table output (good for sharing / commits)
|
|
48
49
|
--json Raw JSON output (for piping into jq)
|
|
49
50
|
--plan pro|max Suppress dollar columns (token utilization only)
|
|
51
|
+
--anonymize Replace project paths and session ids with safe
|
|
52
|
+
placeholders (~/repo-A, sess-A) so the output is
|
|
53
|
+
shareable without doxing yourself.
|
|
50
54
|
|
|
51
55
|
Environment:
|
|
52
56
|
NO_COLOR=1 Disable ANSI colors
|
|
@@ -101,9 +105,31 @@ function parseArgs(argv: string[]): Args {
|
|
|
101
105
|
plan,
|
|
102
106
|
by,
|
|
103
107
|
summary: rest.includes('--summary'),
|
|
108
|
+
anonymize: rest.includes('--anonymize'),
|
|
104
109
|
};
|
|
105
110
|
}
|
|
106
111
|
|
|
112
|
+
// Replace identifying labels with safe placeholders. Run after aggregation,
|
|
113
|
+
// before formatting. We anonymize project paths (which leak employer + repo
|
|
114
|
+
// names) and session ids; leave subagent / model / day untouched (those are
|
|
115
|
+
// either public Anthropic info or generic agent type names).
|
|
116
|
+
function anonymizeReport(report: LedgerReport, group: GroupKey): LedgerReport {
|
|
117
|
+
if (group !== 'project' && group !== 'session') return report;
|
|
118
|
+
const prefix = group === 'project' ? '~/repo-' : 'sess-';
|
|
119
|
+
const labelFor = (i: number): string => {
|
|
120
|
+
// Use A..Z then AA..ZZ for many entries
|
|
121
|
+
if (i < 26) return prefix + String.fromCharCode(65 + i);
|
|
122
|
+
return prefix + String.fromCharCode(65 + Math.floor(i / 26) - 1) + String.fromCharCode(65 + (i % 26));
|
|
123
|
+
};
|
|
124
|
+
const newRows = report.rows.map((r, i) => ({ ...r, subagent: labelFor(i) }));
|
|
125
|
+
return { ...report, rows: newRows };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// For --summary we may need to anonymize multiple sub-reports.
|
|
129
|
+
function anonymizeForSummary(byProject: LedgerReport): LedgerReport {
|
|
130
|
+
return anonymizeReport(byProject, 'project');
|
|
131
|
+
}
|
|
132
|
+
|
|
107
133
|
function planMask(report: LedgerReport, plan: Plan): LedgerReport {
|
|
108
134
|
if (plan === 'payg') return report;
|
|
109
135
|
const zero = () => ({
|
|
@@ -139,17 +165,19 @@ async function main(): Promise<void> {
|
|
|
139
165
|
const turns = await collectTurns({ from, to });
|
|
140
166
|
|
|
141
167
|
if (args.summary) {
|
|
142
|
-
const [bySub, byMod, byDay,
|
|
168
|
+
const [bySub, byMod, byDay, byProjRaw] = await Promise.all([
|
|
143
169
|
aggregate(iterTurns(turns), from, to, 'subagent'),
|
|
144
170
|
aggregate(iterTurns(turns), from, to, 'model'),
|
|
145
171
|
aggregate(iterTurns(turns), from, to, 'day'),
|
|
146
172
|
aggregate(iterTurns(turns), from, to, 'project'),
|
|
147
173
|
]);
|
|
174
|
+
const byProj = args.anonymize ? anonymizeForSummary(byProjRaw) : byProjRaw;
|
|
148
175
|
console.log(formatSummary(bySub, byMod, byDay, byProj, args.period));
|
|
149
176
|
return;
|
|
150
177
|
}
|
|
151
178
|
|
|
152
179
|
let report = await aggregate(iterTurns(turns), from, to, args.by);
|
|
180
|
+
if (args.anonymize) report = anonymizeReport(report, args.by);
|
|
153
181
|
report = planMask(report, args.plan);
|
|
154
182
|
|
|
155
183
|
let output: string;
|