Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
OPENAI_ANALYSIS_MODEL=gpt-5.4-mini
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Group sessions by project. Click into any project to see every session that ran
- Per-session table with session name (first user message), model, all token buckets + cost

### 🔬 Analysis — LLM-powered waste detection
Every 2 hours, Claude Haiku analyses your projects using your existing OAuth session — **no API key needed**.
Every 2 hours, GPT 5.4 mini analyzes your projects using aggregated token statistics. Set `OPENAI_API_KEY` to enable LLM recommendations; without it, the dashboard falls back to heuristic analysis.

**Detected patterns:**
- 🔴 Context bloat — loading huge context for minimal output
Expand All @@ -49,6 +49,14 @@ Each finding includes a **projected savings estimate**, a concrete recommendatio
- **Claude context files** — horizontal bar chart of every `CLAUDE.md` across your projects, sized by byte count
- **Codex context files** — same for `AGENTS.md`

### 📤 Export — Excel-ready workbook

Download a multi-sheet `.xlsx` from the Export tab or directly from `http://localhost:4317/api/export.xlsx`.

- Summary, Sessions, Projects, Daily Usage
- Analysis Projects, Findings, Finding Examples, Applied Actions
- Context Files, Wire Stats, and a sheet dictionary

### ⚡ Delta WebSocket updates
The dashboard never sends a full payload twice. Only changed sessions are pushed on each update — typically **99%+ bandwidth reduction** vs naive full-refresh, visible in the footer.

Expand All @@ -62,7 +70,7 @@ The dashboard never sends a full payload twice. Only changed sessions are pushed
**Projects** — every project as a card with tokens, cost, composition bar and per-category breakdown
![Projects](public/screenshots/projects.jpg)

**Analysis** — Haiku-powered waste detection, per-project findings with severity, impact $ and apply button
**Analysis** — GPT-powered waste detection, per-project findings with severity, impact $ and apply button
![Analysis](public/screenshots/analysis.jpg)

**Charts** — daily token/cost bar chart (Today / 7 Days / 30 Days) and context file size inventory
Expand Down Expand Up @@ -109,15 +117,18 @@ npm run dev

## LLM Analysis setup (optional but recommended)

The Analysis page works out of the box using **Claude Code's OAuth session** — the same login as your `claude` CLI. No API key needed.
The Analysis page uses OpenAI's Responses API with `gpt-5.4-mini` by default.

If the analysis shows `⚙ Heuristic` instead of `✦ Haiku`, check that `claude` CLI is authenticated:
Create `.env` from the example and set your API key:

```bash
claude --version # should respond without prompting for login
cp .env.example .env
# edit .env:
# OPENAI_API_KEY=sk-...
# OPENAI_ANALYSIS_MODEL=gpt-5.4-mini
```

The tool shells out to the bundled `claude.exe` binary in `node_modules/@anthropic-ai/claude-code/bin/` with `HOME` pointing at your host home directory, so it picks up your existing OAuth tokens automatically.
If the analysis shows `⚙ Heuristic` instead of `✦ gpt-5.4-mini`, the OpenAI key is missing or the analysis request failed.

---

Expand All @@ -143,11 +154,11 @@ The tool shells out to the bundled `claude.exe` binary in `node_modules/@anthrop
│ Chart.js for charts │
└────────────────────────────┘

Every 2h: claude.exe (Haiku, OAuth) ──► per-project analysis
Every 2h: OpenAI Responses API (gpt-5.4-mini) ──► per-project analysis
results cached, delta-pushed via WebSocket
```

**Data never leaves your machine.** Session files are read locally. The only outbound traffic is the Haiku analysis call — which sends only aggregated stats (no conversation content).
Session files are read locally. The only outbound analysis traffic sends aggregated stats (no conversation content) to OpenAI when `OPENAI_API_KEY` is configured.

---

Expand All @@ -174,14 +185,14 @@ File watching uses polling inside Docker (`USE_POLLING=true`) because Mac's FUSE

