slash-tokens 1.1.0 → 1.1.2

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
@@ -1,8 +1,7 @@
1
1
  # /slash-tokens
2
2
 
3
- Pre-flight checks for API calls. 4.8 KB WASM. Sub-millisecond. Zero dependencies.
4
-
5
- **Fly First. Pay Economy.**
3
+ Token Optimization for Context Engineers.
4
+ For anyone building with LLMs. 4.8 KB WASM. Sub-millisecond. Zero dependencies.
6
5
 
7
6
  ```bash
8
7
  npm install slash-tokens
@@ -15,7 +14,7 @@ const check = preflight(prompt, 'claude-opus')
15
14
  // tokens: 47,000 | cost: $0.71 | 11 cheaper options | save 99%
16
15
  ```
17
16
 
18
- Or one line — every API call pre-flighted automatically:
17
+ Or one line — every API call checked pre-call:
19
18
 
20
19
  ```js
21
20
  import 'slash-tokens/auto'
package/dist/auto.js CHANGED
@@ -1,30 +1,61 @@
1
1
  import { patchFetch, onIntercept } from './intercept.js';
2
2
  import { report } from './transact.js';
3
- import { hasKey } from './config.js';
4
- // Auto mode: import this file and every LLM call is pre-flighted.
3
+ import { hasKey, isQuiet } from './config.js';
4
+ // Auto mode: import this file and every LLM call is optimized.
5
5
  // Usage: import 'slash-tokens/auto'
6
6
  //
7
- // No key: console logging only (free forever)
8
- // With key (init or SLASH_KEY): flight records sent to API
7
+ // Silent by default. Routes to cheapest model within same provider.
8
+ // Tracks cumulative savings. Nudges at thresholds.
9
9
  //
10
- // Auto mode observes every call — it doesn't block.
11
- // The data shows what you're spending and what you COULD save.
12
- // When you start acting on preflight results (skip/reduce/route),
13
- // the savings become real.
10
+ // No key: routing still works (free forever), no dashboard tracking
11
+ // With key (init or SLASH_KEY): savings reported to MCPaaS
12
+ // Session state
13
+ let totalSalvaged = 0;
14
+ let totalCalls = 0;
15
+ let totalRouted = 0;
16
+ let nextNudge = 5; // First nudge at $5
17
+ // ANSI
18
+ const R = '\x1b[0m';
19
+ const B = '\x1b[1m';
20
+ const GREEN = '\x1b[38;2;74;222;128m';
21
+ const GOLD = '\x1b[38;2;230;161;65m';
14
22
  patchFetch();
15
23
  onIntercept((event) => {
16
- const status = event.fits ? 'OK' : 'OVER LIMIT';
17
- console.log(`[slash] ${event.provider} ${event.model} | ${event.tokens.toLocaleString()} tokens | $${event.cost.toFixed(4)} | ${status}`);
18
- // If key is configured, log the flight record (non-blocking)
19
- if (hasKey() && event.tokens > 0) {
24
+ totalCalls++;
25
+ totalSalvaged += event.salvaged;
26
+ if (event.routed) {
27
+ totalRouted++;
28
+ }
29
+ // Verbose mode (default) — log every call
30
+ if (!isQuiet()) {
31
+ const route = event.routed
32
+ ? `${GOLD}${event.originalModel} → ${event.model}${R}`
33
+ : `${event.model}`;
34
+ const salvageTag = event.salvaged > 0 ? ` ${GREEN}saved $${event.salvaged.toFixed(4)}${R}` : '';
35
+ console.log(`${GREEN}[slash]${R} ${event.provider} ${route} | ${event.tokens.toLocaleString()} tok | $${event.cost.toFixed(4)}${salvageTag}`);
36
+ }
37
+ // Nudge at thresholds (always, even in quiet mode)
38
+ if (totalSalvaged >= nextNudge) {
39
+ console.log(`${GREEN}${B} ⚡ $${Math.floor(totalSalvaged)} salvaged${R}${GOLD} — keep building${R}`);
40
+ if (nextNudge < 10)
41
+ nextNudge = 10;
42
+ else if (nextNudge < 25)
43
+ nextNudge = 25;
44
+ else if (nextNudge < 50)
45
+ nextNudge = 50;
46
+ else if (nextNudge < 100)
47
+ nextNudge = 100;
48
+ else
49
+ nextNudge += 100;
50
+ }
51
+ // Report real savings to MCPaaS (non-blocking)
52
+ if (hasKey() && event.salvaged > 0) {
20
53
  report({
21
54
  tokens_estimated: event.tokens,
22
- tokens_saved: 0,
23
- model: event.model,
55
+ tokens_saved: event.tokens,
56
+ model: event.originalModel,
24
57
  action: 'routed',
25
- cost_saved_usd: 0,
26
- }).catch(() => {
27
- // Silent — never break the app for billing failures
28
- });
58
+ cost_saved_usd: event.salvaged,
59
+ }).catch(() => { });
29
60
  }
30
61
  });
package/dist/cli.js CHANGED
@@ -181,9 +181,9 @@ function formatNum(n) {
181
181
  function printReport(sites, filesScanned, timeMs, cwd) {
182
182
  console.log("");
183
183
  console.log(`${ORANGE}${B} ⚡ /slash${R}`);
184
- console.log(`${GRAY} Token burn analysis${R}`);
184
+ console.log(`${GRAY} Token optimization analysis${R}`);
185
185
  console.log("");
186
- console.log(`${DIM} Scanned ${filesScanned} files in ${timeMs}ms${R}`);
186
+ console.log(`${DIM} Evaluated ${filesScanned} files in ${timeMs}ms${R}`);
187
187
  console.log("");
188
188
  if (sites.length === 0) {
189
189
  console.log(`${WHITE} No AI API call sites detected.${R}`);
@@ -206,7 +206,7 @@ function printReport(sites, filesScanned, timeMs, cwd) {
206
206
  const monthlyCostInput = dailyInput * 30 / 1e6 * 3;
207
207
  const monthlyCostOutput = dailyOutput * 30 / 1e6 * 15;
208
208
  const monthlyCost = monthlyCostInput + monthlyCostOutput;
209
- console.log(`${ORANGE} DAILY BURN ${GRAY}(${callsPerDay} calls/site/day)${R}`);
209
+ console.log(`${ORANGE} DAILY TOKEN VOLUME ${GRAY}(${callsPerDay} calls/site/day)${R}`);
210
210
  console.log(`${DIM} ${"─".repeat(60)}${R}`);
211
211
  console.log(`${WHITE} Input tokens: ${B}${formatNum(dailyInput)}${R}`);
212
212
  console.log(`${WHITE} Output tokens: ${B}${formatNum(dailyOutput)}${R} ${GRAY}(estimated)${R}`);
@@ -218,12 +218,12 @@ function printReport(sites, filesScanned, timeMs, cwd) {
218
218
  console.log(`${WHITE} Output: ${B}$${monthlyCostOutput.toFixed(2)}/mo${R}`);
219
219
  console.log(`${WHITE} Total: ${ORANGE}${B}$${monthlyCost.toFixed(2)}/mo${R}`);
220
220
  console.log("");
221
- const savingsPct = 10;
222
- const monthlySaved = monthlyCost * savingsPct / 100;
223
- console.log(`${GOLD} ⚡ SLASH SAVINGS ${GRAY}(${savingsPct}% gate efficiency)${R}`);
221
+ const recoveryPct = 10;
222
+ const monthlyRecovered = monthlyCost * recoveryPct / 100;
223
+ console.log(`${GOLD} ⚡ TOKENS SALVAGED ${GRAY}(${recoveryPct}% gate efficiency)${R}`);
224
224
  console.log(`${DIM} ${"─".repeat(60)}${R}`);
225
- console.log(`${WHITE} Monthly savings: ${GOLD}${B}$${monthlySaved.toFixed(2)}/mo${R}`);
226
- console.log(`${WHITE} Annual savings: ${GOLD}${B}$${(monthlySaved * 12).toFixed(2)}/yr${R}`);
225
+ console.log(`${WHITE} Monthly salvaged: ${GOLD}${B}$${monthlyRecovered.toFixed(2)}/mo${R}`);
226
+ console.log(`${WHITE} Annual salvaged: ${GOLD}${B}$${(monthlyRecovered * 12).toFixed(2)}/yr${R}`);
227
227
  console.log("");
228
228
  console.log(`${GRAY} 4.8 KB WASM · sub-ms · zero deps${R}`);
229
229
  console.log(`${GRAY} → ${ORANGE}slashtokens.com${R}`);
@@ -233,50 +233,20 @@ function printReport(sites, filesScanned, timeMs, cwd) {
233
233
  // src/config.ts
234
234
  var _key = null;
235
235
  var _endpoint = "https://mcpaas.live/api/slash/transact";
236
+ var _route = true;
237
+ var _models = null;
238
+ var _quiet = false;
236
239
  function init(opts) {
237
- _key = opts.key;
240
+ if (opts.key !== undefined)
241
+ _key = opts.key;
238
242
  if (opts.endpoint)
239
243
  _endpoint = opts.endpoint;
240
- }
241
- function resolveKey(perCallKey) {
242
- const key = perCallKey || _key || process.env.SLASH_KEY;
243
- if (!key) {
244
- throw new Error(`No Slash API key. Set SLASH_KEY env var, call init({ key }), or pass key to report().
245
- ` + "Register: POST https://mcpaas.live/api/slash/register");
246
- }
247
- return key;
248
- }
249
- function getEndpoint() {
250
- return _endpoint;
251
- }
252
-
253
- // src/transact.ts
254
- async function report(opts) {
255
- const key = resolveKey(opts.key);
256
- const endpoint = getEndpoint();
257
- const res = await fetch(endpoint, {
258
- method: "POST",
259
- headers: {
260
- "Content-Type": "application/json",
261
- Authorization: `Bearer ${key}`
262
- },
263
- body: JSON.stringify({
264
- tokens_estimated: opts.tokens_estimated,
265
- tokens_saved: opts.tokens_saved,
266
- model: opts.model || "unknown",
267
- action: opts.action,
268
- cost_saved_usd: opts.cost_saved_usd
269
- })
270
- });
271
- if (res.status === 401)
272
- throw new Error("Invalid Slash API key");
273
- if (res.status === 402)
274
- throw new Error("Insufficient balance. Purchase credits at slashtokens.com");
275
- if (res.status === 429)
276
- throw new Error("Rate limited. Try again shortly.");
277
- if (!res.ok)
278
- throw new Error(`Slash API error: ${res.status}`);
279
- return res.json();
244
+ if (opts.route !== undefined)
245
+ _route = opts.route;
246
+ if (opts.models)
247
+ _models = new Set(opts.models.map((m) => m.toLowerCase()));
248
+ if (opts.quiet !== undefined)
249
+ _quiet = opts.quiet;
280
250
  }
281
251
 
282
252
  // src/cli.ts
@@ -291,33 +261,14 @@ printReport(sites, filesScanned, timeMs, cwd);
291
261
  var R2 = "\x1B[0m";
292
262
  var B2 = "\x1B[1m";
293
263
  var GREEN = "\x1B[38;2;74;222;128m";
294
- var GOLD2 = "\x1B[38;2;230;161;65m";
295
264
  var GRAY2 = "\x1B[90m";
296
265
  var ORANGE2 = "\x1B[38;2;255;68;0m";
297
- if (key && sites.length > 0) {
298
- const callsPerDay = 100;
299
- const dailyInput = sites.reduce((sum, s) => sum + s.tokensPerCall * callsPerDay, 0);
300
- const dailyOutput = dailyInput * 2;
301
- const monthlyCostInput = dailyInput * 30 / 1e6 * 3;
302
- const monthlyCostOutput = dailyOutput * 30 / 1e6 * 15;
303
- const monthlyCost = monthlyCostInput + monthlyCostOutput;
304
- const monthlySaved = monthlyCost * 0.1;
305
- report({
306
- tokens_estimated: dailyInput * 30,
307
- tokens_saved: Math.round(dailyInput * 30 * 0.1),
308
- model: "scan",
309
- action: "reduced",
310
- cost_saved_usd: monthlySaved
311
- }).then((result) => {
312
- console.log(`${GREEN}${B2} ✓ Reported to MCPaaS${R2} ${GRAY2}(${result.transaction_id})${R2}`);
313
- console.log(`${GRAY2} Fee: $${result.fee_usd.toFixed(4)} · Balance: $${result.balance_remaining_usd.toFixed(2)}${R2}`);
314
- console.log("");
315
- }).catch((err) => {
316
- console.log(`${GOLD2} ⚠ Report failed: ${err.message}${R2}`);
317
- console.log("");
318
- });
319
- } else if (!key && sites.length > 0) {
320
- console.log(`${GRAY2} → Report savings: ${ORANGE2}bunx slash-tokens --key=YOUR_KEY${R2}`);
321
- console.log(`${GRAY2} → Get a key: POST https://mcpaas.live/api/slash/register${R2}`);
266
+ if (sites.length > 0) {
267
+ if (key) {
268
+ console.log(`${GREEN}${B2} ✓ Key active${R2} ${GRAY2}— evaluation complete, no charges on CLI scans${R2}`);
269
+ console.log(`${GRAY2} → Dashboard: ${ORANGE2}https://mcpaas.live/slash/dashboard${R2}`);
270
+ } else {
271
+ console.log(`${GRAY2} → Next: ${ORANGE2}https://mcpaas.live/slash/setup${R2}`);
272
+ }
322
273
  console.log("");
323
274
  }
