kompactor 0.7.3__tar.gz → 0.7.5__tar.gz

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.

Potentially problematic release.


This version of kompactor might be problematic. Click here for more details.

Files changed (42) hide show
  1. {kompactor-0.7.3 → kompactor-0.7.5}/PKG-INFO +53 -8
  2. {kompactor-0.7.3 → kompactor-0.7.5}/README.md +52 -7
  3. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/__init__.py +1 -1
  4. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/cli.py +8 -3
  5. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/hooks.py +1 -23
  6. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/installer.py +14 -5
  7. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/usage.html +1 -1
  8. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/store.py +17 -0
  9. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor.egg-info/PKG-INFO +53 -8
  10. {kompactor-0.7.3 → kompactor-0.7.5}/pyproject.toml +1 -1
  11. {kompactor-0.7.3 → kompactor-0.7.5}/tests/test_kompactor.py +12 -17
  12. {kompactor-0.7.3 → kompactor-0.7.5}/LICENSE +0 -0
  13. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/__main__.py +0 -0
  14. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/_launch.py +0 -0
  15. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/config.py +0 -0
  16. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/estimate.py +0 -0
  17. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/filters.py +0 -0
  18. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/graph.py +0 -0
  19. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/langs.py +0 -0
  20. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/outline.py +0 -0
  21. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/prove.py +0 -0
  22. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/rules.py +0 -0
  23. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/samples.py +0 -0
  24. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/server.py +0 -0
  25. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/shellwrap.py +0 -0
  26. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/fonts/OFL.txt +0 -0
  27. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/fonts/plex-mono-400.woff2 +0 -0
  28. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/fonts/plex-mono-500.woff2 +0 -0
  29. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/fonts/plex-sans-400.woff2 +0 -0
  30. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/fonts/plex-sans-500.woff2 +0 -0
  31. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/fonts/plex-sans-600.woff2 +0 -0
  32. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/static/logo-mark.svg +0 -0
  33. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/tokens.py +0 -0
  34. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor/ui.html +0 -0
  35. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor.egg-info/SOURCES.txt +0 -0
  36. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor.egg-info/dependency_links.txt +0 -0
  37. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor.egg-info/entry_points.txt +0 -0
  38. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor.egg-info/requires.txt +0 -0
  39. {kompactor-0.7.3 → kompactor-0.7.5}/kompactor.egg-info/top_level.txt +0 -0
  40. {kompactor-0.7.3 → kompactor-0.7.5}/setup.cfg +0 -0
  41. {kompactor-0.7.3 → kompactor-0.7.5}/tests/test_graph.py +0 -0
  42. {kompactor-0.7.3 → kompactor-0.7.5}/tests/test_shell.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kompactor
3
- Version: 0.7.3
3
+ Version: 0.7.5
4
4
  Summary: Cut the tokens Claude Code spends on noise: compacted command output (failing runs included), outline-first reads, a local code graph, and per-turn savings.
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/amarkum/kompactor
@@ -21,7 +21,10 @@ Requires-Dist: tiktoken>=0.7; extra == "exact"
21
21
  Dynamic: license-file
22
22
 
23
23
  <p align="center">
24
- <img src="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-light.png" alt="Kompactor" width="240">
24
+ <picture>
25
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-dark.png">
26
+ <img src="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-light.png" alt="Kompactor" width="240">
27
+ </picture>
25
28
  </p>
26
29
 
27
30
  <p align="center"><b>Cut the tokens Claude Code spends on noise.</b><br>
@@ -39,7 +42,7 @@ Most of a Claude Code session's tokens aren't your prompt or Claude's answer. Th
39
42
 
40
43
  ## Contents
41
44
 
