burnwatch 0.4.1 → 0.4.3
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 +18 -0
- package/dist/cli.js +50 -21
- 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/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,22 @@ 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.3] - 2026-03-24
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Non-interactive init auto-applies default plans**: `burnwatch init` (non-interactive or re-run) now picks each service's default plan from the registry. Flat-rate services with a cost (e.g., Scrapfly Scale at $80/mo) get budget auto-set to plan cost. Services with existing API keys in global config are auto-detected as LIVE.
|
|
13
|
+
- **Init output shows tier and budget per service**: Non-interactive init now prints each service with its confidence tier, plan name, and budget (if set), plus a clear list of which services still need budgets with the exact `burnwatch add` commands.
|
|
14
|
+
- **Untracked message is actionable**: Changed circular "run burnwatch status" message to "run burnwatch init to configure".
|
|
15
|
+
|
|
16
|
+
## [0.4.2] - 2026-03-24
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **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.
|
|
21
|
+
- **Budget prompt fires for all services**: Budget prompt was gated inside the `requiresKey` block - now every non-excluded service gets a budget prompt during interactive init.
|
|
22
|
+
- **Untracked message fix**: Same as 0.4.3 (first shipped here).
|
|
23
|
+
|
|
8
24
|
## [0.4.0] - 2026-03-24
|
|
9
25
|
|
|
10
26
|
### Added
|
|
@@ -43,5 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
43
59
|
- Snapshot system for delta computation across sessions
|
|
44
60
|
- Claude Code skills: `/spend` (on-demand brief), `/setup-burnwatch` (guided onboarding)
|
|
45
61
|
|
|
62
|
+
[0.4.3]: https://github.com/RaleighSF/burnwatch/compare/v0.4.2...v0.4.3
|
|
63
|
+
[0.4.2]: https://github.com/RaleighSF/burnwatch/compare/v0.4.0...v0.4.2
|
|
46
64
|
[0.4.0]: https://github.com/RaleighSF/burnwatch/compare/v0.1.0...v0.4.0
|
|
47
65
|
[0.1.0]: https://github.com/RaleighSF/burnwatch/releases/tag/v0.1.0
|
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
|
}
|
|
@@ -1007,11 +1007,7 @@ async function main() {
|
|
|
1007
1007
|
async function cmdInit() {
|
|
1008
1008
|
const projectRoot = process.cwd();
|
|
1009
1009
|
const nonInteractive = flags.has("--non-interactive") || flags.has("--ni");
|
|
1010
|
-
|
|
1011
|
-
console.log("\u2705 burnwatch is already initialized in this project.");
|
|
1012
|
-
console.log(` Config: ${projectConfigDir(projectRoot)}/config.json`);
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1010
|
+
const alreadyInitialized = isInitialized(projectRoot);
|
|
1015
1011
|
let projectName = path5.basename(projectRoot);
|
|
1016
1012
|
try {
|
|
1017
1013
|
const pkgPath = path5.join(projectRoot, "package.json");
|
|
@@ -1022,24 +1018,44 @@ async function cmdInit() {
|
|
|
1022
1018
|
ensureProjectDirs(projectRoot);
|
|
1023
1019
|
console.log("\u{1F50D} Scanning project for paid services...\n");
|
|
1024
1020
|
const detected = detectServices(projectRoot);
|
|
1021
|
+
const existingConfig = alreadyInitialized ? readProjectConfig(projectRoot) : null;
|
|
1025
1022
|
const config = {
|
|
1026
|
-
projectName,
|
|
1027
|
-
services: {},
|
|
1028
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1023
|
+
projectName: existingConfig?.projectName ?? projectName,
|
|
1024
|
+
services: existingConfig?.services ?? {},
|
|
1025
|
+
createdAt: existingConfig?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1029
1026
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1030
1027
|
};
|
|
1031
1028
|
if (!nonInteractive && detected.length > 0 && process.stdin.isTTY) {
|
|
1032
1029
|
const result = await runInteractiveInit(detected);
|
|
1033
1030
|
config.services = result.services;
|
|
1034
1031
|
} else {
|
|
1032
|
+
const globalConfig = readGlobalConfig();
|
|
1035
1033
|
for (const det of detected) {
|
|
1034
|
+
const existing = config.services[det.service.id];
|
|
1035
|
+
const service = det.service;
|
|
1036
|
+
const plans = service.plans ?? [];
|
|
1037
|
+
const defaultPlan = plans.find((p) => p.default) ?? plans[0];
|
|
1038
|
+
if (existing?.budget !== void 0 && existing.budget > 0) continue;
|
|
1036
1039
|
const tracked2 = {
|
|
1037
|
-
serviceId:
|
|
1038
|
-
detectedVia: det.sources,
|
|
1039
|
-
hasApiKey: false,
|
|
1040
|
-
firstDetected: (/* @__PURE__ */ new Date()).toISOString()
|
|
1040
|
+
serviceId: service.id,
|
|
1041
|
+
detectedVia: existing?.detectedVia ?? det.sources,
|
|
1042
|
+
hasApiKey: existing?.hasApiKey ?? false,
|
|
1043
|
+
firstDetected: existing?.firstDetected ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1041
1044
|
};
|
|
1042
|
-
|
|
1045
|
+
if (defaultPlan && defaultPlan.type !== "exclude") {
|
|
1046
|
+
tracked2.planName = defaultPlan.name;
|
|
1047
|
+
if (defaultPlan.type === "flat" && defaultPlan.monthlyBase !== void 0) {
|
|
1048
|
+
tracked2.planCost = defaultPlan.monthlyBase;
|
|
1049
|
+
if (defaultPlan.monthlyBase > 0) {
|
|
1050
|
+
tracked2.budget = defaultPlan.monthlyBase;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
const existingKey = globalConfig.services[service.id]?.apiKey;
|
|
1055
|
+
if (existingKey) {
|
|
1056
|
+
tracked2.hasApiKey = true;
|
|
1057
|
+
}
|
|
1058
|
+
config.services[service.id] = tracked2;
|
|
1043
1059
|
}
|
|
1044
1060
|
if (detected.length === 0) {
|
|
1045
1061
|
console.log(" No paid services detected yet.");
|
|
@@ -1047,12 +1063,25 @@ async function cmdInit() {
|
|
|
1047
1063
|
} else {
|
|
1048
1064
|
console.log(` Found ${detected.length} paid service${detected.length > 1 ? "s" : ""}:
|
|
1049
1065
|
`);
|
|
1066
|
+
const needBudget = [];
|
|
1050
1067
|
for (const det of detected) {
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1068
|
+
const svc = config.services[det.service.id];
|
|
1069
|
+
const tierBadge = det.service.apiTier === "live" ? svc?.hasApiKey ? "\u2705 LIVE" : "\u{1F534} BLIND" : det.service.apiTier === "calc" ? "\u{1F7E1} CALC" : det.service.apiTier === "est" ? "\u{1F7E0} EST" : "\u{1F534} BLIND";
|
|
1070
|
+
const planStr = svc?.planName ? ` (${svc.planName})` : "";
|
|
1071
|
+
const budgetStr = svc?.budget ? ` - $${svc.budget}/mo budget` : "";
|
|
1072
|
+
console.log(` \u2022 ${det.service.name}${planStr} ${tierBadge}${budgetStr}`);
|
|
1073
|
+
if (!svc?.budget) {
|
|
1074
|
+
needBudget.push(det.service.id);
|
|
1075
|
+
}
|
|
1054
1076
|
}
|
|
1055
1077
|
console.log("");
|
|
1078
|
+
if (needBudget.length > 0) {
|
|
1079
|
+
console.log(` ${needBudget.length} service${needBudget.length > 1 ? "s" : ""} need budgets. Set them with:`);
|
|
1080
|
+
for (const id of needBudget) {
|
|
1081
|
+
console.log(` burnwatch add ${id} --budget <AMOUNT>`);
|
|
1082
|
+
}
|
|
1083
|
+
console.log("");
|
|
1084
|
+
}
|
|
1056
1085
|
}
|
|
1057
1086
|
}
|
|
1058
1087
|
writeProjectConfig(config, projectRoot);
|
|
@@ -1196,11 +1225,11 @@ async function cmdStatus() {
|
|
|
1196
1225
|
if (blindCount > 0) {
|
|
1197
1226
|
console.log(`\u26A0\uFE0F ${blindCount} service${blindCount > 1 ? "s" : ""} untracked:`);
|
|
1198
1227
|
for (const snap of snapshots.filter((s) => s.tier === "blind")) {
|
|
1199
|
-
console.log(
|
|
1200
|
-
` \u2022 ${snap.serviceId} \u2014 run 'burnwatch add ${snap.serviceId} --key YOUR_KEY --budget N'`
|
|
1201
|
-
);
|
|
1228
|
+
console.log(` \u2022 ${snap.serviceId}`);
|
|
1202
1229
|
}
|
|
1203
|
-
console.log(
|
|
1230
|
+
console.log(`
|
|
1231
|
+
Run 'burnwatch init' to configure budgets and API keys.
|
|
1232
|
+
`);
|
|
1204
1233
|
}
|
|
1205
1234
|
}
|
|
1206
1235
|
function cmdServices() {
|