tanuki-context 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -73,6 +73,63 @@ No AI client at all, just curious what it would save you:
73
73
  npx tanuki-context estimate some-big-file.log 0
74
74
  ```
75
75
 
76
+ ## Installing it: two ways, same tools
77
+
78
+ **npm (the default).** `npx -y tanuki-context` downloads a 0.98 MB tarball
79
+ with zero dependencies and starts the MCP server on stdio. That is the
80
+ whole install. Your client spawns one short-lived process per session
81
+ (35 ms to first response under node, 27 under bun); the model calls the
82
+ seven `tanuki_*` tools; pages come back inline as PNG blocks. On disk it
83
+ touches exactly two places, both yours to delete: `~/.tanuki/stash` for
84
+ stashed text and `~/.pxpipe/events.jsonl` for the savings log.
85
+
86
+ **cargo (the static binary).** No node at all:
87
+
88
+ ```
89
+ cargo install --git https://github.com/Osyna/tanuki-context --branch rust
90
+ ```
91
+
92
+ One 5.7 MB binary, same seven tools, same numbers — a 103-check parity
93
+ harness holds the two engines to byte-identical JSON and pixel-identical
94
+ PNGs on every release. It spawns in 0.4 ms and idles at 3.8 MB RSS, which
95
+ matters when every session forks its own server. Point any client config
96
+ at `"command": "tanuki-context"` instead of the npx line and nothing else
97
+ changes.
98
+
99
+ ## Should you use it at all?
100
+
101
+ Reach for tanuki when:
102
+
103
+ - **bulky text is about to enter context** — logs, build output, command
104
+ results, long docs, anything past roughly 2,000 tokens;
105
+ - **an agent keeps re-reading big files** — the proxy dedupes exact
106
+ repeats, and stash turns re-reads into cheap fetches;
107
+ - **a long session is running out of context** — pages carry ~4x more
108
+ characters per token, and the proxy can image old bulky turns in place;
109
+ - **you cannot change the client** — the proxy mode needs only a base-URL
110
+ export.
111
+
112
+ Leave it alone when:
113
+
114
+ - **your model cannot read images.** Hard requirement. Everything here
115
+ assumes a vision-capable reader (any current Claude model qualifies).
116
+ - **the exact bytes must survive a round trip** — secrets, hashes, code
117
+ the model is about to edit. Rendering is exact, but model read-back of
118
+ dense random strings is not guaranteed (pxpipe measured 13/15 on
119
+ 12-char hex). Keep those in text.
120
+ - **the content is small.** A 500-token snippet is not worth a modality
121
+ switch even when the math technically favors it; `estimate` and the
122
+ proxy gate both say so.
123
+ - **your bill is output-dominated.** tanuki cuts input tokens only. If
124
+ most of your spend is the model's own output, fix that first.
125
+ - **you are not on Anthropic pricing.** Verdicts use Anthropic's
126
+ 28-px patch grid. OpenAI and Google price images differently (tiles,
127
+ fixed per-image rates), so every verdict needs re-deriving before you
128
+ trust it there.
129
+ - **the bulk is already prompt-cached.** Cache reads cost a tenth of
130
+ fresh input; imaging content that was riding the cache can be a net
131
+ loss. The proxy never touches `cache_control` blocks for this reason.
132
+
76
133
  ## The three ways to run it
77
134
 
78
135
  **Explicit (MCP tools) — the default and the recommendation.** Your AI gets
@@ -461,6 +518,20 @@ pi install npm:tanuki-context
461
518
  claude mcp add tanuki-context -- npx -y tanuki-context
462
519
  ```
463
520
 
521
+ The package also ships a **skill** — a short instruction file that teaches
522
+ the model the whole workflow (estimate first, read `recommend`, stash big
523
+ references, the page decode grammar, the do-nots) without you prompting
524
+ any of it. Install it once and the tools get used correctly on their own:
525
+
526
+ ```
527
+ npm i -g tanuki-context # or any install that gives you the package on disk
528
+ cp -r "$(npm root -g)/tanuki-context/skills/tanuki-context" ~/.claude/skills/
529
+ ```
530
+
531
+ From a checkout it is just `cp -r skills/tanuki-context ~/.claude/skills/`.
532
+ The same file works for any skill-aware harness (OMP picks it up from the
533
+ same directory).
534
+
464
535
  **Rust instead of Node?** Same pipeline, same numbers, one 5.7 MB static
465
536
  binary:
466
537
 
