@vimoxshah/tokenflow 1.1.1 → 1.2.0
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 +228 -0
- package/Dockerfile.team +20 -0
- package/README.md +30 -11
- package/bin/tokenflow.js +147 -12
- package/design/tokens.yaml +330 -0
- package/docs/architecture.md +5 -4
- package/docs/cli.md +204 -0
- package/docs/configuration.md +117 -2
- package/docs/design-system.md +187 -0
- package/docs/exports-and-budgets.md +85 -0
- package/docs/guard-codex.md +132 -0
- package/docs/ledger.md +144 -0
- package/docs/live-mode.md +40 -0
- package/docs/media/overview-aurora-dark.png +0 -0
- package/docs/media/receipts-aurora-dark.png +0 -0
- package/docs/providers-otel.md +179 -0
- package/docs/providers.md +54 -1
- package/docs/receipt-schema.md +74 -0
- package/docs/roadmap.md +182 -0
- package/docs/team-server.md +170 -0
- package/docs/ui-views.md +322 -0
- package/package.json +7 -2
- package/schemas/receipt.v0.json +160 -0
- package/scripts/build-dmg.sh +11 -2
- package/scripts/build-menubar-app.sh +58 -7
- package/scripts/design-build.js +475 -0
- package/src/analytics/anatomy.js +467 -0
- package/src/analytics/branch-compare.js +159 -0
- package/src/analytics/cache-health.js +141 -0
- package/src/analytics/live-view.js +266 -0
- package/src/analytics/receipt-schema.js +214 -0
- package/src/analytics/receipt.js +709 -0
- package/src/analytics/rhythm.js +184 -0
- package/src/analytics/whatif.js +263 -0
- package/src/commands/budget-scopes.js +133 -0
- package/src/commands/doctor-checks.js +400 -0
- package/src/commands/guard.js +531 -0
- package/src/commands/hooks.js +238 -0
- package/src/commands/pricing-diff.js +316 -0
- package/src/commands/receipt.js +226 -0
- package/src/commands/team-serve.js +407 -0
- package/src/commands/week.js +86 -0
- package/src/core/annotations.js +97 -0
- package/src/core/budget.js +33 -0
- package/src/core/bundle.js +45 -2
- package/src/core/ingest.js +33 -0
- package/src/core/live-status.js +227 -2
- package/src/core/policy.js +103 -0
- package/src/core/receipt-note.js +123 -0
- package/src/core/repo.js +64 -0
- package/src/core/sync.js +163 -26
- package/src/core/team.js +0 -0
- package/src/export/html-snapshot.js +28 -1
- package/src/export/menubar.js +21 -0
- package/src/export/receipt-card.js +210 -0
- package/src/export/week-card.js +185 -0
- package/src/providers/mock/index.js +383 -52
- package/src/providers/openai/index.js +31 -1
- package/src/providers/otel/index.js +656 -0
- package/src/server/routes/annotations.js +42 -0
- package/src/server/routes/cache-health.js +95 -0
- package/src/server/routes/index.js +54 -0
- package/src/server/routes/session.js +157 -0
- package/src/server/server.js +47 -1
- package/src/ui/app.js +541 -308
- package/src/ui/charts.js +95 -0
- package/src/ui/first-run.js +144 -0
- package/src/ui/index.html +4 -1
- package/src/ui/palette.js +335 -0
- package/src/ui/styles/anatomy.css +117 -0
- package/src/ui/styles/annotations.css +40 -0
- package/src/ui/styles/branches.css +99 -0
- package/src/ui/styles/cache.css +6 -0
- package/src/ui/styles/first-run.css +31 -0
- package/src/ui/styles/live.css +100 -0
- package/src/ui/styles/palette.css +85 -0
- package/src/ui/styles/rhythm.css +8 -0
- package/src/ui/styles/whatif.css +55 -0
- package/src/ui/styles.css +303 -196
- package/src/ui/views/anatomy.js +567 -0
- package/src/ui/views/annotations.js +121 -0
- package/src/ui/views/branches.js +304 -0
- package/src/ui/views/cache.js +232 -0
- package/src/ui/views/index.js +85 -0
- package/src/ui/views/live.js +683 -0
- package/src/ui/views/rhythm.js +206 -0
- package/src/ui/views/whatif.js +196 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,234 @@
|
|
|
3
3
|
All notable changes to TokenFlow are recorded here. Versions follow
|
|
4
4
|
[semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## 1.2.0 — 2026-09-06
|
|
7
|
+
|
|
8
|
+
Spend attributed to the unit of work, a guard that acts while a session is
|
|
9
|
+
still running, and a design system that every surface compiles from. Nothing
|
|
10
|
+
here reads prompt or code content.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Command palette.** ⌘K or Ctrl+K, or the "⌘K" chip at the end of the tab
|
|
15
|
+
bar: every tab, the quick ranges, each skin and mode, export, refresh and
|
|
16
|
+
clear filters, matched by subsequence or word prefix, with the last eight
|
|
17
|
+
commands first. It opens and closes with no animation because it is used
|
|
18
|
+
dozens of times a day. Works from the offline snapshot too. Inside a text
|
|
19
|
+
field, Ctrl+K keeps its editing meaning and ⌘K still opens the palette.
|
|
20
|
+
- **First-run screen.** The first time the live dashboard opens, and again
|
|
21
|
+
after an upgrade, one screen says what was found on this machine: what each
|
|
22
|
+
source contributed, which detected adapters have read nothing yet, and why.
|
|
23
|
+
Never shown from a saved snapshot.
|
|
24
|
+
- **Tab bar that wraps.** Twenty tabs wrap to two rows at 1280 and 1024 pixels
|
|
25
|
+
with the active tab always visible.
|
|
26
|
+
- **A `--hairline` token.** The subtlest divider now has its own colour role in
|
|
27
|
+
`design/tokens.yaml` for every skin and mode, so views stop guessing.
|
|
28
|
+
- **A design system with a compiler.** `design/tokens.yaml` is the single
|
|
29
|
+
source for colour roles, the two axes (mode × skin), a type scale with
|
|
30
|
+
size-specific tracking, a 4-pt space scale, radii, two motion tiers and the
|
|
31
|
+
authored chart ramps. `npm run design` compiles it into a generated block in
|
|
32
|
+
the dashboard stylesheet, a generated block in the landing stylesheet and
|
|
33
|
+
`menubar/TokenFlow/DesignTokens.swift`, and refuses to write when a gate
|
|
34
|
+
fails: ink contrast, series-on-surface contrast, adjacent-series perceptual
|
|
35
|
+
distance, sequential monotonicity, status-vs-series distinctness, the 300ms
|
|
36
|
+
ceiling on UI motion. `test/design.test.js` fails the build when a generated
|
|
37
|
+
block is hand-edited or stale. Doctrine in `docs/design-system.md`.
|
|
38
|
+
- **Receipts view** in the dashboard: spend per repository and branch, with
|
|
39
|
+
context share, sessions, turns, subagent share and rank against the repo's
|
|
40
|
+
median. Click a branch for the receipt and copy it as a PR comment. Receipts
|
|
41
|
+
ship inside the data bundle, computed once per refresh and streamed rather
|
|
42
|
+
than materialized, so the offline snapshot has them too. The scan costs
|
|
43
|
+
about two seconds on a 160K-turn store, so the watcher's status snapshot
|
|
44
|
+
and the one-line CLI summaries opt out of it.
|
|
45
|
+
- **Story strip** on the Overview: the three insights that matter for the
|
|
46
|
+
current slice, as sentences, with their numbers set in the figure face.
|
|
47
|
+
- **Deep links**: `#tab=receipts&skin=terminal&mode=light` opens the dashboard
|
|
48
|
+
on that view and look. The tab is reflected in the URL as you switch.
|
|
49
|
+
- **Motion and type polish through the tokens**: feedback on press for every
|
|
50
|
+
pressable, popovers that scale from their trigger, tooltips that respond in
|
|
51
|
+
a frame, cards that stagger in only when the view changes, a translucent
|
|
52
|
+
header material, hover states gated to pointer devices, and reduced-motion
|
|
53
|
+
and reduced-transparency that degrade to a complete state. Muted ink was
|
|
54
|
+
nudged in every skin to clear 4.5:1 on cards.
|
|
55
|
+
- **Menu bar on the same tokens.** Accent, status colours and the categorical
|
|
56
|
+
palette now come from the generated Swift file; the saturated gradients on
|
|
57
|
+
the brand mark and the milestone banner became solid accent surfaces, per the
|
|
58
|
+
system's own rule that a gradient sits only behind a single number.
|
|
59
|
+
- **Landing page rewritten around the finding, not the features**: a real
|
|
60
|
+
receipt in the hero, the three numbers from nine weeks of logs, the
|
|
61
|
+
marginal-cost curve, the receipt rules with the correction that produced
|
|
62
|
+
them, the guard, and the cap table with its upper-bound caveat. Built on the
|
|
63
|
+
same tokens, with self-hosted fonts and no third-party requests; the GSAP
|
|
64
|
+
and three.js dependencies are gone.
|
|
65
|
+
|
|
66
|
+
- **`tokenflow receipt`** — what a branch or pull request cost. Each turn is
|
|
67
|
+
attributed to the branch checked out when it ran, so one long session that
|
|
68
|
+
moved across branches is split across them. Repositories are identified by
|
|
69
|
+
walking up from the recorded working directory to `.git` and following a
|
|
70
|
+
worktree's `gitdir:` pointer home, so `.worktrees/<x>` no longer counts as
|
|
71
|
+
its own project. `--gh` joins merged pull requests by head branch and prices
|
|
72
|
+
each at `$ per 100 changed lines`; `--md` renders a PR-comment receipt. A PR
|
|
73
|
+
owns the turns on its branch up to the merge; turns after the merge are
|
|
74
|
+
follow-up on a checkout that kept the branch name and are shown beside the
|
|
75
|
+
receipt, never inside it. Long-lived branches are flagged as such. The
|
|
76
|
+
estimate is split into *context* dollars (cache reads + writes: re-sending
|
|
77
|
+
the conversation so far) and *work* dollars (fresh input + output).
|
|
78
|
+
`--sessions` reports how concentrated spend is across sessions, the median
|
|
79
|
+
cost of a turn by how deep into a session it is, and the dollars above each
|
|
80
|
+
candidate per-session cap — labelled an upper bound, not a saving.
|
|
81
|
+
- **`tokenflow guard`** — a Claude Code hook that reads the live transcript,
|
|
82
|
+
prices it, and reports running spend, current prompt size and the median cost
|
|
83
|
+
of the last ten turns, then warns or blocks against thresholds you declared
|
|
84
|
+
with `guard --set`. Nothing declared means informational only, never a
|
|
85
|
+
block. Reads are incremental per session. `--install` prints the hooks block
|
|
86
|
+
and does not write your settings.
|
|
87
|
+
- **`tokenflow team serve`.** A self-hosted team server for the folder-sync
|
|
88
|
+
rollups: one process on a machine your team owns, fed by the same
|
|
89
|
+
per-machine files (`<id>.jsonl`, `<id>.receipts.json`) the folder sync
|
|
90
|
+
already writes. A single shared bearer token gates writes and reads once
|
|
91
|
+
configured; a non-loopback host always requires one. `GET /health` always
|
|
92
|
+
answers; `GET /api/team` and `GET /` return the same aggregate `tokenflow
|
|
93
|
+
team` prints. See `docs/team-server.md` and `Dockerfile.team`.
|
|
94
|
+
- **The Ledger: cost per branch and per merged PR, across the team.**
|
|
95
|
+
`sync.push()` now also writes `<machineId>.receipts.json`, a whole-state,
|
|
96
|
+
opt-in-anonymous ledger of branch receipts (unless `sync.receipts: false`).
|
|
97
|
+
`tokenflow team` joins every synced ledger into `receipts.totals`,
|
|
98
|
+
`byRepo`, `byMonth`, `perMergedPr`, `concentration` and `longLivedShare`.
|
|
99
|
+
`sync.to` (plus `sync.token` or `TOKENFLOW_SYNC_TOKEN`) pushes both files to
|
|
100
|
+
a server instead of a shared folder; `tokenflow sync --to <url> --token
|
|
101
|
+
<t>` runs it from the CLI. See `docs/ledger.md`.
|
|
102
|
+
- **Receipts travel with the code.** `tokenflow hooks install` writes a
|
|
103
|
+
`pre-push` hook that attaches a receipt to the pushed commit as a git note
|
|
104
|
+
under `refs/notes/tokenflow`. The hook never blocks: every failure is
|
|
105
|
+
reported on stderr with exit 0, and a hook it replaces is kept and chained,
|
|
106
|
+
running first and keeping its own exit code. Receipt schema v0
|
|
107
|
+
(`schemas/receipt.v0.json`, `src/analytics/receipt-schema.js`) makes the
|
|
108
|
+
note machine readable. A zero-dependency GitHub Action (`action/`) reads
|
|
109
|
+
the note on a pull request and posts or updates one PR comment. See
|
|
110
|
+
`docs/receipt-schema.md`.
|
|
111
|
+
- **Per-repository guard policy and a Codex guard.** `.tokenflow/policy.yaml`
|
|
112
|
+
declares guard thresholds that travel with a repository and win over the
|
|
113
|
+
personal config, key by key; `tokenflow guard --policy [--cwd <dir>]`
|
|
114
|
+
shows the effective policy and each value's source. Codex CLI has no
|
|
115
|
+
blocking hook, so `tokenflow guard --install --codex [--apply]` wires up
|
|
116
|
+
its `notify` setting instead: it reads only `thread-id` and `cwd` from
|
|
117
|
+
Codex's payload and sends one OS notification on warn or block, never a
|
|
118
|
+
block itself. See `docs/guard-codex.md`.
|
|
119
|
+
- **Exports that travel, and budgets per repo and per team.** `tokenflow
|
|
120
|
+
receipt` gains `--svg`, `--png` and `--csv`: a shareable receipt card, or
|
|
121
|
+
one CSV row per branch across every repository. `tokenflow week` builds
|
|
122
|
+
"your AI week" as text, JSON, or the same kind of card (`--svg`, `--png`).
|
|
123
|
+
A `budgets:` list in `config.yaml` adds caps scoped to one repository or to
|
|
124
|
+
the whole team, alongside the existing monthly budget; `tokenflow budget`
|
|
125
|
+
prints both. See `docs/exports-and-budgets.md`.
|
|
126
|
+
- **`tokenflow doctor` data-quality audit and `tokenflow pricing diff`.**
|
|
127
|
+
Doctor now runs seven checks over the last three months of records:
|
|
128
|
+
worktree-split spend, cwd outside any repository, Codex records with no
|
|
129
|
+
git branch, unpriced models, a stale price table, session-level sources in
|
|
130
|
+
per-turn views, and `metadata.repoResolved === false`. `tokenflow pricing
|
|
131
|
+
diff <table.json> [--apply] [--yes]` compares a candidate price table to
|
|
132
|
+
the current effective one and merges it in only on explicit confirmation.
|
|
133
|
+
- **Live status carries sessions, receipts, guard state and sparklines.**
|
|
134
|
+
`data/status.json` now includes `liveSessions` (up to 8 sessions active
|
|
135
|
+
within 10 minutes of the last refresh), `receiptsToday` (the top 3
|
|
136
|
+
branches by spend today), `guard` (declared thresholds plus the last
|
|
137
|
+
verdict, sourced `cache` or `derived`) and `sparklines` (24 hourly buckets
|
|
138
|
+
per source). The xbar/SwiftBar text shows a "Live now" line and a "Guard:"
|
|
139
|
+
line from the same data.
|
|
140
|
+
- **The native menu bar shows the same live state.** A "Live, as of" section
|
|
141
|
+
lists running sessions with a context gauge and the guard's colour dot;
|
|
142
|
+
"Today's receipts" lists the day's top branches; "Guard" shows the
|
|
143
|
+
declared caps, the last verdict, and Raise-cap / Clear-caps buttons that
|
|
144
|
+
write through `tokenflow guard --set`, never by editing config.yaml
|
|
145
|
+
directly; a "Last 24 hours by source" sparkline section closes it out. A
|
|
146
|
+
transient card slides in from the status item only when a live session's
|
|
147
|
+
guard level rises, never on a recovery, and only one shows at a time.
|
|
148
|
+
Density (compact/comfortable) and the popover's global keyboard shortcut
|
|
149
|
+
are now preferences.
|
|
150
|
+
- **Demo data is schema-complete.** The synthetic dataset now has a hot
|
|
151
|
+
repository and branch, five repositories, and volume that stays within
|
|
152
|
+
10% across weekdays, so every new view has something realistic to render
|
|
153
|
+
offline.
|
|
154
|
+
- **`otel`, an OpenTelemetry (GenAI) adapter.** Reads a standards-based
|
|
155
|
+
OTLP JSON export (`resourceSpans`/`resourceLogs`, `gen_ai.usage.*`
|
|
156
|
+
attributes) from any tool's file exporter, and Gemini CLI's own file
|
|
157
|
+
telemetry (`~/.gemini/telemetry.log` by default, or `sources.otel.paths`),
|
|
158
|
+
deduplicating Gemini's paired usage events. See `docs/providers-otel.md`.
|
|
159
|
+
- **Seven new dashboard views, each a registered module, not an app.js
|
|
160
|
+
edit.** Session anatomy: a session's turn-by-turn cost waterfall, growing
|
|
161
|
+
context and subagent fan-out, backed by `GET /api/session`. Live v2: the
|
|
162
|
+
four "right now" sections above, as its own tab. Cache health: hit rate,
|
|
163
|
+
write split and churn events with their dollar cost. What-if: reprice the
|
|
164
|
+
same tokens at another model's rates, labelled as a price-only comparison.
|
|
165
|
+
Compare branches: any two branch receipts on a symmetric log scale.
|
|
166
|
+
Rhythm: deep-work sessions, project switching, and the hour marginal cost
|
|
167
|
+
peaks. Annotations: mark a day and see it on every daily chart, stored in
|
|
168
|
+
`annotations.json`. All seven route through the view registry
|
|
169
|
+
(`src/ui/views/index.js`) and the route registry
|
|
170
|
+
(`src/server/routes/index.js`) documented in `docs/ui-views.md`.
|
|
171
|
+
|
|
172
|
+
### Fixed
|
|
173
|
+
|
|
174
|
+
- **Git worktrees no longer split one repository's spend.** A session in
|
|
175
|
+
`<repo>/.worktrees/<x>` used to file under `x`; ingestion now resolves
|
|
176
|
+
`project` and `repository` through the main checkout at ingest time
|
|
177
|
+
(`src/core/repo.js`, `metadata.repoResolved`), and `tokenflow doctor`
|
|
178
|
+
flags any repository still fragmented across worktree names. Repair
|
|
179
|
+
existing history with `tokenflow refresh --full`.
|
|
180
|
+
- **Codex sessions now carry a branch and a repository name.** CLI builds
|
|
181
|
+
0.149 and later report a `git` block on `session_meta` (`branch`,
|
|
182
|
+
`repository_url`); the openai adapter sets `git_branch` and `repository`
|
|
183
|
+
from it, falling back to the cwd basename exactly as before when the block
|
|
184
|
+
is absent.
|
|
185
|
+
|
|
186
|
+
## 1.1.2 — 2026-09-03
|
|
187
|
+
|
|
188
|
+
Makes the downloadable app usable on a machine that is not the one that built
|
|
189
|
+
it. 1.1.1 was correct as source and as an npm package; its DMG was not.
|
|
190
|
+
|
|
191
|
+
### Fixed
|
|
192
|
+
|
|
193
|
+
- **The released app pointed at the machine that built it.** Every build
|
|
194
|
+
embedded the absolute path of its own node binary and CLI, which is right for
|
|
195
|
+
a developer driving their checkout and wrong for a release: the 1.1.1 DMG
|
|
196
|
+
shipped `TokenFlowCLIPath = /Users/runner/work/tokenflow/...`, a path that
|
|
197
|
+
exists on no user's machine. Only path-discovery fallbacks kept the app
|
|
198
|
+
working at all. A distributable build now embeds no path
|
|
199
|
+
(`TOKENFLOW_PORTABLE=1`), the DMG script refuses to package one that does,
|
|
200
|
+
and the release workflow re-checks it after mounting the finished image.
|
|
201
|
+
- **A cask-only install had nothing to run.** The cask installs the app; the
|
|
202
|
+
CLI came from npm. Anyone who only ran `brew install --cask tokenflow` got a
|
|
203
|
+
menu bar that could read an existing status file and do nothing else — no
|
|
204
|
+
refresh, no watcher, no dashboard. The app now carries its own CLI (see
|
|
205
|
+
below).
|
|
206
|
+
- **Node discovery named one specific version.** The first candidate was
|
|
207
|
+
`~/.nvm/versions/node/v24.13.1/bin/node` — whichever version the developer
|
|
208
|
+
happened to have. One `nvm install` away from being wrong for everybody.
|
|
209
|
+
nvm installs are now discovered and the highest one at or above the engine
|
|
210
|
+
floor wins.
|
|
211
|
+
|
|
212
|
+
### Added
|
|
213
|
+
|
|
214
|
+
- **The app bundles its own CLI**, at
|
|
215
|
+
`Contents/Resources/cli/package/bin/tokenflow.js`. It is packed with
|
|
216
|
+
`npm pack` and then pruned to the files the CLI actually executes, so the
|
|
217
|
+
bundle is always a subset of the published package and never carries a file
|
|
218
|
+
npm does not ship. The bundled copy takes priority over any CLI found on the
|
|
219
|
+
system, because the app and the CLI share a contract — the status file's
|
|
220
|
+
shape, the watcher lock format, `/api/ping` — and the copy shipped beside the
|
|
221
|
+
binary is the only one guaranteed to match it. A local build still embeds the
|
|
222
|
+
developer's clone, which wins, so an installed app keeps driving the checkout
|
|
223
|
+
being edited.
|
|
224
|
+
- **A missing dependency now says so.** With no CLI or no Node the menu bar
|
|
225
|
+
reported nothing and every button failed silently. It names the problem and
|
|
226
|
+
the command that fixes it, at launch rather than on the first click.
|
|
227
|
+
|
|
228
|
+
### Changed
|
|
229
|
+
|
|
230
|
+
- The cask states the Node requirement in `caveats` instead of declaring
|
|
231
|
+
`depends_on formula: "node"`, which would install a second Node beside an
|
|
232
|
+
nvm- or asdf-managed one and fight the version manager.
|
|
233
|
+
|
|
6
234
|
## 1.1.1 — 2026-09-03
|
|
7
235
|
|
|
8
236
|
A reliability release. Five defects had combined to leave the app paused, the
|
package/Dockerfile.team
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# TokenFlow team server — tiny image, zero runtime dependencies, no `npm install`.
|
|
2
|
+
# See docs/team-server.md.
|
|
3
|
+
#
|
|
4
|
+
# docker build -f Dockerfile.team -t tokenflow-team .
|
|
5
|
+
# docker run -d -e TOKENFLOW_TEAM_TOKEN=<shared-secret> -p 7790:7790 \
|
|
6
|
+
# -v tokenflow-team-data:/data tokenflow-team
|
|
7
|
+
FROM node:22-alpine
|
|
8
|
+
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY package.json ./
|
|
11
|
+
COPY bin ./bin
|
|
12
|
+
COPY src ./src
|
|
13
|
+
|
|
14
|
+
ENV TOKENFLOW_HOME=/data
|
|
15
|
+
VOLUME ["/data"]
|
|
16
|
+
EXPOSE 7790
|
|
17
|
+
|
|
18
|
+
# --host 0.0.0.0 so the container's port mapping can reach the server; a
|
|
19
|
+
# non-loopback bind requires TOKENFLOW_TEAM_TOKEN to be set at `docker run`.
|
|
20
|
+
CMD ["node", "bin/tokenflow.js", "team", "serve", "--host", "0.0.0.0", "--port", "7790"]
|
package/README.md
CHANGED
|
@@ -39,12 +39,12 @@ per-source and per-model usage, capacity meters and forecast, in light and dark.
|
|
|
39
39
|
|
|
40
40
|
| Layer | What's inside |
|
|
41
41
|
|---|---|
|
|
42
|
-
| **Ingestion** |
|
|
42
|
+
| **Ingestion** | 11 adapters (Anthropic, OpenAI/Codex, OpenCode, Hermes, Cline, Cursor, Headroom gateway, git, OpenTelemetry/GenAI (otel), generic CSV/JSONL/SQLite import, demo). Incremental byte-offset resumes, re-read windows for upserted rows, budgeted refresh that stops cleanly mid-corpus |
|
|
43
43
|
| **Correctness** | Cache read/write/input kept as mutually exclusive buckets; vendor convention differences handled; streaming re-reports collapsed to max-of-run; `null` never coerced to 0; measured gateway cost kept separate from estimates |
|
|
44
44
|
| **Costing** | Versioned price table with per-entry source URLs and fetch dates; service-tier multipliers; long-TTL cache-write tier priced separately; unpriced models surface as `null` with a configure action — never silent `$0` |
|
|
45
|
-
| **Analytics** |
|
|
46
|
-
| **Live mode** | Watcher daemon, native Swift menu bar app (provider/source/model breakdowns, capacity meters with reset countdowns & ETAs, forecast with stated confidence, MAD-based anomaly alerts, appearance toggle), SwiftBar/xbar plugin |
|
|
47
|
-
| **Engineering** | Zero runtime dependencies ·
|
|
45
|
+
| **Analytics** | 20 views: overview KPIs with a story strip, receipts per branch and pull request, session anatomy, live, composition, provider/model intelligence, interfaces, hour×weekday heatmap + calendar, peaks, efficiency ratios, cache health, cost with coverage, model what-if, git correlations (labelled), rhythm and focus, period compare, compare branches, searchable data explorer, annotations, per-field data health |
|
|
46
|
+
| **Live mode** | Watcher daemon, native Swift menu bar app (live sessions, today's receipts, guard state, provider/source/model breakdowns, capacity meters with reset countdowns & ETAs, forecast with stated confidence, MAD-based anomaly alerts, per-source sparklines, appearance toggle), SwiftBar/xbar plugin |
|
|
47
|
+
| **Engineering** | Zero runtime dependencies · 520 tests · lint invariants (e.g. "no `\|\| 0` on a token field") · tsc-clean JSDoc types · CI on macOS/Linux/Windows × Node 22/24 |
|
|
48
48
|
|
|
49
49
|
Zero runtime dependencies. Nothing leaves your machine. No API keys, no accounts, no telemetry.
|
|
50
50
|
|
|
@@ -121,7 +121,8 @@ dashboard.
|
|
|
121
121
|
|
|
122
122
|
| Question | Where |
|
|
123
123
|
|---|---|
|
|
124
|
-
|
|
|
124
|
+
| What did this branch or pull request cost? | Receipts — spend per repository and branch, context share, copy as a PR comment |
|
|
125
|
+
| How much AI did I use, and how has that changed? | Overview — the three insights that matter, then KPIs, daily series, trend |
|
|
125
126
|
| Input vs output vs cache? | Token composition — four buckets that sum to the total |
|
|
126
127
|
| Which provider and model do I rely on? | Provider & model share, growth, per-model efficiency |
|
|
127
128
|
| When do I use AI most? | Time patterns — hour/weekday profiles, heatmap, calendar |
|
|
@@ -133,9 +134,14 @@ dashboard.
|
|
|
133
134
|
<details>
|
|
134
135
|
<summary>All pages</summary>
|
|
135
136
|
|
|
136
|
-
Overview · Token composition · Providers & models · Interfaces ·
|
|
137
|
-
Efficiency · Cost ·
|
|
138
|
-
Data health
|
|
137
|
+
Overview · Receipts · Session anatomy · Live · Token composition · Providers & models · Interfaces ·
|
|
138
|
+
Time patterns · Peaks · Efficiency · Cache health · Cost · What-if · Productivity · Rhythm · Compare ·
|
|
139
|
+
Compare branches · Data explorer (searchable/sortable/exportable) · Annotations · Data health.
|
|
140
|
+
Deep-link any of them: `#tab=receipts&skin=terminal&mode=light`.
|
|
141
|
+
|
|
142
|
+
Press ⌘K (or Ctrl+K) anywhere in the dashboard for the command palette: jump to a tab, change the
|
|
143
|
+
range, skin or mode, export, refresh or clear filters without touching the mouse. The first time
|
|
144
|
+
the live dashboard opens it explains what was found on this machine and what was not, and why.
|
|
139
145
|
|
|
140
146
|
</details>
|
|
141
147
|
|
|
@@ -196,12 +202,23 @@ node bin/tokenflow.js dashboard # live UI at http://127.0.0.1:7799 (loopbac
|
|
|
196
202
|
node bin/tokenflow.js watch # auto-refresh every N seconds (default 120)
|
|
197
203
|
node bin/tokenflow.js import f.csv # CSV/JSONL/SQLite via saved field mapping
|
|
198
204
|
node bin/tokenflow.js export --csv # --all for everything; --html for offline snapshot
|
|
199
|
-
node bin/tokenflow.js digest # shareable
|
|
205
|
+
node bin/tokenflow.js digest # shareable weekly digest (--format text, --from/--to, --out f.md)
|
|
206
|
+
node bin/tokenflow.js week # this week's spend vs last week (--svg/--png card, --json)
|
|
200
207
|
node bin/tokenflow.js models-compare # cost/usage efficiency per model — your data
|
|
201
208
|
node bin/tokenflow.js budget --set 200 # monthly cap + forecast alerts (fires once per state/month)
|
|
202
209
|
node bin/tokenflow.js schedule --install --at "Monday 09:00" # weekly digest via launchd
|
|
203
210
|
node bin/tokenflow.js diagnostics # local observability — nothing transmitted
|
|
204
211
|
node bin/tokenflow.js team # per-developer team view (needs sync + opt-in names)
|
|
212
|
+
node bin/tokenflow.js team serve # self-hosted team server (LAN/Docker), no shared folder needed
|
|
213
|
+
node bin/tokenflow.js sync --to <url> --token <t> # push this machine's rollups to a team server
|
|
214
|
+
node bin/tokenflow.js receipt --repo . --gh # what each branch / merged PR cost (sees through worktrees)
|
|
215
|
+
node bin/tokenflow.js receipt --sessions # where the money goes: concentration, context vs work, marginal cost/turn
|
|
216
|
+
node bin/tokenflow.js receipt --csv # one row per branch, every repository
|
|
217
|
+
node bin/tokenflow.js hooks install # pre-push git hook: attach a receipt note, never blocks
|
|
218
|
+
node bin/tokenflow.js guard --install # Claude Code hook: warn/block a session against caps you declare
|
|
219
|
+
node bin/tokenflow.js guard --install --codex # same circuit breaker for Codex CLI (warns only)
|
|
220
|
+
node bin/tokenflow.js pricing diff table.json --apply # merge a candidate price table in
|
|
221
|
+
node bin/tokenflow.js doctor # environment, store, adapters, and a data-quality audit
|
|
205
222
|
node bin/tokenflow.js up # refresh → rebuild offline HTML → serve + open
|
|
206
223
|
|
|
207
224
|
npm link # optional: global `tokenflow` command
|
|
@@ -247,7 +264,7 @@ from `file://` with no server. A full CSV export doubles as a portable dataset:
|
|
|
247
264
|
## Contributing
|
|
248
265
|
|
|
249
266
|
```bash
|
|
250
|
-
npm test #
|
|
267
|
+
npm test # 520 tests: normalization, adapters, analytics, store, receipts, guard
|
|
251
268
|
npm run lint # project invariants (incl. "no || 0 on a token field")
|
|
252
269
|
npm run typecheck # tsc over JSDoc types — must be zero errors
|
|
253
270
|
npm run validate # self-check: runtime, adapters, store↔cube agreement
|
|
@@ -276,8 +293,10 @@ Easiest contribution: an adapter (`src/providers/<id>/index.js`) plus a fixture
|
|
|
276
293
|
|
|
277
294
|
Local data → local normalization → local analytics → local dashboard. The server binds to
|
|
278
295
|
`127.0.0.1`. No prompt text, code, or file content is ever stored — adapters read token counts and
|
|
279
|
-
discard the rest. No telemetry.
|
|
296
|
+
discard the rest. No telemetry. Three features can touch the network, all strictly opt-in:
|
|
280
297
|
|
|
298
|
+
- **Team server push** (`sync.to` in config, or `tokenflow sync --to <url>`) sends the same two
|
|
299
|
+
sync files to a server you run with `tokenflow team serve`, behind a bearer token. Nothing else.
|
|
281
300
|
- **Digest delivery** (`delivery:` in config) — sends the digest you generate to your own
|
|
282
301
|
Telegram chat, email, or webhook. Credentials live only in `~/.tokenflow/config.yaml`.
|
|
283
302
|
- **Multi-machine sync** (`sync:` in config) — exchanges daily totals (date, tokens, requests,
|
package/bin/tokenflow.js
CHANGED
|
@@ -80,12 +80,16 @@ async function main() {
|
|
|
80
80
|
case 'forecast': return cmdForecast();
|
|
81
81
|
case 'menubar': return cmdMenubar();
|
|
82
82
|
case 'digest': return cmdDigest();
|
|
83
|
+
case 'week': return cmdWeek();
|
|
83
84
|
case 'schedule': return cmdSchedule();
|
|
84
85
|
case 'budget': return cmdBudget();
|
|
85
86
|
case 'sync': return cmdSync();
|
|
86
87
|
case 'models-compare': return cmdModelsCompare();
|
|
87
88
|
case 'diagnostics': return cmdDiagnostics();
|
|
88
89
|
case 'team': return cmdTeam();
|
|
90
|
+
case 'receipt': case 'receipts': return cmdReceipt();
|
|
91
|
+
case 'hooks': return cmdHooks();
|
|
92
|
+
case 'guard': return cmdGuard();
|
|
89
93
|
default:
|
|
90
94
|
console.error(`${C.red}Unknown command "${cmd}".${C.r}\n`);
|
|
91
95
|
return help(1);
|
|
@@ -264,7 +268,7 @@ async function cmdStatus() {
|
|
|
264
268
|
const line = barLine(st, String(flags.mode || 'auto'), String(flags.prefix || 'TF'));
|
|
265
269
|
return console.log(flags.json ? JSON.stringify(line, null, 2) : line.text);
|
|
266
270
|
}
|
|
267
|
-
const b = buildBundle();
|
|
271
|
+
const b = buildBundle({ receipts: false });
|
|
268
272
|
if (flags.json) return console.log(JSON.stringify({ meta: b.meta, health: b.health }, null, 2));
|
|
269
273
|
const h = b.health;
|
|
270
274
|
if (!h.records) {
|
|
@@ -446,6 +450,16 @@ async function cmdExport() {
|
|
|
446
450
|
// ================================================================== pricing ==
|
|
447
451
|
|
|
448
452
|
async function cmdPricing() {
|
|
453
|
+
if (argv[1] === 'diff') {
|
|
454
|
+
const { run } = await import('../src/commands/pricing-diff.js');
|
|
455
|
+
const file = argv[2];
|
|
456
|
+
if (!file) throw new Error('usage: tokenflow pricing diff <file.json|-> [--apply] [--yes]');
|
|
457
|
+
const res = await run({ file, apply: !!flags.apply, yes: !!flags.yes });
|
|
458
|
+
if (res.stdout) console.log(res.stdout);
|
|
459
|
+
if (res.stderr) console.error(res.stderr);
|
|
460
|
+
if (res.exitCode) process.exitCode = res.exitCode;
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
449
463
|
const p = paths();
|
|
450
464
|
const cur = readJson(p.pricing, { models: {} });
|
|
451
465
|
if (flags.set) {
|
|
@@ -488,7 +502,7 @@ async function cmdPricing() {
|
|
|
488
502
|
return;
|
|
489
503
|
}
|
|
490
504
|
|
|
491
|
-
const b = buildBundle();
|
|
505
|
+
const b = buildBundle({ receipts: false });
|
|
492
506
|
const v = computeView(b, {});
|
|
493
507
|
const book = buildPriceBook(readJson(p.pricing, {}));
|
|
494
508
|
console.log(`\n ${C.b}Pricing${C.r} ${C.dim}built-in table ${PRICING_TABLE_VERSION} · overrides in ${p.pricing}${C.r}\n`);
|
|
@@ -747,6 +761,9 @@ async function cmdDoctor() {
|
|
|
747
761
|
console.log(` cube ${int(store.cube().rows.length)} rows`);
|
|
748
762
|
console.log(` sessions ${int(Object.keys(store.sessions().rows).length)}`);
|
|
749
763
|
console.log(` stale gens ${(store.state.stale || []).length}${(store.state.stale || []).length ? ` ${C.y}run 'tokenflow compact'${C.r}` : ''}`);
|
|
764
|
+
const { auditChecks, renderChecks } = await import('../src/commands/doctor-checks.js');
|
|
765
|
+
console.log(`\n ${C.b}Audit${C.r} ${C.dim}data-quality checks over the last 3 months${C.r}`);
|
|
766
|
+
renderChecks(auditChecks({ store, config: cfg, now: new Date() }));
|
|
750
767
|
console.log(`\n ${C.b}Providers${C.r}`);
|
|
751
768
|
await cmdProviders();
|
|
752
769
|
console.log(` ${C.dim}Troubleshooting guide: docs/troubleshooting.md${C.r}\n`);
|
|
@@ -1166,6 +1183,49 @@ async function cmdDigest() {
|
|
|
1166
1183
|
}
|
|
1167
1184
|
}
|
|
1168
1185
|
|
|
1186
|
+
/** `tokenflow week` — "Your AI week": this week's spend vs last week, as text, JSON, or a card. */
|
|
1187
|
+
async function cmdWeek() {
|
|
1188
|
+
const { run } = await import('../src/commands/week.js');
|
|
1189
|
+
const out = run(flags);
|
|
1190
|
+
if (flags.json) return console.log(JSON.stringify(out.json, null, 2));
|
|
1191
|
+
console.log(out.text);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/** `tokenflow receipt` — AI spend attributed to a branch / pull request. */
|
|
1195
|
+
async function cmdReceipt() {
|
|
1196
|
+
const { run } = await import('../src/commands/receipt.js');
|
|
1197
|
+
const out = run(flags);
|
|
1198
|
+
if (flags.json) return console.log(JSON.stringify(out.json, null, 2));
|
|
1199
|
+
if (typeof flags.out === 'string') {
|
|
1200
|
+
fs.writeFileSync(flags.out, out.text + '\n');
|
|
1201
|
+
return console.log(`${C.g}✓${C.r} wrote ${flags.out}`);
|
|
1202
|
+
}
|
|
1203
|
+
console.log(out.text);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/** `tokenflow hooks` — install/uninstall the pre-push git hook that attaches a receipt note. */
|
|
1207
|
+
async function cmdHooks() {
|
|
1208
|
+
const { run } = await import('../src/commands/hooks.js');
|
|
1209
|
+
const action = argv[1];
|
|
1210
|
+
const out = run({ ...flags, action, args: argv.slice(2) });
|
|
1211
|
+
if (out.stdout) process.stdout.write(out.stdout + '\n');
|
|
1212
|
+
if (out.stderr) process.stderr.write(out.stderr + '\n');
|
|
1213
|
+
process.exitCode = out.exitCode;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/** `tokenflow guard` — the in-session circuit breaker, run as a Claude Code hook. */
|
|
1217
|
+
async function cmdGuard() {
|
|
1218
|
+
const { run } = await import('../src/commands/guard.js');
|
|
1219
|
+
// Hooks run in a shell whose PATH may not include this node (nvm, asdf), so
|
|
1220
|
+
// the printed command pins the interpreter and the CLI that are running now.
|
|
1221
|
+
// `run()` is synchronous except for the `--codex-notify` branch, which
|
|
1222
|
+
// returns a Promise — awaiting unconditionally handles both.
|
|
1223
|
+
const out = await run({ ...flags, bin: `${process.execPath} ${path.join(root(), 'bin', 'tokenflow.js')}` });
|
|
1224
|
+
if (out.stdout) process.stdout.write(out.stdout + '\n');
|
|
1225
|
+
if (out.stderr) process.stderr.write(out.stderr + '\n');
|
|
1226
|
+
process.exitCode = out.exitCode;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1169
1229
|
/** `tokenflow schedule` — install/remove the weekly digest LaunchAgent. */
|
|
1170
1230
|
async function cmdSchedule() {
|
|
1171
1231
|
const sched = await import('../src/core/schedule.js');
|
|
@@ -1191,9 +1251,19 @@ async function cmdBudget() {
|
|
|
1191
1251
|
saveConfig(merge(cfg, { budget }));
|
|
1192
1252
|
console.log(`${C.g}✓${C.r} monthly budget set to $${v.toLocaleString('en-US')}`);
|
|
1193
1253
|
}
|
|
1254
|
+
// Scoped budgets (repo | team) are independent of the single monthly cap
|
|
1255
|
+
// below, so they are evaluated once and printed either way — a user who
|
|
1256
|
+
// only declared `budgets:` and never set a monthly cap must still see them.
|
|
1257
|
+
const { evaluateScopedBudgets, renderScopedBudgets } = await import('../src/commands/budget-scopes.js');
|
|
1258
|
+
const scopedRows = evaluateScopedBudgets({ config: cfg });
|
|
1259
|
+
|
|
1194
1260
|
if (!budget.monthly) {
|
|
1195
1261
|
console.log('No monthly budget configured. Set one:');
|
|
1196
1262
|
console.log(` ${C.b}tokenflow budget --set 200${C.r} # $200/month, warn at 80% projected`);
|
|
1263
|
+
if (scopedRows.length) {
|
|
1264
|
+
console.log(`\n${C.b}Scoped budgets${C.r}`);
|
|
1265
|
+
console.log(renderScopedBudgets(scopedRows));
|
|
1266
|
+
}
|
|
1197
1267
|
return;
|
|
1198
1268
|
}
|
|
1199
1269
|
|
|
@@ -1204,7 +1274,14 @@ async function cmdBudget() {
|
|
|
1204
1274
|
const today = new Date().toISOString().slice(0, 10);
|
|
1205
1275
|
|
|
1206
1276
|
const st = computeBudgetState(status, { monthly: budget.monthly, warnAtPct: budget.warnAtPct }, today);
|
|
1207
|
-
if (!st) {
|
|
1277
|
+
if (!st) {
|
|
1278
|
+
console.log('No usage data yet.');
|
|
1279
|
+
if (scopedRows.length) {
|
|
1280
|
+
console.log(`\n${C.b}Scoped budgets${C.r}`);
|
|
1281
|
+
console.log(renderScopedBudgets(scopedRows));
|
|
1282
|
+
}
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1208
1285
|
|
|
1209
1286
|
const { fire } = shouldAlert(st, { force: !!flags.force });
|
|
1210
1287
|
|
|
@@ -1228,6 +1305,11 @@ async function cmdBudget() {
|
|
|
1228
1305
|
} else if (st.state !== 'safe' && st.state !== 'unknown') {
|
|
1229
1306
|
console.log(C.dim + ' (already alerted for this state this month — no spam)');
|
|
1230
1307
|
}
|
|
1308
|
+
|
|
1309
|
+
if (scopedRows.length) {
|
|
1310
|
+
console.log(`\n${C.b}Scoped budgets${C.r}`);
|
|
1311
|
+
console.log(renderScopedBudgets(scopedRows));
|
|
1312
|
+
}
|
|
1231
1313
|
}
|
|
1232
1314
|
|
|
1233
1315
|
/** `tokenflow sync` — optional multi-machine aggregation via a shared folder. */
|
|
@@ -1241,6 +1323,25 @@ async function cmdSync() {
|
|
|
1241
1323
|
return;
|
|
1242
1324
|
}
|
|
1243
1325
|
|
|
1326
|
+
// A team server is a separate destination from the folder-sync `isEnabled`
|
|
1327
|
+
// gate below: `push()`'s remote branch needs only `sync.enabled`, never
|
|
1328
|
+
// `sync.dir`, so `--to` must not be blocked by "no folder configured". This
|
|
1329
|
+
// path only ever pushes — there is no merged view to pull from a server.
|
|
1330
|
+
const id = machineId();
|
|
1331
|
+
const to = typeof flags.to === 'string' ? flags.to : (cfg.sync?.to || null);
|
|
1332
|
+
const token = typeof flags.token === 'string' ? flags.token : undefined;
|
|
1333
|
+
if (to) {
|
|
1334
|
+
try {
|
|
1335
|
+
const r = await push({ config: cfg, to, token });
|
|
1336
|
+
if ('pushedTo' in r) console.log(`${C.g}✓${C.r} pushed → ${r.pushedTo}`);
|
|
1337
|
+
else console.log(r.days ? `${C.g}✓${C.r} pushed ${r.days} days` : `${C.dim}nothing to push yet${C.r}`);
|
|
1338
|
+
console.log(C.dim + ` this machine's id: ${id}${C.r}`);
|
|
1339
|
+
} catch (e) {
|
|
1340
|
+
throw Object.assign(new Error(e.message), { exitCode: 1 });
|
|
1341
|
+
}
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1244
1345
|
if (!isEnabled(cfg)) {
|
|
1245
1346
|
console.log(`Multi-machine sync is ${C.b}OFF${C.r} by default. To enable it:
|
|
1246
1347
|
|
|
@@ -1253,22 +1354,21 @@ async function cmdSync() {
|
|
|
1253
1354
|
dir: ~/Sync/TokenFlow # that shared folder
|
|
1254
1355
|
machineName: MacBook Pro # label shown in aggregated views
|
|
1255
1356
|
|
|
1256
|
-
3. Run ${C.b}tokenflow sync
|
|
1357
|
+
3. Run ${C.b}tokenflow sync${C.r} on each machine.
|
|
1358
|
+
|
|
1359
|
+
A shared folder is not the only option: ${C.b}tokenflow sync --to <url> --token <t>${C.r}
|
|
1360
|
+
pushes to a self-hosted team server instead — see docs/team-server.md.
|
|
1257
1361
|
|
|
1258
1362
|
What is shared: daily totals only (date, tokens, requests, est. cost).
|
|
1259
1363
|
What is never shared: prompts, code, file paths, credentials.`);
|
|
1260
1364
|
return;
|
|
1261
1365
|
}
|
|
1262
1366
|
|
|
1263
|
-
const id = machineId();
|
|
1264
|
-
if (flags.push || flags.pull === undefined) {
|
|
1265
|
-
// default action with no sub-flag = push + pull
|
|
1266
|
-
}
|
|
1267
1367
|
try {
|
|
1268
1368
|
if (!flags.pull) {
|
|
1269
|
-
const r = push({ config: cfg });
|
|
1369
|
+
const r = await push({ config: cfg });
|
|
1270
1370
|
console.log(r.days
|
|
1271
|
-
? `${C.g}✓${C.r} pushed ${r.days} days → ${path.basename(r.file)}`
|
|
1371
|
+
? `${C.g}✓${C.r} pushed ${r.days} days → ${path.basename(r.file || '')}`
|
|
1272
1372
|
: `${C.dim}nothing to push yet${C.r}`);
|
|
1273
1373
|
}
|
|
1274
1374
|
const merged = pull({ config: cfg });
|
|
@@ -1312,6 +1412,13 @@ async function cmdDiagnostics() {
|
|
|
1312
1412
|
|
|
1313
1413
|
/** `tokenflow team` — per-developer usage from the shared sync folder (P4-B). */
|
|
1314
1414
|
async function cmdTeam() {
|
|
1415
|
+
// `team serve` runs a self-hosted server for machines that push with
|
|
1416
|
+
// `--to` instead of writing into a shared folder — it needs no `sync.dir`
|
|
1417
|
+
// of its own, so this must not fall through to the folder-sync gate below.
|
|
1418
|
+
if (argv[1] === 'serve') {
|
|
1419
|
+
const { run } = await import('../src/commands/team-serve.js');
|
|
1420
|
+
return run(flags);
|
|
1421
|
+
}
|
|
1315
1422
|
const cfg = loadConfig();
|
|
1316
1423
|
if (!cfg.sync?.enabled || !cfg.sync?.dir) {
|
|
1317
1424
|
console.error(`${C.red}Team view reads the shared sync folder.${C.r}
|
|
@@ -1460,28 +1567,56 @@ function help(code = 0) {
|
|
|
1460
1567
|
${C.b}Intelligence${C.r}
|
|
1461
1568
|
tokenflow models-compare cost/request, tokens/request, cache-hit% per model
|
|
1462
1569
|
(--from/--to <date> to pick the window)
|
|
1570
|
+
tokenflow week this week's spend vs last week (--svg/--png card, --json)
|
|
1463
1571
|
tokenflow budget --set 200 monthly cap → projected-overrun alerts (dedup'd)
|
|
1464
1572
|
tokenflow budget current state: safe / approaching / over
|
|
1465
|
-
|
|
1573
|
+
(also evaluates any scoped budgets: repo | team)
|
|
1574
|
+
tokenflow digest --deliver build the weekly digest and send via configured channels
|
|
1466
1575
|
tokenflow schedule --install --at "Monday 09:00" weekly digest via launchd
|
|
1467
1576
|
tokenflow schedule --status is the digest schedule installed?
|
|
1468
1577
|
tokenflow team per-developer usage from the shared sync folder
|
|
1578
|
+
tokenflow team serve self-hosted team server for the folder-sync rollups (--host --port --token --dir; non-loopback host needs a token)
|
|
1469
1579
|
tokenflow diagnostics version, providers, store freshness, feature states
|
|
1470
1580
|
|
|
1581
|
+
${C.b}Receipts & guard${C.r}
|
|
1582
|
+
tokenflow receipt spend per branch, per repository (sees through worktrees)
|
|
1583
|
+
tokenflow receipt --repo <path> --gh
|
|
1584
|
+
join branches to merged pull requests via gh
|
|
1585
|
+
tokenflow receipt --repo <path> --gh --pr 478 --md
|
|
1586
|
+
one PR-comment receipt (cost, context share, $/100 lines)
|
|
1587
|
+
tokenflow receipt --csv one row per branch, every repository, as CSV
|
|
1588
|
+
tokenflow receipt --branch <b> --svg r.svg [--png r.png]
|
|
1589
|
+
a shareable receipt card (SVG, or SVG + PNG)
|
|
1590
|
+
tokenflow receipt --sessions where the money goes: concentration, context vs work,
|
|
1591
|
+
marginal cost per turn, dollars above a per-session cap
|
|
1592
|
+
tokenflow hooks install|uninstall|status
|
|
1593
|
+
pre-push git hook that attaches a receipt note (never blocks)
|
|
1594
|
+
tokenflow guard --install print the Claude Code hooks block for the circuit breaker
|
|
1595
|
+
tokenflow guard --set warnCostUsd=25,maxCostUsd=200
|
|
1596
|
+
declare thresholds (none declared = informational only)
|
|
1597
|
+
tokenflow guard --session <f> judge one transcript file
|
|
1598
|
+
tokenflow guard --policy [--cwd <dir>]
|
|
1599
|
+
effective guard policy for a directory, and each value's source
|
|
1600
|
+
tokenflow guard --install --codex [--apply]
|
|
1601
|
+
wire up Codex CLI's notify hook (warns only, never blocks)
|
|
1602
|
+
|
|
1471
1603
|
${C.b}Sync (optional, off by default)${C.r}
|
|
1472
1604
|
tokenflow sync push this machine's daily rollups + show merged view
|
|
1473
1605
|
tokenflow sync --off disable sync entirely
|
|
1606
|
+
tokenflow sync --to <url> --token <t> push both sync files to a team server
|
|
1474
1607
|
|
|
1475
1608
|
${C.b}Configure${C.r}
|
|
1476
1609
|
tokenflow pricing show which models have a price, and from where
|
|
1477
1610
|
tokenflow pricing --sources provenance of every built-in rate + tier multipliers
|
|
1478
1611
|
tokenflow pricing --set "m=3,15,0.3,3.75"
|
|
1612
|
+
tokenflow pricing diff <table.json> [--apply] [--yes]
|
|
1613
|
+
compare a candidate price table to the current one
|
|
1479
1614
|
tokenflow import <file> CSV / JSON / JSONL / SQLite with field mapping
|
|
1480
1615
|
tokenflow restore <file.csv> rebuild the store from a full export, re-priced
|
|
1481
1616
|
tokenflow config show|path|export|import
|
|
1482
1617
|
|
|
1483
1618
|
${C.b}Maintain${C.r}
|
|
1484
|
-
tokenflow doctor environment, paths, store, adapters
|
|
1619
|
+
tokenflow doctor environment, paths, store, adapters, data-quality audit
|
|
1485
1620
|
tokenflow validate re-validate every stored record
|
|
1486
1621
|
tokenflow compact drop superseded records after a rewrite
|
|
1487
1622
|
tokenflow reset --yes delete ingested data (keeps config)
|