@drakulavich/zapara 0.1.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 +28 -0
- package/LICENSE +21 -0
- package/README.md +217 -0
- package/assets/characters.webp +0 -0
- package/assets/fonts/LICENSE-inter.txt +92 -0
- package/assets/fonts/LICENSE-jetbrains-mono.txt +93 -0
- package/assets/fonts/inter-400.woff2 +0 -0
- package/assets/fonts/inter-700.woff2 +0 -0
- package/assets/fonts/inter-800.woff2 +0 -0
- package/assets/fonts/jetbrains-mono-500.woff2 +0 -0
- package/package.json +27 -0
- package/src/analyze.ts +12 -0
- package/src/card.ts +162 -0
- package/src/cardhtml.ts +133 -0
- package/src/derive.ts +129 -0
- package/src/format.ts +20 -0
- package/src/image.ts +70 -0
- package/src/index.ts +141 -0
- package/src/parse.ts +105 -0
- package/src/render.ts +110 -0
- package/src/report.ts +18 -0
- package/src/scan.ts +40 -0
- package/src/score.ts +58 -0
- package/src/types.ts +15 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-09-18
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Project scaffold: Bun runs `src/index.ts` directly, `bun run check` typechecks and tests.
|
|
12
|
+
- `analyze()`: hourly buckets with sessions, prompts, decisions, context switches, active minutes, streak and late-night flag, scored 0–100, from real-format transcripts.
|
|
13
|
+
- CLI: `zapara week` and `zapara day` with `--json`, `--to`, `--days`, `--projects`; scans `~/.claude/projects`, skipping subagent transcripts and files older than the window.
|
|
14
|
+
- Week heatmap and day table with `--explain`; colors in a TTY, JSON in a pipe.
|
|
15
|
+
- Inbound messages from subagents, other sessions and background tasks are counted as `reports`, separate from human `prompts`; assistant output tokens are summed once per request as `outputTokens`. Both are measured and shown in the day table, week totals and JSON.
|
|
16
|
+
- `bun run stats`: per-hour signal distributions (n, p50, p75, p90, max, zero) over the active hours of a window, top 8 hours by reports and by human prompts, and a transcript-format drift line (records vs. events recognised by the parser), for comparing the same window across two machines before calibrating `src/score.ts`. Numbers only; never a file path or message text.
|
|
17
|
+
- `zapara card`: the last 14 days as one 2400×1260 picture (PNG, WebP, or the HTML page itself): one of four characters by dominant load, a sentence, the peak hour, the share of hours at each level, three highlights. Rendered locally by `Bun.WebView`; the page embeds its fonts and character sheet and references nothing. `--json` prints the card's data. The only command that writes a file.
|
|
18
|
+
- Published on npm as `@drakulavich/zapara`: `bunx @drakulavich/zapara@latest` runs it without a clone; releases publish from GitHub Actions with OIDC trusted publishing and provenance, no npm token.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Score weights are integer points of 100 so half-point sums round exactly; the index is the rounded sum of the unrounded weighted parts.
|
|
22
|
+
- Index calibrated on two machines × 14 days: parallel 25 (norm 4 sessions), pace 15 (norm 20 prompts/h), supervision 30 (3·decisions + reports + context switches, norm 45), reading 10 (norm 80k output tokens), streak 10, late night 10. `parts` in JSON and `--explain` are now six: `par pace sup read strk late`.
|
|
23
|
+
- Week footer: the legend drops the ranges and the "none" entry, the totals line drops its prefix and commas, and both are dimmed in a TTY; the level ranges are in `--help`; counts past 9 999 print compact (12k, 1.2M).
|
|
24
|
+
- Day table: an event column that is 0 in every row is left out, and one dimmed line names what the day had none of; the skeleton columns always show.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- A symlink loop or an unreadable directory anywhere under the projects root no longer aborts the scan as `projects directory not found`; that directory is skipped and every other transcript still counts. Symlinks are not followed.
|
|
28
|
+
- "1 session at once", "1 agent report", "1 prompt": the card sentence and the week footer use the singular for exactly one.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anton Yakutovich
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<h1 align="center">zapara</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
|
|
5
|
+
<a href="https://bun.sh"><img src="https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun" alt="Bun"></a>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center"><b>How hard was today?</b> zapara reads the transcripts Claude Code already writes on your machine and scores every hour 0–100 from parallel sessions, prompt pace, agent supervision, model output, streak length and late-night work. Nothing leaves the machine, no message text is kept.</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="assets/demo.webp" alt="zapara demo: week heatmap, day table, JSON" width="800">
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
Claude Code writes a JSONL transcript for every session under `~/.claude/projects`. zapara reads those files, puts each record in the local hour it happened in, and turns the hour into one number. A week is a heatmap of seven rows by 24 cells; a day is a table with one row per hour and, with `--explain`, the weighted contribution of each component. Nothing is installed into Claude Code, no hook is registered, and no network call is made.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install Bun if you do not have it (zapara needs 1.4 or newer)
|
|
20
|
+
curl -fsSL https://bun.sh/install | bash
|
|
21
|
+
|
|
22
|
+
# Your week, straight from the registry
|
|
23
|
+
bunx @drakulavich/zapara@latest
|
|
24
|
+
|
|
25
|
+
# A day, with the components behind each index
|
|
26
|
+
bunx @drakulavich/zapara@latest day --explain
|
|
27
|
+
|
|
28
|
+
# The card
|
|
29
|
+
bunx @drakulavich/zapara@latest card
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
To keep it around, `bun add -g @drakulavich/zapara` puts `zapara` on your PATH. There is no build step and no runtime dependency: Bun runs `src/index.ts` from the package as it is.
|
|
33
|
+
|
|
34
|
+
## What it looks like
|
|
35
|
+
|
|
36
|
+
Both pictures below come from the synthetic fixture in `tests/fixtures/busy-week`: a calm morning of one session, a five-session storm in the middle of the day, and a late tail that runs past midnight.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 peak active
|
|
40
|
+
Mon 14/09 · · · · · · · · · ░ ░ ░ █ █ █ · · · · · ░ ░ · ░ 87 6h00
|
|
41
|
+
Tue 15/09 · · · · · · · · · · ░ ░ ░ ░ ▒ ▒ ▒ ░ · · · · · · 33 4h00
|
|
42
|
+
Wed 16/09 · · · · · · · · · · · · · · · · · · · · · · · · - 0h00
|
|
43
|
+
Thu 17/09 · · · · · · · · · · · ░ ░ · · · · · · · · · · · 14 1h00
|
|
44
|
+
Fri 18/09 · · · · · · · · · · · · · · · ▓ · · · · · · · · 82 1h00
|
|
45
|
+
Sat 19/09 ░ ░ · · · · · · · · · · · · · · · · · · · · · · 24 1h00
|
|
46
|
+
Sun 20/09 · · · · · · · · · · · · · · · · · · · · · · · · - 0h00
|
|
47
|
+
|
|
48
|
+
░ calm ▒ warming ▓ heating █ fried
|
|
49
|
+
13h00 active 346 prompts 0 reports 100 decisions 5 sessions at once
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
hour index level sess prompts intr rej quest plan mode ctx-sw streak out-tok par pace sup read strk late
|
|
54
|
+
09:00 9 Calm 1 6 0 0 0 0 0 0 53m 600 0 4.5 0 0.1 4.4 0
|
|
55
|
+
10:00 14 Calm 1 6 0 0 0 0 0 0 113m 600 0 4.5 0 0.1 9.4 0
|
|
56
|
+
11:00 15 Calm 1 6 0 0 0 0 0 0 173m 600 0 4.5 0 0.1 10 0
|
|
57
|
+
12:00 87 Fried 5 55 20 1 1 1 2 54 237m 55.0k 25 15 30 6.9 10 0
|
|
58
|
+
13:00 87 Fried 5 55 20 1 1 1 2 54 297m 55.0k 25 15 30 6.9 10 0
|
|
59
|
+
14:00 87 Fried 5 55 20 1 1 1 2 54 357m 55.0k 25 15 30 6.9 10 0
|
|
60
|
+
20:00 9 Calm 1 6 0 0 0 0 0 0 53m 600 0 4.5 0 0.1 4.4 0
|
|
61
|
+
21:00 14 Calm 1 6 0 0 0 0 0 0 113m 600 0 4.5 0 0.1 9.4 0
|
|
62
|
+
23:00 19 Calm 1 6 0 0 0 0 0 0 53m 600 0 4.5 0 0.1 4.4 10
|
|
63
|
+
no reports today
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
<details>
|
|
67
|
+
<summary><b>Reproduce these pictures from the repository</b></summary>
|
|
68
|
+
|
|
69
|
+
Two commands in a terminal reproduce them. The fixture's timestamps are UTC and zapara buckets by local time, so pin the zone to get these exact hours:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
TZ=UTC bun src/index.ts week --projects tests/fixtures/busy-week --to 2026-09-20 --no-color
|
|
73
|
+
TZ=UTC bun src/index.ts day 2026-09-14 --projects tests/fixtures/busy-week --explain --no-color
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The grid is a fixed 98 columns wide, 100 with its hour header, and does not reflow, so it needs a terminal at least that wide.
|
|
77
|
+
|
|
78
|
+
</details>
|
|
79
|
+
|
|
80
|
+
## Share a card
|
|
81
|
+
|
|
82
|
+
`zapara card` turns your last two weeks into one picture: a character named after the kind of load that dominates your hours, the sentence behind it, the peak hour, the share of calm, warming, heating and fried hours, and three highlights. No dates, no hour totals, nothing that reads as a timesheet.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
zapara card # writes zapara-card.png in the current directory
|
|
86
|
+
zapara card --out card.webp # WebP instead; --out card.html writes the page itself
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
The Marathoner: Longest streak 7h53m without a break, 68% of your hours calm.
|
|
91
|
+
wrote zapara-card.png
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
<p align="center"><img src="assets/card.webp" alt="zapara card: The Marathoner, longest streak 7h53m, 68% of hours calm" width="800"></p>
|
|
95
|
+
|
|
96
|
+
This one comes from the same `busy-week` fixture as the pictures above. The picture is taken by a headless browser that Bun drives: WebKit on macOS, an installed Google Chrome elsewhere, so on Linux or Windows install Chrome, or write `--out card.html` and open the page in any browser.
|
|
97
|
+
|
|
98
|
+
## Usage
|
|
99
|
+
|
|
100
|
+
| Command | What it does |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `zapara` | Same as `zapara week`. |
|
|
103
|
+
| `zapara week` | The last 7 days ending today, in local time. |
|
|
104
|
+
| `zapara week --days 14 --to 2026-09-17` | Any window. `--days` takes an integer from 1 to 90. |
|
|
105
|
+
| `zapara day` | Today, one row per hour that had activity. |
|
|
106
|
+
| `zapara day 2026-09-14 --explain` | One day, with the six weighted components behind each index. |
|
|
107
|
+
| `zapara card` | The last 14 days as one shareable picture, `zapara-card.png` in the current directory. |
|
|
108
|
+
| `zapara card --days 30 --out me.webp` | Any window from 1 to 90 days; `.png`, `.webp` or `.html` by extension. `--json` prints the card's data instead. |
|
|
109
|
+
|
|
110
|
+
| Flag | What it does |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `--json` | Print the whole window as one JSON document instead of a table. |
|
|
113
|
+
| `--projects <dir>` | Read this directory instead of `~/.claude/projects`. |
|
|
114
|
+
| `--out <path>` | Where `card` writes; the extension picks the format. |
|
|
115
|
+
| `--no-color` | Plain glyphs with no ANSI codes. `NO_COLOR` in the environment does the same. |
|
|
116
|
+
| `--help` | Usage, exit 0. |
|
|
117
|
+
| `--version` | The version from `package.json`, exit 0. |
|
|
118
|
+
|
|
119
|
+
Levels: calm 0–29, warming 30–59, heating 60–84, fried 85–100.
|
|
120
|
+
|
|
121
|
+
`week` and `day` print a text table when stdout is a terminal and JSON otherwise, so `zapara week | cat` prints JSON; there is no flag to force text in a pipe yet. `card` always writes its file and prints its two lines, piped or not, and only `card --json` prints JSON.
|
|
122
|
+
|
|
123
|
+
Exit codes are 0 on success, 1 when the projects directory cannot be read, and 2 for a usage error such as a bad date or an unknown flag. A window with no data prints an empty grid and exits 0.
|
|
124
|
+
|
|
125
|
+
## Privacy
|
|
126
|
+
|
|
127
|
+
zapara reads `~/.claude/projects/**/*.jsonl`, taking only files modified inside the window and skipping subagent transcripts under `subagents/`. Message text is compared against a few fixed markers, for interrupts, tool rejections and inbound agent messages, and then discarded. What survives into an event is a timestamp, a session id, an event kind and a token count.
|
|
128
|
+
|
|
129
|
+
No message text, prompt length, file path or session title is kept, written or printed. The CLI never prints a path it derived or read, not even the projects root when it cannot open it. Nothing is sent anywhere, no file is written except the card you ask for, and nothing is installed into Claude Code.
|
|
130
|
+
|
|
131
|
+
## Limits
|
|
132
|
+
|
|
133
|
+
- Time is local and buckets are whole hours, so an hour that straddles midnight or a daylight-saving change is bucketed by the local clock. On a fall-back day two wall-clock hours share one label and merge.
|
|
134
|
+
- Only Claude Code transcripts are read. Work in other tools, and time away from the keyboard, is invisible.
|
|
135
|
+
- Files are chosen by modification time. A very old session touched today is read in full, but only its in-window events count.
|
|
136
|
+
- The transcript format is Claude Code's private format, built against version 2.1.274, and it may drift. `bun run stats` shows when it has.
|
|
137
|
+
- The norms come from two machines of one user working in auto mode. They are a starting point for a conversation about the metric, not a study.
|
|
138
|
+
- The 98-column grid does not adapt to a narrow terminal.
|
|
139
|
+
- For `week` and `day` a pipe always gets JSON, and there is no flag to ask for text instead.
|
|
140
|
+
- The card needs a browser engine: WebKit comes with macOS, elsewhere Google Chrome must be installed. `--out card.html` works everywhere.
|
|
141
|
+
|
|
142
|
+
## Under the hood
|
|
143
|
+
|
|
144
|
+
The formula behind the number and the transcript record behind every column, for when the number surprises you.
|
|
145
|
+
|
|
146
|
+
<details>
|
|
147
|
+
<summary><b>How the index works</b></summary>
|
|
148
|
+
|
|
149
|
+
Every hour that had activity gets six components normalized into `[0, 1]` and summed with integer weights:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
parallel = clamp((sessions - 1) / 4) # 1 session → 0, 3 → 0.5, 5+ → 1
|
|
153
|
+
pace = clamp(prompts / 20) # 10 prompts/hour → 0.5, 20+ → 1
|
|
154
|
+
supervision = clamp((3 * decisions + reports + contextSwitches) / 45) # 15 decisions alone → 1; 45 reports alone → 1
|
|
155
|
+
reading = clamp(outputTokens / 80000) # 40k → 0.5, 80k+ → 1
|
|
156
|
+
streak = clamp(streakMin / 120) # 60 min → 0.5, 2h+ → 1
|
|
157
|
+
late = lateNight ? 1 : 0
|
|
158
|
+
|
|
159
|
+
index = round(25*parallel + 15*pace + 30*supervision + 10*reading + 10*streak + 10*late)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`decisions` is the sum of interrupts, tool rejections, questions, plan reviews and permission-mode switches. Without the late-night flag the index tops out at 90. An hour with no activity has no index at all: it renders as `·` and is `null` in JSON.
|
|
163
|
+
|
|
164
|
+
| Level | Range |
|
|
165
|
+
|---|---|
|
|
166
|
+
| Calm | 0–29 |
|
|
167
|
+
| Warming | 30–59 |
|
|
168
|
+
| Heating | 60–84 |
|
|
169
|
+
| Fried | 85–100 |
|
|
170
|
+
|
|
171
|
+
The norms come from two machines, 14 days each, of real transcripts covering 116 and 114 active hours. The surprise in that data was how rare explicit decisions are: in auto mode the p90 is 3 decisions per hour, so a component built on decisions alone reads near zero on hours that felt heavy. What those hours actually cost is reading the reports agents send back, switching between sessions, and getting through the volume of model output, which is why supervision carries 30 points and reading 10. Human prompts reached a p90 of 13 per hour on one machine and 20 on the other, hence the pace norm of 20. The parallel-session threshold follows the research this project started from rather than the transcripts: BCG and HBR report that productivity drops past three simultaneous AI tools, and Osmani makes the same point as three focused teammates beating five scattered ones.
|
|
172
|
+
|
|
173
|
+
Weights and norms live in one exported constant in `src/score.ts`, so a recalibration is one diff there plus a line in `CHANGELOG.md`.
|
|
174
|
+
|
|
175
|
+
</details>
|
|
176
|
+
|
|
177
|
+
<details>
|
|
178
|
+
<summary><b>What each column of the day table counts</b></summary>
|
|
179
|
+
|
|
180
|
+
| Column | What it counts |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `sess` | Distinct session ids with at least one user or assistant record in the hour. |
|
|
183
|
+
| `prompts` | Messages the human typed. A `user` record whose text is neither an interrupt marker nor an agent-message marker; `isMeta` and sidechain records are excluded. |
|
|
184
|
+
| `rep` | Inbound messages from subagents, other sessions and background tasks. A `user` record whose text starts with one of the agent-message markers, which is something to read and react to rather than something typed. |
|
|
185
|
+
| `intr` | Interrupts. A `user` record whose text block starts with `[Request interrupted by user`, covering both the plain and the tool-use form. |
|
|
186
|
+
| `rej` | Tool rejections. A `tool_result` block saying the user did not want to proceed with that tool use. |
|
|
187
|
+
| `quest` | `AskUserQuestion` tool calls in an assistant message, one per block. |
|
|
188
|
+
| `plan` | `ExitPlanMode` tool calls in an assistant message, one per block. |
|
|
189
|
+
| `mode` | Permission-mode switches. A `permission-mode` record whose mode differs from the previous one; the first record of a session sets the baseline and repeats of the same mode count nothing. |
|
|
190
|
+
| `ctx-sw` | Context switches. Over the hour's prompts in time order, the number of consecutive pairs that came from different sessions. |
|
|
191
|
+
| `streak` | Minutes since the current activity streak began, which may reach back before the hour. A gap longer than 10 minutes between records breaks the streak. |
|
|
192
|
+
| `out-tok` | Assistant output tokens, summed once per request and only for requests that produced a text block. Claude Code repeats the same usage on each content block of a response, and a request holding only tool calls is not text anyone reads. |
|
|
193
|
+
|
|
194
|
+
`bun run stats --days 14` is the tool the norms were set with. It prints the per-hour distribution of each signal over the active hours of a window (n, p50, p75, p90, max, and how many hours were zero), the top hours by reports and by human prompts, and a format-drift line comparing records seen against events the parser recognised. Run it on another machine, or after a Claude Code update, to see whether the norms and the parser still fit. It prints numbers and nothing else.
|
|
195
|
+
|
|
196
|
+
</details>
|
|
197
|
+
|
|
198
|
+
## Development
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
git clone git@github.com:drakulavich/zapara.git
|
|
202
|
+
cd zapara
|
|
203
|
+
bun install
|
|
204
|
+
bun link
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`bun link` registers the clone's `bin` entry, so `zapara` runs this checkout; without it, `bun src/index.ts week` does the same thing.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
bun run check # tsc --noEmit, then the test suite under TZ=UTC
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Tests are fixture-driven: they build or load transcripts in the real Claude Code format and assert the statistics that come out of the public seams, `analyze()`, `report()` and the CLI itself. No test imports the parser, the deriver or the scanner, so refactoring internals never touches a test. The rules every change follows are in [CLAUDE.md](CLAUDE.md), the design is in [docs/superpowers/specs/2026-09-17-zapara-design.md](docs/superpowers/specs/2026-09-17-zapara-design.md), and every change is recorded in [CHANGELOG.md](CHANGELOG.md).
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
MIT. See [LICENSE](LICENSE).
|
|
Binary file
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Copyright (c) 2016 The Inter Project Authors (https://github.com/rsms/inter)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
-----------------------------------------------------------
|
|
8
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
9
|
+
-----------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
PREAMBLE
|
|
12
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
13
|
+
development of collaborative font projects, to support the font creation
|
|
14
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
15
|
+
open framework in which fonts may be shared and improved in partnership
|
|
16
|
+
with others.
|
|
17
|
+
|
|
18
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
19
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
20
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
21
|
+
redistributed and/or sold with any software provided that any reserved
|
|
22
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
23
|
+
however, cannot be released under any other type of license. The
|
|
24
|
+
requirement for fonts to remain under this license does not apply
|
|
25
|
+
to any document created using the fonts or their derivatives.
|
|
26
|
+
|
|
27
|
+
DEFINITIONS
|
|
28
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
29
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
30
|
+
include source files, build scripts and documentation.
|
|
31
|
+
|
|
32
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
33
|
+
copyright statement(s).
|
|
34
|
+
|
|
35
|
+
"Original Version" refers to the collection of Font Software components as
|
|
36
|
+
distributed by the Copyright Holder(s).
|
|
37
|
+
|
|
38
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
39
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
40
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
41
|
+
new environment.
|
|
42
|
+
|
|
43
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
44
|
+
writer or other person who contributed to the Font Software.
|
|
45
|
+
|
|
46
|
+
PERMISSION AND CONDITIONS
|
|
47
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
48
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
49
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
50
|
+
Software, subject to the following conditions:
|
|
51
|
+
|
|
52
|
+
1) Neither the Font Software nor any of its individual components,
|
|
53
|
+
in Original or Modified Versions, may be sold by itself.
|
|
54
|
+
|
|
55
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
56
|
+
redistributed and/or sold with any software, provided that each copy
|
|
57
|
+
contains the above copyright notice and this license. These can be
|
|
58
|
+
included either as stand-alone text files, human-readable headers or
|
|
59
|
+
in the appropriate machine-readable metadata fields within text or
|
|
60
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
61
|
+
|
|
62
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
63
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
64
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
65
|
+
presented to the users.
|
|
66
|
+
|
|
67
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
68
|
+
Software shall not be used to promote, endorse or advertise any
|
|
69
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
70
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
71
|
+
permission.
|
|
72
|
+
|
|
73
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
74
|
+
must be distributed entirely under this license, and must not be
|
|
75
|
+
distributed under any other license. The requirement for fonts to
|
|
76
|
+
remain under this license does not apply to any document created
|
|
77
|
+
using the Font Software.
|
|
78
|
+
|
|
79
|
+
TERMINATION
|
|
80
|
+
This license becomes null and void if any of the above conditions are
|
|
81
|
+
not met.
|
|
82
|
+
|
|
83
|
+
DISCLAIMER
|
|
84
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
85
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
86
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
87
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
88
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
89
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
90
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
91
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
92
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
https://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drakulavich/zapara",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cognitive load index for people driving Claude Code, computed locally from transcripts",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Anton Yakutovich",
|
|
7
|
+
"repository": { "type": "git", "url": "git+https://github.com/drakulavich/zapara.git" },
|
|
8
|
+
"homepage": "https://github.com/drakulavich/zapara#readme",
|
|
9
|
+
"bugs": "https://github.com/drakulavich/zapara/issues",
|
|
10
|
+
"keywords": ["claude-code", "cognitive-load", "developer-experience", "cli", "bun"],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"bin": { "zapara": "src/index.ts" },
|
|
13
|
+
"files": ["src/", "assets/fonts/", "assets/characters.webp", "CHANGELOG.md", "!src/**/*.test.ts"],
|
|
14
|
+
"publishConfig": { "access": "public" },
|
|
15
|
+
"engines": { "bun": ">=1.4.0" },
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "bun run src/index.ts",
|
|
18
|
+
"stats": "bun scripts/signal-stats.ts",
|
|
19
|
+
"test": "TZ=UTC bun test",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"check": "bun run typecheck && bun run test"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/bun": "^1.4.0",
|
|
25
|
+
"typescript": "^7.0.2"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/analyze.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { derive } from "./derive.ts";
|
|
2
|
+
import { parseTranscript } from "./parse.ts";
|
|
3
|
+
import type { Day, Transcript, Window } from "./types.ts";
|
|
4
|
+
|
|
5
|
+
// The core's entry point: transcripts already in memory, in the real JSONL format, in.
|
|
6
|
+
// Day[] out. Transcripts are sorted by path so file discovery order cannot change a result.
|
|
7
|
+
export function analyze(transcripts: Transcript[], w: Window): Day[] {
|
|
8
|
+
const events = [...transcripts]
|
|
9
|
+
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0))
|
|
10
|
+
.flatMap((t) => parseTranscript(t.text));
|
|
11
|
+
return derive(events, w);
|
|
12
|
+
}
|