@statforge/claudestat 1.6.1 → 1.8.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.
Files changed (63) hide show
  1. package/README.md +36 -3
  2. package/dashboard/dist/assets/AnalyticsView-DDGLDoCN.js +7 -0
  3. package/dashboard/dist/assets/HistoryView-DkPfrNrv.js +1 -0
  4. package/dashboard/dist/assets/LineChart-BOWYkkEW.js +2 -0
  5. package/dashboard/dist/assets/ProjectsView-VRoRiEL4.js +6 -0
  6. package/dashboard/dist/assets/SystemView-B2zbIxhY.js +1 -0
  7. package/dashboard/dist/assets/TopView-C2qdsy0Y.js +1 -0
  8. package/dashboard/dist/assets/index-CMhe3KaT.js +84 -0
  9. package/dashboard/dist/assets/shared-BbBtsdh1.js +1 -0
  10. package/dashboard/dist/assets/{vendor-lucide-Cym0q5l_.js → vendor-lucide-ClCW-axQ.js} +79 -64
  11. package/dashboard/dist/assets/{vendor-react-B_Jzs0gY.js → vendor-react-gHSHIE2L.js} +1 -1
  12. package/dashboard/dist/index.html +3 -3
  13. package/dist/config.d.ts +7 -0
  14. package/dist/config.js +36 -0
  15. package/dist/daemon.js +113 -9
  16. package/dist/db.d.ts +87 -2
  17. package/dist/db.js +325 -65
  18. package/dist/doctor.js +21 -3
  19. package/dist/enricher.d.ts +3 -2
  20. package/dist/enricher.js +10 -5
  21. package/dist/export.d.ts +2 -1
  22. package/dist/export.js +41 -6
  23. package/dist/index.js +406 -20
  24. package/dist/insights.d.ts +1 -0
  25. package/dist/insights.js +26 -0
  26. package/dist/install.js +28 -1
  27. package/dist/intelligence.d.ts +66 -4
  28. package/dist/intelligence.js +205 -17
  29. package/dist/logger.d.ts +6 -0
  30. package/dist/logger.js +49 -0
  31. package/dist/notifier.d.ts +15 -0
  32. package/dist/notifier.js +26 -0
  33. package/dist/paths.d.ts +23 -0
  34. package/dist/paths.js +42 -0
  35. package/dist/pricing.d.ts +2 -0
  36. package/dist/pricing.js +12 -1
  37. package/dist/routes/events.js +136 -5
  38. package/dist/routes/helpers.d.ts +5 -0
  39. package/dist/routes/helpers.js +21 -1
  40. package/dist/routes/history.js +6 -2
  41. package/dist/routes/intents.d.ts +1 -0
  42. package/dist/routes/intents.js +155 -0
  43. package/dist/routes/misc.js +150 -4
  44. package/dist/routes/opencode-reader.js +39 -3
  45. package/dist/routes/projects.js +19 -1
  46. package/dist/routes/replay.d.ts +1 -0
  47. package/dist/routes/replay.js +29 -0
  48. package/dist/routes/reports.js +7 -0
  49. package/dist/routes/top.js +8 -1
  50. package/dist/service.js +11 -0
  51. package/dist/watchers/adapter.d.ts +1 -0
  52. package/dist/watchers/claude-code.d.ts +16 -1
  53. package/dist/watchers/claude-code.js +201 -76
  54. package/dist/watchers/opencode.d.ts +1 -0
  55. package/dist/watchers/opencode.js +152 -14
  56. package/hooks/event.js +44 -26
  57. package/package.json +1 -1
  58. package/dashboard/dist/assets/AnalyticsView-5bUM3UHp.js +0 -8
  59. package/dashboard/dist/assets/HistoryView-C-AsEqos.js +0 -1
  60. package/dashboard/dist/assets/ProjectsView-D9bZBdY2.js +0 -6
  61. package/dashboard/dist/assets/SystemView-DIYDCCF3.js +0 -1
  62. package/dashboard/dist/assets/TopView-DhdLpsiA.js +0 -1
  63. package/dashboard/dist/assets/index-DgbWvj42.js +0 -84
