@statforge/claudestat 1.0.1

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 (80) hide show
  1. package/README.md +437 -0
  2. package/dashboard/dist/assets/AnalyticsView-BApcOGsD.js +8 -0
  3. package/dashboard/dist/assets/HistoryView-B331k5oL.js +1 -0
  4. package/dashboard/dist/assets/ProjectsView-DUleaXsP.js +6 -0
  5. package/dashboard/dist/assets/SystemView-BGe__vl1.js +1 -0
  6. package/dashboard/dist/assets/TopView-CXggyydU.js +1 -0
  7. package/dashboard/dist/assets/index-CB01c5lb.js +84 -0
  8. package/dashboard/dist/assets/vendor-lucide-Cym0q5l_.js +344 -0
  9. package/dashboard/dist/assets/vendor-react-B_Jzs0gY.js +24 -0
  10. package/dashboard/dist/index.html +21 -0
  11. package/dist/cache/projects-cache.d.ts +9 -0
  12. package/dist/cache/projects-cache.js +51 -0
  13. package/dist/claude-auth.d.ts +38 -0
  14. package/dist/claude-auth.js +133 -0
  15. package/dist/claude-stats.d.ts +32 -0
  16. package/dist/claude-stats.js +98 -0
  17. package/dist/config.d.ts +43 -0
  18. package/dist/config.js +110 -0
  19. package/dist/daemon.d.ts +15 -0
  20. package/dist/daemon.js +247 -0
  21. package/dist/db.d.ts +134 -0
  22. package/dist/db.js +546 -0
  23. package/dist/doctor.d.ts +1 -0
  24. package/dist/doctor.js +191 -0
  25. package/dist/enricher.d.ts +34 -0
  26. package/dist/enricher.js +394 -0
  27. package/dist/export.d.ts +8 -0
  28. package/dist/export.js +82 -0
  29. package/dist/git.d.ts +22 -0
  30. package/dist/git.js +57 -0
  31. package/dist/github.d.ts +27 -0
  32. package/dist/github.js +62 -0
  33. package/dist/index.d.ts +8 -0
  34. package/dist/index.js +319 -0
  35. package/dist/install.d.ts +14 -0
  36. package/dist/install.js +202 -0
  37. package/dist/intelligence.d.ts +45 -0
  38. package/dist/intelligence.js +105 -0
  39. package/dist/meta-stats.d.ts +28 -0
  40. package/dist/meta-stats.js +137 -0
  41. package/dist/middleware/rate-limiter.d.ts +2 -0
  42. package/dist/middleware/rate-limiter.js +30 -0
  43. package/dist/notifier.d.ts +1 -0
  44. package/dist/notifier.js +22 -0
  45. package/dist/paths.d.ts +79 -0
  46. package/dist/paths.js +134 -0
  47. package/dist/pattern-analyzer.d.ts +35 -0
  48. package/dist/pattern-analyzer.js +123 -0
  49. package/dist/project-scanner.d.ts +71 -0
  50. package/dist/project-scanner.js +619 -0
  51. package/dist/quota-tracker.d.ts +45 -0
  52. package/dist/quota-tracker.js +320 -0
  53. package/dist/render.d.ts +55 -0
  54. package/dist/render.js +229 -0
  55. package/dist/routes/events.d.ts +18 -0
  56. package/dist/routes/events.js +272 -0
  57. package/dist/routes/history.d.ts +1 -0
  58. package/dist/routes/history.js +65 -0
  59. package/dist/routes/misc.d.ts +1 -0
  60. package/dist/routes/misc.js +280 -0
  61. package/dist/routes/projects.d.ts +15 -0
  62. package/dist/routes/projects.js +153 -0
  63. package/dist/routes/reports.d.ts +11 -0
  64. package/dist/routes/reports.js +205 -0
  65. package/dist/routes/stream.d.ts +8 -0
  66. package/dist/routes/stream.js +70 -0
  67. package/dist/routes/top.d.ts +1 -0
  68. package/dist/routes/top.js +30 -0
  69. package/dist/session-state.d.ts +35 -0
  70. package/dist/session-state.js +50 -0
  71. package/dist/summarizer.d.ts +18 -0
  72. package/dist/summarizer.js +137 -0
  73. package/dist/watch.d.ts +8 -0
  74. package/dist/watch.js +157 -0
  75. package/dist/watchdog.d.ts +11 -0
  76. package/dist/watchdog.js +75 -0
  77. package/dist/weekly.d.ts +13 -0
  78. package/dist/weekly.js +39 -0
  79. package/hooks/event.js +80 -0
  80. package/package.json +78 -0
