claude-token-saver 3.29.0 → 3.30.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.en.md +22 -1
- package/README.md +22 -1
- package/package.json +1 -1
- package/src/doc2md-ledger.cjs +27 -8
- package/src/doc2md.cjs +7 -4
- package/src/formatters/statusline.js +3 -3
package/README.en.md
CHANGED
|
@@ -452,7 +452,28 @@ Planning documents are moving from PowerPoint to Figma, so the same hook catches
|
|
|
452
452
|
|
|
453
453
|
The result is an outline: pages and frames become headings, text nodes become body lines, and shapes are counted rather than listed — in a planning document the words are the content, and two hundred `Rectangle 173` lines would drown them. A file with no text at all is refused rather than dressed up as an empty document.
|
|
454
454
|
|
|
455
|
-
Verified against real files: a community Bootstrap UI kit (8.1MB, 4,155 nodes, 1,312 of them text) and a 52MB Tailwind kit, each converting in under a second. Both `.fig` vintages parse — the current zip container and the older bare fig-kiwi stream.
|
|
455
|
+
Verified against real files: a community Bootstrap UI kit (8.1MB, 4,155 nodes, 1,312 of them text) and a 52MB Tailwind kit, each converting in under a second. Both `.fig` vintages parse — the current zip container and the older bare fig-kiwi stream.
|
|
456
|
+
|
|
457
|
+
**`.fig` saves the most of any format.** Unlike the Office containers, `Read` does not refuse a `.fig`: the extension means nothing to it, so it pulls the binary in as text and the context window fills with tokenised noise. Measured against the same 42,760-token control:
|
|
458
|
+
|
|
459
|
+
| File | Size | Extra tokens for a Read | Conversion |
|
|
460
|
+
|---|---|---|---|
|
|
461
|
+
| plan.fig | 26KB | +44,195 | 100 tokens |
|
|
462
|
+
| bootstrap-kit.fig | 8.1MB | +43,994 | 18,397 tokens |
|
|
463
|
+
|
|
464
|
+
Two files three hundred times apart in size cost the same, because Read truncates long before the file ends — you pay for a whole document and receive a fraction of one. The baseline is therefore a flat 44,000 tokens. For comparison, the same probe on a pptx cost +317 tokens and on a docx +185: a refusal message, and nothing else.
|
|
465
|
+
|
|
466
|
+
#### Why the baseline does not scale with file size
|
|
467
|
+
|
|
468
|
+
A baseline has to be what would actually have been spent without the converter. Intuition says a bigger file burns more, but the `Read` tool has a cap (2,000 lines by default, plus a per-line character limit), and a binary file hits it almost immediately: even the 26KB file was already truncated, which is why two files 300× apart came out 201 tokens apart. Had the 8.1MB file gone in whole it would have been millions of tokens — money nobody could have spent, since it does not fit in a 200k context window. Claiming to have saved unspendable money is flattery, not measurement.
|
|
469
|
+
|
|
470
|
+
The same principle runs through every baseline here:
|
|
471
|
+
|
|
472
|
+
- **`.fig`, flat 44,000** — set below both measurements (44,195 and 43,994). A model could burn size-proportional tokens by re-Reading at successive offsets, but one Read is what a sane agent does once the bytes turn out to be binary noise, so one Read is the honest counterfactual.
|
|
473
|
+
- **PDF, 2,500 per page** — below both measured values (2,542 and 2,934).
|
|
474
|
+
- **Office formats, the file's actual XML size** — the one case where proportional is right, because a person really does end up reading that XML; it is measured per file rather than applied as a ratio.
|
|
475
|
+
|
|
476
|
+
The common rule: wherever an estimate and a measurement diverge, the lower number wins. A figure the user can trust is worth more than one that flatters the tool.
|
|
456
477
|
|
|
457
478
|
### Editing a document: copy, then script
|
|
458
479
|
|
package/README.md
CHANGED
|
@@ -448,7 +448,28 @@ PDF 는 첨부하면 모델이 내용을 그대로 읽습니다. 대신 페이
|
|
|
448
448
|
|
|
449
449
|
변환 결과는 페이지·프레임 계층을 헤딩으로, 텍스트 노드를 본문으로 정리한 아웃라인입니다. 도형·벡터 같은 시각 요소는 나열하지 않고 개수만 남깁니다. 기획서에서 내용은 글이고, `Rectangle 173` 이 이백 줄 나오면 글이 묻히기 때문입니다. 텍스트가 하나도 없는 파일(순수 그래픽)은 빈 문서로 꾸미지 않고 변환 불가로 알립니다.
|
|
450
450
|
|
|
451
|
-
실제 파일로 검증했습니다: 피그마 커뮤니티의 Bootstrap UI kit(8.1MB, 노드 4,155개, 텍스트 1,312개)와 Tailwind kit(52MB)이 각각 0.2초 안에 71.9KB·44KB 아웃라인으로 변환됐고, 한국어 텍스트 왕복도 무손실이었습니다. `.fig` 는 두 세대가 있습니다. 요즘 익스포트는 zip 컨테이너, 옛 익스포트는 fig-kiwi 바이너리 원형인데 둘 다 처리합니다.
|
|
451
|
+
실제 파일로 검증했습니다: 피그마 커뮤니티의 Bootstrap UI kit(8.1MB, 노드 4,155개, 텍스트 1,312개)와 Tailwind kit(52MB)이 각각 0.2초 안에 71.9KB·44KB 아웃라인으로 변환됐고, 한국어 텍스트 왕복도 무손실이었습니다. `.fig` 는 두 세대가 있습니다. 요즘 익스포트는 zip 컨테이너, 옛 익스포트는 fig-kiwi 바이너리 원형인데 둘 다 처리합니다.
|
|
452
|
+
|
|
453
|
+
**`.fig` 의 절감이 가장 큽니다.** 오피스 형식과 달리 `Read` 가 `.fig` 를 거부하지 않습니다. 확장자를 모르니 이진 파일을 그대로 텍스트로 읽어들이고, 컨텍스트가 토큰화된 잡음으로 찹니다. 같은 42,760 토큰 대조군으로 측정했습니다.
|
|
454
|
+
|
|
455
|
+
| 파일 | 크기 | Read 가 더 쓴 토큰 | 변환본 |
|
|
456
|
+
|---|---|---|---|
|
|
457
|
+
| plan.fig | 26KB | +44,195 | 100 토큰 |
|
|
458
|
+
| bootstrap-kit.fig | 8.1MB | +43,994 | 18,397 토큰 |
|
|
459
|
+
|
|
460
|
+
크기가 300배 차이인데 비용이 같습니다. Read 가 상한에서 자르기 때문인데, 바꿔 말하면 **문서 전체 값을 치르고 일부만 받습니다.** 그래서 기준선은 파일 크기와 무관한 44,000 토큰 고정입니다. 참고로 같은 방법으로 재보니 pptx 는 +317, docx 는 +185 토큰이었습니다. 거부 메시지 한 줄이 전부입니다.
|
|
461
|
+
|
|
462
|
+
#### 왜 파일 크기에 비례시키지 않는가
|
|
463
|
+
|
|
464
|
+
기준선은 "변환이 없었으면 실제로 나갔을 비용"이어야 합니다. 직관으로는 파일이 클수록 더 태울 것 같지만, `Read` 도구에는 상한이 있어(기본 2,000줄, 줄당 문자 제한) 이진 파일은 그 지점에서 잘립니다. 26KB 파일조차 이미 상한을 넘기므로, 크기가 300배 차이 나는 두 파일이 201 토큰 차이로 같은 값이 나왔습니다. 8.1MB 가 통째로 들어갔다면 수백만 토큰인데, 그 돈은 200k 컨텍스트에 물리적으로 들어가지 않아 애초에 아무도 지불할 수 없습니다. 지불할 수 없는 돈을 아꼈다고 적으면 부풀리기가 됩니다.
|
|
465
|
+
|
|
466
|
+
이 원칙은 세 곳에 일관되게 적용됩니다.
|
|
467
|
+
|
|
468
|
+
- **`.fig` 44,000 고정** — 실측 두 값(44,195·43,994)보다 낮게 잡습니다. 모델이 offset 을 바꿔 가며 반복 Read 하면 크기에 비례해 태울 수는 있지만, 첫 Read 에서 이진 잡음임이 드러나면 정상적인 에이전트는 더 읽지 않으므로 1회 Read 가 현실적인 대안입니다.
|
|
469
|
+
- **PDF 페이지당 2,500** — 실측치 2,542·2,934 를 밑도는 값입니다.
|
|
470
|
+
- **오피스 형식은 파일별 실제 XML 크기** — 이쪽은 사람이 정말 그 XML 을 읽게 되므로 비례가 맞고, 계수 대신 파일마다 잽니다.
|
|
471
|
+
|
|
472
|
+
공통 규칙: 기준선이 추정과 실측 사이에서 갈리면 항상 낮은 쪽을 택합니다. 도구를 돋보이게 하는 숫자보다 사용자가 신뢰할 수 있는 숫자가 가치 있습니다.
|
|
452
473
|
|
|
453
474
|
### 문서를 수정해야 할 때: 복사본 + 스크립트
|
|
454
475
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-token-saver",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.30.1",
|
|
4
4
|
"description": "Route the easy work your expensive Claude model keeps repeating down to haiku/sonnet — post-hoc session analysis, no realtime router, no extra LLM calls.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/doc2md-ledger.cjs
CHANGED
|
@@ -31,9 +31,11 @@
|
|
|
31
31
|
*
|
|
32
32
|
* pptx/xlsx/docx: unpacking the container. These never reach the model as
|
|
33
33
|
* attachments at all — the same probe on a docx added 78 tokens and the model
|
|
34
|
-
* replied that it had no file
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* replied that it had no file. Read refuses them too, measured the same way:
|
|
35
|
+
* a Read of the 31.8MB deck cost +317 tokens and of the 185KB docx +185, which
|
|
36
|
+
* is a refusal message and nothing more. What a reader does instead is unzip
|
|
37
|
+
* the archive and wade through its XML, where tags and style attributes
|
|
38
|
+
* outweigh the text many times over:
|
|
37
39
|
*
|
|
38
40
|
* aws-summit-seoul.pptx 2.1MB of slide XML ~540,429 tokens 23.8× the conversion
|
|
39
41
|
* 우리은행이력서.docx 312KB of body XML ~78,113 tokens ~46× the conversion
|
|
@@ -43,6 +45,21 @@
|
|
|
43
45
|
* a real fallback — this very session unzipped a pptx to verify a conversion
|
|
44
46
|
* before this ledger existed.
|
|
45
47
|
*
|
|
48
|
+
* .fig: the file itself. Unlike the Office formats, Read does not refuse a
|
|
49
|
+
* .fig — the extension means nothing to it, so it pulls the binary in as text
|
|
50
|
+
* and the context window fills with tokenised noise. Measured against the same
|
|
51
|
+
* 42,760-token control:
|
|
52
|
+
*
|
|
53
|
+
* plan.fig 26KB +44,195 tokens conversion: 100
|
|
54
|
+
* bootstrap-kit.fig 8.1MB +43,994 tokens conversion: 18,397
|
|
55
|
+
*
|
|
56
|
+
* Two files three hundred times apart in size cost the same, because Read
|
|
57
|
+
* truncates at a cap long before the file ends — which also means the reader
|
|
58
|
+
* gets a fraction of a document for the price of a whole one. The baseline is
|
|
59
|
+
* therefore a flat 44,000 tokens rather than anything per-byte. An earlier
|
|
60
|
+
* version of this file claimed .fig had no measurable baseline at all; that
|
|
61
|
+
* was an assumption about Read's behaviour that turned out to be wrong.
|
|
62
|
+
*
|
|
46
63
|
* Erring low is deliberate throughout. A savings figure that flatters the
|
|
47
64
|
* tool is worth less than one the user can trust.
|
|
48
65
|
*
|
|
@@ -79,10 +96,10 @@ const ATTACHMENT_BASELINE = {
|
|
|
79
96
|
'.docx': { markup: true },
|
|
80
97
|
'.xlsx': { markup: true },
|
|
81
98
|
'.xls': { ratio: 1 },
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
'.fig': {
|
|
99
|
+
// Read swallows a .fig instead of refusing it, at a flat ~44,000 tokens
|
|
100
|
+
// whatever the file's size (see the header). Rounded down from the two
|
|
101
|
+
// measurements, both of which landed just under 44,200.
|
|
102
|
+
'.fig': { fixed: 44_000 },
|
|
86
103
|
};
|
|
87
104
|
|
|
88
105
|
/**
|
|
@@ -106,7 +123,9 @@ function estimateSaving({ ext, pages = 0, markupBytes = 0, markdown = '' }) {
|
|
|
106
123
|
const tokens = estimateTokens(markdown);
|
|
107
124
|
const rule = ATTACHMENT_BASELINE[String(ext).toLowerCase()] || { ratio: 1 };
|
|
108
125
|
let baseline;
|
|
109
|
-
if (rule.
|
|
126
|
+
if (rule.fixed) {
|
|
127
|
+
baseline = rule.fixed;
|
|
128
|
+
} else if (rule.perPage && pages > 0) {
|
|
110
129
|
baseline = pages * rule.perPage;
|
|
111
130
|
} else if (rule.markup && markupBytes > 0) {
|
|
112
131
|
baseline = Math.ceil(markupBytes / 4);
|
package/src/doc2md.cjs
CHANGED
|
@@ -165,10 +165,13 @@ function banner(sourcePath, saving) {
|
|
|
165
165
|
if (saving.usd > 0) {
|
|
166
166
|
const savedTokens = Math.max(0, saving.baseline - saving.tokens);
|
|
167
167
|
// The alternative differs by format, so the sentence names it: a PDF
|
|
168
|
-
// would have been attached, a zip document
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
// would have been attached, a zip document unpacked, and a .fig read
|
|
169
|
+
// straight into the context window, which is the one Read does not
|
|
170
|
+
// refuse.
|
|
171
|
+
const ext = path.extname(sourcePath).toLowerCase();
|
|
172
|
+
const alternative = ext === '.pdf' ? '원본을 첨부하면'
|
|
173
|
+
: ext === '.fig' ? '원본을 그대로 Read 하면'
|
|
174
|
+
: '압축을 풀어 본문 XML을 읽으면';
|
|
172
175
|
lines.push(
|
|
173
176
|
`${alternative} 약 ${saving.baseline.toLocaleString('en-US')} 토큰이 드는데, `
|
|
174
177
|
+ `변환본은 ${saving.tokens.toLocaleString('en-US')} 토큰입니다. `
|
|
@@ -425,9 +425,9 @@ export function formatReport(data, { color = true, verbose = false, timer = true
|
|
|
425
425
|
const byExt = Array.isArray(doc2md.byExt) ? doc2md.byExt : [];
|
|
426
426
|
// Formats that earned money show it; formats with no baseline to measure
|
|
427
427
|
// against are counted instead. Printing "$0.00" next to real amounts reads
|
|
428
|
-
// as "this format saved nothing", when what it means is that
|
|
429
|
-
//
|
|
430
|
-
//
|
|
428
|
+
// as "this format saved nothing", when what it means is that nobody has
|
|
429
|
+
// measured what the alternative costs. Only .xls sits there now — the
|
|
430
|
+
// pre-2007 binary, which has no container to price against.
|
|
431
431
|
const paid = byExt.filter((r) => r.usd > 0);
|
|
432
432
|
const unpriced = byExt.filter((r) => !(r.usd > 0));
|
|
433
433
|
const parts = paid.map((r) => `${c(GRAY)}${r.ext} ${r.docs}× ${formatMoney(r.usd)}${c(RESET)}`);
|