package/dist/enricher.js CHANGED
@@ -15,7 +15,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
15
15
  return (mod && mod.__esModule) ? mod : { "default": mod };
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.getSessionPrompts = exports.getContextWindow = exports.getAllBlockCostsForSession = void 0;
18
+ exports.getContextWindow = exports.getSessionPrompts = exports.getAllBlockCostsForSession = void 0;
19
19
  exports.startEnricher = startEnricher;
20
20
  exports.stopEnricher = stopEnricher;
21
21
  exports.cleanupSession = cleanupSession;
@@ -30,11 +30,13 @@ require("./watchers/opencode");
30
30
  require("./watchers/amp");
31
31
  require("./watchers/droid");
32
32
  require("./watchers/codebuff");
33
+ const db_1 = require("./db");
33
34
  // Re-export Claude Code-specific utilities for routes/stream and routes/misc
34
35
  var claude_code_1 = require("./watchers/claude-code");
35
36
  Object.defineProperty(exports, "getAllBlockCostsForSession", { enumerable: true, get: function () { return claude_code_1.getAllBlockCostsForSession; } });
36
- Object.defineProperty(exports, "getContextWindow", { enumerable: true, get: function () { return claude_code_1.getContextWindow; } });
37
37
  Object.defineProperty(exports, "getSessionPrompts", { enumerable: true, get: function () { return claude_code_1.getSessionPrompts; } });
38
+ var pricing_1 = require("./pricing");
39
+ Object.defineProperty(exports, "getContextWindow", { enumerable: true, get: function () { return pricing_1.getContextWindow; } });
38
40
  const prevContextBySession = new Map();
39
41
  let watcher = null;
40
42
  const pendingFiles = new Map();