package/dist/paths.js ADDED
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ /**
3
+ * paths.ts — Cross-platform path resolution for Claude Code data directories
4
+ *
5
+ * Claude Code stores data in the same location on all platforms:
6
+ * All platforms: ~/.claude/
7
+ *
8
+ * ClaudeStat stores its own data in:
9
+ * All platforms: ~/.claudestat/ (or CLAUDESTAT_DATA_DIR env var)
10
+ *
11
+ * Claude Code encodes project paths by replacing path separators AND colons with '-'.
12
+ * This module provides helpers to encode/decode those paths cross-platform.
13
+ */
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.isWindows = void 0;
19
+ exports.getClaudeDir = getClaudeDir;
20
+ exports.getClaudestatDir = getClaudestatDir;
21
+ exports.getPidFile = getPidFile;
22
+ exports.encodeClaudePath = encodeClaudePath;
23
+ exports.decodeClaudePath = decodeClaudePath;
24
+ exports.homeSlugRegex = homeSlugRegex;
25
+ exports.getHomeSlug = getHomeSlug;
26
+ exports.whichCmd = whichCmd;
27
+ exports.whichAllCmd = whichAllCmd;
28
+ exports.portCheckCmd = portCheckCmd;
29
+ const os_1 = __importDefault(require("os"));
30
+ const path_1 = __importDefault(require("path"));
31
+ const isWin = process.platform === 'win32';
32
+ // ─── Claude Code data directory ────────────────────────────────────────────────
33
+ /**
34
+ * Returns the Claude Code data directory (~/.claude on all platforms).
35
+ * Empirically verified: Claude Code CLI stores settings at ~/.claude on macOS, Linux, and Windows.
36
+ */
37
+ function getClaudeDir() {
38
+ return path_1.default.join(os_1.default.homedir(), '.claude');
39
+ }
40
+ // ─── ClaudeStat data directory ─────────────────────────────────────────────────
41
+ /**
42
+ * Returns the ClaudeStat data directory.
43
+ * Can be overridden via CLAUDESTAT_DATA_DIR env var.
44
+ */
45
+ function getClaudestatDir() {
46
+ return process.env.CLAUDESTAT_DATA_DIR ?? path_1.default.join(os_1.default.homedir(), '.claudestat');
47
+ }
48
+ /**
49
+ * Returns the PID file path for the daemon.
50
+ */
51
+ function getPidFile() {
52
+ return path_1.default.join(getClaudestatDir(), 'daemon.pid');
53
+ }
54
+ // ─── Path encoding (Claude Code format) ────────────────────────────────────────
55
+ /**
56
+ * Encodes a real filesystem path into Claude Code's internal format.
57
+ * Claude Code replaces path separators AND colons with '-'.
58
+ *
59
+ * macOS: /Users/db/Documents/GitHub → -Users-db-Documents-GitHub
60
+ * Windows: C:\Users\db\Documents → C--Users-db-Documents
61
+ */
62
+ function encodeClaudePath(realPath) {
63
+ return realPath.replace(/[/\\:]/g, '-');
64
+ }
65
+ /**
66
+ * Decodes a Claude Code encoded directory name back to a real filesystem path.
67
+ *
68
+ * Since directory names with '-' are ambiguous (is "gmail-ai-agent" one dir or three?),
69
+ * this function requires a reference to the greedy filesystem resolver.
70
+ * Use project-scanner's findRealPath for the actual resolution.
71
+ *
72
+ * Returns the encodedHome + rest with '-' replaced by path.sep,
73
+ * but actual resolution should be done via findRealPath in project-scanner.
74
+ */
75
+ function decodeClaudePath(encoded) {
76
+ const homeDir = os_1.default.homedir();
77
+ const encodedHome = encodeClaudePath(homeDir);
78
+ if (!encoded.startsWith(encodedHome))
79
+ return null;
80
+ const rest = encoded.slice(encodedHome.length);
81
+ if (!rest || rest === '')
82
+ return null;
83
+ // Replace '-' with path separator — but note this is ambiguous for dirs with '-'
84
+ // For actual resolution, use findRealPath() from project-scanner
85
+ return homeDir + rest.replace(/-/g, path_1.default.sep);
86
+ }
87
+ /**
88
+ * Creates the regex pattern for the encoded home path.
89
+ * Handles both / and \ separators.
90
+ */
91
+ function homeSlugRegex() {
92
+ const escaped = encodeClaudePath(os_1.default.homedir()).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
93
+ return new RegExp('^' + escaped);
94
+ }
95
+ /**
96
+ * Returns the engram-compatible slug for the home directory.
97
+ * Used for MEMORY.md path resolution.
98
+ * macOS: /Users/db → -Users-db
99
+ * Windows: C:\Users\db → C--Users-db
100
+ */
101
+ function getHomeSlug() {
102
+ return encodeClaudePath(os_1.default.homedir());
103
+ }
104
+ // ─── Platform utilities ────────────────────────────────────────────────────────
105
+ /**
106
+ * Returns the appropriate command to find an executable in PATH.
107
+ * Unix: which <name>
108
+ * Windows: where <name>
109
+ */
110
+ function whichCmd(name) {
111
+ return isWin ? `where ${name}` : `which ${name}`;
112
+ }
113
+ /**
114
+ * Returns the appropriate command to find all instances of an executable in PATH.
115
+ * Unix: which -a <name>
116
+ * Windows: where <name> (where already lists all matches)
117
+ */
118
+ function whichAllCmd(name) {
119
+ return isWin ? `where ${name}` : `which -a ${name} 2>/dev/null`;
120
+ }
121
+ /**
122
+ * Returns the appropriate command to check if a port is in use.
123
+ * Unix: lsof -i :<port>
124
+ * Windows: netstat -ano | findstr :<port>
125
+ */
126
+ function portCheckCmd(port) {
127
+ return isWin
128
+ ? `netstat -ano | findstr :${port}`
129
+ : `lsof -i :${port}`;
130
+ }
131
+ /**
132
+ * Returns true if running on Windows.
133
+ */
134
+ exports.isWindows = isWin;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * pattern-analyzer.ts — Detects inefficiency patterns in Claude Code usage.
3
+ *
4
+ * Pure logic module: receives pre-fetched DB data, returns actionable insights.
5
+ * No DB or filesystem access here — easier to test and reason about.
6
+ *
7
+ * Patterns detected:
8
+ * - Read dominance → suggest offset+limit or batching
9
+ * - Bash overuse → suggest Read/Grep instead
10
+ * - High loop rate → review prompts / agent instructions
11
+ * - Low cache ratio → context changes too much between messages
12
+ * - High cache ratio → positive: great cost efficiency
13
+ * - High avg cost → consider Haiku for simpler tasks
14
+ * - Low efficiency → linked to loops
15
+ */
16
+ export type InsightLevel = 'tip' | 'warning' | 'positive';
17
+ export interface PatternInsight {
18
+ level: InsightLevel;
19
+ title: string;
20
+ description: string;
21
+ metric?: string;
22
+ }
23
+ export interface ToolCount {
24
+ tool_name: string;
25
+ count: number;
26
+ }
27
+ export interface SessionStats {
28
+ session_count: number;
29
+ avg_cache_read: number;
30
+ avg_total_input: number;
31
+ avg_loops: number;
32
+ avg_cost_usd: number;
33
+ avg_efficiency: number;
34
+ }
35
+ export declare function analyzePatterns(toolCounts: ToolCount[], stats: SessionStats): PatternInsight[];
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /**
3
+ * pattern-analyzer.ts — Detects inefficiency patterns in Claude Code usage.
4
+ *
5
+ * Pure logic module: receives pre-fetched DB data, returns actionable insights.
6
+ * No DB or filesystem access here — easier to test and reason about.
7
+ *
8
+ * Patterns detected:
9
+ * - Read dominance → suggest offset+limit or batching
10
+ * - Bash overuse → suggest Read/Grep instead
11
+ * - High loop rate → review prompts / agent instructions
12
+ * - Low cache ratio → context changes too much between messages
13
+ * - High cache ratio → positive: great cost efficiency
14
+ * - High avg cost → consider Haiku for simpler tasks
15
+ * - Low efficiency → linked to loops
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.analyzePatterns = analyzePatterns;
19
+ // ─── Thresholds ───────────────────────────────────────────────────────────────
20
+ const MIN_SESSIONS = 2; // need at least 2 sessions for meaningful patterns
21
+ const MIN_TOOLS = 15; // need at least 15 tool calls total to trust ratios
22
+ // ─── Analyzer ────────────────────────────────────────────────────────────────
23
+ function analyzePatterns(toolCounts, stats) {
24
+ const insights = [];
25
+ if (stats.session_count < MIN_SESSIONS)
26
+ return insights;
27
+ const totalTools = toolCounts.reduce((s, t) => s + t.count, 0);
28
+ if (totalTools < MIN_TOOLS)
29
+ return insights;
30
+ const byName = new Map(toolCounts.map(t => [t.tool_name, t.count]));
31
+ const get = (name) => byName.get(name) ?? 0;
32
+ // ── Read dominance ────────────────────────────────────────────────────────
33
+ const readCount = get('Read');
34
+ const readPct = Math.round(readCount / totalTools * 100);
35
+ if (readPct >= 45 && readCount >= 20) {
36
+ insights.push({
37
+ level: 'tip',
38
+ title: 'High Read frequency',
39
+ description: 'More than 40% of tool calls are Read. Consider using offset+limit to read only the needed lines, or batch reads of multiple files in a single response.',
40
+ metric: `${readPct}% of calls (${readCount} uses)`,
41
+ });
42
+ }
43
+ // ── Bash overuse vs Read/Grep ─────────────────────────────────────────────
44
+ const bashCount = get('Bash');
45
+ const readGrep = get('Read') + get('Grep') + get('Glob');
46
+ const bashPct = Math.round(bashCount / totalTools * 100);
47
+ if (bashCount > readGrep && bashCount >= 10) {
48
+ insights.push({
49
+ level: 'tip',
50
+ title: 'Bash used more than Read/Grep',
51
+ description: 'Bash can do the same as cat, grep or find, but is slower and less transparent. Dedicated tools (Read, Grep, Glob) are safer and faster for Claude.',
52
+ metric: `${bashPct}% Bash (${bashCount}) vs ${readGrep} Read+Grep+Glob`,
53
+ });
54
+ }
55
+ // ── High loop rate ────────────────────────────────────────────────────────
56
+ if (stats.avg_loops >= 1.5) {
57
+ insights.push({
58
+ level: 'warning',
59
+ title: 'Frequent loops detected',
60
+ description: 'Claude repeats the same tools in a loop more than 1.5 times per session on average. This usually indicates ambiguous instructions or an unnecessary confirmation cycle.',
61
+ metric: `~${stats.avg_loops.toFixed(1)} loops / session`,
62
+ });
63
+ }
64
+ // ── Cache efficiency ──────────────────────────────────────────────────────
65
+ const cacheRatio = stats.avg_total_input > 0
66
+ ? stats.avg_cache_read / stats.avg_total_input
67
+ : 0;
68
+ if (cacheRatio < 0.15 && stats.avg_total_input > 5000) {
69
+ insights.push({
70
+ level: 'tip',
71
+ title: 'Low cache reuse',
72
+ description: 'Only 15% or less of the context comes from cache. The context varies a lot between messages. If there are fixed instructions (CLAUDE.md, long system prompts), Claude reprocesses them on every response.',
73
+ metric: `${Math.round(cacheRatio * 100)}% cache hit ratio`,
74
+ });
75
+ }
76
+ else if (cacheRatio >= 0.65 && stats.avg_total_input > 5000) {
77
+ insights.push({
78
+ level: 'positive',
79
+ title: 'Excellent cache usage',
80
+ description: 'More than 65% of the context is served from cache in this project. A large portion of input cost is avoided thanks to Claude prompt caching.',
81
+ metric: `${Math.round(cacheRatio * 100)}% cache hit ratio`,
82
+ });
83
+ }
84
+ // ── High average cost per session ─────────────────────────────────────────
85
+ if (stats.avg_cost_usd >= 0.50) {
86
+ insights.push({
87
+ level: 'tip',
88
+ title: 'High cost per session',
89
+ description: 'The average cost per session exceeds $0.50. If there are repetitive analysis or file reading tasks, consider using Haiku (10× cheaper) for those parts.',
90
+ metric: `~$${stats.avg_cost_usd.toFixed(2)} / session`,
91
+ });
92
+ }
93
+ // ── Low efficiency score ──────────────────────────────────────────────────
94
+ if (stats.avg_efficiency !== null && stats.avg_efficiency < 65) {
95
+ insights.push({
96
+ level: 'warning',
97
+ title: 'Low efficiency score',
98
+ description: 'The average efficiency score is below 65/100. This is correlated with high loop counts. Check if prompts provide enough context on the first attempt.',
99
+ metric: `${Math.round(stats.avg_efficiency)}/100 average`,
100
+ });
101
+ }
102
+ // ── Heavy context per session ─────────────────────────────────────────────
103
+ if (stats.avg_total_input > 150000) {
104
+ insights.push({
105
+ level: 'tip',
106
+ title: 'Very large context per session',
107
+ description: 'The average context exceeds 150K tokens per session. Consider using /checkpoint + /compact frequently to reduce context size and lower the cost of each call.',
108
+ metric: `~${Math.round(stats.avg_total_input / 1000)}K tokens / session`,
109
+ });
110
+ }
111
+ // ── Agent heavy usage (positive) ─────────────────────────────────────────
112
+ const agentCount = get('Agent');
113
+ const agentPct = Math.round(agentCount / totalTools * 100);
114
+ if (agentPct >= 20) {
115
+ insights.push({
116
+ level: 'positive',
117
+ title: 'Heavy agent usage',
118
+ description: 'More than 20% of calls are to Agent. This project makes good use of Claude Code multi-agent mode to parallelize tasks.',
119
+ metric: `${agentPct}% Agent (${agentCount} uses)`,
120
+ });
121
+ }
122
+ return insights;
123
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * project-scanner.ts — Descubrimiento de proyectos desde ~/.claude/projects/
3
+ *
4
+ * Escanea los directorios de Claude Code, decodifica sus paths reales,
5
+ * lee los HANDOFF.md y extrae métricas de progreso.
6
+ */
7
+ export interface HandoffProgress {
8
+ done: number;
9
+ total: number;
10
+ pct: number;
11
+ nextTask: string | null;
12
+ }
13
+ export interface ProjectScanResult {
14
+ path: string;
15
+ name: string;
16
+ encodedDir: string;
17
+ hasHandoff: boolean;
18
+ autoHandoff: boolean;
19
+ progress: HandoffProgress;
20
+ jsonlStats: JSONLStats;
21
+ }
22
+ export interface ModelUsage {
23
+ opusTokens: number;
24
+ sonnetTokens: number;
25
+ haikuTokens: number;
26
+ }
27
+ export interface JSONLStats {
28
+ session_count: number;
29
+ total_cost_usd: number;
30
+ total_tokens: number;
31
+ last_active: number | null;
32
+ modelUsage: ModelUsage;
33
+ }
34
+ /**
35
+ * Decodifica el nombre de directorio de Claude Code al path real.
36
+ * "-Users-db-Documents-GitHub-claudestat" → "/Users/db/Documents/GitHub/claudestat"
37
+ *
38
+ * Problema: directorios con '-' en el nombre (ej: "gmail-ai-agent") se confunden
39
+ * con separadores de path. Solución: búsqueda greedy recursiva por el filesystem.
40
+ */
41
+ export declare function decodeProjectDir(encodedName: string): string | null;
42
+ /**
43
+ * Dado un directorio base y un string con segmentos separados por '-',
44
+ * encuentra el path real en disco probando cada combinación posible.
45
+ * Ejemplo: base="/Users/db/Documents/GitHub", remaining="gmail-ai-agent"
46
+ * → prueba "gmail" (no existe), "gmail-ai" (no existe), "gmail-ai-agent" (✓)
47
+ */
48
+ export declare function findRealPath(base: string, remaining: string, depth?: number): string | null;
49
+ export declare function parseHandoffProgress(content: string): HandoffProgress;
50
+ /**
51
+ * Lee todos los JSONL del directorio codificado de un proyecto y acumula
52
+ * tokens y coste. No requiere que el daemon haya estado corriendo.
53
+ */
54
+ export declare function getJSONLStats(encodedDir: string): JSONLStats;
55
+ /**
56
+ * Genera y escribe un HANDOFF.md mínimo para proyectos que no tienen uno.
57
+ * El archivo se marca como auto-generado para que el usuario lo complete.
58
+ * No sobreescribe si ya existe.
59
+ */
60
+ export declare function autoCreateHandoff(projectPath: string, stats: JSONLStats): void;
61
+ /**
62
+ * Descubre todos los proyectos en los que se ha trabajado con Claude Code.
63
+ *
64
+ * Estrategia (Opción A):
65
+ * 1. Para cada directorio en ~/.claude/projects/:
66
+ * a. Intenta decodificar el nombre (rápido) → busca mejor raíz subiendo árbol
67
+ * b. Si falla → infiere desde file paths del JSONL
68
+ * 2. Agrupa por raíz de proyecto (varios dirs Claude Code → mismo repo)
69
+ * 3. Para cada raíz única: lee HANDOFF, calcula progreso, suma stats JSONL
70
+ */
71
+ export declare function discoverProjects(): ProjectScanResult[];