exe-watcher 0.2.11 → 0.2.13

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 CHANGED
@@ -1660,7 +1660,8 @@ function emptyEntry(date) {
1660
1660
  oneShotTurns: 0,
1661
1661
  models: {},
1662
1662
  categories: {},
1663
- providers: {}
1663
+ providers: {},
1664
+ projects: {}
1664
1665
  };
1665
1666
  }
1666
1667
  function emptyProviderBreakdown() {
@@ -1700,10 +1701,20 @@ function aggregateProjectsIntoDays(projects) {
1700
1701
  }
1701
1702
  return breakdown;
1702
1703
  };
1704
+ const ensureProject = (date, projectName) => {
1705
+ const day = ensure(date);
1706
+ let project = day.projects[projectName];
1707
+ if (!project) {
1708
+ project = { cost: 0, sessions: 0 };
1709
+ day.projects[projectName] = project;
1710
+ }
1711
+ return project;
1712
+ };
1703
1713
  for (const project of projects) {
1704
1714
  for (const session of project.sessions) {
1705
1715
  const sessionDate = dateKey(session.firstTimestamp);
1706
1716
  ensure(sessionDate).sessions += 1;
1717
+ ensureProject(sessionDate, project.project).sessions += 1;
1707
1718
  const providersSeenInSession = /* @__PURE__ */ new Set();
1708
1719
  for (const turn of session.turns) {
1709
1720
  if (turn.assistantCalls.length === 0) continue;
@@ -1748,6 +1759,7 @@ function aggregateProjectsIntoDays(projects) {
1748
1759
  model.cacheReadTokens += call.usage.cacheReadInputTokens;
1749
1760
  model.cacheWriteTokens += call.usage.cacheCreationInputTokens;
1750
1761
  callDay.models[call.model] = model;
1762
+ ensureProject(callDate, project.project).cost += call.costUSD;
1751
1763
  }
1752
1764
  for (const [provider, calls] of callsByProvider) {
1753
1765
  providersSeenInSession.add(provider);
@@ -3670,31 +3682,35 @@ function estimateAgentCosts(stats) {
3670
3682
  }))
3671
3683
  };
3672
3684
  }
3673
- function buildProjectSpend(projects24h, projects7d, projects30d) {
3674
- const aggregate = (projects) => {
3685
+ function buildProjectSpendFromDays(selectedDays, days24h, days7d, days30d) {
3686
+ const aggregate = (days) => {
3675
3687
  const byName = {};
3676
- for (const p of projects) {
3677
- const name = cleanProjectName(p.project);
3678
- if (!byName[name]) byName[name] = { cost: 0, sessions: 0 };
3679
- byName[name].cost += p.totalCostUSD;
3680
- byName[name].sessions += p.sessions.length;
3688
+ for (const day of days) {
3689
+ for (const [projectName, totals] of Object.entries(day.projects)) {
3690
+ const name = cleanProjectName(projectName);
3691
+ if (!byName[name]) byName[name] = { cost: 0, sessions: 0 };
3692
+ byName[name].cost += totals.cost;
3693
+ byName[name].sessions += totals.sessions;
3694
+ }
3681
3695
  }
3682
3696
  return byName;
3683
3697
  };
3684
- const d24 = aggregate(projects24h);
3685
- const d7 = aggregate(projects7d);
3686
- const d30 = aggregate(projects30d);
3687
- const allNames = /* @__PURE__ */ new Set([...Object.keys(d24), ...Object.keys(d7), ...Object.keys(d30)]);
3698
+ const selected = aggregate(selectedDays);
3699
+ const d24 = aggregate(days24h);
3700
+ const d7 = aggregate(days7d);
3701
+ const d30 = aggregate(days30d);
3702
+ const allNames = /* @__PURE__ */ new Set([...Object.keys(selected), ...Object.keys(d24), ...Object.keys(d7), ...Object.keys(d30)]);
3688
3703
  return Array.from(allNames).map((name) => ({
3689
3704
  name,
3690
3705
  cost24h: d24[name]?.cost ?? 0,
3691
3706
  cost7d: d7[name]?.cost ?? 0,
3692
3707
  cost30d: d30[name]?.cost ?? 0,
3693
- sessions: d30[name]?.sessions ?? d7[name]?.sessions ?? d24[name]?.sessions ?? 0
3694
- })).filter((d) => d.cost30d > 0 || d.cost7d > 0 || d.cost24h > 0).sort((a, b) => b.cost30d - a.cost30d);
3708
+ selectedPeriodCost: selected[name]?.cost ?? 0,
3709
+ sessions: selected[name]?.sessions ?? d30[name]?.sessions ?? d7[name]?.sessions ?? d24[name]?.sessions ?? 0
3710
+ })).filter((d) => d.selectedPeriodCost > 0).sort((a, b) => b.selectedPeriodCost - a.selectedPeriodCost);
3695
3711
  }