@@ -90,7 +92,7 @@ function startEnricher(onUpdate, onCompact) {
90
92
  console.log(`[enricher] Watching ${adapters.map(a => a.label).join(', ')}`);
91
93
  watcher = chokidar_1.default.watch(watchPaths, {
92
94
  persistent: true,
93
- ignoreInitial: true,
95
+ ignoreInitial: false,
94
96
  awaitWriteFinish: {
95
97
  stabilityThreshold: 200,
96
98
  pollInterval: 100,
@@ -119,16 +121,19 @@ function startEnricher(onUpdate, onCompact) {
119
121
  watcher.on('add', handleFile);
120
122
  // ─── Poll-based adapters (e.g. OpenCode SQLite) ─────────────────────────────
121
123
  const POLL_INTERVAL_MS = 10000;
124
+ const POLL_LOOKBACK_MS = 7 * 24 * 60 * 60000; // backfill 7 days on first start
122
125
  for (const adapter of adapters) {
123
126
  if (!(0, adapter_1.isPollable)(adapter))
124
127
  continue;
125
- let lastPoll = Date.now();
128
+ let lastPoll = Date.now() - POLL_LOOKBACK_MS;
126
129
  const interval = setInterval(async () => {
127
130
  const since = lastPoll;
128
131
  lastPoll = Date.now();
129
132
  const sessions = await adapter.pollSessions(since);
130
- for (const { sessionId, cost } of sessions) {
133
+ for (const { sessionId, cost, cwd } of sessions) {
131
134
  onUpdate(sessionId, cost, adapter.name);
135
+ if (cwd)
136
+ db_1.dbOps.updateSessionProject(sessionId, cwd);
132
137
  }
133
138
  }, POLL_INTERVAL_MS);
134
139
  interval.unref();
package/dist/export.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export interface ExportOpts {
2
- format: 'json' | 'csv';
2
+ format: 'json' | 'csv' | 'markdown';
3
3
  from?: string;
4
4
  to?: string;
5
+ since?: string;
5
6
  project?: string;
6
7
  output?: string;
7
8
  }
package/dist/export.js CHANGED
@@ -5,9 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runExport = runExport;
7
7
  const fs_1 = __importDefault(require("fs"));
8
- const os_1 = __importDefault(require("os"));
9
8
  const path_1 = __importDefault(require("path"));
10
9
  const db_1 = require("./db");
10
+ const MD_HEADERS = ['Date', 'Project', 'Cost (USD)', 'Input tokens', 'Output tokens', 'Efficiency', 'Loops'];
11
11
  function parseDate(str, endOfDay = false) {
12
12
  const ms = Date.parse(str);
13
13
  if (isNaN(ms))
@@ -23,6 +23,23 @@ const CSV_HEADERS = [
23
23
  'total_cost_usd', 'total_input_tokens', 'total_output_tokens',
24
24
  'efficiency_score', 'loops_detected',
25
25
  ];
26
+ function parseSince(since) {
27
+ const match = since.match(/^(\d+)d$/);
28
+ if (!match)
29
+ throw new Error(`Invalid --since format: "${since}" — use e.g. "7d" or "30d"`);
30
+ return Date.now() - parseInt(match[1], 10) * 86400000;
31
+ }
32
+ function toMarkdownRow(r) {
33
+ return [
34
+ String(r.started_at).slice(0, 10),
35
+ r.project_path ? String(r.project_path).split('/').pop() ?? '—' : '—',
36
+ `$${Number(r.total_cost_usd).toFixed(4)}`,
37
+ String(r.total_input_tokens),
38
+ String(r.total_output_tokens),
39
+ `${r.efficiency_score}/100`,
40
+ String(r.loops_detected),
41
+ ].map(v => v.replace(/\|/g, '\\|')).join(' | ');
42
+ }
26
43
  function toRow(s) {
27
44
  return {
28
45
  id: s.id,
@@ -40,6 +57,10 @@ function runExport(opts) {
40
57
  let fromMs;
41
58
  let toMs;
42
59
  try {
60
+ if (opts.since && !opts.from) {
61
+ const sinceMs = parseSince(opts.since);
62
+ opts.from = new Date(sinceMs).toISOString().slice(0, 10);
63
+ }
43
64
  if (opts.from)
44
65
  fromMs = parseDate(opts.from);
45
66
  if (opts.to)
@@ -71,12 +92,26 @@ function runExport(opts) {
71
92
  ];
72
93
  output = lines.join('\n') + '\n';
73
94
  }
95
+ else if (opts.format === 'markdown') {
96
+ const separator = MD_HEADERS.map(() => '---').join(' | ');
97
+ const lines = [
98
+ MD_HEADERS.join(' | '),
99
+ separator,
100
+ ...rows.map(toMarkdownRow),
101
+ ];
102
+ output = lines.join('\n') + '\n';
103
+ }
74
104
  else {
75
105
  output = JSON.stringify(rows, null, 2) + '\n';
76
106
  }
77
- const date = new Date().toISOString().slice(0, 10);
78
- const dest = path_1.default.resolve(opts.output ?? path_1.default.join(os_1.default.homedir(), 'Downloads', `claudestat-export-${date}.${opts.format}`));
79
- fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
80
- fs_1.default.writeFileSync(dest, output);
81
- console.log(`✓ Exported ${rows.length} session(s) → ${dest}`);
107
+ if (!opts.output) {
108
+ process.stdout.write(output);
109
+ console.error(`✓ Exported ${rows.length} session(s)`); // stderr para no contaminar stdout
110
+ }
111
+ else {
112
+ const dest = path_1.default.resolve(opts.output);
113
+ fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
114
+ fs_1.default.writeFileSync(dest, output);
115
+ console.log(`✓ Exported ${rows.length} session(s) → ${dest}`);
116
+ }
82
117
  }