package/dist/config.d.ts CHANGED
@@ -1,7 +1,13 @@
1
1
  export declare function init(opts: {
2
- key: string;
2
+ key?: string;
3
3
  endpoint?: string;
4
+ route?: boolean;
5
+ models?: string[];
6
+ quiet?: boolean;
4
7
  }): void;
5
8
  export declare function resolveKey(perCallKey?: string): string;
6
9
  export declare function getEndpoint(): string;
7
10
  export declare function hasKey(): boolean;
11
+ export declare function shouldRoute(): boolean;
12
+ export declare function isModelAllowed(model: string): boolean;
13
+ export declare function isQuiet(): boolean;
package/dist/config.js CHANGED
@@ -1,9 +1,19 @@
1
1
  let _key = null;
2
2
  let _endpoint = 'https://mcpaas.live/api/slash/transact';
3
+ let _route = true; // default: route to cheapest model
4
+ let _models = null; // null = all models allowed
5
+ let _quiet = false; // default: verbose (show every call)
3
6
  export function init(opts) {
4
- _key = opts.key;
7
+ if (opts.key !== undefined)
8
+ _key = opts.key;
5
9
  if (opts.endpoint)
6
10
  _endpoint = opts.endpoint;
11
+ if (opts.route !== undefined)
12
+ _route = opts.route;
13
+ if (opts.models)
14
+ _models = new Set(opts.models.map(m => m.toLowerCase()));
15
+ if (opts.quiet !== undefined)
16
+ _quiet = opts.quiet;
7
17
  }
