burnwatch 0.10.1 → 0.11.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/cli.js +95 -27
- package/dist/cli.js.map +1 -1
- package/dist/hooks/on-prompt.js.map +1 -1
- package/dist/hooks/on-session-start.js +58 -42
- package/dist/hooks/on-session-start.js.map +1 -1
- package/dist/hooks/on-stop.js +29 -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 +30 -2
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/burnwatch-interview/SKILL.md +12 -6
package/dist/cli.js
CHANGED
|
@@ -925,9 +925,37 @@ async function pollService(tracked) {
|
|
|
925
925
|
serviceConfig
|
|
926
926
|
);
|
|
927
927
|
if (!result.error) return result;
|
|
928
|
-
|
|
928
|
+
return {
|
|
929
|
+
serviceId: tracked.serviceId,
|
|
930
|
+
spend: tracked.planCost ?? 0,
|
|
931
|
+
isEstimate: true,
|
|
932
|
+
tier: tracked.planCost !== void 0 ? "calc" : "blind",
|
|
933
|
+
error: `LIVE failed (${result.error}) \u2014 showing ${tracked.planCost !== void 0 ? "CALC" : "BLIND"} fallback`
|
|
934
|
+
};
|
|
935
|
+
} catch (err) {
|
|
936
|
+
return {
|
|
937
|
+
serviceId: tracked.serviceId,
|
|
938
|
+
spend: tracked.planCost ?? 0,
|
|
939
|
+
isEstimate: true,
|
|
940
|
+
tier: tracked.planCost !== void 0 ? "calc" : "blind",
|
|
941
|
+
error: `LIVE failed (${err instanceof Error ? err.message : "unknown"}) \u2014 showing ${tracked.planCost !== void 0 ? "CALC" : "BLIND"} fallback`
|
|
942
|
+
};
|
|
929
943
|
}
|
|
930
944
|
}
|
|
945
|
+
if (connector && tracked.hasApiKey && !serviceConfig?.apiKey) {
|
|
946
|
+
const projectedSpend = tracked.planCost !== void 0 ? (() => {
|
|
947
|
+
const now = /* @__PURE__ */ new Date();
|
|
948
|
+
const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
|
|
949
|
+
return tracked.planCost / daysInMonth * now.getDate();
|
|
950
|
+
})() : 0;
|
|
951
|
+
return {
|
|
952
|
+
serviceId: tracked.serviceId,
|
|
953
|
+
spend: projectedSpend,
|
|
954
|
+
isEstimate: true,
|
|
955
|
+
tier: tracked.planCost !== void 0 ? "calc" : "blind",
|
|
956
|
+
error: "API key marked as configured but not found in ~/.config/burnwatch/ \u2014 re-run configure with --key"
|
|
957
|
+
};
|
|
958
|
+
}
|
|
931
959
|
if (tracked.planCost !== void 0) {
|
|
932
960
|
const now = /* @__PURE__ */ new Date();
|
|
933
961
|
const daysInMonth = new Date(
|
|
@@ -999,7 +1027,7 @@ function formatBrief(brief) {
|
|
|
999
1027
|
formatRow("Service", "Spend", "Conf", "Budget", "Left", width)
|
|
1000
1028
|
);
|
|
1001
1029
|
lines.push(`\u2551 ${hrSingle} \u2551`);
|
|
1002
|
-
for (const svc of brief.services) {
|
|
1030
|
+
for (const svc of brief.services.filter((s) => s.tier !== "excluded")) {
|
|
1003
1031
|
const spendStr = svc.tier === "blind" && svc.spend === 0 ? "\u2014" : svc.isEstimate ? `~$${svc.spend.toFixed(2)}` : `$${svc.spend.toFixed(2)}`;
|
|
1004
1032
|
const badge = CONFIDENCE_BADGES[svc.tier];
|
|
1005
1033
|
const budgetStr = svc.budget ? `$${svc.budget}` : "\u2014";
|
|
@@ -1568,6 +1596,9 @@ async function main() {
|
|
|
1568
1596
|
case "configure":
|
|
1569
1597
|
await cmdConfigure();
|
|
1570
1598
|
break;
|
|
1599
|
+
case "reset":
|
|
1600
|
+
cmdReset();
|
|
1601
|
+
break;
|
|
1571
1602
|
case "help":
|
|
1572
1603
|
case "--help":
|
|
1573
1604
|
case "-h":
|
|
@@ -1693,37 +1724,32 @@ async function cmdInterview() {
|
|
|
1693
1724
|
let keySource = null;
|
|
1694
1725
|
const envKeysFound = [];
|
|
1695
1726
|
const globalKey = globalConfig.services[serviceId]?.apiKey;
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
if (process.env[pattern]) {
|
|
1701
|
-
keySource = `env:${pattern}`;
|
|
1702
|
-
envKeysFound.push(pattern);
|
|
1703
|
-
break;
|
|
1704
|
-
}
|
|
1727
|
+
for (const pattern of definition.envPatterns) {
|
|
1728
|
+
if (process.env[pattern]) {
|
|
1729
|
+
envKeysFound.push(pattern);
|
|
1730
|
+
if (!keySource && !globalKey) keySource = `env:${pattern}`;
|
|
1705
1731
|
}
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
}
|
|
1732
|
+
}
|
|
1733
|
+
const envFiles = [".env", ".env.local", ".env.development"];
|
|
1734
|
+
for (const envFile of envFiles) {
|
|
1735
|
+
try {
|
|
1736
|
+
const envPath = path5.join(projectRoot, envFile);
|
|
1737
|
+
const envContent = fs5.readFileSync(envPath, "utf-8");
|
|
1738
|
+
for (const pattern of definition.envPatterns) {
|
|
1739
|
+
const regex = new RegExp(`^${pattern}=(.+)$`, "m");
|
|
1740
|
+
const match = envContent.match(regex);
|
|
1741
|
+
if (match?.[1]) {
|
|
1742
|
+
const label = `${pattern} (in ${envFile})`;
|
|
1743
|
+
if (!envKeysFound.some((k) => k.startsWith(pattern))) {
|
|
1744
|
+
envKeysFound.push(label);
|
|
1720
1745
|
}
|
|
1721
|
-
if (keySource)
|
|
1722
|
-
} catch {
|
|
1746
|
+
if (!keySource && !globalKey) keySource = `file:${envFile}:${pattern}`;
|
|
1723
1747
|
}
|
|
1724
1748
|
}
|
|
1749
|
+
} catch {
|
|
1725
1750
|
}
|
|
1726
1751
|
}
|
|
1752
|
+
if (globalKey) keySource = "global_config";
|
|
1727
1753
|
let apiKey = globalKey;
|
|
1728
1754
|
if (!apiKey && keySource?.startsWith("env:")) {
|
|
1729
1755
|
apiKey = process.env[keySource.slice(4)];
|
|
@@ -2132,6 +2158,47 @@ async function cmdReconcile() {
|
|
|
2132
2158
|
}
|
|
2133
2159
|
console.log("");
|
|
2134
2160
|
}
|
|
2161
|
+
function cmdReset() {
|
|
2162
|
+
const projectRoot = process.cwd();
|
|
2163
|
+
const burnwatchDir = path5.join(projectRoot, ".burnwatch");
|
|
2164
|
+
const claudeSkillsDir = path5.join(projectRoot, ".claude", "skills");
|
|
2165
|
+
if (!fs5.existsSync(burnwatchDir)) {
|
|
2166
|
+
console.log("burnwatch is not initialized in this project.");
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
fs5.rmSync(burnwatchDir, { recursive: true, force: true });
|
|
2170
|
+
console.log(`\u{1F5D1}\uFE0F Removed ${burnwatchDir}`);
|
|
2171
|
+
const skillNames = ["setup-burnwatch", "burnwatch-interview", "spend"];
|
|
2172
|
+
for (const skill of skillNames) {
|
|
2173
|
+
const skillDir = path5.join(claudeSkillsDir, skill);
|
|
2174
|
+
if (fs5.existsSync(skillDir)) {
|
|
2175
|
+
fs5.rmSync(skillDir, { recursive: true, force: true });
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
console.log("\u{1F5D1}\uFE0F Removed burnwatch skills from .claude/skills/");
|
|
2179
|
+
const settingsPath = path5.join(projectRoot, ".claude", "settings.json");
|
|
2180
|
+
if (fs5.existsSync(settingsPath)) {
|
|
2181
|
+
try {
|
|
2182
|
+
const settings = JSON.parse(fs5.readFileSync(settingsPath, "utf-8"));
|
|
2183
|
+
const hooks = settings["hooks"];
|
|
2184
|
+
if (hooks) {
|
|
2185
|
+
for (const [event, hookList] of Object.entries(hooks)) {
|
|
2186
|
+
hooks[event] = hookList.filter(
|
|
2187
|
+
(h) => !h.hooks?.some((inner) => inner.command?.includes("burnwatch"))
|
|
2188
|
+
);
|
|
2189
|
+
if (hooks[event].length === 0) delete hooks[event];
|
|
2190
|
+
}
|
|
2191
|
+
if (Object.keys(hooks).length === 0) delete settings["hooks"];
|
|
2192
|
+
fs5.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
2193
|
+
console.log("\u{1F5D1}\uFE0F Removed burnwatch hooks from .claude/settings.json");
|
|
2194
|
+
}
|
|
2195
|
+
} catch {
|
|
2196
|
+
console.log("\u26A0\uFE0F Could not clean .claude/settings.json \u2014 remove burnwatch hooks manually");
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
console.log("\n\u2705 burnwatch fully reset. Global API keys in ~/.config/burnwatch/ were preserved.");
|
|
2200
|
+
console.log(" Run 'burnwatch init' to set up again.\n");
|
|
2201
|
+
}
|
|
2135
2202
|
function cmdHelp() {
|
|
2136
2203
|
console.log(`
|
|
2137
2204
|
burnwatch \u2014 Passive cost memory for AI-assisted development
|
|
@@ -2146,6 +2213,7 @@ Usage:
|
|
|
2146
2213
|
burnwatch reconcile Scan for untracked services
|
|
2147
2214
|
burnwatch interview --json Export state for agent-driven interview
|
|
2148
2215
|
burnwatch configure --service <id> [opts] Agent writes back interview answers
|
|
2216
|
+
burnwatch reset Remove all burnwatch config from this project
|
|
2149
2217
|
|
|
2150
2218
|
Options for 'configure':
|
|
2151
2219
|
--service <ID> Service to configure (required)
|