burnwatch 0.4.0 → 0.4.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/CHANGELOG.md +9 -0
- package/README.md +30 -30
- package/dist/cli.js +35 -33
- package/dist/cli.js.map +1 -1
- package/dist/hooks/on-prompt.js.map +1 -1
- package/dist/hooks/on-session-start.js +1 -1
- package/dist/hooks/on-session-start.js.map +1 -1
- package/dist/hooks/on-stop.js +1 -1
- package/dist/hooks/on-stop.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interactive-init.js +16 -13
- package/dist/interactive-init.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to burnwatch will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.2] - 2026-03-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Init is re-runnable**: `burnwatch init` no longer early-returns on already-initialized projects. Re-running init re-detects services and walks through interactive setup again, so users who initialized before v0.4.0 can configure budgets without manually running `burnwatch add` 14 times.
|
|
13
|
+
- **Budget prompt fires for all services**: Budget prompt was gated inside the `requiresKey` block - services without API key requirements never got asked. Now every non-excluded service gets a budget prompt during interactive init.
|
|
14
|
+
- **Untracked message is actionable**: Changed circular "run burnwatch status" message to "run burnwatch init to configure" so users know what to do next.
|
|
15
|
+
|
|
8
16
|
## [0.4.0] - 2026-03-24
|
|
9
17
|
|
|
10
18
|
### Added
|
|
@@ -43,5 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
43
51
|
- Snapshot system for delta computation across sessions
|
|
44
52
|
- Claude Code skills: `/spend` (on-demand brief), `/setup-burnwatch` (guided onboarding)
|
|
45
53
|
|
|
54
|
+
[0.4.2]: https://github.com/RaleighSF/burnwatch/compare/v0.4.0...v0.4.2
|
|
46
55
|
[0.4.0]: https://github.com/RaleighSF/burnwatch/compare/v0.1.0...v0.4.0
|
|
47
56
|
[0.1.0]: https://github.com/RaleighSF/burnwatch/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -12,24 +12,24 @@
|
|
|
12
12
|
|
|
13
13
|
<br>
|
|
14
14
|
|
|
15
|
-
burnwatch detects every paid service in your project, tracks what you're spending, and injects budget context directly into your AI coding sessions
|
|
15
|
+
burnwatch detects every paid service in your project, tracks what you're spending, and injects budget context directly into your AI coding sessions - so the agent knows what things cost before it recommends burning more money.
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
|
|
19
|
-
║ BURNWATCH
|
|
20
|
-
|
|
21
|
-
║ Service Spend Conf
|
|
22
|
-
║ ──────────────────────────────────────────────────────────
|
|
23
|
-
║ Anthropic $47.20 ✅ LIVE
|
|
24
|
-
║ Vercel $23.00 ✅ LIVE
|
|
25
|
-
║ Scrapfly $127.00 ✅ LIVE
|
|
26
|
-
║ Browserbase ~$63.00 🟠 EST
|
|
27
|
-
║ Supabase $25.00 ✅ LIVE
|
|
28
|
-
║ PostHog ~$49.00 🟡 CALC
|
|
29
|
-
|
|
30
|
-
║ TOTAL: ~$334.20 Untracked: 0 ✅ Est margin: ±$20
|
|
31
|
-
║ 🚨
|
|
32
|
-
|
|
18
|
+
╔══════════════════════════════════════════════════════════════
|
|
19
|
+
║ BURNWATCH - your-app - March 2026
|
|
20
|
+
╠══════════════════════════════════════════════════════════════
|
|
21
|
+
║ Service Spend Conf Budget Left
|
|
22
|
+
║ ──────────────────────────────────────────────────────────
|
|
23
|
+
║ Anthropic $47.20 ✅ LIVE $100 53%
|
|
24
|
+
║ Vercel $23.00 ✅ LIVE $50 54%
|
|
25
|
+
║ Scrapfly $127.00 ✅ LIVE $50 ⚠️ OVR
|
|
26
|
+
║ Browserbase ~$63.00 🟠 EST $75 16%
|
|
27
|
+
║ Supabase $25.00 ✅ LIVE $100 75%
|
|
28
|
+
║ PostHog ~$49.00 🟡 CALC $49 0%
|
|
29
|
+
╠══════════════════════════════════════════════════════════════
|
|
30
|
+
║ TOTAL: ~$334.20 Untracked: 0 ✅ Est margin: ±$20
|
|
31
|
+
║ 🚨 SCRAPFLY 254% OVER BUDGET - review before use
|
|
32
|
+
╚══════════════════════════════════════════════════════════════
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
This brief appears automatically at the start of every [Claude Code](https://claude.ai/code) session. You don't open a dashboard. You don't remember to check anything. You just see what you're spending.
|
|
@@ -38,7 +38,7 @@ This brief appears automatically at the start of every [Claude Code](https://cla
|
|
|
38
38
|
|
|
39
39
|
## Why
|
|
40
40
|
|
|
41
|
-
Agentic development lets you ship 10x faster. It also lets you burn through $400 in Scrapfly credits, rack up unexpected Browserbase bills, and discover PostHog overages three weeks after the code that caused them was written
|
|
41
|
+
Agentic development lets you ship 10x faster. It also lets you burn through $400 in Scrapfly credits, rack up unexpected Browserbase bills, and discover PostHog overages three weeks after the code that caused them was written - by an agent, in a session you barely remember.
|
|
42
42
|
|
|
43
43
|
**78% of IT leaders experienced unexpected charges** tied to consumption-based or AI pricing in the past 12 months ([Zylo 2026 SaaS Management Index](https://zylo.com/research/saas-management-index/)).
|
|
44
44
|
|
|
@@ -56,7 +56,7 @@ npx burnwatch init
|
|
|
56
56
|
|
|
57
57
|
That's it. burnwatch scans your project, detects paid services, creates a `.burnwatch/` directory, and registers Claude Code hooks. Next time you start a session, you see your spend.
|
|
58
58
|
|
|
59
|
-
> **Requirements:** Node.js 18+
|
|
59
|
+
> **Requirements:** Node.js 18+ · Zero dependencies · Works with or without Claude Code
|
|
60
60
|
|
|
61
61
|
<br>
|
|
62
62
|
|
|
@@ -91,12 +91,12 @@ npx burnwatch init
|
|
|
91
91
|
### 2. Add API keys and budgets
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
# LIVE tracking
|
|
94
|
+
# LIVE tracking - real billing API data
|
|
95
95
|
burnwatch add anthropic --key $ANTHROPIC_ADMIN_KEY --budget 100
|
|
96
96
|
burnwatch add scrapfly --key $SCRAPFLY_KEY --budget 50
|
|
97
97
|
burnwatch add vercel --token $VERCEL_TOKEN --budget 50
|
|
98
98
|
|
|
99
|
-
# CALC tracking
|
|
99
|
+
# CALC tracking - flat-rate services
|
|
100
100
|
burnwatch add posthog --plan-cost 0 --budget 0
|
|
101
101
|
burnwatch add inngest --plan-cost 25 --budget 25
|
|
102
102
|
|
|
@@ -119,13 +119,13 @@ Start a Claude Code session. The spend brief appears automatically. When you men
|
|
|
119
119
|
```
|
|
120
120
|
You: "Use Scrapfly to scrape the competitor pricing pages"
|
|
121
121
|
|
|
122
|
-
[BURNWATCH] scrapfly
|
|
122
|
+
[BURNWATCH] scrapfly - current period
|
|
123
123
|
Spend: $127.00 | Budget: $50 | ⚠️ 254% over
|
|
124
124
|
Confidence: ✅ LIVE
|
|
125
125
|
⚠️ 254% of budget consumed
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
Claude factors this into its response
|
|
128
|
+
Claude factors this into its response - it might suggest Cheerio instead, or warn you before proceeding.
|
|
129
129
|
|
|
130
130
|
When a new paid service enters your project (new dependency, new env var, new import), burnwatch alerts immediately:
|
|
131
131
|
|
|
@@ -138,7 +138,7 @@ When a new paid service enters your project (new dependency, new env var, new im
|
|
|
138
138
|
|
|
139
139
|
## How It Works
|
|
140
140
|
|
|
141
|
-
burnwatch runs as [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks)
|
|
141
|
+
burnwatch runs as [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) - background scripts that fire on session lifecycle events. It never proxies your traffic. It never intercepts API calls. It watches the exhaust of your sessions silently, completely, and without interrupting the work.
|
|
142
142
|
|
|
143
143
|
### Four Detection Surfaces
|
|
144
144
|
|
|
@@ -172,18 +172,18 @@ If burnwatch can't track a service accurately, it says so. The ledger always sho
|
|
|
172
172
|
|
|
173
173
|
### The Ledger
|
|
174
174
|
|
|
175
|
-
burnwatch writes `.burnwatch/spend-ledger.md` at the end of every session
|
|
175
|
+
burnwatch writes `.burnwatch/spend-ledger.md` at the end of every session - human-readable, git-committable, designed to be read in 10 seconds:
|
|
176
176
|
|
|
177
177
|
```markdown
|
|
178
|
-
# Burnwatch Ledger
|
|
178
|
+
# Burnwatch Ledger - your-app
|
|
179
179
|
Last updated: 2026-03-24T14:32:11Z
|
|
180
180
|
|
|
181
181
|
## This Month (March 2026)
|
|
182
182
|
| Service | Spend | Conf | Budget | Status |
|
|
183
183
|
|---------|-------|------|--------|--------|
|
|
184
|
-
| Anthropic | $47.20 | ✅ LIVE | $100 | 53%
|
|
184
|
+
| Anthropic | $47.20 | ✅ LIVE | $100 | 53% - healthy |
|
|
185
185
|
| Scrapfly | $127.00 | ✅ LIVE | $50 | ⚠️ 254% over |
|
|
186
|
-
| Vercel | $23.00 | ✅ LIVE | $50 | 54%
|
|
186
|
+
| Vercel | $23.00 | ✅ LIVE | $50 | 54% - healthy |
|
|
187
187
|
|
|
188
188
|
## TOTAL: ~$209.70 (±$2 estimated margin)
|
|
189
189
|
## Untracked services: 0
|
|
@@ -216,7 +216,7 @@ Last updated: 2026-03-24T14:32:11Z
|
|
|
216
216
|
|
|
217
217
|
## How the Agent Changes Behavior
|
|
218
218
|
|
|
219
|
-
The real power isn't showing _you_ what you spent
|
|
219
|
+
The real power isn't showing _you_ what you spent - it's telling _the agent_ what everything costs, in context, so cost becomes a factor in every recommendation.
|
|
220
220
|
|
|
221
221
|
When Claude sees `Scrapfly: $127 / $50 budget, 254% over` in its context, it:
|
|
222
222
|
|
|
@@ -281,7 +281,7 @@ burnwatch doesn't need to run in every session. It takes snapshots when present
|
|
|
281
281
|
burnwatch reconcile
|
|
282
282
|
```
|
|
283
283
|
|
|
284
|
-
Re-scans your project for services introduced in sessions where burnwatch wasn't active. For billing APIs that expose cumulative usage (like Scrapfly's credit counter), it computes the delta between snapshots
|
|
284
|
+
Re-scans your project for services introduced in sessions where burnwatch wasn't active. For billing APIs that expose cumulative usage (like Scrapfly's credit counter), it computes the delta between snapshots - attributing spend across the gap.
|
|
285
285
|
|
|
286
286
|
<br>
|
|
287
287
|
|
|
@@ -304,7 +304,7 @@ Re-scans your project for services introduced in sessions where burnwatch wasn't
|
|
|
304
304
|
}
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
-
The `gotchas` and `alternatives` fields aren't just metadata
|
|
307
|
+
The `gotchas` and `alternatives` fields aren't just metadata - the agent reads them and uses them to make better recommendations. Every PR that adds a service makes burnwatch smarter for every user.
|
|
308
308
|
|
|
309
309
|
<br>
|
|
310
310
|
|
package/dist/cli.js
CHANGED
|
@@ -661,7 +661,7 @@ function buildBrief(projectName, snapshots, blindCount) {
|
|
|
661
661
|
alerts.push({
|
|
662
662
|
serviceId: "_blind",
|
|
663
663
|
type: "blind_service",
|
|
664
|
-
message: `${blindCount} service${blindCount > 1 ? "s" : ""} detected but untracked
|
|
664
|
+
message: `${blindCount} service${blindCount > 1 ? "s" : ""} detected but untracked - run 'burnwatch init' to configure`,
|
|
665
665
|
severity: "warning"
|
|
666
666
|
});
|
|
667
667
|
}
|
|
@@ -897,10 +897,12 @@ ${RISK_LABELS[category]}`);
|
|
|
897
897
|
planName: chosen.name
|
|
898
898
|
};
|
|
899
899
|
if (chosen.type === "flat" && chosen.monthlyBase !== void 0) {
|
|
900
|
-
tracked.budget = chosen.monthlyBase;
|
|
901
900
|
tracked.planCost = chosen.monthlyBase;
|
|
901
|
+
if (chosen.monthlyBase > 0) {
|
|
902
|
+
tracked.budget = chosen.monthlyBase;
|
|
903
|
+
}
|
|
902
904
|
}
|
|
903
|
-
if (chosen.requiresKey) {
|
|
905
|
+
if (service.apiTier === "live" || chosen.requiresKey) {
|
|
904
906
|
const existingKey = globalConfig.services[service.id]?.apiKey;
|
|
905
907
|
if (existingKey) {
|
|
906
908
|
console.log(` \u{1F510} Using existing API key from global config`);
|
|
@@ -913,7 +915,7 @@ ${RISK_LABELS[category]}`);
|
|
|
913
915
|
tracked.planName = planName;
|
|
914
916
|
}
|
|
915
917
|
}
|
|
916
|
-
} else {
|
|
918
|
+
} else if (chosen.requiresKey) {
|
|
917
919
|
const keyAnswer = await ask(
|
|
918
920
|
rl,
|
|
919
921
|
` Enter API key (or press Enter to skip): `
|
|
@@ -934,16 +936,17 @@ ${RISK_LABELS[category]}`);
|
|
|
934
936
|
}
|
|
935
937
|
}
|
|
936
938
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
939
|
+
}
|
|
940
|
+
if (tracked.budget === void 0 || tracked.budget === 0) {
|
|
941
|
+
const suggestion = chosen.monthlyBase && chosen.monthlyBase > 0 ? ` [${chosen.monthlyBase}]` : "";
|
|
942
|
+
const budgetAnswer = await ask(
|
|
943
|
+
rl,
|
|
944
|
+
` Monthly budget in USD${suggestion} (or press Enter to skip): $`
|
|
945
|
+
);
|
|
946
|
+
if (budgetAnswer) {
|
|
947
|
+
const budget = parseFloat(budgetAnswer);
|
|
948
|
+
if (!isNaN(budget)) {
|
|
949
|
+
tracked.budget = budget;
|
|
947
950
|
}
|
|
948
951
|
}
|
|
949
952
|
}
|
|
@@ -1004,11 +1007,7 @@ async function main() {
|
|
|
1004
1007
|
async function cmdInit() {
|
|
1005
1008
|
const projectRoot = process.cwd();
|
|
1006
1009
|
const nonInteractive = flags.has("--non-interactive") || flags.has("--ni");
|
|
1007
|
-
|
|
1008
|
-
console.log("\u2705 burnwatch is already initialized in this project.");
|
|
1009
|
-
console.log(` Config: ${projectConfigDir(projectRoot)}/config.json`);
|
|
1010
|
-
return;
|
|
1011
|
-
}
|
|
1010
|
+
const alreadyInitialized = isInitialized(projectRoot);
|
|
1012
1011
|
let projectName = path5.basename(projectRoot);
|
|
1013
1012
|
try {
|
|
1014
1013
|
const pkgPath = path5.join(projectRoot, "package.json");
|
|
@@ -1019,10 +1018,11 @@ async function cmdInit() {
|
|
|
1019
1018
|
ensureProjectDirs(projectRoot);
|
|
1020
1019
|
console.log("\u{1F50D} Scanning project for paid services...\n");
|
|
1021
1020
|
const detected = detectServices(projectRoot);
|
|
1021
|
+
const existingConfig = alreadyInitialized ? readProjectConfig(projectRoot) : null;
|
|
1022
1022
|
const config = {
|
|
1023
|
-
projectName,
|
|
1024
|
-
services: {},
|
|
1025
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1023
|
+
projectName: existingConfig?.projectName ?? projectName,
|
|
1024
|
+
services: existingConfig?.services ?? {},
|
|
1025
|
+
createdAt: existingConfig?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1026
1026
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1027
1027
|
};
|
|
1028
1028
|
if (!nonInteractive && detected.length > 0 && process.stdin.isTTY) {
|
|
@@ -1030,13 +1030,15 @@ async function cmdInit() {
|
|
|
1030
1030
|
config.services = result.services;
|
|
1031
1031
|
} else {
|
|
1032
1032
|
for (const det of detected) {
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1033
|
+
if (!config.services[det.service.id]) {
|
|
1034
|
+
const tracked2 = {
|
|
1035
|
+
serviceId: det.service.id,
|
|
1036
|
+
detectedVia: det.sources,
|
|
1037
|
+
hasApiKey: false,
|
|
1038
|
+
firstDetected: (/* @__PURE__ */ new Date()).toISOString()
|
|
1039
|
+
};
|
|
1040
|
+
config.services[det.service.id] = tracked2;
|
|
1041
|
+
}
|
|
1040
1042
|
}
|
|
1041
1043
|
if (detected.length === 0) {
|
|
1042
1044
|
console.log(" No paid services detected yet.");
|
|
@@ -1193,11 +1195,11 @@ async function cmdStatus() {
|
|
|
1193
1195
|
if (blindCount > 0) {
|
|
1194
1196
|
console.log(`\u26A0\uFE0F ${blindCount} service${blindCount > 1 ? "s" : ""} untracked:`);
|
|
1195
1197
|
for (const snap of snapshots.filter((s) => s.tier === "blind")) {
|
|
1196
|
-
console.log(
|
|
1197
|
-
` \u2022 ${snap.serviceId} \u2014 run 'burnwatch add ${snap.serviceId} --key YOUR_KEY --budget N'`
|
|
1198
|
-
);
|
|
1198
|
+
console.log(` \u2022 ${snap.serviceId}`);
|
|
1199
1199
|
}
|
|
1200
|
-
console.log(
|
|
1200
|
+
console.log(`
|
|
1201
|
+
Run 'burnwatch init' to configure budgets and API keys.
|
|
1202
|
+
`);
|
|
1201
1203
|
}
|
|
1202
1204
|
}
|
|
1203
1205
|
function cmdServices() {
|