meter-ai 0.3.1 → 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.
@@ -65,7 +65,7 @@ async function installClaudeHooks(meterDir) {
65
65
  await mkdir(hooksDir, { recursive: true });
66
66
  // Copy hook scripts to ~/.meter/hooks/
67
67
  const srcDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'hooks');
68
- for (const file of ['on-prompt.js', 'statusline.js']) {
68
+ for (const file of ['on-prompt.js', 'on-stop.js', 'statusline.js']) {
69
69
  try {
70
70
  await copyFile(join(srcDir, file), join(hooksDir, file));
71
71
  await chmod(join(hooksDir, file), 0o755);
@@ -104,6 +104,17 @@ async function installClaudeHooks(meterDir) {
104
104
  });
105
105
  console.log('✓ Added meter estimation hook to Claude Code');
106
106
  }
107
+ // Add Stop hook for post-response cost tracking
108
+ const meterStopCommand = `node "${join(hooksDir, 'on-stop.js')}"`;
109
+ if (!settings.hooks.Stop)
110
+ settings.hooks.Stop = [];
111
+ const alreadyHasStopHook = settings.hooks.Stop.some((h) => h.hooks?.some((hh) => hh.command?.includes('meter')));
112
+ if (!alreadyHasStopHook) {
113
+ settings.hooks.Stop.push({
114
+ hooks: [{ type: 'command', command: meterStopCommand }]
115
+ });
116
+ console.log('✓ Added meter cost tracking hook to Claude Code');
117
+ }
107
118
  // Add/update statusline command
108
119
  const meterStatuslineCommand = `node "${join(hooksDir, 'statusline.js')}"`;
109
120
  const existingStatusLine = settings.statusLine;
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meter-ai",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Intelligent CLI wrapper for Claude Code — pre-task cost estimation, live status bar, budget protection",
5
5
  "bin": {
6
6
  "meter": "dist/index.js"
@@ -84,7 +84,7 @@ async function installClaudeHooks(meterDir: string): Promise<void> {
84
84
 
85
85
  // Copy hook scripts to ~/.meter/hooks/
86
86
  const srcDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'hooks')
87
- for (const file of ['on-prompt.js', 'statusline.js']) {
87
+ for (const file of ['on-prompt.js', 'on-stop.js', 'statusline.js']) {
88
88
  try {
89
89
  await copyFile(join(srcDir, file), join(hooksDir, file))
90
90
  await chmod(join(hooksDir, file), 0o755)
@@ -125,6 +125,21 @@ async function installClaudeHooks(meterDir: string): Promise<void> {
125
125
  console.log('✓ Added meter estimation hook to Claude Code')
126
126
  }
127
127
 
128
+ // Add Stop hook for post-response cost tracking
129
+ const meterStopCommand = `node "${join(hooksDir, 'on-stop.js')}"`
130
+ if (!settings.hooks.Stop) settings.hooks.Stop = []
131
+
132
+ const alreadyHasStopHook = settings.hooks.Stop.some((h: any) =>
133
+ h.hooks?.some((hh: any) => hh.command?.includes('meter'))
134
+ )
135
+
136
+ if (!alreadyHasStopHook) {
137
+ settings.hooks.Stop.push({
138
+ hooks: [{ type: 'command', command: meterStopCommand }]
139
+ })
140
+ console.log('✓ Added meter cost tracking hook to Claude Code')
141
+ }
142
+
128
143
  // Add/update statusline command
129
144
  const meterStatuslineCommand = `node "${join(hooksDir, 'statusline.js')}"`
130
145
  const existingStatusLine = settings.statusLine
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * meter — Stop hook
4
+ *
5
+ * Fires after Claude Code finishes responding to a prompt.
6
+ * Reads token usage from the response, calculates actual cost,
7
+ * and updates the session tracker in ~/.meter/cache/session-costs.json
8
+ * The statusline reads this to show actual costs.
9
+ */
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const os = require('os');
13
+
14
+ const METER_DIR = path.join(os.homedir(), '.meter');
15
+ const CACHE_DIR = path.join(METER_DIR, 'cache');
16
+ const SESSION_FILE = path.join(CACHE_DIR, 'session-costs.json');
17
+ const CONFIG_FILE = path.join(METER_DIR, 'config.json');
18
+
19
+ // Default pricing (per million tokens)
20
+ const PRICING = {
21
+ 'claude-opus-4-20250514': { input: 15, output: 75 },
22
+ 'claude-sonnet-4-20250514': { input: 3, output: 15 },
23
+ 'claude-haiku-4-20250307': { input: 0.25, output: 1.25 },
24
+ 'default': { input: 15, output: 75 },
25
+ };
26
+
27
+ try {
28
+ const input = fs.readFileSync(0, 'utf-8').trim();
29
+ if (!input) process.exit(0);
30
+
31
+ let data;
32
+ try { data = JSON.parse(input); } catch { process.exit(0); }
33
+
34
+ // Extract token usage from stop event data
35
+ const tokensIn = data.usage?.input_tokens || data.input_tokens || 0;
36
+ const tokensOut = data.usage?.output_tokens || data.output_tokens || 0;
37
+ const totalTokens = tokensIn + tokensOut;
38
+
39
+ // If no token data, estimate from response length
40
+ const responseLen = (data.response || data.message || '').length;
41
+ const estimatedTokensOut = tokensOut || Math.ceil(responseLen / 4);
42
+
43
+ // Get model and pricing
44
+ let model = 'default';
45
+ try {
46
+ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
47
+ model = config.models?.claude_chain?.[0] || 'default';
48
+ } catch {}
49
+
50
+ const pricing = PRICING[model] || PRICING['default'];
51
+ const cost = ((tokensIn || 500) / 1_000_000) * pricing.input +
52
+ ((estimatedTokensOut || 200) / 1_000_000) * pricing.output;
53
+
54
+ // Load or create session tracker
55
+ fs.mkdirSync(CACHE_DIR, { recursive: true });
56
+ let session = { prompts: 0, total_cost: 0, last_cost: 0, heaviest_cost: 0, started_at: Date.now() };
57
+
58
+ try {
59
+ const existing = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
60
+ // If last activity was more than 30 minutes ago, start a new session
61
+ const age = Date.now() - (existing.last_activity || 0);
62
+ if (age < 1_800_000) {
63
+ session = existing;
64
+ }
65
+ } catch {}
66
+
67
+ // Update session
68
+ session.prompts += 1;
69
+ session.last_cost = cost;
70
+ session.total_cost += cost;
71
+ session.heaviest_cost = Math.max(session.heaviest_cost, cost);
72
+ session.last_activity = Date.now();
73
+
74
+ fs.writeFileSync(SESSION_FILE, JSON.stringify(session));
75
+ } catch {
76
+ // Never block Claude Code
77
+ process.exit(0);
78
+ }
@@ -2,35 +2,117 @@
2
2
  /**
3
3
  * meter — Statusline command for Claude Code
4
4
  *
5
- * Reads the latest estimation from ~/.meter/cache/latest-estimate.json
6
- * and outputs a formatted status string for Claude Code's bottom bar.
5
+ * Shows:
6
+ * 1. Estimation for the current prompt
7
+ * 2. Session actuals (total spend, prompt count, last cost)
8
+ * 3. Conservative model nudge (only when clearly appropriate)
9
+ *
10
+ * Output: meter ~$0.09 medium | session $0.47 (5) | last $0.12 | sonnet could save ~$0.07
7
11
  */
8
12
  const fs = require('fs');
9
13
  const path = require('path');
10
14
  const os = require('os');
11
15
 
12
- const ESTIMATE_FILE = path.join(os.homedir(), '.meter', 'cache', 'latest-estimate.json');
16
+ const CACHE_DIR = path.join(os.homedir(), '.meter', 'cache');
17
+ const CONFIG_FILE = path.join(os.homedir(), '.meter', 'config.json');
18
+ const ESTIMATE_FILE = path.join(CACHE_DIR, 'latest-estimate.json');
19
+ const SESSION_FILE = path.join(CACHE_DIR, 'session-costs.json');
13
20
 
14
- try {
15
- if (!fs.existsSync(ESTIMATE_FILE)) {
16
- process.stdout.write('meter: ready');
17
- process.exit(0);
18
- }
21
+ // Pricing per million tokens (output, which dominates cost)
22
+ const MODEL_COST_RATIO = {
23
+ 'opus': { output: 75 },
24
+ 'sonnet': { output: 15 },
25
+ 'haiku': { output: 1.25 },
26
+ };
27
+
28
+ /**
29
+ * Conservative model nudge rules:
30
+ * - Only nudge ONE tier down (never skip tiers)
31
+ * - Only for low/medium complexity (never heavy/critical)
32
+ * - Only when heuristic confidence was high
33
+ * - Opus on low task → suggest Sonnet
34
+ * - Opus on medium task → suggest Sonnet
35
+ * - Sonnet on low task → suggest Haiku
36
+ * - Everything else → no nudge
37
+ */
38
+ function getModelNudge(complexity, currentModel, lastCost) {
39
+ if (!complexity || !currentModel || !lastCost || lastCost <= 0) return null;
40
+
41
+ const model = currentModel.toLowerCase();
42
+ const isOpus = model.includes('opus');
43
+ const isSonnet = model.includes('sonnet');
19
44
 
20
- const data = JSON.parse(fs.readFileSync(ESTIMATE_FILE, 'utf-8'));
21
- const age = Date.now() - (data.timestamp || 0);
45
+ if (complexity === 'heavy' || complexity === 'critical') return null;
46
+
47
+ if (isOpus && (complexity === 'low' || complexity === 'medium')) {
48
+ // Opus → Sonnet: output cost ratio is 75/15 = 5x
49
+ const sonnetCost = lastCost / 5;
50
+ const savings = lastCost - sonnetCost;
51
+ if (savings >= 0.01) {
52
+ return `sonnet: ~$${sonnetCost.toFixed(2)} (save $${savings.toFixed(2)})`;
53
+ }
54
+ }
22
55
 
23
- // If estimate is older than 10 minutes, show stale indicator
24
- if (age > 600_000) {
25
- process.stdout.write('meter: idle');
26
- process.exit(0);
56
+ if (isSonnet && complexity === 'low') {
57
+ // Sonnet Haiku: output cost ratio is 15/1.25 = 12x
58
+ const haikuCost = lastCost / 12;
59
+ const savings = lastCost - haikuCost;
60
+ if (savings >= 0.01) {
61
+ return `haiku: ~$${haikuCost.toFixed(2)} (save $${savings.toFixed(2)})`;
62
+ }
27
63
  }
28
64
 
29
- const cost = data.cost != null ? `~$${data.cost.toFixed(2)}` : '?';
30
- const complexity = data.complexity || '?';
31
- const prompt = (data.prompt || '').slice(0, 30);
65
+ return null;
66
+ }
67
+
68
+ try {
69
+ const parts = [];
70
+ let complexity = null;
71
+ let currentModel = null;
72
+
73
+ // Read config for current model
74
+ try {
75
+ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
76
+ currentModel = config.models?.claude_chain?.[0] || null;
77
+ } catch {}
78
+
79
+ // Part 1: Latest estimation
80
+ try {
81
+ const est = JSON.parse(fs.readFileSync(ESTIMATE_FILE, 'utf-8'));
82
+ const age = Date.now() - (est.timestamp || 0);
83
+ if (age < 600_000) {
84
+ parts.push(`~$${est.cost.toFixed(2)} ${est.complexity}`);
85
+ complexity = est.complexity;
86
+ }
87
+ } catch {}
32
88
 
33
- process.stdout.write(`meter ${cost} ${complexity} ${prompt}`);
89
+ // Part 2: Session actuals
90
+ let lastCost = 0;
91
+ try {
92
+ const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
93
+ const age = Date.now() - (session.last_activity || 0);
94
+ if (age < 1_800_000 && session.prompts > 0) {
95
+ parts.push(`session $${session.total_cost.toFixed(2)} (${session.prompts})`);
96
+ if (session.last_cost > 0) {
97
+ parts.push(`last $${session.last_cost.toFixed(2)}`);
98
+ lastCost = session.last_cost;
99
+ }
100
+ }
101
+ } catch {}
102
+
103
+ // Part 3: Model nudge (conservative, only when appropriate)
104
+ if (complexity && currentModel && lastCost > 0) {
105
+ const nudge = getModelNudge(complexity, currentModel, lastCost);
106
+ if (nudge) {
107
+ parts.push(nudge);
108
+ }
109
+ }
110
+
111
+ if (parts.length === 0) {
112
+ process.stdout.write('meter: ready');
113
+ } else {
114
+ process.stdout.write('meter ' + parts.join(' | '));
115
+ }
34
116
  } catch {
35
117
  process.stdout.write('meter: ready');
36
118
  }