@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
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# TokenFlow design tokens — the single source every surface compiles from.
|
|
2
|
+
#
|
|
3
|
+
# npm run design compile → src/ui/styles.css (generated block),
|
|
4
|
+
# site/styles.css (generated block),
|
|
5
|
+
# menubar/TokenFlow/DesignTokens.swift
|
|
6
|
+
# npm run design:check compile in memory, diff against the files, run gates
|
|
7
|
+
#
|
|
8
|
+
# Roles, not values: --plane / --text-muted / --accent stay true when a skin
|
|
9
|
+
# flips mode. Hex values are quoted because `#` starts a YAML comment.
|
|
10
|
+
# Doctrine and the reasoning behind every rule: docs/design-system.md.
|
|
11
|
+
|
|
12
|
+
version: 1
|
|
13
|
+
name: tokenflow
|
|
14
|
+
|
|
15
|
+
# ------------------------------------------------------------------ fonts ---
|
|
16
|
+
# The dashboard and the menu bar stay on the platform's own faces: they ship
|
|
17
|
+
# optical sizing and tracking tables, and the offline snapshot must open from
|
|
18
|
+
# file:// with nothing to fetch. The landing page self-hosts its faces (below).
|
|
19
|
+
fonts:
|
|
20
|
+
sans: "system-ui, -apple-system, 'Segoe UI', sans-serif"
|
|
21
|
+
mono: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace"
|
|
22
|
+
serif: "'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua', Georgia, serif"
|
|
23
|
+
|
|
24
|
+
# ------------------------------------------------------------------- type ---
|
|
25
|
+
# Size-specific tracking and leading: large text tightens, small text opens.
|
|
26
|
+
# One letter-spacing for every size is wrong somewhere.
|
|
27
|
+
type:
|
|
28
|
+
micro:
|
|
29
|
+
size: 10.5
|
|
30
|
+
tracking: ".02em"
|
|
31
|
+
leading: 1.3
|
|
32
|
+
label:
|
|
33
|
+
size: 11.5
|
|
34
|
+
tracking: ".012em"
|
|
35
|
+
leading: 1.35
|
|
36
|
+
caption:
|
|
37
|
+
size: 12.5
|
|
38
|
+
tracking: ".004em"
|
|
39
|
+
leading: 1.4
|
|
40
|
+
body:
|
|
41
|
+
size: 13.5
|
|
42
|
+
tracking: "0"
|
|
43
|
+
leading: 1.45
|
|
44
|
+
title:
|
|
45
|
+
size: 15
|
|
46
|
+
tracking: "-.006em"
|
|
47
|
+
leading: 1.3
|
|
48
|
+
figure:
|
|
49
|
+
size: 25
|
|
50
|
+
tracking: "-.015em"
|
|
51
|
+
leading: 1.12
|
|
52
|
+
hero:
|
|
53
|
+
size: 42
|
|
54
|
+
tracking: "-.025em"
|
|
55
|
+
leading: 1.05
|
|
56
|
+
display:
|
|
57
|
+
size: 60
|
|
58
|
+
tracking: "-.03em"
|
|
59
|
+
leading: 1.0
|
|
60
|
+
|
|
61
|
+
# ------------------------------------------------------------------ space ---
|
|
62
|
+
# A 4-pt scale with the two half-steps the dense layouts actually use.
|
|
63
|
+
space: [2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 32, 40, 56, 72]
|
|
64
|
+
|
|
65
|
+
radius:
|
|
66
|
+
xs: 5
|
|
67
|
+
sm: 8
|
|
68
|
+
md: 12
|
|
69
|
+
lg: 16
|
|
70
|
+
pill: 999
|
|
71
|
+
|
|
72
|
+
# ----------------------------------------------------------------- motion ---
|
|
73
|
+
# Two tiers. `ui` governs the dashboard and menu bar: nothing over 300ms, press
|
|
74
|
+
# feedback instant, no animation on keyboard-initiated actions. `narrative`
|
|
75
|
+
# governs the landing page and other reading surfaces, where a reveal may take
|
|
76
|
+
# a beat. Under prefers-reduced-motion every primitive degrades to an instant,
|
|
77
|
+
# complete state — never to a half-state.
|
|
78
|
+
motion:
|
|
79
|
+
ui:
|
|
80
|
+
press: 120ms
|
|
81
|
+
fast: 160ms
|
|
82
|
+
base: 220ms
|
|
83
|
+
slow: 300ms
|
|
84
|
+
ease-out: "cubic-bezier(.23, 1, .32, 1)"
|
|
85
|
+
ease-in-out: "cubic-bezier(.77, 0, .175, 1)"
|
|
86
|
+
ease-drawer: "cubic-bezier(.32, .72, 0, 1)"
|
|
87
|
+
stagger: 40ms
|
|
88
|
+
stagger-max: 6
|
|
89
|
+
narrative:
|
|
90
|
+
fast: 180ms
|
|
91
|
+
base: 320ms
|
|
92
|
+
slow: 700ms
|
|
93
|
+
ease: "cubic-bezier(.2, .7, .3, 1)"
|
|
94
|
+
ease-out: "cubic-bezier(.16, 1, .3, 1)"
|
|
95
|
+
stagger: 85ms
|
|
96
|
+
stagger-max: 7
|
|
97
|
+
|
|
98
|
+
# ----------------------------------------------------------------- status ---
|
|
99
|
+
# Reserved for state. Never a series colour, never themed, always paired with
|
|
100
|
+
# an icon or a word. Values are the dataviz reference status palette.
|
|
101
|
+
status:
|
|
102
|
+
good: "#0ca30c"
|
|
103
|
+
warning: "#fab219"
|
|
104
|
+
serious: "#ec835a"
|
|
105
|
+
critical: "#d03b3b"
|
|
106
|
+
# On light surfaces `warning` sits near series-4 (yellow) and `critical` near
|
|
107
|
+
# series-8 (red). Status is never drawn as a chart mark and always travels
|
|
108
|
+
# with an icon or a word, so the two can share a hue family without one
|
|
109
|
+
# impersonating the other. The compiler reports these as warnings, not passes.
|
|
110
|
+
relief:
|
|
111
|
+
light: [warning, critical]
|
|
112
|
+
|
|
113
|
+
# ----------------------------------------------------------------- charts ---
|
|
114
|
+
# Authored ramps, never derived. The categorical steps belong to the MODE, not
|
|
115
|
+
# the skin, and were validated against every skin's chart surface, so a skin
|
|
116
|
+
# restyles the room and never the data. Fixed order, assigned by entity: a
|
|
117
|
+
# filter that removes a series must not repaint the survivors. Past eight,
|
|
118
|
+
# fold into "Other".
|
|
119
|
+
series:
|
|
120
|
+
dark: ["#3987e5", "#d95926", "#199e70", "#c98500", "#d55181", "#008300", "#9085e9", "#e66767"]
|
|
121
|
+
light: ["#2a78d6", "#eb6834", "#1baf7a", "#eda100", "#e87ba4", "#008300", "#4a3aa7", "#e34948"]
|
|
122
|
+
# Slots documented under 3:1 on light chart surfaces. The relief ships with
|
|
123
|
+
# every chart: a table twin and direct labels, so colour never carries the
|
|
124
|
+
# value alone. The compiler warns on these instead of failing.
|
|
125
|
+
relief:
|
|
126
|
+
light: [3, 4, 5]
|
|
127
|
+
|
|
128
|
+
# One hue, light → dark, for magnitude.
|
|
129
|
+
sequential:
|
|
130
|
+
dark: ["#184f95", "#1c5cab", "#256abf", "#2a78d6", "#3987e5", "#5598e7", "#86b6ef"]
|
|
131
|
+
light: ["#cde2fb", "#9ec5f4", "#6da7ec", "#3987e5", "#2a78d6", "#1c5cab", "#104281"]
|
|
132
|
+
|
|
133
|
+
# Two hues and a neutral grey midpoint, for polarity (period compare, vs-median).
|
|
134
|
+
# Never a hue at the midpoint.
|
|
135
|
+
diverging:
|
|
136
|
+
dark: ["#d95926", "#e39a78", "#6b7385", "#7fb0f0", "#3987e5"]
|
|
137
|
+
light: ["#eb6834", "#f2b28f", "#9aa2b3", "#8dbaf0", "#2a78d6"]
|
|
138
|
+
|
|
139
|
+
# ------------------------------------------------------------------ skins ---
|
|
140
|
+
# A skin restyles the room: surfaces, ink, borders, accent, density, the faces
|
|
141
|
+
# used for headings and figures. It never touches the series steps above.
|
|
142
|
+
# Hierarchy is carried by lightness, never by hue; the accent is an accent.
|
|
143
|
+
skins:
|
|
144
|
+
aurora:
|
|
145
|
+
label: Aurora
|
|
146
|
+
note: "Indigo-slate, layered, luminous"
|
|
147
|
+
default: true
|
|
148
|
+
display: sans
|
|
149
|
+
figure: sans
|
|
150
|
+
density:
|
|
151
|
+
radius: 14
|
|
152
|
+
radius-sm: 9
|
|
153
|
+
pad-card: "14px 16px"
|
|
154
|
+
gap-grid: 14px
|
|
155
|
+
fig-scale: 1
|
|
156
|
+
sec-space: 26px
|
|
157
|
+
tracking-h: "-0.015em"
|
|
158
|
+
dark:
|
|
159
|
+
plane: "#0a0d14"
|
|
160
|
+
surface-1: "#111624"
|
|
161
|
+
surface-2: "#171d2e"
|
|
162
|
+
surface-3: "#1f2637"
|
|
163
|
+
text-primary: "#f3f6fc"
|
|
164
|
+
text-secondary: "#a7b2c9"
|
|
165
|
+
text-muted: "#7d879e"
|
|
166
|
+
grid: "#1e2436"
|
|
167
|
+
axis: "#2b3348"
|
|
168
|
+
border: "rgba(154, 172, 255, 0.11)"
|
|
169
|
+
border-strong: "rgba(154, 172, 255, 0.20)"
|
|
170
|
+
hairline: "rgba(154, 172, 255, 0.066)"
|
|
171
|
+
accent: "#8f9dff"
|
|
172
|
+
accent-solid: "#4a5ae0"
|
|
173
|
+
accent-ink: "#ffffff"
|
|
174
|
+
shadow: "0 1px 2px rgba(0, 0, 0, .45), 0 14px 40px rgba(4, 8, 20, .45)"
|
|
175
|
+
glow: "radial-gradient(1100px 460px at 8% -10%, rgba(94, 114, 255, .17), transparent 62%), radial-gradient(820px 420px at 92% -14%, rgba(25, 158, 112, .11), transparent 60%)"
|
|
176
|
+
hero-glow: "radial-gradient(460px 220px at 8% 0%, rgba(94, 114, 255, .22), transparent 72%)"
|
|
177
|
+
light:
|
|
178
|
+
plane: "#f5f7fc"
|
|
179
|
+
surface-1: "#ffffff"
|
|
180
|
+
surface-2: "#f1f4fa"
|
|
181
|
+
surface-3: "#e6eaf4"
|
|
182
|
+
text-primary: "#0d1220"
|
|
183
|
+
text-secondary: "#4b5468"
|
|
184
|
+
text-muted: "#6c7488"
|
|
185
|
+
grid: "#e4e8f1"
|
|
186
|
+
axis: "#c7cddb"
|
|
187
|
+
border: "rgba(13, 18, 32, 0.09)"
|
|
188
|
+
border-strong: "rgba(13, 18, 32, 0.17)"
|
|
189
|
+
hairline: "rgba(13, 18, 32, 0.054)"
|
|
190
|
+
accent: "#3d4dd6"
|
|
191
|
+
accent-solid: "#3d4dd6"
|
|
192
|
+
accent-ink: "#ffffff"
|
|
193
|
+
shadow: "0 1px 2px rgba(16, 24, 48, .06), 0 12px 32px rgba(16, 24, 48, .08)"
|
|
194
|
+
glow: "radial-gradient(1100px 460px at 8% -10%, rgba(94, 114, 255, .10), transparent 62%)"
|
|
195
|
+
hero-glow: "radial-gradient(420px 180px at 12% 0%, rgba(94, 114, 255, .09), transparent 70%)"
|
|
196
|
+
|
|
197
|
+
terminal:
|
|
198
|
+
label: Terminal
|
|
199
|
+
note: "Near-black, hairlines, mono"
|
|
200
|
+
display: mono
|
|
201
|
+
figure: mono
|
|
202
|
+
density:
|
|
203
|
+
radius: 5
|
|
204
|
+
radius-sm: 4
|
|
205
|
+
pad-card: "11px 13px"
|
|
206
|
+
gap-grid: 9px
|
|
207
|
+
fig-scale: 0.94
|
|
208
|
+
sec-space: 20px
|
|
209
|
+
tracking-h: "0.01em"
|
|
210
|
+
dark:
|
|
211
|
+
plane: "#06070a"
|
|
212
|
+
surface-1: "#0c0e13"
|
|
213
|
+
surface-2: "#11141a"
|
|
214
|
+
surface-3: "#171b22"
|
|
215
|
+
text-primary: "#eef2f6"
|
|
216
|
+
text-secondary: "#9aa4ad"
|
|
217
|
+
text-muted: "#737b85"
|
|
218
|
+
grid: "#171b22"
|
|
219
|
+
axis: "#262c34"
|
|
220
|
+
border: "rgba(255, 255, 255, 0.09)"
|
|
221
|
+
border-strong: "rgba(255, 255, 255, 0.16)"
|
|
222
|
+
hairline: "rgba(255, 255, 255, 0.054)"
|
|
223
|
+
accent: "#24d67e"
|
|
224
|
+
accent-solid: "#24d67e"
|
|
225
|
+
accent-ink: "#04140b"
|
|
226
|
+
shadow: "0 0 0 1px rgba(255, 255, 255, .05), 0 10px 26px rgba(0, 0, 0, .55)"
|
|
227
|
+
glow: "none"
|
|
228
|
+
hero-glow: "none"
|
|
229
|
+
light:
|
|
230
|
+
plane: "#f7f8f9"
|
|
231
|
+
surface-1: "#ffffff"
|
|
232
|
+
surface-2: "#f2f4f5"
|
|
233
|
+
surface-3: "#e8eaec"
|
|
234
|
+
text-primary: "#0a0c0f"
|
|
235
|
+
text-secondary: "#494f57"
|
|
236
|
+
text-muted: "#666d75"
|
|
237
|
+
grid: "#e8eaec"
|
|
238
|
+
axis: "#c9cdd2"
|
|
239
|
+
border: "rgba(10, 12, 15, 0.10)"
|
|
240
|
+
border-strong: "rgba(10, 12, 15, 0.18)"
|
|
241
|
+
hairline: "rgba(10, 12, 15, 0.06)"
|
|
242
|
+
accent: "#0b6b3d"
|
|
243
|
+
accent-solid: "#0b6b3d"
|
|
244
|
+
accent-ink: "#ffffff"
|
|
245
|
+
shadow: "0 0 0 1px rgba(10, 12, 15, .05), 0 8px 20px rgba(10, 12, 15, .07)"
|
|
246
|
+
glow: "none"
|
|
247
|
+
hero-glow: "none"
|
|
248
|
+
|
|
249
|
+
editorial:
|
|
250
|
+
label: Editorial
|
|
251
|
+
note: "Warm charcoal, serif figures"
|
|
252
|
+
display: serif
|
|
253
|
+
figure: serif
|
|
254
|
+
density:
|
|
255
|
+
radius: 8
|
|
256
|
+
radius-sm: 6
|
|
257
|
+
pad-card: "20px 22px"
|
|
258
|
+
gap-grid: 18px
|
|
259
|
+
fig-scale: 1.12
|
|
260
|
+
sec-space: 38px
|
|
261
|
+
tracking-h: "-0.005em"
|
|
262
|
+
dark:
|
|
263
|
+
plane: "#16130c"
|
|
264
|
+
surface-1: "#201c13"
|
|
265
|
+
surface-2: "#282318"
|
|
266
|
+
surface-3: "#322b1f"
|
|
267
|
+
text-primary: "#f8f4ec"
|
|
268
|
+
text-secondary: "#c4bcab"
|
|
269
|
+
text-muted: "#998f7d"
|
|
270
|
+
grid: "#262218"
|
|
271
|
+
axis: "#3a3529"
|
|
272
|
+
border: "rgba(255, 240, 214, 0.10)"
|
|
273
|
+
border-strong: "rgba(255, 240, 214, 0.18)"
|
|
274
|
+
hairline: "rgba(255, 240, 214, 0.06)"
|
|
275
|
+
accent: "#e0a852"
|
|
276
|
+
accent-solid: "#e0a852"
|
|
277
|
+
accent-ink: "#1a1409"
|
|
278
|
+
shadow: "0 1px 2px rgba(0, 0, 0, .4), 0 12px 34px rgba(20, 14, 4, .38)"
|
|
279
|
+
glow: "radial-gradient(900px 420px at 88% -12%, rgba(224, 168, 82, .11), transparent 60%)"
|
|
280
|
+
hero-glow: "none"
|
|
281
|
+
light:
|
|
282
|
+
plane: "#f9f6ef"
|
|
283
|
+
surface-1: "#fffdf8"
|
|
284
|
+
surface-2: "#f4f0e6"
|
|
285
|
+
surface-3: "#eae5d8"
|
|
286
|
+
text-primary: "#1a1712"
|
|
287
|
+
text-secondary: "#544e42"
|
|
288
|
+
text-muted: "#736c60"
|
|
289
|
+
grid: "#e8e2d4"
|
|
290
|
+
axis: "#cbc4b2"
|
|
291
|
+
border: "rgba(26, 23, 18, 0.10)"
|
|
292
|
+
border-strong: "rgba(26, 23, 18, 0.18)"
|
|
293
|
+
hairline: "rgba(26, 23, 18, 0.06)"
|
|
294
|
+
accent: "#8a5514"
|
|
295
|
+
accent-solid: "#8a5514"
|
|
296
|
+
accent-ink: "#ffffff"
|
|
297
|
+
shadow: "0 1px 2px rgba(60, 45, 20, .06), 0 12px 30px rgba(60, 45, 20, .08)"
|
|
298
|
+
glow: "radial-gradient(900px 420px at 88% -12%, rgba(224, 168, 82, .13), transparent 60%)"
|
|
299
|
+
hero-glow: "none"
|
|
300
|
+
|
|
301
|
+
# ---------------------------------------------------------------- landing ---
|
|
302
|
+
# The landing page is a single-world dark surface in the Aurora room, with the
|
|
303
|
+
# instrument-panel voice of design-kit's `house-plex` pairing: a geometric
|
|
304
|
+
# grotesque for display, IBM Plex Sans for body, and the same tabular Plex Mono
|
|
305
|
+
# the receipts use for figures. A sans display is the default for a developer
|
|
306
|
+
# tool; a serif would need a brand reason this product does not have. Faces are
|
|
307
|
+
# self-hosted under site/fonts/ (OFL-1.1, copied from the design-kit cache); a
|
|
308
|
+
# product that promises nothing leaves your machine does not load fonts from a
|
|
309
|
+
# third party.
|
|
310
|
+
landing:
|
|
311
|
+
world: aurora
|
|
312
|
+
mode: dark
|
|
313
|
+
pairing: house-plex
|
|
314
|
+
fonts:
|
|
315
|
+
display:
|
|
316
|
+
family: Space Grotesk
|
|
317
|
+
file: space-grotesk
|
|
318
|
+
weights: [500, 600, 700]
|
|
319
|
+
fallback: sans
|
|
320
|
+
tracking: "-.018em"
|
|
321
|
+
body:
|
|
322
|
+
family: IBM Plex Sans
|
|
323
|
+
file: ibm-plex-sans
|
|
324
|
+
weights: [400, 500, 600]
|
|
325
|
+
fallback: sans
|
|
326
|
+
mono:
|
|
327
|
+
family: IBM Plex Mono
|
|
328
|
+
file: ibm-plex-mono
|
|
329
|
+
weights: [400, 500, 600]
|
|
330
|
+
fallback: mono
|
package/docs/architecture.md
CHANGED
|
@@ -180,8 +180,9 @@ Every chart ships a **table twin** toggled per card, so no value is reachable on
|
|
|
180
180
|
|
|
181
181
|
## Privacy by construction
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
The product makes no network call on its own. The only outbound HTTP is the opt-in
|
|
184
|
+
`tokenflow sync --to`, which posts your two sync files to a team server you run, and the GitHub
|
|
185
|
+
Action, which runs in CI. Nothing for telemetry, pricing, or updates. The server binds to loopback and refuses non-GET requests from a foreign origin without a
|
|
185
186
|
token. Adapters read token counts and metadata and discard content; the schema has nowhere to put
|
|
186
187
|
a prompt. SQLite sources are read from a temp snapshot so a live editor is never disturbed.
|
|
187
188
|
|
|
@@ -189,5 +190,5 @@ a prompt. SQLite sources are read from a temp snapshot so a live editor is never
|
|
|
189
190
|
|
|
190
191
|
`user`, `machine`, `session_id`, `project` and `repository` are first-class, and the cube is
|
|
191
192
|
additive — so cubes from several machines can be merged by concatenating rows and re-summing
|
|
192
|
-
duplicated dimension tuples.
|
|
193
|
-
|
|
193
|
+
duplicated dimension tuples. Identity stays opt-in per person. Sync is file-based, or to a server you run
|
|
194
|
+
(`tokenflow team serve`) behind a shared token. No hosted service holds anyone's usage.
|
package/docs/cli.md
CHANGED
|
@@ -142,6 +142,143 @@ tokenflow digest --out week.md # write instead of print
|
|
|
142
142
|
|
|
143
143
|
---
|
|
144
144
|
|
|
145
|
+
### `receipt`
|
|
146
|
+
What a branch or pull request cost. Spend is attributed per turn to the branch checked out when
|
|
147
|
+
the turn ran, so a long session that moves across branches is split across them. Repositories are
|
|
148
|
+
identified by walking up from each recorded working directory to `.git` and following a worktree's
|
|
149
|
+
`gitdir:` pointer back to the main checkout — every `.worktrees/<x>` counts as the repo it belongs
|
|
150
|
+
to. With `--gh`, merged pull requests are fetched through the GitHub CLI and joined by head branch;
|
|
151
|
+
each matched receipt then carries `+/−` lines and cost per 100 changed lines.
|
|
152
|
+
|
|
153
|
+
Each receipt also splits the estimate into **context** dollars (cache reads + writes: the cost of
|
|
154
|
+
re-sending the conversation so far) and **work** dollars (fresh input + output). Context is the
|
|
155
|
+
cheap path per token — a high share is not waste, it is where the lever is.
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
tokenflow receipt # every repo, top branches by spend
|
|
159
|
+
tokenflow receipt --repo ~/code/api --gh # one repo, joined to its merged PRs
|
|
160
|
+
tokenflow receipt --repo api --branch feat/x # one branch as a PR-comment receipt
|
|
161
|
+
tokenflow receipt --repo ~/code/api --gh --pr 478 --md
|
|
162
|
+
tokenflow receipt --prs prs.json # PR list you exported yourself:
|
|
163
|
+
# gh pr list --state merged --json number,headRefName,additions,deletions,title,mergedAt
|
|
164
|
+
tokenflow receipt --sessions # where the money goes across sessions
|
|
165
|
+
tokenflow receipt --sessions --cap 25,100,500 # dollars above each candidate per-session cap
|
|
166
|
+
tokenflow receipt --from 2026-06-01 --top 30 --automated '^release/' # flag automated PRs
|
|
167
|
+
tokenflow receipt --json # everything, machine-readable
|
|
168
|
+
tokenflow receipt --csv > receipts.csv # one row per branch, every repository
|
|
169
|
+
tokenflow receipt --branch feat/x --svg r.svg --png r.png # a shareable receipt card
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
A merged PR owns the turns on its branch **up to the merge**, including work done before the PR
|
|
173
|
+
was opened (that work is how the PR came to exist). Turns *after* the merge are follow-up on a
|
|
174
|
+
checkout that kept the old branch name; they appear beside the receipt as "After the merge, same
|
|
175
|
+
branch" and are never counted inside it. A branch with several merged PRs attributes each turn to
|
|
176
|
+
the PR it shipped in. Long-lived branches (`main`, `staging`, …) are flagged: a receipt there is
|
|
177
|
+
for a period of work, not one change. A branch name that is deleted and reused inherits the
|
|
178
|
+
earlier branch's pre-merge turns under this rule; give reused names a suffix if that matters.
|
|
179
|
+
|
|
180
|
+
Sessions on a detached `HEAD` or with no branch recorded are reported as *unattributed*, never
|
|
181
|
+
guessed. Unpriced turns are counted and excluded from the total, never shown as `$0`.
|
|
182
|
+
`--sessions` labels its cap table as an upper bound: the dollars above a cap are what a guard could
|
|
183
|
+
have held back at most, not a saving.
|
|
184
|
+
|
|
185
|
+
`--csv` writes one row per branch (`repo, branch, costUsd, contextShare, sessions, turns,
|
|
186
|
+
subagentTurns, first, last, longLived, prNumber, mergedAt, changedLines, costPer100Lines`) across
|
|
187
|
+
every repository the plain-text table would show; combine with `--repo` to scope it to one. `--svg`
|
|
188
|
+
writes a shareable card built from a single branch or PR receipt (`--branch` or `--pr`); add `--png`
|
|
189
|
+
to also render a PNG through a local Chromium/Chrome (the SVG is still written if none is found).
|
|
190
|
+
Every colour on the card comes from `design/tokens.yaml`, so it matches the configured skin and
|
|
191
|
+
mode. See [exports-and-budgets.md](exports-and-budgets.md).
|
|
192
|
+
|
|
193
|
+
### `week`
|
|
194
|
+
"Your AI week": spend this calendar week (Monday through today) against last week, context share,
|
|
195
|
+
turns, sessions, the three costliest branches, and the single most expensive session, computed
|
|
196
|
+
from the same store as everything else.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
tokenflow week # text summary
|
|
200
|
+
tokenflow week --svg week.svg # a shareable card
|
|
201
|
+
tokenflow week --svg week.svg --png week.png # card + a local screenshot
|
|
202
|
+
tokenflow week --json # everything, machine-readable
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
See [exports-and-budgets.md](exports-and-budgets.md).
|
|
206
|
+
|
|
207
|
+
### `budget`
|
|
208
|
+
Monthly spend cap with projected-overrun alerts, deduplicated so a state only alerts once.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
tokenflow budget --set 200 # $200/month, warn at 80% projected
|
|
212
|
+
tokenflow budget # current state: safe / approaching / over
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
A `budgets:` list in `config.yaml` adds caps scoped to one repository or to the whole team,
|
|
216
|
+
alongside the single monthly cap above; `tokenflow budget` prints both when any are configured. See
|
|
217
|
+
[configuration.md](configuration.md) for the `budgets:` shape and
|
|
218
|
+
[exports-and-budgets.md](exports-and-budgets.md) for how each scope's spend is computed.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
### `guard`
|
|
223
|
+
A circuit breaker for the session you are in, run as a Claude Code hook. It reads the live
|
|
224
|
+
transcript with the same adapter and price table as everything else, then reports the running
|
|
225
|
+
spend, the size of the prompt now being re-sent, and the median cost of the last ten turns — and
|
|
226
|
+
warns or blocks against thresholds **you** declared. With none declared it is informational and
|
|
227
|
+
never blocks.
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
tokenflow guard --install # print the settings.json hooks block (not written)
|
|
231
|
+
tokenflow guard --set warnCostUsd=25,maxCostUsd=200,warnContextTokens=200000
|
|
232
|
+
tokenflow guard --set maxCostUsd= # clear one threshold
|
|
233
|
+
tokenflow guard --session ~/.claude/projects/<slug>/<id>.jsonl # judge one transcript
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Hook contract: stdin carries `{session_id, transcript_path, hook_event_name, …}`. A warning is
|
|
237
|
+
returned as JSON — `hookSpecificOutput.additionalContext` reaches the model,
|
|
238
|
+
`systemMessage` reaches you. A declared cap that is reached exits `2`, which blocks a
|
|
239
|
+
`PreToolUse` tool call or rejects a `UserPromptSubmit` prompt with the reason on stderr; `Stop`
|
|
240
|
+
and `SessionStart` are never blocked. The read is incremental: each session's byte offset and open
|
|
241
|
+
streaming groups are kept under `~/.tokenflow/guard/`, so a large transcript is read once.
|
|
242
|
+
|
|
243
|
+
Keys: `warnCostUsd`, `maxCostUsd`, `warnContextTokens`, `maxContextTokens`, `warnMarginalUsd`.
|
|
244
|
+
|
|
245
|
+
A repository can declare its own caps in `.tokenflow/policy.yaml`, committed with the code; a
|
|
246
|
+
declared key there wins over the personal config, key by key.
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
tokenflow guard --policy # effective policy for the current directory
|
|
250
|
+
tokenflow guard --policy --cwd <dir> # for some other directory / repository
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Codex CLI has no blocking hook, so its integration only warns, through an OS notification:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
tokenflow guard --install --codex # print the notify = [...] line for config.toml
|
|
257
|
+
tokenflow guard --install --codex --apply # append it, only if safe to do so
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
See [guard-codex.md](guard-codex.md) for the policy file shape and the Codex `notify` contract.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### `hooks`
|
|
265
|
+
Installs a `pre-push` git hook that attaches a receipt to the pushed commit as a git note under
|
|
266
|
+
`refs/notes/tokenflow`, so the receipt travels with the code instead of living only on this
|
|
267
|
+
machine. The [GitHub Action](../action/README.md) reads that note on a pull request and posts or
|
|
268
|
+
updates one comment.
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
tokenflow hooks install # write .git/hooks/pre-push
|
|
272
|
+
tokenflow hooks uninstall # remove it, restoring anything it replaced
|
|
273
|
+
tokenflow hooks status # installed? chained? where?
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The hook never blocks a push: every failure is reported on stderr with exit `0`. A hook it
|
|
277
|
+
replaces is kept as `pre-push.tokenflow-chained` and always runs first, keeping its own exit
|
|
278
|
+
code, so a pre-existing gate still gates. See [receipt-schema.md](receipt-schema.md).
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
145
282
|
### `providers`
|
|
146
283
|
What is detected, connected, or disabled, with the reason for each. `--json`.
|
|
147
284
|
|
|
@@ -168,6 +305,50 @@ Missing values are written as empty cells, never as `0`.
|
|
|
168
305
|
|
|
169
306
|
---
|
|
170
307
|
|
|
308
|
+
## Team & sync
|
|
309
|
+
|
|
310
|
+
### `sync`
|
|
311
|
+
Optional, off by default: exchange daily rollups (and, unless disabled, a branch/PR receipt
|
|
312
|
+
ledger) between machines through a folder you already sync, or by pushing to a server you run.
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
tokenflow sync # push this machine's files, then show the merged view
|
|
316
|
+
tokenflow sync --pull # merged view only, no push
|
|
317
|
+
tokenflow sync --off # disable sync entirely
|
|
318
|
+
tokenflow sync --to <url> --token <t> # push both files to a team server instead of a folder
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
`--to` (or `sync.to` in `config.yaml`) sends the exact same two files to
|
|
322
|
+
`tokenflow team serve` (or any server that speaks its `/api/rollup` contract) instead of writing
|
|
323
|
+
them into `sync.dir`; there is no merged view to pull from a server, so this only pushes. The
|
|
324
|
+
token can come from `--token`, `sync.token`, or the `TOKENFLOW_SYNC_TOKEN` environment variable.
|
|
325
|
+
`sync.receipts: false` skips the ledger file. See [ledger.md](ledger.md) and
|
|
326
|
+
[team-server.md](team-server.md).
|
|
327
|
+
|
|
328
|
+
### `team`
|
|
329
|
+
Per-developer usage, joined from every machine's synced rollups, plus the branch/PR cost ledger
|
|
330
|
+
once any machine has pushed one.
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
tokenflow team # aggregated view from the shared sync folder
|
|
334
|
+
tokenflow team --json # machine-readable
|
|
335
|
+
tokenflow team serve # self-hosted server: same aggregate, over HTTP
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
`team serve` runs one process on a machine the team owns (LAN or Docker) so every machine can push
|
|
339
|
+
with `sync --to` instead of writing into a shared folder.
|
|
340
|
+
|
|
341
|
+
| Flag | Default | Meaning |
|
|
342
|
+
|---|---|---|
|
|
343
|
+
| `--host` | `127.0.0.1` | bind address; non-loopback requires a token |
|
|
344
|
+
| `--port` | `7790` | bind port |
|
|
345
|
+
| `--dir` | `<TOKENFLOW_HOME>/team` | where uploaded rollups are stored |
|
|
346
|
+
| `--token` | — (falls back to `TOKENFLOW_TEAM_TOKEN`) | shared bearer token |
|
|
347
|
+
|
|
348
|
+
See [team-server.md](team-server.md) for the auth model and routes.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
171
352
|
## Live
|
|
172
353
|
|
|
173
354
|
These four read the watcher's snapshot (`data/status.json`) when it is fresh
|
|
@@ -261,6 +442,22 @@ fetched, and whether the source is official or third-party — plus the service-
|
|
|
261
442
|
that get applied per request, and the one premium tier that is deliberately *not* applied
|
|
262
443
|
(long-context).
|
|
263
444
|
|
|
445
|
+
### `pricing diff`
|
|
446
|
+
Compares a candidate price table to the current effective one (the overrides file layered on the
|
|
447
|
+
built-in table, same as every other command resolves it) and prints what would change: models
|
|
448
|
+
added, rates changed with the percent change per field, and current overrides the candidate says
|
|
449
|
+
nothing about.
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
tokenflow pricing diff table.json # print the diff only
|
|
453
|
+
tokenflow pricing diff table.json --apply # print, then confirm interactively (y/N)
|
|
454
|
+
tokenflow pricing diff table.json --apply --yes # print and apply, no prompt (scripts, CI)
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
`--apply` merges the candidate's models into the overrides file; it never replaces it, and a
|
|
458
|
+
model the candidate does not mention keeps its existing override untouched. Refused when the
|
|
459
|
+
candidate has an invalid entry, or (without `--yes`) when stdin is not an interactive terminal.
|
|
460
|
+
|
|
264
461
|
### `import <file>`
|
|
265
462
|
Generic import for CSV / TSV / JSON / JSONL / SQLite. With no `--field` flags it prints the
|
|
266
463
|
columns it found, infers a mapping, and previews five normalized rows.
|
|
@@ -328,6 +525,13 @@ tokenflow config import backup.json # restore on another machine
|
|
|
328
525
|
Runtime, `node:sqlite` availability, timezone, every path, store size, cube rows, sessions,
|
|
329
526
|
pending compaction, and every adapter's detection status. Start here when something is off.
|
|
330
527
|
|
|
528
|
+
After the Store section, an **Audit** block runs seven data-quality checks over the last three
|
|
529
|
+
months of records: git worktrees fragmenting a repository's spend, a recorded cwd outside any
|
|
530
|
+
repository, Codex records with no git branch, unpriced models, a stale built-in price table,
|
|
531
|
+
session-level sources present in per-turn views, and records explicitly marked
|
|
532
|
+
`metadata.repoResolved === false`. Each line names the check, its severity (`ok` / `info` / `warn`
|
|
533
|
+
/ `fail`), and a one-line fix when it is not clean.
|
|
534
|
+
|
|
331
535
|
### `validate`
|
|
332
536
|
Re-validates every stored record against the schema and reports the failure modes by frequency.
|
|
333
537
|
Exits non-zero if anything is invalid.
|