package/dist/agent.js CHANGED
@@ -78,7 +78,7 @@ function oJ(){let J=cJ.env.TANUKI_STASH;if(J!==void 0&&J!=="")return J;return`${
78
78
  //! tanuki-context estimate <file> [level] [--distill]
79
79
  //! tanuki-context render <file> [level] [outdir]
80
80
  //! tanuki-context proxy [--port N] [--upstream URL] [knobs] (implicit mode)
81
- var rJ="0.4.0",RJ=6;function BZ(J,Q){let Z=Math.min(J.length,Q.length),$=0;while($<Z&&J.charCodeAt($)===Q.charCodeAt($))$++;if($>=Z)return J.length-Q.length;return J.codePointAt($)-Q.codePointAt($)}function L(J,Q,Z=""){if(J===null||J===void 0)return"null";if(J instanceof YJ){let z=J.value;return Number.isFinite(z)&&Number.isInteger(z)?z.toFixed(1):String(z)}let $=typeof J;if($==="string")return JSON.stringify(J);if($==="number"||$==="boolean")return String(J);if(Array.isArray(J)){if(J.length===0)return"[]";if(!Q){let O="[";for(let B=0;B<J.length;B++){if(B>0)O+=",";O+=L(J[B],!1)}return O+"]"}let z=Z+" ",K=`[
81
+ var rJ="0.4.1",RJ=6;function BZ(J,Q){let Z=Math.min(J.length,Q.length),$=0;while($<Z&&J.charCodeAt($)===Q.charCodeAt($))$++;if($>=Z)return J.length-Q.length;return J.codePointAt($)-Q.codePointAt($)}function L(J,Q,Z=""){if(J===null||J===void 0)return"null";if(J instanceof YJ){let z=J.value;return Number.isFinite(z)&&Number.isInteger(z)?z.toFixed(1):String(z)}let $=typeof J;if($==="string")return JSON.stringify(J);if($==="number"||$==="boolean")return String(J);if(Array.isArray(J)){if(J.length===0)return"[]";if(!Q){let O="[";for(let B=0;B<J.length;B++){if(B>0)O+=",";O+=L(J[B],!1)}return O+"]"}let z=Z+" ",K=`[
82
82
  `;for(let O=0;O<J.length;O++){if(O>0)K+=`,
83
83
  `;K+=z+L(J[O],!0,z)}return K+`
84
84
  `+Z+"]"}let V=J,G=Object.keys(V).sort(BZ);if(G.length===0)return"{}";if(!Q){let z="{";for(let K=0;K<G.length;K++){if(K>0)z+=",";z+=JSON.stringify(G[K])+":"+L(V[G[K]],!1)}return z+"}"}let Y=Z+" ",H=`{
package/dist/cli.js CHANGED
@@ -84,7 +84,7 @@ function zQ(){let J=VQ.env.TANUKI_STASH;if(J!==void 0&&J!=="")return J;return`${
84
84
  //! tanuki-context estimate <file> [level] [--distill]
85
85
  //! tanuki-context render <file> [level] [outdir]
86
86
  //! tanuki-context proxy [--port N] [--upstream URL] [knobs] (implicit mode)
87
- var TZ="0.4.0",wJ=6,bJ=8000;function LZ(J,Q){let Z=Math.min(J.length,Q.length),$=0;while($<Z&&J.charCodeAt($)===Q.charCodeAt($))$++;if($>=Z)return J.length-Q.length;return J.codePointAt($)-Q.codePointAt($)}function P(J,Q,Z=""){if(J===null||J===void 0)return"null";if(J instanceof ZJ){let H=J.value;return Number.isFinite(H)&&Number.isInteger(H)?H.toFixed(1):String(H)}let $=typeof J;if($==="string")return JSON.stringify(J);if($==="number"||$==="boolean")return String(J);if(Array.isArray(J)){if(J.length===0)return"[]";if(!Q){let B="[";for(let U=0;U<J.length;U++){if(U>0)B+=",";B+=P(J[U],!1)}return B+"]"}let H=Z+" ",K=`[
87
+ var TZ="0.4.1",wJ=6,bJ=8000;function LZ(J,Q){let Z=Math.min(J.length,Q.length),$=0;while($<Z&&J.charCodeAt($)===Q.charCodeAt($))$++;if($>=Z)return J.length-Q.length;return J.codePointAt($)-Q.codePointAt($)}function P(J,Q,Z=""){if(J===null||J===void 0)return"null";if(J instanceof ZJ){let H=J.value;return Number.isFinite(H)&&Number.isInteger(H)?H.toFixed(1):String(H)}let $=typeof J;if($==="string")return JSON.stringify(J);if($==="number"||$==="boolean")return String(J);if(Array.isArray(J)){if(J.length===0)return"[]";if(!Q){let B="[";for(let U=0;U<J.length;U++){if(U>0)B+=",";B+=P(J[U],!1)}return B+"]"}let H=Z+" ",K=`[
88
88
  `;for(let B=0;B<J.length;B++){if(B>0)K+=`,
89
89
  `;K+=H+P(J[B],!0,H)}return K+`
90
90
  `+Z+"]"}let V=J,z=Object.keys(V).sort(LZ);if(z.length===0)return"{}";if(!Q){let H="{";for(let K=0;K<z.length;K++){if(K>0)H+=",";H+=JSON.stringify(z[K])+":"+P(V[z[K]],!1)}return H+"}"}let G=Z+" ",Y=`{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tanuki-context",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Token-cutting context pipeline as an MCP server: distill logs, compress text, render dense PNG pages (pxpipe imaging engine). Zero dependencies.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,6 +17,7 @@
17
17
  "files": [
18
18
  "dist",
19
19
  "assets",
20
+ "skills",
20
21
  "README.md",
21
22
  "LICENSE"
22
23
  ],
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: tanuki-context
3
+ version: 0.4.1
4
+ description: |
5
+ Cut input-token cost by rendering bulky text (logs, command output, long
6
+ docs) as dense PNG pages the model reads at a fraction of the price, or by
7
+ parking it outside context and fetching slices. Use when pasting or reading
8
+ anything over ~2,000 tokens of logs, build output, or documents; when a
9
+ session is close to its context limit; or before re-reading a large file.
10
+ Requires the tanuki-context MCP server (tanuki_* tools) and a
11
+ vision-capable model.
12
+ ---
13
+
14
+ # tanuki-context: pay pixels, not tokens
15
+
16
+ Text costs ~1 token per 4 characters. A dense PNG page costs a fixed 1,456
17
+ tokens and carries up to 28,080 characters. The tanuki_* tools exploit that
18
+ gap deterministically: nothing is summarized by a model, errors stay
19
+ verbatim, and every drop is counted.
20
+
21
+ ## Workflow
22
+
23
+ 1. **Estimate first, always.** `tanuki_estimate { text }` is instant and
24
+ never renders pixels. Read two things from the answer:
25
+ - `recommend` - the cheapest reversible knob set (pack/codebook), priced.
26
+ - `recommend.withDistill` - the log route (repeats collapsed with exact
27
+ counts). Cheaper, and honest for logs; do not use it on source code.
28
+ 2. **Act on the verdict.**
29
+ - `"PIPELINE cheaper"` and you need the content in front of you ->
30
+ `tanuki_render` with the recommended knobs. Use the returned pages
31
+ instead of pasting the text.
32
+ - You only need parts of it, now or later -> `tanuki_stash { text }`
33
+ (returns a ~300-token map + id), then `tanuki_fetch { id, query }` or
34
+ `{ id, lines: "a-b" }`. Big slices arrive as pages automatically.
35
+ - `"TEXT cheaper"` -> just use the text. Small inputs are not worth an
36
+ image even when the math technically favors one.
37
+ 3. **For noisy logs**, add `distill: true` (and `query: "regex"` for a
38
+ slice). Error/warn/fail lines survive verbatim; repeats become one
39
+ exemplar plus an exact xN count.
40
+ 4. **Shell commands with chatty output**: run them as
41
+ `tanuki-context run -- <cmd>` instead of reading the firehose. Exit code
42
+ passes through; the full capture is stashed and fetchable.
43
+
44
+ ## Reading the pages
45
+
46
+ `↵` = original newline · `→` = tab · `⇥N` = N leading spaces · a trailing
47
+ `·legend·` line maps codebook sigils back to full tokens · `[U+XXXX]` = a
48
+ codepoint the atlas has no glyph for · `[×N similar]` = N near-identical
49
+ lines collapsed here.
50
+
51
+ ## Do not
52
+
53
+ - Do not image content whose exact bytes you must retype later (secrets,
54
+ hashes, code you are about to edit). Rendering is exact; model read-back
55
+ of dense hex is not guaranteed.
56
+ - Do not use `font: "tiny"` or ladder levels 2-4 on anything you may need
57
+ to quote. Tiny is a 99.7%-read-back trade; levels 2-4 reword prose.
58
+ - Do not use `withDistill` numbers on source code or docs you want intact -
59
+ distill collapses similar-looking lines.
60
+ - Do not probe knob combinations by hand; `recommend` already priced them.