meter-ai 0.3.1 → 0.4.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/dist/commands/init.js +12 -1
- package/dist/commands/init.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +16 -1
- package/src/hooks/on-stop.js +78 -0
- package/src/hooks/statusline.js +33 -19
package/dist/commands/init.js
CHANGED
|
@@ -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
package/src/commands/init.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/hooks/statusline.js
CHANGED
|
@@ -2,35 +2,49 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* meter — Statusline command for Claude Code
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Shows two things:
|
|
6
|
+
* 1. The latest estimation (from on-prompt.js) — what the current prompt is expected to cost
|
|
7
|
+
* 2. Session actuals (from on-stop.js) — what you've actually spent this session
|
|
8
|
+
*
|
|
9
|
+
* Output format: meter ~$0.09 medium | session $0.47 (5) | last $0.12
|
|
7
10
|
*/
|
|
8
11
|
const fs = require('fs');
|
|
9
12
|
const path = require('path');
|
|
10
13
|
const os = require('os');
|
|
11
14
|
|
|
12
|
-
const
|
|
15
|
+
const CACHE_DIR = path.join(os.homedir(), '.meter', 'cache');
|
|
16
|
+
const ESTIMATE_FILE = path.join(CACHE_DIR, 'latest-estimate.json');
|
|
17
|
+
const SESSION_FILE = path.join(CACHE_DIR, 'session-costs.json');
|
|
13
18
|
|
|
14
19
|
try {
|
|
15
|
-
|
|
16
|
-
process.stdout.write('meter: ready');
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}
|
|
20
|
+
const parts = [];
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
// Part 1: Latest estimation
|
|
23
|
+
try {
|
|
24
|
+
const est = JSON.parse(fs.readFileSync(ESTIMATE_FILE, 'utf-8'));
|
|
25
|
+
const age = Date.now() - (est.timestamp || 0);
|
|
26
|
+
if (age < 600_000) {
|
|
27
|
+
parts.push(`~$${est.cost.toFixed(2)} ${est.complexity}`);
|
|
28
|
+
}
|
|
29
|
+
} catch {}
|
|
22
30
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
// Part 2: Session actuals
|
|
32
|
+
try {
|
|
33
|
+
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
|
|
34
|
+
const age = Date.now() - (session.last_activity || 0);
|
|
35
|
+
if (age < 1_800_000 && session.prompts > 0) {
|
|
36
|
+
parts.push(`session $${session.total_cost.toFixed(2)} (${session.prompts})`);
|
|
37
|
+
if (session.last_cost > 0) {
|
|
38
|
+
parts.push(`last $${session.last_cost.toFixed(2)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} catch {}
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
if (parts.length === 0) {
|
|
44
|
+
process.stdout.write('meter: ready');
|
|
45
|
+
} else {
|
|
46
|
+
process.stdout.write('meter ' + parts.join(' | '));
|
|
47
|
+
}
|
|
34
48
|
} catch {
|
|
35
49
|
process.stdout.write('meter: ready');
|
|
36
50
|
}
|