claude-token-saver 3.29.0 → 3.30.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/README.en.md CHANGED
@@ -452,7 +452,16 @@ 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. No savings are claimed: a `.fig` unzips to another binary, so there is no readable fallback to price against.
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.
456
465
 
457
466
  ### Editing a document: copy, then script
458
467
 
package/README.md CHANGED
@@ -448,7 +448,16 @@ 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 바이너리 원형인데 둘 다 처리합니다. 절감액은 청구하지 않습니다. `.fig` 는 압축을 풀어도 또 바이너리라 비교할 대안 자체가 없고, 변환이 유일한 읽기 경로입니다.
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 토큰이었습니다. 거부 메시지 한 줄이 전부입니다. 절감액은 청구하지 않습니다. `.fig` 는 압축을 풀어도 또 바이너리라 비교할 대안 자체가 없고, 변환이 유일한 읽기 경로입니다.
452
461
 
453
462
  ### 문서를 수정해야 할 때: 복사본 + 스크립트
454
463
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-token-saver",
3
- "version": "3.29.0",
3
+ "version": "3.30.0",
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": {
@@ -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, and Read refuses the format outright. What a
35
- * reader does instead is unzip the archive and wade through its XML, where
36
- * tags and style attributes outweigh the text many times over:
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
- // A .fig unzips to another binary (Figma's kiwi format), so unlike the
83
- // office containers there is no readable markup to price the fallback
84
- // against. Converting is the only way to read it at all; no money claimed.
85
- '.fig': { ratio: 1 },
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.perPage && pages > 0) {
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 would have been unpacked.
169
- const alternative = path.extname(sourcePath).toLowerCase() === '.pdf'
170
- ? '원본을 첨부하면'
171
- : '압축을 풀어 본문 XML을 읽으면';
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 there is no
429
- // working alternative to price it against for .fig there is no readable
430
- // fallback at all, so the conversion is the only way to open the file.
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)}`);