42
- [Install](#install) · [After you install](#after-you-install) · [How it works](#how-it-works) · [Use it from Claude Code](#use-it-from-claude-code) · [Quality vs tokens](#quality-vs-tokens) · [See your savings](#see-your-savings) · [Turn it off](#turn-it-off-or-remove-it) · [Evidence](#evidence) · [Code graph](#code-graph) · [Teams](#use-it-across-a-team) · [Commands](#commands)
45
+ [Install](#install) · [After you install](#after-you-install) · [How it works](#how-it-works) · [Examples](#examples) · [Use it from Claude Code](#use-it-from-claude-code) · [Quality vs tokens](#quality-vs-tokens) · [See your savings](#see-your-savings) · [Turn it off](#turn-it-off-or-remove-it) · [What it runs and changes](#what-kompactor-runs-and-changes) · [Evidence](#evidence) · [Code graph](#code-graph) · [Teams](#use-it-across-a-team) · [Troubleshooting](#troubleshooting) · [Commands](#commands) · [Support](#support-and-security)
43
46
 
44
47
  ## Install
45
48
 
@@ -59,7 +62,7 @@ pipx install kompactor # or: pip install kompactor · uv tool install kom
59
62
  kompactor setup
60
63
  ```
61
64
 
62
- **VS Code.** Download `kompactor-0.7.3.vsix` from [Releases](https://github.com/amarkum/kompactor/releases), install it (Extensions › `⋯` › *Install from VSIX…*), and click **Enable** when Kompactor asks.
65
+ **VS Code.** Download `kompactor-0.7.5.vsix` from [Releases](https://github.com/amarkum/kompactor/releases), install it (Extensions › `⋯` › *Install from VSIX…*), and click **Enable** when Kompactor asks.
63
66
 
64
67
  ## After you install
65
68
 
@@ -67,7 +70,7 @@ kompactor setup
67
70
  2. **Work as usual.** You don't call Kompactor; it acts on Claude's tool calls by itself. After each reply you'll see a line such as `Kompactor: −8.2k tokens this turn`, and the status line shows the running total.
68
71
  3. **Check it's on:** run `kompactor` in a terminal (or `/kompactor` in Claude Code). It shows whether it's on, your level, savings so far and how to change things:
69
72
  ```
70
- Kompactor 0.7.3: ON
73
+ Kompactor 0.7.5: ON
71
74
  Connected via the Claude Code plugin. New Claude Code sessions are compacted.
72
75
  Level: Balanced (level 3 of 5). Change it: `kompactor config preset 1-5`, the dashboard, or /kompactor balanced
73
76
  Saved so far: 41.3k tokens (−38%) over 52 tool calls. Details: `kompactor gain`
@@ -88,6 +91,18 @@ Kompactor uses Claude Code's hooks, so it sits between Claude's tools and Claude
88
91
 
89
92
  **Nothing is lost.** Every shortened output keeps a raw copy that Claude can fetch with `kompactor show <id>`. Kompactor never changes what a command does, your permission rules still apply, and if anything goes wrong it lets the original output through. Everything runs on your machine; nothing is sent anywhere ([privacy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md)).
90
93
 
94
+ ## Examples
95
+
96
+ Ask Claude Code the way you normally would. These prompts come from the [evidence](#evidence) runs:
97
+
98
+ | You ask | What Kompactor does | Measured |
99
+ |---|---|---|
100
+ | "Run the tests and tell me which one fails and why." | Claude sees the failing test, its error and the summary line instead of hundreds of passing tests, even when the run exits non-zero. | Go: −33% tokens; vitest: −37% cost |
101
+ | "Where is the session cookie signed?" | Big files arrive as outlines with line ranges, and the first search adds a short map of the main modules, so Claude finds the code in fewer turns. | Flask: −26% tokens |
102
+ | "Install the dependencies and check that it builds." | Download and progress chatter is dropped; warnings, errors and the result are kept. | `npm install`: ~33,000 › 469 tokens |
103
+ | "What does changing `createStore` affect?" | The code graph lists the files that use it and the tests to run. | 27/27 answers correct |
104
+ | `/kompactor review` | Lists cuts in your current diff: duplicated helpers, unused options, dead branches. | |
105
+
91
106
  ## Use it from Claude Code
92
107
 
93
108
  Type these in any Claude Code chat:
@@ -140,6 +155,20 @@ kompactor uninstall # remove hooks, status line, shell prefix and skill (add -
140
155
  ```
141
156
  Then remove the package the way you installed it: `claude plugin uninstall kompactor`, `pipx uninstall kompactor`, or uninstall the extension in VS Code. Setup never replaces a status line or shell prefix you already had, and keeps a backup in `~/.claude/settings.json.kompactor-backup`.
142
157
 
158
+ ## What Kompactor runs and changes
159
+
160
+ Everything below happens on your machine. Kompactor makes no network requests of its own and has no telemetry. (If you install the optional `tiktoken` package for exact token counts, tiktoken downloads its token table once, on first use.)
161
+
162
+ **What it runs.** Python hooks that Claude Code calls (`python3 kompactor/_launch.py hook <name>` from the plugin folder): after Bash, Read, Grep, Glob and edit tool calls; before Read; at session start, subagent start and before compaction; and when a turn ends. After `/kompactor setup` on macOS and Linux, Claude's Bash commands run through Kompactor's shell prefix (`~/.kompactor/bin/kompactor-shell`) so failing runs can be compacted too. The prefix runs after Claude Code's permission check, on Claude's original command, and returns its exit code unchanged.
163
+
164
+ **What it adds to Claude's context.** Compacted tool output, outlines in place of large file reads, a code map once per session, a one-line savings note after each turn, and short reply rules at session start and for subagents. The rules are plain text you can read in [`kompactor/rules.py`](https://github.com/amarkum/kompactor/blob/main/kompactor/rules.py); the level decides which ones apply (none at level 1).
165
+
166
+ **What it reads.** The tool output Claude Code passes to its hooks, the files Claude reads (to outline them), your project's source (for the code graph), and the context size Claude Code gives the status line. Hooks never read your session transcripts. `kompactor estimate` and the dashboard's **Replay your history** panel read your local Claude Code session logs in `~/.claude/projects`, only when you run them, to show what Kompactor would have saved; the result isn't stored or sent anywhere.
167
+
168
+ **What it writes.** `~/.kompactor/`: settings, the savings ledger, raw copies of shortened output (for `kompactor show`), the code graph index and the `~/.kompactor/bin/` launchers (written at session start). History is deleted after 30 days by default (`retention_days`). Setup edits `~/.claude/settings.json` to add Kompactor's hooks (non-plugin installs), a status line (only if you have none) and `CLAUDE_CODE_SHELL_PREFIX` (only if it isn't set), keeping a backup in `settings.json.kompactor-backup`; non-plugin installs also get the `/kompactor` skill in `~/.claude/skills`. Nothing is written to your repositories.
169
+
170
+ **What it never does.** Change your permission rules or sandbox settings, change what a command does, or send your code or output anywhere. Details: [privacy policy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md).
171
+
143
172
  ## Evidence
144
173
 
145
174
  `kompactor prove` runs each task in real headless Claude Code sessions (`claude -p`), 3 times without Kompactor and 3 times with it, interleaved, with the same prompt, tools, turn limit and budget cap. Tokens are input + cache writes + cache reads + output across every API call; cost is Claude Code's own figure.
@@ -218,7 +247,17 @@ When a teammate trusts the folder, Claude Code installs the plugin; each person
218
247
  { "preset": 4, "overrides": { "never_compact": ["terraform plan"] } }
219
248
  ```
220
249
 
221
- **Admins** can put the same keys in Claude Code's managed settings to roll it out to every machine. To control what your team runs, fork the repo and use `your-org/kompactor` in the commands above. The [usage guide](https://github.com/amarkum/kompactor/blob/main/docs/usage.html) (also in the dashboard as **Guide**) covers rollout, troubleshooting and FAQs.
250
+ **Admins** can put the same keys in Claude Code's managed settings to roll it out to every machine. To control what your team runs, fork the repo and use `your-org/kompactor` in the commands above. The dashboard's **Guide** page covers rollout, settings and FAQs in more detail.
251
+
252
+ ## Troubleshooting
253
+
254
+ | Problem | What to do |
255
+ |---|---|
256
+ | No savings line after replies | Restart Claude Code after setup, then run `kompactor doctor`: it checks the installation and shows recent hook errors. The dashboard's **Self-test** panel runs every hook once. |
257
+ | An output looks too short | Claude can fetch the original with `kompactor show <id>` (the id is in the shortened output), or search earlier outputs with `kompactor search "text"`. |
258
+ | A command should never be shortened | Add it to **Never compact these commands** on the dashboard, or in `.kompactor.json` (`"never_compact": ["terraform plan"]`). For a single run, start the command with `KOMPACTOR_RAW=1`. |
259
+ | Something seems off | `kompactor stop` turns Kompactor off at once; every hook also lets the original output through if it hits an error. Errors are logged to `~/.kompactor/hook-errors.log`. |
260
+ | The dashboard port is busy | `kompactor start` picks a free port by itself and prints the address. |
222
261
 
223
262
  ## Commands
224
263
 
@@ -239,7 +278,7 @@ kompactor ui the dashboard, in the foreground
239
278
  ## Development
240
279
 
241
280
  ```bash
242
- python -m unittest discover tests # 81 tests, including the real shell prefix run as Claude Code runs it
281
+ python -m unittest discover tests # 80 tests, including the real shell prefix run as Claude Code runs it
243
282
  node vscode/test/smoke.js # VS Code extension against a mock API
244
283
  ./scripts/build-vsix.sh # › dist/kompactor-<version>.vsix
245
284
  python scripts/bump_version.py X.Y.Z # bump every version; pushing to main publishes to PyPI
@@ -249,4 +288,10 @@ How Claude Code's hooks behave on success, failure and very long output, and why
249
288
 
250
289
  Kompactor was called Kompact until version 0.4; installing it replaces an old Kompact install's settings.
251
290
 
252
- [Privacy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md) · [MIT License](https://github.com/amarkum/kompactor/blob/main/LICENSE)
291
+ ## Support and security
292
+
293
+ - **Questions, bugs and ideas:** [open an issue](https://github.com/amarkum/kompactor/issues).
294
+ - **Security problems:** please report them privately, as described in [SECURITY.md](https://github.com/amarkum/kompactor/blob/main/SECURITY.md).
295
+ - **Privacy:** [PRIVACY.md](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md) explains what Kompactor stores and how to delete it.
296
+
297
+ [MIT License](https://github.com/amarkum/kompactor/blob/main/LICENSE)
@@ -1,5 +1,8 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-light.png" alt="Kompactor" width="240">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-dark.png">
4
+ <img src="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-light.png" alt="Kompactor" width="240">
5
+ </picture>
3
6
  </p>
4
7
 
5
8
  <p align="center"><b>Cut the tokens Claude Code spends on noise.</b><br>
@@ -17,7 +20,7 @@ Most of a Claude Code session's tokens aren't your prompt or Claude's answer. Th
17
20
 
18
21
  ## Contents
19
22
 
20
- [Install](#install) · [After you install](#after-you-install) · [How it works](#how-it-works) · [Use it from Claude Code](#use-it-from-claude-code) · [Quality vs tokens](#quality-vs-tokens) · [See your savings](#see-your-savings) · [Turn it off](#turn-it-off-or-remove-it) · [Evidence](#evidence) · [Code graph](#code-graph) · [Teams](#use-it-across-a-team) · [Commands](#commands)
23
+ [Install](#install) · [After you install](#after-you-install) · [How it works](#how-it-works) · [Examples](#examples) · [Use it from Claude Code](#use-it-from-claude-code) · [Quality vs tokens](#quality-vs-tokens) · [See your savings](#see-your-savings) · [Turn it off](#turn-it-off-or-remove-it) · [What it runs and changes](#what-kompactor-runs-and-changes) · [Evidence](#evidence) · [Code graph](#code-graph) · [Teams](#use-it-across-a-team) · [Troubleshooting](#troubleshooting) · [Commands](#commands) · [Support](#support-and-security)
21
24
 
22
25
  ## Install
23
26
 
@@ -37,7 +40,7 @@ pipx install kompactor # or: pip install kompactor · uv tool install kom
37
40
  kompactor setup
38
41
  ```
39
42
 
40
- **VS Code.** Download `kompactor-0.7.3.vsix` from [Releases](https://github.com/amarkum/kompactor/releases), install it (Extensions › `⋯` › *Install from VSIX…*), and click **Enable** when Kompactor asks.
43
+ **VS Code.** Download `kompactor-0.7.5.vsix` from [Releases](https://github.com/amarkum/kompactor/releases), install it (Extensions › `⋯` › *Install from VSIX…*), and click **Enable** when Kompactor asks.
41
44
 
42
45
  ## After you install
43
46
 
@@ -45,7 +48,7 @@ kompactor setup
45
48
  2. **Work as usual.** You don't call Kompactor; it acts on Claude's tool calls by itself. After each reply you'll see a line such as `Kompactor: −8.2k tokens this turn`, and the status line shows the running total.
46
49
  3. **Check it's on:** run `kompactor` in a terminal (or `/kompactor` in Claude Code). It shows whether it's on, your level, savings so far and how to change things:
47
50
  ```
48
- Kompactor 0.7.3: ON
51
+ Kompactor 0.7.5: ON
49
52
  Connected via the Claude Code plugin. New Claude Code sessions are compacted.
50
53
  Level: Balanced (level 3 of 5). Change it: `kompactor config preset 1-5`, the dashboard, or /kompactor balanced
51
54
  Saved so far: 41.3k tokens (−38%) over 52 tool calls. Details: `kompactor gain`
@@ -66,6 +69,18 @@ Kompactor uses Claude Code's hooks, so it sits between Claude's tools and Claude
66
69
 
67
70
  **Nothing is lost.** Every shortened output keeps a raw copy that Claude can fetch with `kompactor show <id>`. Kompactor never changes what a command does, your permission rules still apply, and if anything goes wrong it lets the original output through. Everything runs on your machine; nothing is sent anywhere ([privacy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md)).
68
71
 
72
+ ## Examples
73
+
74
+ Ask Claude Code the way you normally would. These prompts come from the [evidence](#evidence) runs:
75
+
76
+ | You ask | What Kompactor does | Measured |
77
+ |---|---|---|
78
+ | "Run the tests and tell me which one fails and why." | Claude sees the failing test, its error and the summary line instead of hundreds of passing tests, even when the run exits non-zero. | Go: −33% tokens; vitest: −37% cost |
79
+ | "Where is the session cookie signed?" | Big files arrive as outlines with line ranges, and the first search adds a short map of the main modules, so Claude finds the code in fewer turns. | Flask: −26% tokens |
80
+ | "Install the dependencies and check that it builds." | Download and progress chatter is dropped; warnings, errors and the result are kept. | `npm install`: ~33,000 › 469 tokens |
81
+ | "What does changing `createStore` affect?" | The code graph lists the files that use it and the tests to run. | 27/27 answers correct |
82
+ | `/kompactor review` | Lists cuts in your current diff: duplicated helpers, unused options, dead branches. | |
83
+
69
84
  ## Use it from Claude Code
70
85
 
71
86
  Type these in any Claude Code chat:
@@ -118,6 +133,20 @@ kompactor uninstall # remove hooks, status line, shell prefix and skill (add -
118
133
  ```
119
134
  Then remove the package the way you installed it: `claude plugin uninstall kompactor`, `pipx uninstall kompactor`, or uninstall the extension in VS Code. Setup never replaces a status line or shell prefix you already had, and keeps a backup in `~/.claude/settings.json.kompactor-backup`.
120
135
 
136
+ ## What Kompactor runs and changes
137
+
138
+ Everything below happens on your machine. Kompactor makes no network requests of its own and has no telemetry. (If you install the optional `tiktoken` package for exact token counts, tiktoken downloads its token table once, on first use.)
139
+
140
+ **What it runs.** Python hooks that Claude Code calls (`python3 kompactor/_launch.py hook <name>` from the plugin folder): after Bash, Read, Grep, Glob and edit tool calls; before Read; at session start, subagent start and before compaction; and when a turn ends. After `/kompactor setup` on macOS and Linux, Claude's Bash commands run through Kompactor's shell prefix (`~/.kompactor/bin/kompactor-shell`) so failing runs can be compacted too. The prefix runs after Claude Code's permission check, on Claude's original command, and returns its exit code unchanged.
141
+
142
+ **What it adds to Claude's context.** Compacted tool output, outlines in place of large file reads, a code map once per session, a one-line savings note after each turn, and short reply rules at session start and for subagents. The rules are plain text you can read in [`kompactor/rules.py`](https://github.com/amarkum/kompactor/blob/main/kompactor/rules.py); the level decides which ones apply (none at level 1).
143
+
144
+ **What it reads.** The tool output Claude Code passes to its hooks, the files Claude reads (to outline them), your project's source (for the code graph), and the context size Claude Code gives the status line. Hooks never read your session transcripts. `kompactor estimate` and the dashboard's **Replay your history** panel read your local Claude Code session logs in `~/.claude/projects`, only when you run them, to show what Kompactor would have saved; the result isn't stored or sent anywhere.
145
+
146
+ **What it writes.** `~/.kompactor/`: settings, the savings ledger, raw copies of shortened output (for `kompactor show`), the code graph index and the `~/.kompactor/bin/` launchers (written at session start). History is deleted after 30 days by default (`retention_days`). Setup edits `~/.claude/settings.json` to add Kompactor's hooks (non-plugin installs), a status line (only if you have none) and `CLAUDE_CODE_SHELL_PREFIX` (only if it isn't set), keeping a backup in `settings.json.kompactor-backup`; non-plugin installs also get the `/kompactor` skill in `~/.claude/skills`. Nothing is written to your repositories.
147
+
148
+ **What it never does.** Change your permission rules or sandbox settings, change what a command does, or send your code or output anywhere. Details: [privacy policy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md).
149
+
121
150
  ## Evidence
122
151
 
123
152
  `kompactor prove` runs each task in real headless Claude Code sessions (`claude -p`), 3 times without Kompactor and 3 times with it, interleaved, with the same prompt, tools, turn limit and budget cap. Tokens are input + cache writes + cache reads + output across every API call; cost is Claude Code's own figure.
@@ -196,7 +225,17 @@ When a teammate trusts the folder, Claude Code installs the plugin; each person
196
225
  { "preset": 4, "overrides": { "never_compact": ["terraform plan"] } }
197
226
  ```
198
227
 
199
- **Admins** can put the same keys in Claude Code's managed settings to roll it out to every machine. To control what your team runs, fork the repo and use `your-org/kompactor` in the commands above. The [usage guide](https://github.com/amarkum/kompactor/blob/main/docs/usage.html) (also in the dashboard as **Guide**) covers rollout, troubleshooting and FAQs.
228
+ **Admins** can put the same keys in Claude Code's managed settings to roll it out to every machine. To control what your team runs, fork the repo and use `your-org/kompactor` in the commands above. The dashboard's **Guide** page covers rollout, settings and FAQs in more detail.
229
+
230
+ ## Troubleshooting
231
+
232
+ | Problem | What to do |
233
+ |---|---|
234
+ | No savings line after replies | Restart Claude Code after setup, then run `kompactor doctor`: it checks the installation and shows recent hook errors. The dashboard's **Self-test** panel runs every hook once. |
235
+ | An output looks too short | Claude can fetch the original with `kompactor show <id>` (the id is in the shortened output), or search earlier outputs with `kompactor search "text"`. |
236
+ | A command should never be shortened | Add it to **Never compact these commands** on the dashboard, or in `.kompactor.json` (`"never_compact": ["terraform plan"]`). For a single run, start the command with `KOMPACTOR_RAW=1`. |
237
+ | Something seems off | `kompactor stop` turns Kompactor off at once; every hook also lets the original output through if it hits an error. Errors are logged to `~/.kompactor/hook-errors.log`. |
238
+ | The dashboard port is busy | `kompactor start` picks a free port by itself and prints the address. |
200
239
 
201
240
  ## Commands
202
241
 
@@ -217,7 +256,7 @@ kompactor ui the dashboard, in the foreground
217
256
  ## Development
218
257
 
219
258
  ```bash
220
- python -m unittest discover tests # 81 tests, including the real shell prefix run as Claude Code runs it
259
+ python -m unittest discover tests # 80 tests, including the real shell prefix run as Claude Code runs it
221
260
  node vscode/test/smoke.js # VS Code extension against a mock API
222
261
  ./scripts/build-vsix.sh # › dist/kompactor-<version>.vsix
223
262
  python scripts/bump_version.py X.Y.Z # bump every version; pushing to main publishes to PyPI
@@ -227,4 +266,10 @@ How Claude Code's hooks behave on success, failure and very long output, and why
227
266
 
228
267
  Kompactor was called Kompact until version 0.4; installing it replaces an old Kompact install's settings.
229
268
 
230
- [Privacy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md) · [MIT License](https://github.com/amarkum/kompactor/blob/main/LICENSE)
269
+ ## Support and security
270
+
271
+ - **Questions, bugs and ideas:** [open an issue](https://github.com/amarkum/kompactor/issues).
272
+ - **Security problems:** please report them privately, as described in [SECURITY.md](https://github.com/amarkum/kompactor/blob/main/SECURITY.md).
273
+ - **Privacy:** [PRIVACY.md](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md) explains what Kompactor stores and how to delete it.
274
+
275
+ [MIT License](https://github.com/amarkum/kompactor/blob/main/LICENSE)
@@ -1,3 +1,3 @@
1
1
  """Kompactor: cut the tokens Claude Code spends on noise."""
2
2
 
3
- __version__ = "0.7.3"
3
+ __version__ = "0.7.5"
@@ -391,7 +391,7 @@ def cmd_uninstall(a):
391
391
  print(" Kept savings history and config in ~/.kompactor (add --purge to delete).")
392
392
  if installer.status()["plugin_enabled"]:
393
393
  print(" The Claude Code plugin is still enabled; run: claude plugin uninstall kompactor")
394
- print(" If you installed with pip/pipx/uv, also run: pipx uninstall kompactor")
394
+ print(" Installed it as a Python package? Remove that too, with the tool you installed it with (pip, pipx or uv).")
395
395
  return 0
396
396
 
397
397
 
@@ -411,13 +411,18 @@ def cmd_statusline(a):
411
411
  print("Kompactor · stopped (kompactor start)")
412
412
  return 0
413
413
  sid = event.get("session_id", "")
414
+ ctx = (event.get("context_window") or {}).get("total_input_tokens")
415
+ ctx = int(ctx) if isinstance(ctx, (int, float)) and ctx > 0 else 0
416
+ if sid and ctx:
417
+ store.note_context(sid, ctx)
414
418
  cfg = config.load((event.get("workspace") or {}).get("current_dir") or event.get("cwd") or os.getcwd())
415
419
  name = config.PRESETS[cfg["preset"]]["name"]
416
420
  tot = store.session_totals(sid) if sid else {}
417
421
  if tot.get("saved", 0) > 0:
418
422
  parts = [f"Kompactor −{hooks._fmt(tot['saved'])}"]
419
- if tot["context"] and tot["in_context_saved"] > 0:
420
- parts.append(f"ctx −{100 * tot['in_context_saved'] / (tot['context'] + tot['in_context_saved']):.0f}%")
423
+ ctx = ctx or tot["context"]
424
+ if ctx and tot["in_context_saved"] > 0:
425
+ parts.append(f"ctx −{100 * tot['in_context_saved'] / (ctx + tot['in_context_saved']):.0f}%")
421
426
  if tot["last_turn_saved"] > 0:
422
427
  parts.append(f"last turn −{hooks._fmt(tot['last_turn_saved'])}")
423
428
  parts.append(name)
@@ -284,28 +284,6 @@ def session_start(event: dict, cfg: dict) -> None:
284
284
  _emit({"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": text}})
285
285
 
286
286
 
287
- def _context_tokens(transcript_path: str) -> int:
288
- """Size of the context Claude last saw, from the newest usage block in the transcript."""
289
- try:
290
- with open(transcript_path, "rb") as f:
291
- f.seek(0, 2)
292
- f.seek(max(0, f.tell() - 400_000))
293
- tail = f.read().decode("utf-8", "replace").splitlines()
294
- except (OSError, TypeError):
295
- return 0
296
- for line in reversed(tail):
297
- if '"usage"' not in line:
298
- continue
299
- try:
300
- u = json.loads(line).get("message", {}).get("usage", {})
301
- except ValueError:
302
- continue
303
- if u:
304
- return int(u.get("input_tokens", 0) + u.get("cache_read_input_tokens", 0)
305
- + u.get("cache_creation_input_tokens", 0))
306
- return 0
307
-
308
-
309
287
  def turn_message(turn: dict, totals: dict) -> str:
310
288
  parts = []
311
289
  if turn["saved"] > 0:
@@ -325,7 +303,7 @@ def stop(event: dict, cfg: dict) -> None:
325
303
  if event.get("stop_hook_active"):
326
304
  return
327
305
  session = event.get("session_id", "")
328
- ctx = _context_tokens(event.get("transcript_path", ""))
306
+ ctx = store.context_size(session) # recorded by the status line; transcripts are never read
329
307
  turn, totals = store.close_turn(session, cfg["project"], ctx)
330
308
  msg = turn_message(turn, totals)
331
309
  store.write_live({"ts": time.time(), "session": session, "project": cfg["project"], "preset": cfg["preset"],
@@ -91,7 +91,7 @@ def prefix_path() -> Path:
91
91
 
92
92
  def prefix_active() -> bool:
93
93
  """True inside a Claude Code session whose Bash commands run through Kompactor's shell prefix."""
94
- cur = os.environ.get(PREFIX_ENV, "")
94
+ cur = os.environ.get("CLAUDE_CODE_SHELL_PREFIX", "")
95
95
  return bool(PREFIX_OURS.search(cur)) and os.path.exists(cur)
96
96
 
97
97
 
@@ -122,10 +122,10 @@ def parse_source(url: str) -> dict:
122
122
  return {"repo": repo, "clone": url, "marketplace": url, "dir": parts[-1]}
123
123
 
124
124
 
125
- def source() -> dict:
125
+ def source(repo=None) -> dict:
126
126
  """The repo the dashboard's setup instructions name: Kompactor's public repo, or a team's fork set
127
127
  with KOMPACTOR_REPO (owner/repo or a git URL). Nothing is read from git or install records."""
128
- url = os.environ.get("KOMPACTOR_REPO", "").strip() or DEFAULT_REPO
128
+ url = (os.environ.get("KOMPACTOR_REPO", "") if repo is None else repo).strip() or DEFAULT_REPO
129
129
  if re.fullmatch(r"[\w.-]+/[\w.-]+", url):
130
130
  url = f"https://github.com/{url}"
131
131
  return parse_source(url)
@@ -292,6 +292,15 @@ def install(scope: str = "user", project: str = "", statusline: bool = True, uni
292
292
  # self-test: run every installed hook command exactly as Claude Code would
293
293
 
294
294
 
295
+ def _selftest_env(tmp: str) -> dict:
296
+ """Only what a hook needs to start: the self-test never hands hooks the rest of your environment."""
297
+ env = {"PATH": os.environ.get("PATH", ""), "HOME": os.environ.get("HOME", ""),
298
+ "USERPROFILE": os.environ.get("USERPROFILE", ""), "SYSTEMROOT": os.environ.get("SYSTEMROOT", ""),
299
+ "TMPDIR": os.environ.get("TMPDIR", ""), "TEMP": os.environ.get("TEMP", ""),
300
+ "TMP": os.environ.get("TMP", ""), "LANG": os.environ.get("LANG", "")}
301
+ return {**{k: v for k, v in env.items() if v}, "KOMPACTOR_HOME": tmp}
302
+
303
+
295
304
  def selftest() -> list:
296
305
  """Run each Kompactor hook from settings.json with a realistic event in an isolated
297
306
  KOMPACTOR_HOME. Returns [{hook, ok, ms, detail}]."""
@@ -337,7 +346,7 @@ def selftest() -> list:
337
346
  continue
338
347
  cmd = f"{launcher()} hook {handler}" # plugin hooks: exercise the same code path
339
348
  ev = {"session_id": "kompactor-selftest", "cwd": tmp, "hook_event_name": event, **events[handler]}
340
- env = dict(os.environ, KOMPACTOR_HOME=tmp)
349
+ env = _selftest_env(tmp)
341
350
  t0 = _time.time()
342
351
  try:
343
352
  p = subprocess.run(cmd, shell=True, input=json.dumps(ev), capture_output=True, text=True, timeout=20,
@@ -372,7 +381,7 @@ def _check_prefix(prefix: str, tmp: str) -> dict:
372
381
  t0 = _time.time()
373
382
  try:
374
383
  p = subprocess.run([prefix, full], capture_output=True, text=True, timeout=30,
375
- env=dict(os.environ, KOMPACTOR_HOME=tmp))
384
+ env=_selftest_env(tmp))
376
385
  ok = p.returncode == 1 and "test_boom" in p.stdout and "test_7 PASSED" not in p.stdout and "[kompactor:" in p.stdout
377
386
  detail = "ok" if ok else f"exit {p.returncode}: {(p.stdout + p.stderr)[-160:]}"
378
387
  except (OSError, subprocess.TimeoutExpired) as e:
@@ -139,7 +139,7 @@ svg.chev { width: 11px; height: 11px; stroke: var(--ink-3); fill: none; stroke-w
139
139
  <pre><code>pipx install kompactor
140
140
  kompactor setup</code></pre></div>
141
141
  <div class="card"><b>VS Code</b><span class="muted">Download the extension file from the repo's Releases page, then click <b>Enable</b> when Kompactor asks.</span>
142
- <pre><code>code --install-extension kompactor-0.7.3.vsix</code></pre></div>
142
+ <pre><code>code --install-extension kompactor-0.7.5.vsix</code></pre></div>
143
143
  </div>
144
144
  <p class="muted">Windows: run <code>.\kompactor\install.ps1</code> from a clone, or use the VS Code extension. The CLI and the VS Code extension read the same <code>~/.claude/settings.json</code>, so one install covers both. A backup of that file is kept as <code>settings.json.kompactor-backup</code>.</p>
145
145
  <p class="muted">Setup adds hooks, a status line (if you don't have one) and, on macOS and Linux, Claude Code's <code>CLAUDE_CODE_SHELL_PREFIX</code> setting pointing at <code>~/.kompactor/bin/kompactor-shell</code>, which is what lets Kompactor compact failing commands. It never replaces a status line or shell prefix you already have. Restart open Claude Code sessions after setup.</p>
@@ -30,6 +30,9 @@ CREATE TABLE IF NOT EXISTS turns (
30
30
  session TEXT, idx INTEGER, ts REAL, project TEXT, raw_tokens INTEGER, kept_tokens INTEGER,
31
31
  context_tokens INTEGER, features TEXT
32
32
  );
33
+ CREATE TABLE IF NOT EXISTS context (
34
+ session TEXT PRIMARY KEY, tokens INTEGER, ts REAL
35
+ );
33
36
  """
34
37
 
35
38
 
@@ -113,6 +116,19 @@ def close_turn(session: str, project: str, context_tokens: int = 0):
113
116
  return turn, totals
114
117
 
115
118
 
119
+ def note_context(session: str, tokens: int) -> None:
120
+ """The context size Claude Code hands the status line (context_window.total_input_tokens).
121
+ Kompactor never reads session transcripts for it."""
122
+ with connect() as db:
123
+ db.execute("INSERT OR REPLACE INTO context VALUES (?,?,?)", (session, int(tokens), time.time()))
124
+
125
+
126
+ def context_size(session: str) -> int:
127
+ with connect() as db:
128
+ row = db.execute("SELECT tokens FROM context WHERE session=?", (session,)).fetchone()
129
+ return row[0] if row else 0
130
+
131
+
116
132
  def session_totals(session: str) -> dict:
117
133
  """Savings so far for one session (for status lines when several sessions run at once)."""
118
134
  with connect() as db:
@@ -138,6 +154,7 @@ def prune(retention_days: float) -> None:
138
154
  db.execute(f"DELETE FROM {table} WHERE ts < ?", (cutoff,))
139
155
  db.execute("DELETE FROM reads WHERE ts < ?", (time.time() - 2 * 86400,))
140
156
  db.execute("DELETE FROM flags WHERE ts < ?", (time.time() - 2 * 86400,))
157
+ db.execute("DELETE FROM context WHERE ts < ?", (time.time() - 2 * 86400,))
141
158
  db.execute("DELETE FROM sessions WHERE COALESCE(NULLIF(last_turn, 0), started) < ?", (cutoff,))
142
159
 
143
160
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kompactor
3
- Version: 0.7.3
3
+ Version: 0.7.5
4
4
  Summary: Cut the tokens Claude Code spends on noise: compacted command output (failing runs included), outline-first reads, a local code graph, and per-turn savings.
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/amarkum/kompactor
@@ -21,7 +21,10 @@ Requires-Dist: tiktoken>=0.7; extra == "exact"
21
21
  Dynamic: license-file
22
22
 
23
23
  <p align="center">
24
- <img src="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-light.png" alt="Kompactor" width="240">
24
+ <picture>
25
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-dark.png">
26
+ <img src="https://raw.githubusercontent.com/amarkum/kompactor/main/docs/brand/kompactor-logo-light.png" alt="Kompactor" width="240">
27
+ </picture>
25
28
  </p>
26
29
 
27
30
  <p align="center"><b>Cut the tokens Claude Code spends on noise.</b><br>
@@ -39,7 +42,7 @@ Most of a Claude Code session's tokens aren't your prompt or Claude's answer. Th
39
42
 
40
43
  ## Contents
41
44
 
42
- [Install](#install) · [After you install](#after-you-install) · [How it works](#how-it-works) · [Use it from Claude Code](#use-it-from-claude-code) · [Quality vs tokens](#quality-vs-tokens) · [See your savings](#see-your-savings) · [Turn it off](#turn-it-off-or-remove-it) · [Evidence](#evidence) · [Code graph](#code-graph) · [Teams](#use-it-across-a-team) · [Commands](#commands)
45
+ [Install](#install) · [After you install](#after-you-install) · [How it works](#how-it-works) · [Examples](#examples) · [Use it from Claude Code](#use-it-from-claude-code) · [Quality vs tokens](#quality-vs-tokens) · [See your savings](#see-your-savings) · [Turn it off](#turn-it-off-or-remove-it) · [What it runs and changes](#what-kompactor-runs-and-changes) · [Evidence](#evidence) · [Code graph](#code-graph) · [Teams](#use-it-across-a-team) · [Troubleshooting](#troubleshooting) · [Commands](#commands) · [Support](#support-and-security)
43
46
 
44
47
  ## Install
45
48
 
@@ -59,7 +62,7 @@ pipx install kompactor # or: pip install kompactor · uv tool install kom
59
62
  kompactor setup
60
63
  ```
61
64
 
62
- **VS Code.** Download `kompactor-0.7.3.vsix` from [Releases](https://github.com/amarkum/kompactor/releases), install it (Extensions › `⋯` › *Install from VSIX…*), and click **Enable** when Kompactor asks.
65
+ **VS Code.** Download `kompactor-0.7.5.vsix` from [Releases](https://github.com/amarkum/kompactor/releases), install it (Extensions › `⋯` › *Install from VSIX…*), and click **Enable** when Kompactor asks.
63
66
 
64
67
  ## After you install
65
68
 
@@ -67,7 +70,7 @@ kompactor setup
67
70
  2. **Work as usual.** You don't call Kompactor; it acts on Claude's tool calls by itself. After each reply you'll see a line such as `Kompactor: −8.2k tokens this turn`, and the status line shows the running total.
68
71
  3. **Check it's on:** run `kompactor` in a terminal (or `/kompactor` in Claude Code). It shows whether it's on, your level, savings so far and how to change things:
69
72
  ```
70
- Kompactor 0.7.3: ON
73
+ Kompactor 0.7.5: ON
71
74
  Connected via the Claude Code plugin. New Claude Code sessions are compacted.
72
75
  Level: Balanced (level 3 of 5). Change it: `kompactor config preset 1-5`, the dashboard, or /kompactor balanced
73
76
  Saved so far: 41.3k tokens (−38%) over 52 tool calls. Details: `kompactor gain`
@@ -88,6 +91,18 @@ Kompactor uses Claude Code's hooks, so it sits between Claude's tools and Claude
88
91
 
89
92
  **Nothing is lost.** Every shortened output keeps a raw copy that Claude can fetch with `kompactor show <id>`. Kompactor never changes what a command does, your permission rules still apply, and if anything goes wrong it lets the original output through. Everything runs on your machine; nothing is sent anywhere ([privacy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md)).
90
93
 
94
+ ## Examples
95
+
96
+ Ask Claude Code the way you normally would. These prompts come from the [evidence](#evidence) runs:
97
+
98
+ | You ask | What Kompactor does | Measured |
99
+ |---|---|---|
100
+ | "Run the tests and tell me which one fails and why." | Claude sees the failing test, its error and the summary line instead of hundreds of passing tests, even when the run exits non-zero. | Go: −33% tokens; vitest: −37% cost |
101
+ | "Where is the session cookie signed?" | Big files arrive as outlines with line ranges, and the first search adds a short map of the main modules, so Claude finds the code in fewer turns. | Flask: −26% tokens |
102
+ | "Install the dependencies and check that it builds." | Download and progress chatter is dropped; warnings, errors and the result are kept. | `npm install`: ~33,000 › 469 tokens |
103
+ | "What does changing `createStore` affect?" | The code graph lists the files that use it and the tests to run. | 27/27 answers correct |
104
+ | `/kompactor review` | Lists cuts in your current diff: duplicated helpers, unused options, dead branches. | |
105
+
91
106
  ## Use it from Claude Code
92
107
 
93
108
  Type these in any Claude Code chat:
@@ -140,6 +155,20 @@ kompactor uninstall # remove hooks, status line, shell prefix and skill (add -
140
155
  ```
141
156
  Then remove the package the way you installed it: `claude plugin uninstall kompactor`, `pipx uninstall kompactor`, or uninstall the extension in VS Code. Setup never replaces a status line or shell prefix you already had, and keeps a backup in `~/.claude/settings.json.kompactor-backup`.
142
157
 
158
+ ## What Kompactor runs and changes
159
+
160
+ Everything below happens on your machine. Kompactor makes no network requests of its own and has no telemetry. (If you install the optional `tiktoken` package for exact token counts, tiktoken downloads its token table once, on first use.)
161
+
162
+ **What it runs.** Python hooks that Claude Code calls (`python3 kompactor/_launch.py hook <name>` from the plugin folder): after Bash, Read, Grep, Glob and edit tool calls; before Read; at session start, subagent start and before compaction; and when a turn ends. After `/kompactor setup` on macOS and Linux, Claude's Bash commands run through Kompactor's shell prefix (`~/.kompactor/bin/kompactor-shell`) so failing runs can be compacted too. The prefix runs after Claude Code's permission check, on Claude's original command, and returns its exit code unchanged.
163
+
164
+ **What it adds to Claude's context.** Compacted tool output, outlines in place of large file reads, a code map once per session, a one-line savings note after each turn, and short reply rules at session start and for subagents. The rules are plain text you can read in [`kompactor/rules.py`](https://github.com/amarkum/kompactor/blob/main/kompactor/rules.py); the level decides which ones apply (none at level 1).
165
+
166
+ **What it reads.** The tool output Claude Code passes to its hooks, the files Claude reads (to outline them), your project's source (for the code graph), and the context size Claude Code gives the status line. Hooks never read your session transcripts. `kompactor estimate` and the dashboard's **Replay your history** panel read your local Claude Code session logs in `~/.claude/projects`, only when you run them, to show what Kompactor would have saved; the result isn't stored or sent anywhere.
167
+
168
+ **What it writes.** `~/.kompactor/`: settings, the savings ledger, raw copies of shortened output (for `kompactor show`), the code graph index and the `~/.kompactor/bin/` launchers (written at session start). History is deleted after 30 days by default (`retention_days`). Setup edits `~/.claude/settings.json` to add Kompactor's hooks (non-plugin installs), a status line (only if you have none) and `CLAUDE_CODE_SHELL_PREFIX` (only if it isn't set), keeping a backup in `settings.json.kompactor-backup`; non-plugin installs also get the `/kompactor` skill in `~/.claude/skills`. Nothing is written to your repositories.
169
+
170
+ **What it never does.** Change your permission rules or sandbox settings, change what a command does, or send your code or output anywhere. Details: [privacy policy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md).
171
+
143
172
  ## Evidence
144
173
 
145
174
  `kompactor prove` runs each task in real headless Claude Code sessions (`claude -p`), 3 times without Kompactor and 3 times with it, interleaved, with the same prompt, tools, turn limit and budget cap. Tokens are input + cache writes + cache reads + output across every API call; cost is Claude Code's own figure.
@@ -218,7 +247,17 @@ When a teammate trusts the folder, Claude Code installs the plugin; each person
218
247
  { "preset": 4, "overrides": { "never_compact": ["terraform plan"] } }
219
248
  ```
220
249
 
221
- **Admins** can put the same keys in Claude Code's managed settings to roll it out to every machine. To control what your team runs, fork the repo and use `your-org/kompactor` in the commands above. The [usage guide](https://github.com/amarkum/kompactor/blob/main/docs/usage.html) (also in the dashboard as **Guide**) covers rollout, troubleshooting and FAQs.
250
+ **Admins** can put the same keys in Claude Code's managed settings to roll it out to every machine. To control what your team runs, fork the repo and use `your-org/kompactor` in the commands above. The dashboard's **Guide** page covers rollout, settings and FAQs in more detail.
251
+
252
+ ## Troubleshooting
253
+
254
+ | Problem | What to do |
255
+ |---|---|
256
+ | No savings line after replies | Restart Claude Code after setup, then run `kompactor doctor`: it checks the installation and shows recent hook errors. The dashboard's **Self-test** panel runs every hook once. |
257
+ | An output looks too short | Claude can fetch the original with `kompactor show <id>` (the id is in the shortened output), or search earlier outputs with `kompactor search "text"`. |
258
+ | A command should never be shortened | Add it to **Never compact these commands** on the dashboard, or in `.kompactor.json` (`"never_compact": ["terraform plan"]`). For a single run, start the command with `KOMPACTOR_RAW=1`. |
259
+ | Something seems off | `kompactor stop` turns Kompactor off at once; every hook also lets the original output through if it hits an error. Errors are logged to `~/.kompactor/hook-errors.log`. |
260
+ | The dashboard port is busy | `kompactor start` picks a free port by itself and prints the address. |
222
261
 
223
262
  ## Commands
224
263
 
@@ -239,7 +278,7 @@ kompactor ui the dashboard, in the foreground
239
278
  ## Development
240
279
 
241
280
  ```bash
242
- python -m unittest discover tests # 81 tests, including the real shell prefix run as Claude Code runs it
281
+ python -m unittest discover tests # 80 tests, including the real shell prefix run as Claude Code runs it
243
282
  node vscode/test/smoke.js # VS Code extension against a mock API
244
283
  ./scripts/build-vsix.sh # › dist/kompactor-<version>.vsix
245
284
  python scripts/bump_version.py X.Y.Z # bump every version; pushing to main publishes to PyPI
@@ -249,4 +288,10 @@ How Claude Code's hooks behave on success, failure and very long output, and why
249
288
 
250
289
  Kompactor was called Kompact until version 0.4; installing it replaces an old Kompact install's settings.
251
290
 
252
- [Privacy](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md) · [MIT License](https://github.com/amarkum/kompactor/blob/main/LICENSE)
291
+ ## Support and security
292
+
293
+ - **Questions, bugs and ideas:** [open an issue](https://github.com/amarkum/kompactor/issues).
294
+ - **Security problems:** please report them privately, as described in [SECURITY.md](https://github.com/amarkum/kompactor/blob/main/SECURITY.md).
295
+ - **Privacy:** [PRIVACY.md](https://github.com/amarkum/kompactor/blob/main/PRIVACY.md) explains what Kompactor stores and how to delete it.
296
+
297
+ [MIT License](https://github.com/amarkum/kompactor/blob/main/LICENSE)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kompactor"
7
- version = "0.7.3"
7
+ version = "0.7.5"
8
8
  description = "Cut the tokens Claude Code spends on noise: compacted command output (failing runs included), outline-first reads, a local code graph, and per-turn savings."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -435,12 +435,6 @@ class PresetAndLanguageTests(unittest.TestCase):
435
435
 
436
436
 
437
437
  class DocsTests(unittest.TestCase):
438
- def test_docs_copy_in_sync(self):
439
- import subprocess
440
- root = Path(__file__).resolve().parent.parent
441
- self.assertEqual(subprocess.run([sys.executable, str(root / "scripts/sync_docs.py"), "--check"]).returncode, 0,
442
- "docs/usage.html is stale: run python scripts/sync_docs.py")
443
-
444
438
  def test_versions_agree(self):
445
439
  root = Path(__file__).resolve().parent.parent
446
440
  import re as _re
@@ -469,9 +463,15 @@ class TurnAndSetupTests(unittest.TestCase):
469
463
  ev = {"session_id": "turn1", "tool_input": {"command": "git log"}, "cwd": TMP,
470
464
  "tool_response": {"stdout": GIT_LOG, "stderr": "", "interrupted": False, "isImage": False}}
471
465
  run_hook("post-bash", ev)
466
+ from kompactor import cli
467
+ # Claude Code tells the status line how big the context is; Kompactor records that number
468
+ sys.stdin = io.StringIO(json.dumps({"session_id": "turn1", "workspace": {"current_dir": TMP},
469
+ "context_window": {"total_input_tokens": 21000}}))
470
+ with redirect_stdout(io.StringIO()):
471
+ cli.main(["statusline"])
472
+ # ...and never reads the session transcript, even when the event names one
472
473
  transcript = Path(TMP, "t.jsonl")
473
- transcript.write_text(json.dumps({"type": "assistant", "message": {"usage": {
474
- "input_tokens": 5, "cache_read_input_tokens": 20000, "cache_creation_input_tokens": 995}}}) + "\n")
474
+ transcript.write_text(json.dumps({"type": "assistant", "message": {"usage": {"input_tokens": 99999}}}) + "\n")
475
475
  sys.stdin = io.StringIO(json.dumps({"session_id": "turn1", "transcript_path": str(transcript), "cwd": TMP}))
476
476
  buf = io.StringIO()
477
477
  with redirect_stdout(buf):
@@ -482,7 +482,6 @@ class TurnAndSetupTests(unittest.TestCase):
482
482
  self.assertIn("context ≈", msg)
483
483
  live = store.read_live()
484
484
  self.assertEqual(live["session"], "turn1")
485
- from kompactor import cli
486
485
  sys.stdin = io.StringIO(json.dumps({"session_id": "turn1", "workspace": {"current_dir": TMP}}))
487
486
  buf = io.StringIO()
488
487
  with redirect_stdout(buf):
@@ -558,7 +557,7 @@ class SourceTests(unittest.TestCase):
558
557
  """The dashboard shows teammates install commands for the public repo, or a fork named by KOMPACTOR_REPO."""
559
558
 
560
559
  def test_parse_source(self):
561
- self.assertEqual(installer.parse_source("https://ghp_secret@github.com/acme/kompactor.git"),
560
+ self.assertEqual(installer.parse_source("https://someone@github.com/acme/kompactor.git"),
562
561
  {"repo": "acme/kompactor", "clone": "https://github.com/acme/kompactor.git",
563
562
  "marketplace": "acme/kompactor", "dir": "kompactor"})
564
563
  self.assertEqual(installer.parse_source("git@github.com:acme/kompactor.git")["marketplace"], "acme/kompactor")
@@ -567,13 +566,9 @@ class SourceTests(unittest.TestCase):
567
566
  self.assertEqual(installer.parse_source(""), {})
568
567
 
569
568
  def test_source_defaults_to_the_public_repo(self):
570
- from unittest import mock
571
- with mock.patch.dict(os.environ, {"KOMPACTOR_REPO": ""}):
572
- self.assertEqual(installer.source()["marketplace"], "amarkum/kompactor")
573
- with mock.patch.dict(os.environ, {"KOMPACTOR_REPO": "acme/kompactor"}): # a team's fork
574
- self.assertEqual(installer.source()["clone"], "https://github.com/acme/kompactor.git")
575
- with mock.patch.dict(os.environ, {"KOMPACTOR_REPO": "https://ghp_x@git.acme.dev/tools/kompactor.git"}):
576
- self.assertNotIn("ghp_x", json.dumps(installer.source()))
569
+ self.assertEqual(installer.source("")["marketplace"], "amarkum/kompactor")
570
+ self.assertEqual(installer.source("acme/kompactor")["clone"], "https://github.com/acme/kompactor.git") # a fork
571
+ self.assertNotIn("someone@", json.dumps(installer.source("https://someone@git.acme.dev/tools/kompactor.git")))
577
572
 
578
573
 
579
574
  if __name__ == "__main__":
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes