README.md
Rama main · réplica de lectura del repositorio original.
# 🐾 Meow Operations
> **Meow Ops is the local inbox for AI coding work. It shows what happened, what it cost, and what you should decide next.**
Meow Operations turns local Claude Code, OpenAI Codex Desktop, Aider, Cursor, Hermes, and Google Antigravity activity into an installable PWA. Five surfaces: Today, Review, Ledger, Sanctum, Learn. The focus timer sits on every screen as a chip. Companion is gone. Nothing applies until you say so. No required account. No telemetry. MIT-licensed.
## What's new in 1.2.0
The sidebar is five words: Today, Review, Ledger, Sanctum, Learn. The focus timer is a chip on every screen. Companion is removed.
Learn mines concepts from sessions you already ran. Each card has a name, a short industry technical summary, a layman "what you did" line, a source, and an I get this mark. You search YouTube yourself. No school, no XP.
Today answers what happened. Review holds proposals until you approve them. Ledger holds spend. Sanctum and Learn are the two rooms that stayed.
## Screenshots
<table>
<tr>
<td width="50%"><img src="docs/screenshots/01-overview.png" alt="Meow Ops Today"></td>
<td width="50%"><img src="docs/screenshots/04-live-sessions.png" alt="Meow Ops Review"></td>
</tr>
<tr>
<td><strong>Today</strong><br>Sessions, tokens, cost, and where the work went. The focus timer opens from a chip on this screen.</td>
<td><strong>Review</strong><br>Pending proposals. Nothing applies until you say so.</td>
</tr>
<tr>
<td width="50%"><img src="docs/screenshots/02-cost-tracker.png" alt="Meow Ops Ledger"></td>
<td width="50%"><img src="docs/screenshots/03-colony.png" alt="Meow Ops Sanctum"></td>
</tr>
<tr>
<td><strong>Ledger</strong><br>Spend, tokens, and unattributed provider usage.</td>
<td><strong>Sanctum</strong><br>The same runs as a 3D scene.</td>
</tr>
<tr>
<td width="50%"><img src="docs/screenshots/06-by-project.png" alt="Meow Ops Learn"></td>
<td width="50%"><img src="docs/screenshots/05-focus-timer.png" alt="Meow Ops focus timer chip"></td>
</tr>
<tr>
<td><strong>Learn</strong><br>Concepts mined from your sessions. Name, technical summary, what you did, source, I get this.</td>
<td><strong>Focus timer</strong><br>A chip on every screen. Not a page.</td>
</tr>
</table>
---
## Install — 2 minutes, zero accounts
### Prerequisites
- **Node.js 20.19+ or 22.12+**: check with `node --version` (Vite's supported versions)
- **npm** (comes with Node) or pnpm
- At least one supported harness with local session history. Claude Code is optional; its sessions live in `~/.claude/projects/`.
### Download and run
```bash
git clone https://github.com/merak3i/meow-ops.git
cd meow-ops
npm install
node sync/export-local.mjs # parses ~/.claude/projects → public/data/sessions.json
npm run dev # opens http://localhost:5173
```
That's it. Your real session data loads on first page hit.
### First-time setup
1. Open `http://localhost:5173` in Chrome
2. The dashboard loads your parsed local sessions immediately
3. Codex Desktop sessions are read from `~/.codex/sessions/` automatically when present
4. To add Aider sessions: `AIDER_PROJECTS=/path/to/your/project node sync/export-local.mjs`
5. Cursor agent transcripts are read from `~/.cursor/projects` when present. Optional usage enrichment needs a team Admin API key stored in macOS Keychain (see below). An individual plan may not provide this capability; no upgrade is required for local capture.
6. Re-run the sync command any time after new AI sessions to refresh
Every parsed session is preserved in an uncapped, append-only local archive. The
generated `sessions.json` file is only a bounded browser compatibility preview;
it defaults to the latest 1,000 sessions so older browsers and static tooling
stay responsive. Change the preview size with:
```bash
MEOW_SESSION_PREVIEW_LIMIT=2000 node sync/export-local.mjs
```
All-time totals and date-range queries use the complete archive and its
summaries, not the compatibility preview. `MEOW_MAX_SESSIONS` remains accepted
as a legacy alias for the preview limit; it is not a retention setting.
### Install as a PWA (dock-installable)
Run locally, then in Chrome: **address bar → install icon (⊕)** → the dashboard installs to your dock or desktop. Works offline via service worker.
For the hosted demo and the local-data boundary, see **Hosted demo shell (session data stays local)** below.
---
## The Problem Nobody Talks About
Developers using AI tools — Claude, GPT-4o, Gemini, Codex, Cursor, Aider — spend hundreds of dollars a month on tokens with almost no visibility into what they got for it. Not cost-per-model. Not which project burns the most. Not whether a session actually shipped something or just spun its wheels.
As workflows get more layered, it also gets harder to see what is verified, what is assumed, and what still needs review.
The tools that do exist count tokens. None of them:
- Tell you what those tokens **produced**
- Work across **multiple AI models** in one view
- Let you watch your **agents operate in real time**
- Make any of it **fun to look at**
Meow Operations fixes all four. For free. For everyone.
---
## What It Does
### Analytics Dashboard
Tracks sessions from **Claude Code**, **OpenAI Codex Desktop**, **Aider**, **Cursor**, and **Google Antigravity** in one unified view. Cost tables for 30+ models.
> **Google Antigravity note:** Meow Ops reads the [documented transcript logs](https://www.antigravity.google/docs/hooks) under `~/.gemini/antigravity/brain/<id>/.system_generated/logs/transcript.jsonl`. It also reads `conversations/<id>.db` through SQLite's read-only mode to recover step metadata missing from those logs. Repeated updates to the same step are counted once. Database-only sessions can provide time and step counts without a captured prompt or project path. Unknown database step types and unreadable stores are reported as coverage gaps. Binary `.pb` payloads, generation metadata, model names, token usage and cost are not decoded by this adapter; usage remains unavailable rather than invented.
> **Cursor note:** Local agent transcripts under `~/.cursor/projects/*/agent-transcripts/` expose messages, tools and parent/subagent hierarchy; authoritative model, tokens and cost may be unavailable. Meow Ops does not infer these from a selected model or a nested Task argument. Optional usage enrichment reads the official `POST /teams/filtered-usage-events` endpoint with a team Admin API key. Only exact conversation identifiers join provider events to local sessions. Unmatched events stay separate from project totals. API-reported charges, model-cost estimates and billing flags remain distinct; an API charge is not an invoice reconciliation. No model name, including Grok, establishes Grok Bot product identity. See [Cursor Admin API docs](https://prod.cursor.com/docs/account/teams/admin-api).
To connect an existing eligible account, open the [Cursor dashboard](https://cursor.com/dashboard) and its Admin API key settings. In macOS **Keychain Access**, add a password item to the login keychain with item name `com.meow-ops.cursor-admin-api`, account `meow-ops`, and the key as its password. The local collector reads that item through the OS credential manager. An explicitly injected `CURSOR_ADMIN_API_KEY` takes precedence; an empty injected value disables Keychain lookup. This key is ignored in `.env` and `local.env`. Never paste it into chat or shell history. If your individual account has no team Admin key, local collection continues without an upgrade.
Provider refresh runs at most hourly under the shared sync lock. Complete responses are retained as a minimal private usage history outside Git, with owner-only permissions, exact identifiers and verified periods. Emails, prompts and credentials are excluded. Failed or incomplete refreshes preserve previously verified records and show their age. Public summaries contain aggregates, not private event identifiers. Detailed Grok Bot attribution remains unavailable unless an official export supplies product and bot identifiers. Supplying an eligible key authorizes read-only usage requests; it does not invoke paid model inference.
| Surface | What you see |
|---|---|
| **Today** | Sessions, tokens, cost, and where the work went. Tabs: Summary, Sessions, Runs. |
| **Review** | Pending proposals and project evidence. Nothing applies until you say so. Tabs: Inbox, Projects, Map. |
| **Ledger** | Spend, tokens, and unattributed provider usage. |
| **Sanctum** | The same runs as a 3D scene. |
| **Learn** | Concepts you already practiced, mined from your sessions. Name, technical summary, what you did, source, I get this. |
The focus timer is a chip on every screen, not a page. Companion is removed from the product.
### Learn
Learn is a concept map inferred from real sessions. Each card has a name, a short industry technical summary, a layman "what you did", a source, and an I get this mark. No YouTube finder, no school, no XP, no workshop.
### Source Breakdown
When more than one source has data, **Today** shows a source split:
- Sessions, tokens, and cost per source
- Ghost rate per source
- Filter the view to one source from the page
Ledger is the place for unmatched provider usage that cannot be joined to a local session.
### Codex Desktop Parser
Codex Desktop support is first-class, not a CSV import. `sync/parse-codex.mjs` walks:
```text
~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<uuid>.jsonl
```
It extracts rollout ID, project path, model family, token totals, tool calls, first user-message snippet, session title from `session_index.jsonl` when available, and estimated model cost. Malformed historical rows are skipped instead of blocking the export.
### Agent Operations Visualizer
When Claude Code runs with subagents, meow-ops turns the session tree into a Gantt timeline showing exactly what ran in parallel vs. sequentially:
```
Run: storefront-redesign — 3 agents — $0.84 — 12m ago
⏱ 10:32:14 ───────────────────────────── 10:34:57 IST
├── 📐 Sonnet code-explorer ████████████ 47s $0.12
├── 📐 Sonnet code-architect ███████████████████ 1m42s $0.51
└── 🔍 Haiku code-reviewer ████████ 38s $0.09
⚡ 14,200 tok/$ (efficiency index)
```
Click any row for a full breakdown: token split, cache hit rate, tool usage, sidechain flag.
### Agent Swarm (Today · Swarm)
[AutoSwarm](https://github.com/HKUDS/AutoSwarm) (MIT) hands one task to the coding agents already installed on your machine: Codex, Claude Code, Cursor, Hermes, DeepSeek Harness, Grok Build, Kimi Code and others. **Today · Swarm** (`g w`, or `#/swarm`) is its mission control, laid out as an orbit: you at the top as the approval gate, AutoSwarm at the centre, the team on one ring.
- **What it shows.** Which agents AutoSwarm can find, which are proven to reply and resume their own session (a doctor probe pair with one native session id), what is running, which native approvals wait on you, and the latest delegated calls.
- **Read-only.** The helper reads AutoSwarm's receipts under its home (`AUTOSWARM_HOME`, else `~/Library/Application Support/autoswarm` on macOS, `$XDG_STATE_HOME/autoswarm` on Linux). It never starts a call, answers an approval or runs an agent binary. Every action is a command the page copies for you: probe (spends model calls), free executable check, inspect, approve or deny with `--by user`.
- **Missing is never zero.** A version check proves only that the binary exists. An empty native login list does not mean login is needed. Only a completed reply proves login, and only a probe pair proves native resume.
- **Private by construction.** `/swarm/status` answers the local dashboard only. Prompts leave as a short redacted snippet (none with `MEOW_NO_SNIPPETS=1`); call output never leaves the helper. The hosted shell shows a labelled example team.
- **Name your team.** Copy [`examples/swarm.example.json`](examples/swarm.example.json) to `~/.meow-ops/swarm.json` (or point `MEOW_SWARM_CONFIG` at it). It renames agents, recasts their cats, sets the probe workspace, hides agents you do not use and adds harnesses AutoSwarm cannot call to a dashed outer ring. It lives outside the repo and cannot change what is verified.
- **Pinned.** `g w` from any screen, the command palette, and the installed PWA's dock menu (Agent swarm).
### Sanctum
A 3D civic archive atrium for local session data. Follow session activity, token flow, latency, and health as session characters move along signal paths.
```
Record Scout ─────────────── Run Keeper ─────────────── Session Scribe ───────── Review Herald
[active] healthy path [active] slow path [active] [idle]
$0.0009 $0.0041 $0.0223 $0.0003
112ms 1480ms 3240ms 58ms
```
**Features:**
- Seven session roles — builder, detective, commander, architect, guardian, storyteller, and ghost — each with a distinct character class
- Signal-path states: `healthy` (fast flow), `choked` (slow flow), `severed` (flickering)
- Session characters move along animated paths; select one to inspect its available metrics, tools, and milestones
- WebGL archive scene with performance guardrails, minimap, an Archive Warden, modular record stacks, a Session Index Dial, and the engraved LLM Sun token emitter
- Grounded civic archive atrium with low record shelves, four clear aisles, pale stone, copper-trimmed elliptical roof openings, and muted book spines
- The recurring Archive Seal pairs four long teal rays and four short copper rays with a broken octagonal track and a copper diamond with a teal center; see [the visual identity note](docs/sanctum-visual-identity.md)
- Optional local, read-only archive guide for concept explanations and selected-session evidence; local model and Voicebox speech are disclosed in the guide UI
- Demo mode and local-session mode with no Supabase account required
- Supabase Realtime schema remains available for external pipelines in `db/migrations/0003_scrying_sanctum.sql`
The standard scene uses seven originalized, role-matched still cutouts with the exact authored Archive Seal composited on each; they follow session movement but have no limb animation. Vite bundles their WebP runtime assets from `src/pages/sanctum/assets/roster`. Original cutouts, prompts, rejected studies, and review renders stay in the local-only design archive. The fictional archive worker uses practical charcoal workwear, an asymmetric shoulder yoke, a cross-body utility folio, copper page tabs, and the authored Archive Seal. `src/pages/sanctum/guide-identity.ts` preserves the skin texture and charcoal uniform, recolors only the existing shoulder yoke and page tabs, and enlarges the model's existing two-part Archive Seal 1.65× in its plane for a more legible chest mark. The runtime model is `src/pages/sanctum/assets/guide-originalized-v110-runtime.glb` (SHA-256 `068320d713f5694b097adee6f48547b41600957156c583e2386dac042dbfb01f`). The atrium in `src/pages/ScryingSanctum.tsx` uses a grounded circular shelf ring with four clear aisles and up to three separated elliptical roof rims. The Seal recurs on the Warden, index dial, guide, and roster. These authored features, dated prompts, source designs, revisions, and hashes provide comparison points if the work is copied; they do not prevent copying, prove plagiarism, or establish rights. The owner marked the guide's human similarity/rights review and speech audition good to go; this records owner sign-off and does not establish legal rights. The 3D roster models remain local construction studies, not finished character art, and load only in the local development preview at `?roster=3d`. Linked guide evidence is read through the local helper, and enabled Voicebox speech stores spoken text and generated audio in local history.
#### Optional local guide speech
Voicebox speech stays on this Mac. Start Voicebox and download the Kokoro model. Confirm the local API is healthy:
```bash
curl -fsS http://127.0.0.1:17493/health | jq -e '.status == "healthy"'
```
Use a preset profile with engine `kokoro` and voice ID `bm_george`; the guide does not use cloned voice samples. To find its profile ID, query the local Voicebox API:
```bash
curl -fsS http://127.0.0.1:17493/profiles | jq -r '.[] | select(.voice_type == "preset" and .preset_engine == "kokoro" and .preset_voice_id == "bm_george") | .id'
```
Add `MEOW_GUIDE_VOICEBOX_PROFILE=<PROFILE_ID>` to the repository's gitignored `.env`, then start the local helper with `node sync/local-api.mjs`. The helper accepts only the loopback Voicebox URL. It will not download models; Voicebox keeps generated text and audio in its local history. See the [Voicebox profile API](https://docs.voicebox.sh/api-reference/profiles/list_profiles_profiles_get).
See `db/migrations/0003_scrying_sanctum.sql` for the full schema and RLS policies.
### Review Map
Review Map is the read-only topology and evidence view for loop-based workflows. It imports a workbook or workflow spec, builds the hierarchy on top of it, and renders the result as an interactive React Flow canvas. Review Inbox is the owner-governed change surface.
Use it when a loop matters more than a single run:
- Check the shape of the loop before execution starts.
- Open any node to see ownership, access, last verified state, and what remains unverified.
- Keep review and refresh local while the source spec changes.
- Compare recorded runs with current cost and evidence.
- Keep unverified items clearly labeled until evidence appears.
```
Main Coordinator
┌──────────┬──────┴───────┬─────────────┐
Research Dir Build Dir Review Dir Ops Dir
┌──┬──┐ ┌──┬──┐ ┌──┬──┐ ┌──┐
W1 W2 … W3 W4 … W5 W6 … W7 …
[review] [covered] [passed] [blocked]
```
Review Map lives under Review · Map. It is the same inbox as a graph.
**What it shows:**
- Excel workbook importer with fail-loud validation. Unknown groups, duplicate keys, missing columns, or secret-shaped content stop the import with named violations.
- Collapsible wave clusters keep dense lanes readable. Minimap, keyboard access, light and dark theme, and reduced-motion support stay on.
- Every node answers four questions: what owns this, what it can touch, last verified state, and what was not verified.
- Inspector shows workflow-spec knobs, guardrails, eval gates, and copyable validation commands. Review Map never executes anything itself; execution is a separate Review Inbox action that requires approval.
- Run timeline joins recorded loop runs against real session costs. An empty evidence list stays suspicious.
- Permanent "production writes disabled" badge. The alarm branch wears red, never the safe green.
- All loop data is local-only, and the hosted demo intentionally shows the instructional empty state.
Local API endpoints (`sync/local-api.mjs`): `GET /loop-ops/spec|status|runs`, `POST /loop-ops/sync` re-runs the importer. Import manually with `node sync/loop-ops-import.mjs`.
### Review Inbox
Loop Engineering is the review loop around the agents: capture what happened, turn recurring friction into small proposals, let the owner decide, and measure the next run. It is designed to improve the workflow without letting automation approve itself.
The local pipeline can:
1. Capture loop runs and compare them against prior evidence.
2. Intake content-free summaries from Claude, Codex, Antigravity, and optional screenshots.
3. Mine recurring failures, wasted work, high-friction task types, and automation-health drift.
4. Generate deterministic proposals, optionally enrich drafts with a bounded local/DeepSeek model call, and produce a daily digest with history.
5. Route decisions through Review Inbox. Nothing applies until you say so.
6. Execute only an approved, actionable proposal, with evidence recorded back to the local ledger.
Commands:
```bash
npm run loop:capture -- --loop <LOOP_ID> --since <ISO_TIME>
npm run intake
npm run digest
npm run daily
npm run loop:propose
npm run loop:review
npm run loop:simulate -- --proposal <PROPOSAL_ID>
node sync/local-api.mjs
npm run dev
```
`npm run loop:review` runs the local sync tests, evals, lint, typecheck, and build. It writes only check IDs and exit codes outside the worktree, then creates review-only Inbox drafts for failures; raw terminal output is never stored. Include browser coverage when needed with `npm run loop:review -- --with-e2e`.
Open `http://localhost:5173/#/review/inbox` for the owner surface. The optional executor runs an approved proposal in a temporary detached git worktree, applies the proposed diff, runs `npm ci`, `npm run test:sync`, `npm run eval`, and `npm run build`, then either records a dry-run or, in `push` mode, creates an `executor/<proposal-id>` branch and pull request. Only the hard-coded `test` and `prompt` categories may auto-merge after green PR checks; privacy, security, money, client-data, and production-infrastructure paths are forced to review-only.
Guarantees:
- Assistants can only create drafts. They cannot self-approve.
- Owner approvals happen through Review Inbox or the nonce-protected local API.
- Every ledger write goes through `appendRecord()`, field allowlists, validators, and redaction checks.
- The ledger lives outside the git worktree at `~/.meow-ops/loop-ledger/`.
- Real session data, secrets, local paths, and transcript content do not belong in tracked fixtures or PRs.
- Expired drafts are marked by `system:expire`, leave the active queue, and stay visible under the expired filter.
#### Live truth from Supabase (optional)
The Excel workbook is the **structure** (which surfaces exist). The live **truth states** (which gate passed, the db status, last-verified time) come from a Supabase table — e.g. one an upstream system keeps current. They join through the importer's existing truth CSV, so nothing in the importer changes:
```
Supabase table → sync/loop-ops-supabase.mjs → truth.csv → loop-ops-import.mjs --truth → spec.json → Review Map
(live state) (this connector, opt-in) (local) (joins Excel + truth) (local) (renders)
```
The connector is **opt-in and a no-op until configured** (set `LOOP_OPS_SUPABASE_URL/_KEY/_TABLE` in `.env`; remap differing column names with `LOOP_OPS_SUPABASE_COLMAP`). Each row is keyed by `surface_key` — the same key the workbook uses. Only the cloud pull needs network; the importer, `spec.json`, and Review Map stay fully local/offline.
**Three ways to keep it synced:**
| Cadence | How | Freshness |
|---|---|---|
| **Manual** | `node sync/loop-ops-supabase.mjs && node sync/loop-ops-import.mjs --truth public/data/loop-ops/truth.csv` (or Review Map **Refresh** after a pull) | on demand |
| **Near-real-time (pull)** | run that pair on a `launchd` / GitHub Actions cron every N minutes | minutes |
| **Real-time (push)** | `node sync/loop-ops-supabase-watch.mjs` — subscribes to Supabase **Realtime** (`postgres_changes`) and regenerates on every table change | seconds |
The real-time watcher needs Realtime enabled on the table: `ALTER PUBLICATION supabase_realtime ADD TABLE <your_table>;`.
### Seats (hidden)
A local-first SuperAdmin cockpit for the operator's software stack: GitHub Actions run volume, cache and artifact footprint, SaaS subscription run rate, renewal pressure, and source wiring for private usage ledgers. Reachable from the command palette, not the sidebar.
The public build ships a generic demo screen. Real account usage stays local in `public/data/superadmin-usage.json`, which is gitignored. Refresh it with:
```bash
npm run sync:superadmin
```
Optional environment:
```bash
MEOW_SUPERADMIN_GITHUB_REPOS=merak3i/meow-ops,<REPO_OWNER>/<REPO_NAME>
MEOW_SUPERADMIN_USAGE_SNAPSHOT=/absolute/path/to/superadmin-usage.json
MEOW_GITHUB_ACTIONS_MINUTES_LIMIT=3000
MEOW_GITHUB_ACTIONS_STORAGE_GB_LIMIT=10
```
The exporter accepts a local JSON snapshot shaped as `[{...}]`, `{ "services": [...] }`, `{ "rows": [...] }`, or `{ "saas": { "services": [...] } }`. Service-role keys and provider tokens do not belong in the snapshot.
### macOS Menu Bar And Local Sync API
A native-feeling menu bar widget can request a background sync through the same observable runner used by the dashboard. Install the persistent helper and the single daily operator job with:
```bash
npm run agents:install
```
The installer renders paths for the current clone, keeps logs under `~/Library/Logs/meow-ops/`, removes the retired duplicate hourly jobs, keeps `com.meowops.localapi` alive, and runs `com.meowops.daily` once at 08:30 local time.
For a local dashboard to sync from your machine, run the local API:
```bash
node sync/local-api.mjs # export local data (no git push — that is retired)
```
It listens on `http://localhost:7337` by default, serves fresh local `sessions.json` and `cost-summary.json`, and exposes asynchronous `POST /sync`, `GET /sync/status`, and `GET /sync/runs/:id`. A POST returns `202` with a run ID; the local UI then follows `preflight → export sessions → verify artifacts → refresh limits`. Failures remain visible with a sanitized phase, code, and retry hint. Runtime metadata lives outside the worktree at `~/.meow-ops/runtime/`. This process reads local files on your machine and never pushes to git. Private data and operational routes require a local dashboard origin. The one exception is the hosted shell at `https://meow-ops.vercel.app`: it may read a redacted `GET /sync/status` (state, phase names, timestamps, error codes, session and per-source counts) and send `POST /sync` to start a local run. No session rows, costs, titles, projects, paths or summaries cross that bridge. Set `MEOW_HOSTED_SYNC=0` to turn it off, or add more exact https origins with `MEOW_HOSTED_SYNC_ORIGINS`.
The browser bundle uses `VITE_LOCAL_SYNC_URL` at build time and defaults to `http://127.0.0.1:7337`. If the local helper uses another port, set the browser URL to the same loopback port. This value is public in the browser bundle and must not contain credentials.
### Private local sync with Cursor excluded
Open Terminal in the cloned `meow-ops` folder. Run the collector and helper with the same data directory and source settings:
```bash
umask 077
export MEOW_DATA_DIR="$HOME/.meow-ops/data"
export MEOW_SKIP_CURSOR=1
export MEOW_REFRESH_LIMITS=0
mkdir -p "$MEOW_DATA_DIR"
chmod 700 "$MEOW_DATA_DIR"
node sync/export-local.mjs
node sync/local-api.mjs
```
The folder permissions limit access to your local user; `umask 077` also restricts newly created files. The helper stays running in that Terminal. In a second Terminal, open the same clone and start the built dashboard:
```bash
npm run build
npm run preview -- --host 127.0.0.1 --port 4273 --strictPort
```
Open [the local dashboard](http://127.0.0.1:4273/#/today/summary), then use **Sync sessions** or **Sync now**. A local preview build can connect to the helper just like the development dashboard.
| Setting or status | Meaning |
|---|---|
| `MEOW_DATA_DIR` | Keeps generated session and summary files outside the checkout. Set it for both collector and helper. |
| `MEOW_SKIP_CURSOR=1` | Skips new Cursor transcript scans and Cursor Admin API calls. Existing archived Cursor records are preserved. |
| `MEOW_REFRESH_LIMITS=0` | Skips the session runner's optional provider-limit refresh. |
| **Last sync succeeded** | The last collection and artifact verification completed. Check individual source coverage too. |
| **Local sync disconnected** | Collection status is unavailable. **Reload data** reloads the dashboard; it does not collect sessions. |
| **Sample data** | The displayed records are examples, not local session history. |
| **Preview sessions / Preview sources** | Counts from the bounded preview, not the full archive or the number of sources newly collected. |
The sync drawer reports each source as collected, excluded, unavailable or collected with gaps. Aider requires configured `AIDER_PROJECTS`; a missing harness is not represented by invented sessions. Antigravity reports recovered database steps and unreadable or unknown coverage. Full archive totals and the newest-session source breakdown label their different scopes.
For an operator-managed five-minute macOS refresh, a LaunchAgent can run `node sync/sync-runner.mjs --no-limits` with `StartInterval=300`, `Umask=63` (decimal for `077`), and the same environment settings. The repaired local installation uses `com.meowops.harness-sync`; this job is separate from the daily operator installed by `npm run agents:install`. A successful job normally shows `not running` between scheduled runs, with exit code `0`; its run count should increase. Automatic collection requires the Mac to be awake and the user logged in.
The hosted site remains a sample-data shell for session analytics. It can show redacted local sync status, start a local sync, and show repository activity from GitHub. Adding its origin to `MEOW_DASHBOARD_ORIGIN` does not grant access to private history. Session counts, token estimates and elapsed thread duration do not by themselves verify a vendor invoice or active working time.
### How Sessions Are Classified
Every session is auto-tagged by tool usage profile:
| Type | Trigger | Meaning |
|---|---|---|
| 🏗️ Builder | >40% Write + Edit | Heavy coding/writing |
| 🔍 Detective | >50% Read + Grep + Glob | Code exploration |
| 💻 Commander | >40% Bash | Shell/system work |
| 📐 Architect | >20% Agent + PlanMode | Planning/orchestration |
| 🛡️ Guardian | Top tool is Grep/Read | Audits and reviews |
| 📝 Storyteller | Top tool is Write | Docs and content |
| 👻 Ghost | <3 messages or no tools | Empty session |
### Supported Models
30+ models with accurate pricing:
| Family | Models |
|---|---|
| **Claude** | Opus 4, Sonnet 4.6, Sonnet 4.5, Haiku 4.5 |
| **OpenAI** | GPT-4o, GPT-4o-mini, GPT-5, o3, o4-mini |
| **DeepSeek** | V3, R1, R1-0528 |
| **Qwen** | Max, Plus, Turbo (Alibaba DashScope) |
| **Moonshot** | Kimi K2 |
| **Zhipu** | GLM-4, GLM-4-Flash (free) |
| **ByteDance** | Doubao-Pro |
| **xAI** | Grok-3, Grok-3-mini, Grok-2 |
| **Cohere** | Command R+, Command R |
| **Amazon** | Nova Pro, Nova Lite, Nova Micro |
| **Google** | Gemini 3 Pro, 3 Flash, 2.5 Pro, 2.5 Flash, 2.0 Flash, 1.5 Pro, 1.5 Flash |
| **Mistral** | Large, Small |
| **Perplexity** | Sonar Pro, Sonar |
| **Local** | Llama 3.3-70B (cost = $0) |
Unknown variants match by family fuzzy search.
---
## Sync Pipeline
`sync/export-local.mjs` is the source of truth for generated dashboard data.
It currently:
- Reads Claude Code JSONL files from `~/.claude/projects/`
- Reads Codex Desktop rollouts from `~/.codex/sessions/`
- Reads Google Antigravity transcripts and supplements missing steps from read-only local SQLite stores; reports coverage gaps and keeps undecoded usage unavailable
- Reads Hermes sessions and native usage fields from its local SQLite state
- Reads Cursor agent transcripts from `CURSOR_PROJECTS_DIR` or `~/.cursor/projects` (time/tools/project; usage not on disk)
- Optionally enriches Cursor usage from the official team Admin API when a Keychain credential or injected `CURSOR_ADMIN_API_KEY` is available
- Skips both Cursor scanning and API enrichment when `MEOW_SKIP_CURSOR=1`
- Optionally reads Aider project histories from `AIDER_PROJECTS`
- Deduplicates and classifies sessions, refines project names from `cwd`, calculates model cost, and sorts by latest activity
- Writes `sessions.json` and `cost-summary.json` under `MEOW_DATA_DIR`, or under `public/data/` when that override is unset
- Records per-source collection state and latest activity in the summary's `sourceHealth` field
- Strips `cwd`, chat titles, and first-user-message snippets from the exported sessions payload
- Keeps `sync/upload-to-supabase.mjs` and `sync/full-sync.mjs` as optional advanced workflows, not the default analytics path
The default invocation writes local files only. The former `--push` path is retired; passing it prints a warning and never commits or uploads session data.
Useful commands:
```bash
node sync/export-local.mjs
npm run audit:ingestion
npm run evidence:status
node sync/fetch-claude-limits.mjs
```
`audit:ingestion` compares aggregate local-source counts, the private archive, and registered-project coverage without writing or printing session content. It never calls the Cursor Admin API. Both audit and evidence-status commands load this repository's `.env` so they use the configured private stores.
---
## Hosted demo shell (session data stays local)
This is optional. The default Meow Ops setup is local-only.
> **Privacy boundary:** the localhost helper restricts session history, raw summaries, sync, Loop Engineering, Loop Ops, and SuperAdmin usage routes to local dashboard origins. Hosted session, cost, and usage endpoints serve synthetic fixtures; the Privacy section records the latest live check. The hosted shell may read redacted sync status and start a local sync run; nothing else. Open Meow Ops on localhost to view private session data or use local operations.
### 1. Install one daily local cycle
```bash
npm run agents:install
```
At 08:30 local time, `sync/daily-operator.mjs` runs one bounded flow:
1. Export and verify local session artifacts.
2. Refresh usage limits as an optional, timeout-bounded phase.
3. Run intake, automation health, deterministic proposal rules, and the digest.
4. Allow at most one DeepSeek enrichment call.
5. Write a local operator nudge under `~/.meow-ops/runtime/`.
### 2. Keep repository review running while the laptop is off
`.github/workflows/daily-operator.yml` runs at `03:00 UTC` (`08:30 IST`) and can also be started with **Run workflow**. It executes the cloud-safe review gates, writes the result to the GitHub Actions job summary, and uploads a 30-day `meow-ops-daily-<run-id>` artifact containing only check IDs, exit codes, commit metadata, and a deterministic nudge.
The GitHub-hosted runner cannot read `~/.claude`, `~/.codex`, the local ledger, LaunchAgents, or `127.0.0.1:7337`. Its report therefore marks local session sync as `deferred`; the macOS daily operator catches up private session data the next time the laptop is online. This split keeps the repository monitored every day without uploading personal session data.
Run the same cloud-safe review locally with:
```bash
npm run daily:cloud
```
### 3. Optional: run the localhost helper for a local dashboard
The helper serves private local data and operational APIs only to a local dashboard origin. It never sends session data to the hosted `vercel.app` shell. That shell may read a redacted sync status and start a local sync run; Chrome asks once for local network access the first time you press **Connect**. Other workflow actions stay local.
`npm run agents:install` installs this service together with the daily job. To run only in the current terminal, use `node sync/local-api.mjs`.
**Automatic capture.** The helper watches every agent's session folder (Claude Code, Codex, Cursor, Hermes, Antigravity, plus any `MEOW_EXTRA_WATCH_ROOTS`, colon-separated) and starts a sync once new writes settle: 45 s after the last write, at most every 90 s, and within 3 minutes during a live session. The 5-minute `com.meowops.harness-sync` job remains the backstop. Disable with `MEOW_WATCH_SOURCES=0`. Each sync compares transcript files written this week with sessions collected; a large shortfall marks the source "collected with gaps" in the drawer instead of failing silently.
**Dock app.** `npm run dock:install` creates `~/Applications/Meow Ops Local.app` and pins it to the Dock. Clicking it starts the helper and dashboard services if they are down, requests a fresh sync, and opens `http://127.0.0.1:4273` in its own Chrome app window. Launcher log: `~/Library/Logs/meow-ops/dock-launcher.log`.
When the helper is unavailable, a local dashboard can use its bundled demo fallback. The hosted shell uses static demo endpoints; see the Privacy section for the verified state of the current deployment.
### 4. Optional: deploy the static shell to Vercel
```bash
npx vercel --prod
```
That deploy publishes the UI shell only. Session analytics remain local unless you intentionally rewire the app to use a remote store.
### 5. Install to dock
1. Open your Vercel URL in Chrome
2. Address bar → install icon (⊕)
3. Right-click dock icon → Options → Keep in Dock
### 6. Sanctum (Supabase Realtime, optional)
Run the migration to enable live agent pipeline visualization:
```bash
# In Supabase SQL editor:
-- Run db/migrations/0003_scrying_sanctum.sql
```
This creates `ss_pipelines`, `ss_nodes`, `ss_edges`, `ss_runestones` with multi-tenant RLS and enables Realtime publication. Without this, Sanctum runs in demo mode automatically.
---
## Architecture
```
Local machine Hosted demo shell (optional)
───────────── ───────────────────────
~/.claude/projects/ ~/.codex/sessions/
├── <session>.jsonl └── <session>.jsonl
└── subagents/
└── agent-*.jsonl
│
▼
sync/export-local.mjs
(parse · dedupe · classify · cost-calculate)
│
├──── public/data/sessions.json
│ │
│ ├──── localhost:5173 / preview
│ └──── sync/local-api.mjs ──► local dashboard only
│
└──── daily operator: sync → verify → review → nudge
PWA on dock ──► vercel.app ──── reviewed synthetic demo endpoints
React 19 + Vite 8 + Recharts + D3 + AG Grid
Three.js Sanctum scene (WebGL)
Supabase Realtime (Sanctum, opt-in)
```
**No hosted backend. No server-side rendering.** The default setup is a static bundle plus local JSON exports. Supabase Realtime is opt-in for the Sanctum pipeline visualizer only. `sync/upload-to-supabase.mjs` remains available for intentionally operator-managed storage, but it is not part of the default session analytics path.
---
## Tech Stack
| Layer | Tech |
|---|---|
| Frontend | React 19 + Vite 8 |
| Sanctum 3D | Three.js + React Three Fiber |
| Charts | Recharts |
| Pipeline visualizer | D3 (zoom/pan/SVG) |
| Styling | Tailwind CSS 4 + OKLCH design tokens |
| Data grid | AG-Grid (session table) |
| Storage | Local JSON exports by default |
| Realtime | Supabase Realtime (Sanctum, opt-in) |
| Hosting | Vercel (or any static host) |
| Sync | Node.js ESM scripts |
| Local helper | localhost sync API on port `7337` |
---
## Testing
Playwright runs browser tests against both the production preview and the Vite development server:
```bash
npm run build # build dist/
npx playwright test # runs the Playwright suite against npm run preview
```
The `chromium` project checks the built app on port `4275`; `dev-smoke` checks development-only server boundaries on `5176`; `service-worker` checks production privacy behavior. Some Loop Ops tests skip when their local-only workbook/spec fixtures are absent. The sync suite covers parsers, local API boundaries, intake redaction, loop ledger transitions, proposals, digests, execution gates, and SuperAdmin snapshots. Sanctum-specific unit tests run with `npm run test:sanctum`.
`npm run eval` is the blocking privacy + loop-integrity gate.
The Sanctum performance check reports browser `requestAnimationFrame` timing and Chromium compositor-to-presentation intervals. Its strict gate keeps the 16.7 ms presentation-interval p95 budget, requires at least 58 browser callbacks per second with no browser callback gap over 25 ms, and rejects known software WebGL renderers such as SwiftShader. Trace-reported long presentation intervals remain diagnostic because Chromium notes that macOS presentation timestamps may be estimates.
Run the eight-session roster gate in a headed browser on the target desktop GPU:
```bash
SANCTUM_PERF_HEADFUL=1 SANCTUM_PERF_CPU_RATE=1 SANCTUM_PERF_TRACE=1 SANCTUM_PERF_ENFORCE=1 npx playwright test --grep "linked synthetic sessions under configured CPU profile"
```
Run the guide gate with LOW preset and reduced motion:
```bash
SANCTUM_PERF_HEADFUL=1 SANCTUM_PERF_CPU_RATE=4 SANCTUM_PERF_GUIDE=1 SANCTUM_PERF_PRESET=LOW SANCTUM_PERF_START_REDUCED_MOTION=1 SANCTUM_PERF_ENFORCE=1 npx playwright test --grep "eight-session guide with muted local speech stays responsive"
```
The guide test sets system speech volume to zero and does not verify acoustic quality or Voicebox generation. On 2026-09-29, the headed LOW-preset guide gate passed on Apple M4 at 4× CPU throttling after removing the modal backdrop blur and pausing the guide render loop when reduced motion is active without aligned speech audio: 60 browser callbacks per second, 17.6 ms callback p95, 16.67 ms compositor presentation p95, no callback gaps over 25 ms, and zero page errors. Voicebox was unavailable during this test, and the desktop result does not establish low-tier-device performance; verify on the named target device before release.
A local Voicebox smoke check on 2026-09-29 generated a valid 5.22-second mono 24 kHz, 16-bit WAV from synthetic text and returned 26 validated mouth cues. The audio is in Voicebox local history and has not been auditioned, so acoustic quality remains unverified.
To run a single test file or test by name:
```bash
npx playwright test --grep "Sanctum"
npx playwright test --reporter=list
```
---
## Privacy
- **Private sessions and operations stay local.** Local dashboards read session analytics from local files or the localhost helper. Hosted dashboards cannot read private session-history, raw summary, sync run detail, Loop Engineering, Loop Ops, or SuperAdmin usage helper routes. They may read a redacted sync status and trigger a local sync run (opt out with `MEOW_HOSTED_SYNC=0`).
- **Repository activity on the hosted shell comes from GitHub.** `/api/github-activity` (a Vercel function) reads commits and workflow runs for `MEOW_ACTIVITY_REPOS` (default `merak3i/meow-ops`). An optional `GITHUB_TOKEN` raises rate limits and allows private repos; private repos return timing and status only, with no messages, authors, workflow names or links.
- **Current source builds exclude private session files.** The build allowlist publishes only the three reviewed `demo-*` JSON files and validates their synthetic labels and fields.
- **The current hosted fixtures were checked on 2026-09-29.** All five public session, cost, and usage JSON URLs matched the approved synthetic fixture hashes and returned `Cache-Control: no-store`. Requests with `Origin: https://example.com` received no `Access-Control-Allow-Origin` header. This confirms only the bytes and headers served at those URLs during the check; it does not verify historical cache contents, prior deployments, or a cache purge. Verify the served fixtures again after each hosted release.
- **Verify each hosted release.** A clean local build does not prove which fixtures or access settings the current Vercel deployment serves.
- **Current source session fixtures contain metrics only** — token counts, tool counts, durations, model names, and project labels. They omit message content, prompts, first-user-message snippets, chat titles, code, and absolute `cwd` paths. The build allowlist rejects private session files.
- **Supabase is optional and scoped.** The default app no longer depends on Supabase Storage for session analytics. Supabase Realtime remains opt-in for Sanctum.
- **Service key is local-only.** It never appears in the production bundle.
- **Hosted demo prompt is optional and cosmetic.** Vite exposes `VITE_` variables in client code ([Vite environment-variable docs](https://vite.dev/guide/env-and-mode#env-variables)), so `VITE_ACCESS_PASSWORD` can be recovered or bypassed; never rely on it to protect private data. Deploy only reviewed public demo assets unless hosting-level access control is configured.
- **Optional model enrichment is bounded.** Transcript/screenshot intake uses a localhost-only LM Studio endpoint when configured. DeepSeek is optional, with per-process and weekly spend caps. Deterministic answers remain available without it.
- **AutoSwarm receipts stay local.** `/swarm/status` is local-dashboard-only and read-only. It returns redacted prompt snippets and counts, never call output, and the personal roster in `~/.meow-ops/swarm.json` stays outside the repository.
- **No third-party product analytics or tracking scripts are configured in the app.**
---
## Roadmap
### Token Value Index _(designed, not yet built)_
Link sessions to git commits and measure what shipped:
- Lines of code merged per $1 spent
- Successful sessions (committed output) vs. ghost sessions (nothing landed)
- Project ROI: which codebases generate the most value per token
- Model comparison: which model gets you to commit fastest
### Hermes local and routed models
Hermes Agent sessions are imported from `~/.hermes/state.db` without modifying the database. When the installed Hermes version provides `session_model_usage`, Meow Ops also exports its exact per-model and provider breakdown. This includes local models invoked through Hermes (for example, an Ollama provider) and routed cloud models (for example, OpenRouter) without guessing from the currently selected model.
Ollama and LM Studio model inventories are not treated as session usage. They show what is installed or running, not which agent session used it. Direct calls outside Hermes need an invocation receipt or a durable official history source before they can be assigned to Meow Ops sessions. Unknown tokens and cost remain unavailable; local cost is never estimated from electricity usage. The Local Usage Receipt v1 contract and evidence matrix live in `docs/adr/0001-local-usage-receipt-v1.md`.
### Gemini CLI + OpenRouter parsers _(planned)_
Parsers for additional AI tools:
- `sync/parse-gemini.mjs` — Gemini CLI session logs
- `sync/parse-openrouter.mjs` — unified cost across all OpenRouter models
- Direct Ollama/LM Studio receipts — local model calls, once an agent exposes durable session-linked records
### Sanctum enhancements _(planned)_
- Supabase integration guide for connecting your own multi-agent pipelines
- WebSocket bridge for non-Supabase backends
- Node clustering for large pipelines (10+ agents)
- Replay mode: scrub through a completed pipeline run
---
## Contributing
Every feature on the roadmap is an open issue. The highest-impact contributions:
**New model parsers**
- `sync/parse-gemini.mjs` — Gemini CLI
- `sync/parse-openrouter.mjs` — OpenRouter unified
**Analytics**
- Token value index (link sessions to git commits)
- Session quality score (output tokens / ghost ratio / tool diversity)
- Model comparison view (same project, different models, side-by-side cost)
**Sanctum**
- Live replay mode (replay a session's agent operations at 10× speed)
- Cross-run comparison (trend lines: are your runs getting cheaper?)
- Sanctum zoom/pan canvas (true spatial layout, not horizontal scroll)
PRs welcome. Open an issue first for anything substantial.
---
## Project Structure
```
meow-ops/
├── public/
│ ├── manifest.json PWA manifest
│ ├── sw.js Service worker (network-first)
│ └── data/ Generated by export-local.mjs
│ ├── sessions.json Bounded browser compatibility preview (latest 1,000 by default)
│ └── cost-summary.json Today/week/month/year spend buckets
├── db/
│ └── migrations/
│ ├── 0001_initial_schema.sql Core sessions schema
│ ├── 0002_daily_summaries.sql Daily aggregates table
│ ├── 0003_scrying_sanctum.sql Pipeline viz schema + RLS + Realtime
│ └── 0004_rls_tenant_isolation.sql Strict per-tenant SELECT (no NULL-tenant world read)
├── e2e/
│ └── meow-ops.spec.ts Playwright e2e tests
├── src/
│ ├── analytics/ Velocity, efficiency, burn-rate, profile modules
│ ├── components/ Charts, session table, nav, password gate, date filter
│ ├── lib/
│ │ ├── agent-tree.ts Forest builder, efficiency index, cache hit rate
│ │ └── practice-map.ts Learn concepts inferred from sessions
│ ├── pages/
│ │ ├── Overview.jsx Today · Summary
│ │ ├── Sessions.jsx Today · Sessions
│ │ ├── AgentVisualizer.tsx Today · Runs
│ │ ├── LoopReview.tsx Review · Inbox
│ │ ├── ProjectControl.tsx Review · Projects
│ │ ├── LoopOps.tsx Review · Map
│ │ ├── CostTracker.jsx Ledger
│ │ ├── ScryingSanctum.tsx Sanctum
│ │ ├── LearningQuest.tsx Learn
│ │ └── CapacityUsage.jsx Hidden Seats page
│ ├── sanctum/ 3D archive scene, session roster, local guide
│ │ ├── ArchiveWarden.tsx Data-bound archive figure and signature seal
│ │ ├── Sun.tsx Engraved LLM Sun and live token stream
│ │ ├── SanctumGuide.tsx Local concept and session-evidence guide
│ │ ├── GuideCharacter.tsx Guide portrait and speech animation
│ │ ├── classes.ts Session-class colors, profiles, and quotes
│ │ └── textures.ts Procedural floor and original cat sprites
│ └── types/
│ └── session.ts Single source of truth for all session types
├── sync/
│ ├── parse-session.mjs JSONL parser with agent hierarchy extraction
│ ├── parse-codex.mjs OpenAI Codex Desktop parser
│ ├── parse-cursor.mjs Cursor agent-transcript parser (parent + subagent)
│ ├── cursor-admin-usage.mjs Optional official Admin API usage enricher
│ ├── parse-aider.mjs Aider chat history parser
│ ├── parse-antigravity.mjs Google Antigravity transcript parser (time/tools; usage not exposed)
│ ├── session-utils.mjs Shared snippet/project/default-session helpers
│ ├── cost-calculator.mjs 30+ model pricing with fuzzy matching
│ ├── export-local.mjs All sources → sessions.json + cost-summary.json
│ ├── sync-runner.mjs Single-flight observable sync state machine
│ ├── fetch-claude-limits.mjs Refresh local rate-limits.json from explicit env values
│ ├── upload-to-supabase.mjs Optional advanced Storage upload script
│ ├── full-sync.mjs export + upload in one shot
│ ├── local-api.mjs localhost sync, loop, intake, and execution API
│ ├── intake-*.mjs Local transcript, log, and screenshot intake
│ ├── loop-capture.mjs Capture runs and session evidence
│ ├── loop-propose.mjs Deterministic proposal generation and miners
│ ├── loop-simulate.mjs Proposal simulation
│ ├── loop-execute.mjs Gated worktree execution and optional PR creation
│ ├── loop-digest.mjs Daily digest and digest history
│ ├── daily-operator.mjs One daily sync, review, AI-cap, and nudge cycle
│ ├── cloud-daily-review.mjs GitHub-safe repository review and artifact report
│ ├── install-macos-agents.mjs Render and activate current-path LaunchAgents
│ ├── loop-ledger.mjs Redacted append-only local ledger
│ ├── com.meowops.localapi.plist Persistent localhost-helper template
│ └── com.meowops.daily-digest.plist Single daily operator template
├── menubar/
│ ├── MeowOpsBar.swift macOS menu bar source
│ └── build.sh Build script for MeowOpsBar.app
├── playwright.config.ts Playwright configuration
├── .github/workflows/
│ ├── ci.yml Push and pull-request verification
│ └── daily-operator.yml 03:00 UTC repository review schedule
└── .env.example
```
---
## License
MIT. Build with it, fork it, ship it. Keeping derivative tools open source is encouraged, but not required by the license.
---
## Credits
3D fur rendering inspired by Kajiya-Kay shading models. Focus timer mechanics inspired by [Forest: Stay Focused](https://www.forestapp.cc). Visual design language inspired by [ElevenLabs](https://elevenlabs.io). Built with [Claude Code](https://claude.com/claude-code).
---
*Meow Operations is a community tool. It has no business model, no venture funding, and no plans to acquire either. It exists because developers deserve to understand their AI spend — and because the interface for that understanding should feel like something you want to open.*