8
18
  export function resolveKey(perCallKey) {
9
19
  const key = perCallKey || _key || process.env.SLASH_KEY;
@@ -19,3 +29,14 @@ export function getEndpoint() {
19
29
  export function hasKey() {
20
30
  return !!(_key || process.env.SLASH_KEY);
21
31
  }
32
+ export function shouldRoute() {
33
+ return _route;
34
+ }
35
+ export function isModelAllowed(model) {
36
+ if (!_models)
37
+ return true; // null = all allowed
38
+ return _models.has(model.toLowerCase());
39
+ }
40
+ export function isQuiet() {
41
+ return _quiet;
42
+ }
@@ -2,9 +2,13 @@ export interface InterceptEvent {
2
2
  endpoint: string;
3
3
  provider: string;
4
4
  model: string;
5
+ originalModel: string;
5
6
  tokens: number;
6
7
  cost: number;
8
+ originalCost: number;
9
+ salvaged: number;
7
10
  fits: boolean;
11
+ routed: boolean;
8
12
  timestamp: string;
9
13
  }
10
14
  export declare function onIntercept(handler: (event: InterceptEvent) => void): void;
package/dist/intercept.js CHANGED
@@ -1,6 +1,28 @@
1
1
  import { slash } from './slash.js';
2
2
  import { getModel } from './models.js';
3
- // AI API endpoint detection — same patterns as scanner, runtime version
3
+ import { shouldRoute, isModelAllowed } from './config.js';
4
+ // Provider groups — routing only happens within same provider
5
+ const PROVIDER_MODELS = {
6
+ 'Anthropic': ['claude-opus', 'claude-sonnet', 'claude-haiku'],
7
+ 'OpenAI': ['gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano'],
8
+ 'xAI': ['grok-4.20', 'grok-4-1-fast'],
9
+ 'Google': ['gemini-3.1-pro', 'gemini-2.5-flash'],
10
+ };
11
+ // Reverse lookup: model name → provider model names in the API
12
+ // (what to put back in the request body)
13
+ const MODEL_API_NAMES = {
14
+ 'claude-opus': 'claude-opus-4-20250514',
15
+ 'claude-sonnet': 'claude-sonnet-4-20250514',
16
+ 'claude-haiku': 'claude-haiku-4-5-20251001',
17
+ 'gpt-5.4': 'gpt-5.4',
18
+ 'gpt-5.4-mini': 'gpt-5.4-mini',
19
+ 'gpt-5.4-nano': 'gpt-5.4-nano',
20
+ 'grok-4.20': 'grok-4.20',
21
+ 'grok-4-1-fast': 'grok-4-1-fast',
22
+ 'gemini-3.1-pro': 'gemini-3.1-pro',
23
+ 'gemini-2.5-flash': 'gemini-2.5-flash',
24
+ };
25
+ // AI API endpoint detection
4
26
  const AI_ENDPOINTS = [
5
27
  {
6
28
  pattern: /api\.anthropic\.com/,
@@ -10,17 +32,17 @@ const AI_ENDPOINTS = [
10
32
  {
11
33
  pattern: /api\.openai\.com/,
12
34
  provider: 'OpenAI',
13
- modelExtractor: (body) => body?.model || 'gpt-4o',
35
+ modelExtractor: (body) => body?.model || 'gpt-5.4',
14
36
  },
15
37
  {
16
38
  pattern: /generativelanguage\.googleapis\.com/,
17
39
  provider: 'Google',
18
- modelExtractor: (body) => 'gemini-2.0-flash',
40
+ modelExtractor: (body) => 'gemini-2.5-flash',
19
41
  },
20
42
  {
21
43
  pattern: /api\.x\.ai/,
22
44
  provider: 'xAI',
23
- modelExtractor: (body) => body?.model || 'grok-3',
45
+ modelExtractor: (body) => body?.model || 'grok-4.20',
24
46
  },
25
47
  ];
26
48
  // Normalize model names to our pricing table keys
@@ -63,17 +85,13 @@ function normalizeModel(raw) {
63
85
  function extractContent(body) {
64
86
  if (!body)
65
87
  return '';
66
- // Anthropic: { messages: [{ content: "..." }] }
67
- // OpenAI: { messages: [{ content: "..." }] }
68
88
  if (body.messages && Array.isArray(body.messages)) {
69
89
  return body.messages
70
90
  .map((m) => typeof m.content === 'string' ? m.content : JSON.stringify(m.content))
71
91
  .join('\n');
72
92
  }
73
- // Anthropic: { prompt: "..." }
74
93
  if (typeof body.prompt === 'string')
75
94
  return body.prompt;
76
- // Google: { contents: [{ parts: [{ text: "..." }] }] }
77
95
  if (body.contents && Array.isArray(body.contents)) {
78
96
  return body.contents
79
97
  .flatMap((c) => c.parts || [])
@@ -82,6 +100,30 @@ function extractContent(body) {
82
100
  }
83
101
  return JSON.stringify(body);
84
102
  }
103
+ // Find cheapest model from same provider that fits
104
+ function findCheapestRoute(provider, tokens, currentModel) {
105
+ const providerModels = PROVIDER_MODELS[provider];
106
+ if (!providerModels)
107
+ return null;
108
+ let cheapest = null;
109
+ for (const model of providerModels) {
110
+ if (model === currentModel)
111
+ continue;
112
+ if (!isModelAllowed(model))
113
+ continue; // user excluded this model
114
+ const info = getModel(model);
115
+ if (!info)
116
+ continue;
117
+ if (tokens > info.context)
118
+ continue; // doesn't fit
119
+ if (info.input >= (getModel(currentModel)?.input ?? 0))
120
+ continue; // not cheaper
121
+ if (!cheapest || info.input < cheapest.inputCost) {
122
+ cheapest = { model, inputCost: info.input };
123
+ }
124
+ }
125
+ return cheapest?.model ?? null;
126
+ }
85
127
  let _onIntercept = null;
86
128
  export function onIntercept(handler) {
87
129
  _onIntercept = handler;
@@ -99,23 +141,38 @@ export function patchFetch() {
99
141
  const body = JSON.parse(bodyStr);
100
142
  const content = extractContent(body);
101
143
  const rawModel = match.modelExtractor(body);
102
- const model = normalizeModel(rawModel);
144
+ const originalModel = normalizeModel(rawModel);
103
145
  const tokens = slash(content);
104
- const info = getModel(model);
105
- const cost = info ? Math.round(((tokens / 1000000) * info.input) * 1000000) / 1000000 : 0;
106
- const fits = info ? tokens <= info.context : true;
146
+ const originalInfo = getModel(originalModel);
147
+ const originalCost = originalInfo ? Math.round(((tokens / 1000000) * originalInfo.input) * 1000000) / 1000000 : 0;
148
+ const fits = originalInfo ? tokens <= originalInfo.context : true;
149
+ // Find cheapest route within same provider (if routing enabled)
150
+ const routeModel = shouldRoute() ? findCheapestRoute(match.provider, tokens, originalModel) : null;
151
+ const routedInfo = routeModel ? getModel(routeModel) : null;
152
+ const routedCost = routedInfo ? Math.round(((tokens / 1000000) * routedInfo.input) * 1000000) / 1000000 : originalCost;
153
+ const salvaged = routeModel ? Math.round((originalCost - routedCost) * 1000000) / 1000000 : 0;
107
154
  const event = {
108
155
  endpoint: url,
109
156
  provider: match.provider,
110
- model,
157
+ model: routeModel || originalModel,
158
+ originalModel,
111
159
  tokens,
112
- cost,
160
+ cost: routeModel ? routedCost : originalCost,
161
+ originalCost,
162
+ salvaged,
113
163
  fits,
164
+ routed: !!routeModel,
114
165
  timestamp: new Date().toISOString(),
115
166
  };
116
167
  if (_onIntercept) {
117
168
  _onIntercept(event);
118
169
  }
170
+ // If routed, rewrite the request body with the cheaper model
171
+ if (routeModel && init) {
172
+ const apiModelName = MODEL_API_NAMES[routeModel] || routeModel;
173
+ body.model = apiModelName;
174
+ init = { ...init, body: JSON.stringify(body) };
175
+ }
119
176
  }
120
177
  }
121
178
  catch {
@@ -1,8 +1,8 @@
1
1
  export interface Alternative {
2
2
  model: string;
3
3
  cost: number;
4
- savings: number;
5
- savingsPercent: number;
4
+ salvaged: number;
5
+ salvagePercent: number;
6
6
  }
7
7
  export interface PreflightResult {
8
8
  tokens: number;
package/dist/preflight.js CHANGED
@@ -17,11 +17,11 @@ export function preflight(content, model) {
17
17
  return {
18
18
  model: m,
19
19
  cost: altCost,
20
- savings: Math.round((cost - altCost) * 1000000) / 1000000,
21
- savingsPercent: cost > 0 ? Math.round(((cost - altCost) / cost) * 10000) / 100 : 0,
20
+ salvaged: Math.round((cost - altCost) * 1000000) / 1000000,
21
+ salvagePercent: cost > 0 ? Math.round(((cost - altCost) / cost) * 10000) / 100 : 0,
22
22
  };
23
23
  })
24
- .filter(o => o.savings > 0)
24
+ .filter(o => o.salvaged > 0)
25
25
  .sort((a, b) => a.cost - b.cost);
26
26
  return { tokens, cost, fits, model, context: info.context, utilization, options };
27
27
  }
package/dist/report.js CHANGED
@@ -15,9 +15,9 @@ function formatNum(n) {
15
15
  export function printReport(sites, filesScanned, timeMs, cwd) {
16
16
  console.log('');
17
17
  console.log(`${ORANGE}${B} ⚡ /slash${R}`);
18
- console.log(`${GRAY} Token burn analysis${R}`);
18
+ console.log(`${GRAY} Token optimization analysis${R}`);
19
19
  console.log('');
20
- console.log(`${DIM} Scanned ${filesScanned} files in ${timeMs}ms${R}`);
20
+ console.log(`${DIM} Evaluated ${filesScanned} files in ${timeMs}ms${R}`);
21
21
  console.log('');
22
22
  if (sites.length === 0) {
23
23
  console.log(`${WHITE} No AI API call sites detected.${R}`);
@@ -43,7 +43,7 @@ export function printReport(sites, filesScanned, timeMs, cwd) {
43
43
  const monthlyCostInput = (dailyInput * 30 / 1000000) * 3;
44
44
  const monthlyCostOutput = (dailyOutput * 30 / 1000000) * 15;
45
45
  const monthlyCost = monthlyCostInput + monthlyCostOutput;
46
- console.log(`${ORANGE} DAILY BURN ${GRAY}(${callsPerDay} calls/site/day)${R}`);
46
+ console.log(`${ORANGE} DAILY TOKEN VOLUME ${GRAY}(${callsPerDay} calls/site/day)${R}`);
47
47
  console.log(`${DIM} ${'─'.repeat(60)}${R}`);
48
48
  console.log(`${WHITE} Input tokens: ${B}${formatNum(dailyInput)}${R}`);
49
49
  console.log(`${WHITE} Output tokens: ${B}${formatNum(dailyOutput)}${R} ${GRAY}(estimated)${R}`);
@@ -55,13 +55,13 @@ export function printReport(sites, filesScanned, timeMs, cwd) {
55
55
  console.log(`${WHITE} Output: ${B}$${monthlyCostOutput.toFixed(2)}/mo${R}`);
56
56
  console.log(`${WHITE} Total: ${ORANGE}${B}$${monthlyCost.toFixed(2)}/mo${R}`);
57
57
  console.log('');
58
- // Slash savings (conservative 10% gate savings)
59
- const savingsPct = 10;
60
- const monthlySaved = monthlyCost * savingsPct / 100;
61
- console.log(`${GOLD} ⚡ SLASH SAVINGS ${GRAY}(${savingsPct}% gate efficiency)${R}`);
58
+ // Slash — sunk cost recovery (conservative 10% gate efficiency)
59
+ const recoveryPct = 10;
60
+ const monthlyRecovered = monthlyCost * recoveryPct / 100;
61
+ console.log(`${GOLD} ⚡ TOKENS SALVAGED ${GRAY}(${recoveryPct}% gate efficiency)${R}`);
62
62
  console.log(`${DIM} ${'─'.repeat(60)}${R}`);
63
- console.log(`${WHITE} Monthly savings: ${GOLD}${B}$${monthlySaved.toFixed(2)}/mo${R}`);
64
- console.log(`${WHITE} Annual savings: ${GOLD}${B}$${(monthlySaved * 12).toFixed(2)}/yr${R}`);
63
+ console.log(`${WHITE} Monthly salvaged: ${GOLD}${B}$${monthlyRecovered.toFixed(2)}/mo${R}`);
64
+ console.log(`${WHITE} Annual salvaged: ${GOLD}${B}$${(monthlyRecovered * 12).toFixed(2)}/yr${R}`);
65
65
  console.log('');
66
66
  console.log(`${GRAY} 4.8 KB WASM · sub-ms · zero deps${R}`);
67
67
  console.log(`${GRAY} → ${ORANGE}slashtokens.com${R}`);
@@ -3,7 +3,7 @@ export interface ReportOptions {
3
3
  tokens_estimated: number;
4
4
  tokens_saved: number;
5
5
  model?: string;
6
- action: 'skipped' | 'reduced' | 'routed';
6
+ action: 'prevented' | 'routed';
7
7
  cost_saved_usd: number;
8
8
  }
9
9
  export interface ReportResult {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "slash-tokens",
3
- "version": "1.1.0",
4
- "description": "Pre-flight checks for API calls. 4.8 KB WASM. Sub-millisecond. Zero dependencies. Fly First. Pay Economy.",
3
+ "version": "1.1.2",
4
+ "description": "Token Optimization for Context Engineers. 4.8 KB WASM. Sub-millisecond. Zero dependencies.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -19,7 +19,9 @@
19
19
  "types": "./dist/auto.d.ts"
20
20
  }
21
21
  },
22
- "files": ["dist"],
22
+ "files": [
23
+ "dist"
24
+ ],
23
25
  "sideEffects": false,
24
26
  "scripts": {
25
27
  "build": "npx tsc && bun build src/cli.ts --outfile=dist/cli.js --target=node",
@@ -27,7 +29,20 @@
27
29
  },
28
30
  "author": "wolfejam",
29
31
  "license": "MIT",
30
- "keywords": ["tokens", "wasm", "edge", "budget", "estimation", "llm", "ai", "openai", "anthropic", "claude", "grok", "gemini"],
32
+ "keywords": [
33
+ "tokens",
34
+ "wasm",
35
+ "edge",
36
+ "budget",
37
+ "estimation",
38
+ "llm",
39
+ "ai",
40
+ "openai",
41
+ "anthropic",
42
+ "claude",
43
+ "grok",
44
+ "gemini"
45
+ ],
31
46
  "repository": {
32
47
  "type": "git",
33
48
  "url": "https://github.com/Wolfe-Jam/slash-tokens"