3696
3712
  function cleanProjectName(dirName) {
3697
- const base = dirName.replace(/--worktrees-.*$/, "");
3713
+ const base = dirName.replace(/--worktrees-.*$/, "").replace(/-\.worktrees-.*/, "").replace(/\/\.worktrees\/.*/, "");
3698
3714
  const parts = base.split("-");
3699
3715
  const usersIdx = parts.indexOf("Users");
3700
3716
  if (usersIdx >= 0 && usersIdx + 2 < parts.length) {
@@ -3795,7 +3811,7 @@ import { createHash as createHash3, randomBytes as randomBytes5 } from "crypto";
3795
3811
  import { existsSync as existsSync3 } from "fs";
3796
3812
  import { mkdir as mkdir7, open as open5, readFile as readFile7, rename as rename6, unlink as unlink3 } from "fs/promises";
3797
3813
  import { join as join16 } from "path";
3798
- var DAILY_CACHE_VERSION = 4;
3814
+ var DAILY_CACHE_VERSION = 5;
3799
3815
  var DEFAULT_DAILY_CACHE_SCOPE = "global";
3800
3816
  var DAILY_CACHE_FILENAME = "daily-cache.json";
3801
3817
  function getCachePath3(scopeKey) {
@@ -6782,9 +6798,7 @@ function computeProgressiveBackfillStart({
6782
6798
  return gapStart;
6783
6799
  }
6784
6800
  if (oldestCachedDate && neededStart.getTime() < new Date(oldestCachedDate).getTime()) {
6785
- const backTarget = new Date(oldestCachedDate);
6786
- const chunkStart = new Date(backTarget.getTime() - progressiveChunkDays * MS_PER_DAY3);
6787
- return chunkStart < neededStart ? neededStart : chunkStart;
6801
+ return neededStart < fullBackfillStart ? fullBackfillStart : neededStart;
6788
6802
  }
6789
6803
  return gapStart;
6790
6804
  }
@@ -7136,17 +7150,18 @@ program.command("status").description("Compact status output (today + week + mon
7136
7150
  const parseTimeMs = Date.now() - parseStart;
7137
7151
  const todayDays = aggregateProjectsIntoDays(todayProjects);
7138
7152
  let daysCount = 0;
7153
+ let selectedPeriodDays = [];
7139
7154
  {
7140
7155
  const rangeStartStr = toDateString(periodInfo.range.start);
7141
7156
  const rangeEndStr = toDateString(periodInfo.range.end);
7142
7157
  const historicalDays = getDaysInRange(cache, rangeStartStr, yesterdayStr);
7143
7158
  const todayOnly = todayDays.filter((d) => d.date > yesterdayStr && d.date <= rangeEndStr);
7144
- const allDays = [...historicalDays, ...todayOnly].sort((a, b) => a.date.localeCompare(b.date));
7145
- daysCount = allDays.length;
7159
+ selectedPeriodDays = [...historicalDays, ...todayOnly].sort((a, b) => a.date.localeCompare(b.date));
7160
+ daysCount = selectedPeriodDays.length;
7146
7161
  if (isAllProviders) {
7147
- currentData = buildPeriodDataFromDays(allDays, periodInfo.label);
7162
+ currentData = buildPeriodDataFromDays(selectedPeriodDays, periodInfo.label);
7148
7163
  } else {
7149
- const providerDays = filterDaysToProvider(allDays, pf);
7164
+ const providerDays = filterDaysToProvider(selectedPeriodDays, pf);
7150
7165
  currentData = buildPeriodDataFromDays(providerDays, periodInfo.label);
7151
7166
  }
7152
7167
  }
@@ -7259,19 +7274,21 @@ program.command("status").description("Compact status output (today + week + mon
7259
7274
  }
7260
7275
  let projectSpend = null;
7261
7276
  try {
7262
- const [proj7d, proj30d] = await Promise.all([
7263
- Promise.race([
7264
- parseAllSessions(getDateRange2("week").range, "all"),
7265
- new Promise((r) => setTimeout(() => r([]), 500))
7266
- ]),
7267
- Promise.race([
7268
- parseAllSessions(getDateRange2("30days").range, "all"),
7269
- new Promise((r) => setTimeout(() => r([]), 500))
7270
- ])
7271
- ]);
7272
- projectSpend = buildProjectSpend(allTodayProjects, proj7d, proj30d);
7277
+ const weekStartStr = toDateString(getDateRange2("week").range.start);
7278
+ const thirtyDayStartStr = toDateString(getDateRange2("30days").range.start);
7279
+ const todayOnlyDays = todayDays.filter((d) => d.date > yesterdayStr);
7280
+ const days7d = [
7281
+ ...getDaysInRange(cache, weekStartStr, yesterdayStr),
7282
+ ...todayOnlyDays
7283
+ ].sort((a, b) => a.date.localeCompare(b.date));
7284
+ const days30d = [
7285
+ ...getDaysInRange(cache, thirtyDayStartStr, yesterdayStr),
7286
+ ...todayOnlyDays
7287
+ ].sort((a, b) => a.date.localeCompare(b.date));
7288
+ projectSpend = buildProjectSpendFromDays(selectedPeriodDays, todayOnlyDays, days7d, days30d);
7273
7289
  } catch {
7274
- projectSpend = buildProjectSpend(allTodayProjects, [], []);
7290
+ const todayOnlyDays = todayDays.filter((d) => d.date > yesterdayStr);
7291
+ projectSpend = buildProjectSpendFromDays(selectedPeriodDays, todayOnlyDays, [], []);
7275
7292
  }
7276
7293
  const diagnostics = { daysCount, parseTimeMs, warnings };
7277
7294
  const payload = buildMenubarPayload(currentData, providers, optimize, dailyHistory, agentStats, projectSpend, exeOsDetected, statsFileAge, diagnostics);
@@ -7571,7 +7588,7 @@ program.command("plan [action] [id]").description("Show or configure a subscript
7571
7588
  console.log(` Config saved to ${getConfigFilePath()}
7572
7589
  `);
7573
7590
  });
7574
- program.command("optimize").description("Find token waste and get exact fixes").option("-p, --period <period>", "Analysis period: today, week, 30days, month, all", "30days").option("--provider <provider>", "Filter by provider: all, claude, codex, cursor", "all").action(async (opts) => {
7591
+ program.command("optimize").description("Find token waste and get exact fixes").option("-p, --period <period>", "Analysis period: today, week, 30days, month, all", "week").option("--provider <provider>", "Filter by provider: all, claude, codex, cursor", "all").action(async (opts) => {
7575
7592
  await loadPricing();
7576
7593
  const { range, label } = getDateRange2(opts.period);
7577
7594
  const scanPromise = prewarmOptimizeScan(range);