| File | Purpose |
|---|---|
| `.env` | `ANTHROPIC_API_KEY` if you prefer API key over OAuth |
| `pricing.js` | Token rates per model — update when Anthropic changes pricing |
| `.env` | `OPENAI_API_KEY` and optional `OPENAI_ANALYSIS_MODEL` |
| `pricing.js` | Token rates per model — update when providers change pricing |
| `docker-compose.yml` | Port, volume mounts, polling interval |
| `com.agent-optimization.plist` | macOS LaunchAgent definition |

### Supported models (pricing)

Claude Opus 4/3.5/3 · Claude Sonnet 4/3.5/3 · Claude Haiku 4/3.5/3 · GPT-5 · GPT-4o · GPT-4.1 · o1 · o3 · o4-mini · Codex-mini
Claude Opus 4/3.5/3 · Claude Sonnet 4/3.5/3 · Claude Haiku 4/3.5/3 · GPT-5 · GPT-5.4 mini · GPT-4o · GPT-4.1 · o1 · o3 · o4-mini · Codex-mini

Unknown models fall back to Sonnet-tier pricing with a `≈` indicator.

Expand Down Expand Up @@ -218,7 +229,7 @@ docker compose up -d --build # rebuilds image with new code
- **Server:** Express + `ws` WebSocket + `chokidar` file watcher
- **Frontend:** Vanilla JS, Chart.js — no build step, no framework
- **Container:** Docker / OrbStack, `node:22-alpine`
- **Analysis:** `@anthropic-ai/claude-code` (bundled `claude.exe`, OAuth auth)
- **Analysis:** OpenAI Responses API (`gpt-5.4-mini` by default)

---

Expand Down
4 changes: 2 additions & 2 deletions actuators.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function extractDiffSnippet(before, after) {
function previewOverpoweredModel(projectPath, sessions) {
// Choose target tier based on what was actually flagged. Default to Sonnet.
const flagged = sessions.filter(s =>
/opus|gpt-5|^o1$/i.test(s.model || '') &&
(/opus|^o1$/i.test(s.model || '') || (/gpt-5/i.test(s.model || '') && !/mini/i.test(s.model || ''))) &&
(s.output || 0) < 2000 && (s.total || 0) < 100_000
);
if (!flagged.length) {
Expand Down Expand Up @@ -254,7 +254,7 @@ export function previewActuator(findingId, project, sessions) {
case 'fragmented-sessions':
return behavioral('Fragmenting comes from CLI usage patterns. Tracking only.');
default:
return { actionable: false, reason: 'Unknown finding type.' };
return behavioral('This LLM recommendation is advisory and has no safe file-level automation yet. Tracking only.');
}
}

Expand Down
4 changes: 2 additions & 2 deletions analysis.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const SEVERITY_ORDER = { critical: 3, high: 2, medium: 1, low: 0 };

const tier = (model) => {
if (!model) return 'unknown';
if (/haiku|mini/i.test(model)) return 'cheap';
if (/opus|gpt-5|^o1$/i.test(model)) return 'premium';
if (/sonnet|gpt-4o$|gpt-4\.1$|^o3$/i.test(model)) return 'mid';
if (/haiku|mini/i.test(model)) return 'cheap';
return 'mid';
};

Expand Down Expand Up @@ -52,7 +52,7 @@ function findCacheInefficiency(sessions) {
}

function findExpensiveTinyTasks(sessions) {
// Premium-tier sessions that did very little work — Sonnet/Haiku would have sufficed.
// Premium-tier sessions that did very little work — a mid/cheap model would have sufficed.
const out = [];
for (const s of sessions) {
if (tier(s.model) !== 'premium') continue;
Expand Down
6 changes: 4 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## Copy .env.example to .env and add your ANTHROPIC_API_KEY to enable LLM analysis.
## Without it the app works fine but uses heuristic rules instead of Haiku.
## Copy .env.example to .env and add your OPENAI_API_KEY to enable LLM analysis.
## Without it the app works fine but uses heuristic rules instead of GPT analysis.

services:
agent-optimization:
Expand All @@ -12,6 +12,8 @@ services:
environment:
- HOST_HOME=/host-home
- USE_POLLING=true
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- OPENAI_ANALYSIS_MODEL=${OPENAI_ANALYSIS_MODEL:-gpt-5.4-mini}
volumes:
# Full home dir at same logical path so actuator-written paths resolve correctly
- /Users/${USER}:/host-home:cached
Expand Down
Loading