claude-roi 0.1.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/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # Codelens AI
2
+
3
+ **Agent Productivity-to-Cost Correlator** — Is your AI coding agent actually shipping code?
4
+
5
+ Codelens AI ties Claude Code token usage to actual git output. It reads your local Claude Code session files, correlates them with git commits by timestamp, and serves a dashboard answering: *"Am I getting ROI from my AI coding agent?"*
6
+
7
+ - One command, zero config
8
+ - All data stays local
9
+ - Works with any git repo where you've used Claude Code
10
+
11
+ ## Installation
12
+
13
+ ### Option 1: Run directly (no install)
14
+
15
+ ```bash
16
+ npx claude-roi
17
+ ```
18
+
19
+ ### Option 2: Install globally
20
+
21
+ ```bash
22
+ # npm
23
+ npm install -g claude-roi
24
+
25
+ # pnpm
26
+ pnpm add -g claude-roi
27
+
28
+ # yarn
29
+ yarn global add claude-roi
30
+ ```
31
+
32
+ Then run anywhere:
33
+
34
+ ```bash
35
+ claude-roi
36
+ ```
37
+
38
+ ### Option 3: Clone and run from source
39
+
40
+ ```bash
41
+ git clone https://github.com/Akshat2634/Codelens-AI.git
42
+ cd Codelens-AI
43
+
44
+ # Install dependencies (pick one)
45
+ npm install
46
+ # or
47
+ pnpm install
48
+ # or
49
+ yarn install
50
+
51
+ # Run it
52
+ node src/index.js
53
+ ```
54
+
55
+ ## Prerequisites
56
+
57
+ - **Node.js >= 18** — [Download](https://nodejs.org/)
58
+ - **Git** — installed and configured with `user.name` and `user.email`
59
+ - **Claude Code** — you must have used [Claude Code](https://claude.com/claude-code) at least once so session data exists at `~/.claude/projects/`
60
+
61
+ ## Quick Start
62
+
63
+ ```bash
64
+ npx claude-roi
65
+ ```
66
+
67
+ This parses your `~/.claude/projects/` session data, analyzes your git repos, and opens a dashboard at `http://localhost:3457`.
68
+
69
+ ## What It Measures
70
+
71
+ | Metric | Description |
72
+ | --------------------- | --------------------------------------------------------------- |
73
+ | **Cost per Commit** | How much each AI-assisted commit costs in tokens |
74
+ | **Line Survival Rate**| % of AI-written lines that survive 24h without being rewritten |
75
+ | **Orphaned Sessions** | Sessions with 10+ messages that produced zero commits |
76
+ | **ROI Grade (A-F)** | Composite score based on tokens-per-commit and survival rate |
77
+ | **Model Comparison** | Efficiency breakdown across Opus, Sonnet, and Haiku |
78
+ | **Branch Awareness** | What % of AI commits landed on production |
79
+ | **Peak Hours** | Hour-of-day x day-of-week productivity heatmap |
80
+
81
+ ## CLI Options
82
+
83
+ ```bash
84
+ claude-roi # default: last 30 days, port 3457
85
+ claude-roi --days 90 # look back 90 days
86
+ claude-roi --port 8080 # custom port
87
+ claude-roi --no-open # don't auto-open browser
88
+ claude-roi --json # dump all metrics as JSON to stdout
89
+ claude-roi --project techops # filter to a specific project
90
+ claude-roi --refresh # force full re-parse (ignore cache)
91
+ ```
92
+
93
+ ## Dashboard
94
+
95
+ The dashboard includes:
96
+
97
+ - **Hero stats** — total cost, commits shipped, cost per commit, ROI grade
98
+ - **Smart insights** — auto-generated observations about your usage patterns
99
+ - **Cost vs Output timeline** — dual-axis chart of daily cost and lines added
100
+ - **Model comparison** — cost breakdown by Claude model
101
+ - **Session length analysis** — which session sizes have the best ROI
102
+ - **Productivity heatmap** — GitHub-style grid showing when you're most productive
103
+ - **Sessions table** — sortable, expandable table with per-session metrics and matched commits
104
+
105
+ ## How It Works
106
+
107
+ 1. **Parses** JSONL session files from `~/.claude/projects/`
108
+ 2. **Analyzes** git history from each repo you've worked in with Claude
109
+ 3. **Correlates** sessions to commits by timestamp (during session + 30min buffer)
110
+ 4. **Calculates** cost using Anthropic token pricing (input, output, cache read/write)
111
+ 5. **Serves** an interactive dashboard on localhost
112
+
113
+ ### Caching
114
+
115
+ Parsed session data is cached at `~/.cache/claude-roi/parsed-sessions.json`. On subsequent runs, only new or modified JSONL files are re-parsed, making startup near-instant. Use `--refresh` to force a full re-parse.
116
+
117
+ ### Cost Calculation
118
+
119
+ Token costs are version-aware and calculated per model:
120
+
121
+ | Model | Input | Output | Cache Read | Cache Write |
122
+ | --- | --- | --- | --- | --- |
123
+ | Opus 4.5/4.6 | $5/M | $25/M | $0.50/M | $6.25/M |
124
+ | Opus 4.0/4.1 | $15/M | $75/M | $1.50/M | $18.75/M |
125
+ | Sonnet 3.7/4.0/4.5/4.6 | $3/M | $15/M | $0.30/M | $3.75/M |
126
+ | Haiku 4.5 | $1/M | $5/M | $0.10/M | $1.25/M |
127
+ | Haiku 3.5 | $0.80/M | $4/M | $0.08/M | $1.00/M |
128
+ | Haiku 3 | $0.25/M | $1.25/M | $0.03/M | $0.30/M |
129
+
130
+ ### Line Survival
131
+
132
+ Line survival uses an approximate heuristic: if lines added in commit A are deleted by a subsequent commit on the same file within 24 hours, they're counted as "churned." This is not git-blame-based tracking and survival rates are rounded to the nearest 5%.
133
+
134
+ ## Project Structure
135
+
136
+ ```
137
+ Codelens-AI/
138
+ ├── package.json
139
+ ├── README.md
140
+ ├── .gitignore
141
+ └── src/
142
+ ├── index.js # CLI entry point
143
+ ├── claude-parser.js # Parse Claude Code JSONL session files
144
+ ├── cache.js # Parsed data caching layer
145
+ ├── git-analyzer.js # Parse git log with branch awareness
146
+ ├── correlator.js # Match sessions to commits by timestamp
147
+ ├── metrics.js # Calculate ROI metrics and insights
148
+ ├── server.js # Express server + API routes
149
+ └── dashboard.html # Single-file dashboard (inline CSS/JS)
150
+ ```
151
+
152
+ ## Contributing
153
+
154
+ Contributions welcome! Here's how to get started:
155
+
156
+ ```bash
157
+ # 1. Fork the repo on GitHub
158
+
159
+ # 2. Clone your fork
160
+ git clone https://github.com/YOUR_USERNAME/Codelens-AI.git
161
+ cd Codelens-AI
162
+
163
+ # 3. Install dependencies
164
+ npm install
165
+
166
+ # 4. Run in dev mode (no auto-open browser)
167
+ node src/index.js --no-open
168
+
169
+ # 5. Make your changes and test
170
+ node src/index.js --json | head -30 # verify JSON output
171
+ node src/index.js --no-open # test dashboard at localhost:3457
172
+
173
+ # 6. Submit a pull request
174
+ ```
175
+
176
+ ### Ideas for contributions
177
+
178
+ - Support for other AI coding tools (Copilot, Cursor, etc.)
179
+ - Git blame-based line survival tracking (more accurate than the 24h heuristic)
180
+ - Export dashboard as PDF/PNG
181
+ - Historical trend tracking across multiple runs
182
+ - Team/multi-user support
183
+ - Custom pricing configuration via CLI flag or config file
184
+
185
+ ## Privacy
186
+
187
+ All data stays on your machine. The only network requests are for Chart.js and Inter font from CDNs. No telemetry, no data collection.
188
+
189
+ ## License
190
+
191
+ MIT
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "claude-roi",
3
+ "version": "0.1.0",
4
+ "description": "Correlate Claude Code token usage with git output to measure AI coding agent ROI",
5
+ "type": "module",
6
+ "bin": {
7
+ "claude-roi": "src/index.js"
8
+ },
9
+ "main": "./src/index.js",
10
+ "scripts": {
11
+ "start": "node src/index.js",
12
+ "dev": "node src/index.js --no-open"
13
+ },
14
+ "keywords": [
15
+ "claude",
16
+ "ai",
17
+ "roi",
18
+ "productivity",
19
+ "tokens",
20
+ "cost",
21
+ "git",
22
+ "dashboard"
23
+ ],
24
+ "license": "MIT",
25
+ "engines": {
26
+ "node": ">=18.0.0"
27
+ },
28
+ "dependencies": {
29
+ "commander": "^13.0.0",
30
+ "express": "^5.0.0",
31
+ "open": "^10.0.0",
32
+ "playwright": "^1.58.2"
33
+ }
34
+ }
package/src/cache.js ADDED
@@ -0,0 +1,86 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync, unlinkSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+
5
+ const CACHE_DIR = path.join(os.homedir(), '.cache', 'agent-analytics');
6
+ const CACHE_FILE = path.join(CACHE_DIR, 'parsed-sessions.json');
7
+ const CACHE_VERSION = 1;
8
+
9
+ export function loadCache() {
10
+ if (!existsSync(CACHE_FILE)) {
11
+ return null;
12
+ }
13
+
14
+ try {
15
+ const raw = readFileSync(CACHE_FILE, 'utf-8');
16
+ const data = JSON.parse(raw);
17
+ if (data.version !== CACHE_VERSION) return null;
18
+ return data;
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ export function saveCache(sessions, fileIndex) {
25
+ mkdirSync(CACHE_DIR, { recursive: true });
26
+ const data = {
27
+ version: CACHE_VERSION,
28
+ lastParsedAt: new Date().toISOString(),
29
+ fileIndex,
30
+ sessions,
31
+ };
32
+ writeFileSync(CACHE_FILE, JSON.stringify(data));
33
+ }
34
+
35
+ export function deleteCache() {
36
+ if (existsSync(CACHE_FILE)) {
37
+ unlinkSync(CACHE_FILE);
38
+ }
39
+ }
40
+
41
+ export function getStaleFiles(claudeDir, cachedFileIndex) {
42
+ const currentFiles = {};
43
+ const newFiles = [];
44
+ const modifiedFiles = [];
45
+ const deletedFiles = [];
46
+
47
+ // Scan all JSONL files on disk
48
+ if (!existsSync(claudeDir)) return { currentFiles, newFiles, modifiedFiles, deletedFiles };
49
+
50
+ const projectFolders = readdirSync(claudeDir).filter(f => {
51
+ if (f.startsWith('.')) return false;
52
+ const fullPath = path.join(claudeDir, f);
53
+ try { return statSync(fullPath).isDirectory(); } catch { return false; }
54
+ });
55
+
56
+ for (const folder of projectFolders) {
57
+ const projectDir = path.join(claudeDir, folder);
58
+ let files;
59
+ try {
60
+ files = readdirSync(projectDir).filter(f => f.endsWith('.jsonl'));
61
+ } catch { continue; }
62
+
63
+ for (const file of files) {
64
+ const filePath = path.join(projectDir, file);
65
+ try {
66
+ const mtime = statSync(filePath).mtimeMs;
67
+ currentFiles[filePath] = mtime;
68
+
69
+ if (!cachedFileIndex[filePath]) {
70
+ newFiles.push(filePath);
71
+ } else if (mtime > cachedFileIndex[filePath]) {
72
+ modifiedFiles.push(filePath);
73
+ }
74
+ } catch { continue; }
75
+ }
76
+ }
77
+
78
+ // Find deleted files
79
+ for (const filePath of Object.keys(cachedFileIndex)) {
80
+ if (!currentFiles[filePath]) {
81
+ deletedFiles.push(filePath);
82
+ }
83
+ }
84
+
85
+ return { currentFiles, newFiles, modifiedFiles, deletedFiles };
86
+ }