Skip to content

xmartlabs/gnomon

Repository files navigation

gnomon

A local builder-profiler for AI-assisted coding. Reads your agent transcripts on-device and grades how you build (gstack) and how well you operate agents (Agentic Quotient). Everything runs locally — nothing leaves your machine. For AI-powered analysis and to track your evolution over time, run the separate opt-in command uvx xl-ai-insights.

gnomon (γνώμων): the part of a sundial that casts the shadow — "the one that knows/judges." It measures by what you cast.

Fork of paxel-local (by Max Schilling, original LICENSE retained), with two additions:

  1. Agentic Quotient (AQ) — a 4-pillar score for how well you operate agents (separate from the gstack build scorecard).
  2. Codex parser fix — drops injected wrappers (environment_context, AGENTS.md, the whats 2+2? boot probe) and empty seed-sessions that were inflating counts.

Quick start

No dependencies — Python 3 stdlib only.

# Option A: run directly (no install)
python3 <(curl -sL https://raw.githubusercontent.com/xmartlabs/gnomon/main/paxel.py)

# Option B: clone the repo
git clone https://github.com/xmartlabs/gnomon
cd gnomon
python3 paxel.py

Both read all detected local transcripts (Claude, Codex, Gemini, Cursor, …) and open your profile. Option A writes outputs to the current directory; Option B writes them to the repo directory.

Restrict to one or more sources:

python3 paxel.py claude            # Claude Code only
python3 paxel.py claude codex      # Claude Code + Codex
python3 paxel.py --no-open         # don't auto-open profile.html (headless / CI)
python3 paxel.py --summary         # also write summary.json to disk

python3 paxel.py is 100% local — no network, nothing leaves your machine.

Sharing your profile (opt-in)

To upload summary.json and view your evolution over time, run the separate xl-ai-insights command:

# Until published to PyPI — run directly from the repo
uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights

# Once published to PyPI
uvx xl-ai-insights

# Alternative with pipx
pipx run xl-ai-insights

It accepts the same source arguments as paxel.py:

uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights claude
uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights --no-open
uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights --output-dir=.
uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights --output-dir=./debug-artifacts
uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights --window=3
uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights --help

Each scored point is computed over a trailing window of --window=N calendar months (default 6) ending at its anchor month, so a single weak month doesn't tank the score. --window=1 scores each month on its own. The window applies to normal monthly runs and to --backfill/--init.

What happens when you run it:

  1. Runs paxel.py locally (bundled) to compute your metrics.
  2. Opens your browser to mirdash for a one-time browser login (loopback callback on 127.0.0.1:8799).
  3. Uploads summary.json (see below) — associated with your account via the login session.
  4. Opens your report page in the browser.

By default, xl-ai-insights writes paxel outputs to a temporary directory and keeps that directory after the run finishes. This applies to normal monthly runs, --backfill, and --init on macOS, Linux, and Windows. The command prints the temp path unless you pass --quiet. If you want the final files in a specific location, pass --output-dir=PATH (for example --output-dir=. to write into the current directory). Existing files with the same names are overwritten in that destination. The artifacts may include narrative_input.md, which contains local transcript excerpts; don't upload or share it.

If the browser can't open (headless/CI) or the auth times out (120 s), the command prints a warning and exits cleanly — nothing is uploaded. If you don't want to share at all, just don't run it.

What is uploaded — exactly. xl-ai-insights uploads the same summary.json that python3 paxel.py --summary writes to disk:

  • context — date range, list of detected sources, total session count
  • planning_ratio_explore_to_doing
  • errors — error recovery ratio + error rate per 100 tool calls
  • iteration_depth — mean, median, p90, max, files hammered >15×
  • churn — git churn total + tool-authored churn (Edit/Write)
  • orchestration — fanout median + delegate action count
  • compounding_writes
  • ecosystem — distinct skills, total skill uses, distinct MCP servers
  • progression_monthly — per-month counts (prompts, tools, sessions, active days, tool churn lines) plus the names of AI models used that month (top model + per-model turn counts, up to 3 models per month)
  • profile — computed AQ/archetype/scorecard block used by the report UI
  • noticed_stats — share-safe evidence used by the local "What we noticed" cards: counts and derived metrics for shipping, iteration, errors, models, rhythm, prompt lengths, agents, sessions, and top tools

What is NOT uploaded: prompts, verbatim quotes, project names, file paths, narrative_input.md contents, and stats.json. The mirdash server associates the upload with your account via your login token; xl-ai-insights itself sends no email or PII. Note: model names (e.g. claude-opus-4, gpt-5.4) are included in progression_monthly, profile, and noticed_stats.

Overriding the mirdash URL

For xl-ai-insights, precedence (first match wins):

Method How
CLI flag --mirdash-base=https://your-server.example.com
Env var GNOMON_MIRDASH_BASE=https://your-server.example.com
Config file ~/.config/gnomon/config.json with {"mirdash_base": "https://your-server.example.com"}
Default https://mirdash.xmartlabs.com

The config file is optional and only needed to override the default. It lives outside the repo (~/.config/gnomon/) so it won't be committed.

# Dev / self-hosted override
uvx --from git+https://github.com/xmartlabs/gnomon xl-ai-insights --mirdash-base=http://localhost:3000

Scope to a time window (for monthly / quarterly check-ins):

python3 paxel.py --last=30d --summary              # rolling last month
python3 paxel.py --last=90d                        # rolling last quarter (also Nw / Nm)
python3 paxel.py --since=2026-03-01 --until=2026-05-31   # explicit window (until-day inclusive)

Everything follows the window — including git churn, whose git log --since/--until range tracks the kept events. Events without a timestamp are dropped in windowed runs (they can't honor "this period only"); that includes Cursor JSONL-only sessions beyond their single file-mtime timestamp.

Sandbox / self-hosted / copied histories

Histories don't have to live in their default home-dir locations. gnomon honors the same env vars the CLIs use (CLAUDE_CONFIG_DIR, CODEX_HOME) and accepts explicit dir overrides — handy for transcripts mounted or scp'd from a sandbox, devcontainer, or remote box:

python3 paxel.py --claude-dir=/mnt/sandbox-home/.claude     # root or .../projects both work
python3 paxel.py --codex-dir=~/backups/codex                # root or .../sessions both work
# also: --gemini-dir, --pi-dir, --opencode-dir

Outputs (written to the repo dir, git-ignored)

File What
profile.html Branded, shareable profile — scorecard + AQ + signature moves
report.md Human-readable stats
stats.json Machine-readable metrics (incl. the full agentic block)
summary.json (--summary) Shareable subset: the 8 measured high-signal metrics + progression_monthly + computed profile + noticed_stats blocks — no prompts or verbatim quotes. Built for the low-cost feedback loop
narrative_input.md Curated excerpts for an optional LLM narrative pass

These outputs contain your transcript-derived data. They're in .gitignore — don't commit them.


Sources

Auto-detected from their default local locations:

Source Location Notes
Claude Code ~/.claude/projects/**/*.jsonl Fullest signal coverage
Codex CLI (OpenAI/GPT) ~/.codex/**/*.jsonl Injected wrappers + seed-sessions filtered; model read from turn_context; SKILL.md shell-reads counted as skill usage
Gemini CLI ~/.gemini/**/*.json
Others (PI, opencode) per-tool dirs parsed where present
Cursor state.vscdb + ~/.cursor/projects/.../agent-transcripts full (SQLite-first + JSONL, deduped)
Google Antigravity state.vscdb (protobuf) detected; conversation count + date range surfaced as metadata. Transcripts live server-side, so it can't be scored honestly

What it measures

Two independent questions — the report frames both:

1. gstack scorecard — how you build

Three 0–10 axes (Execution / Planning / Engineering) grounded in gstack and a described steering style. Counts are measured; scores are a transparent rubric. Axes unchanged from upstream.

2. Agentic Quotient (AQ) — how you operate agents

0–100, four pillars (each shown with its sub-axes):

Pillar Weight Sub-axes
Breadth 30 Orchestration · Skill fluency · Tool command (MCP+CLI) · Discipline
Craft 35 Verification · Grounding · Compounding
Efficiency 20 Steering leverage (sweet-spot) · Recovery
Savvy 15 Model mix · Token economy

Level (one honest ladder, driven by AQ — no flattery at the floor): Novice <25 · Apprentice 25–45 · Adequate 45–60 · Proficient 60–75 · Advanced 75–88 · Elite 88–100. This is also the profile headline; the quote names your thinnest pillar so the gap is visible.

Orchestration now reads coordination (median agents coordinated per orchestrating session), not raw dispatch volume — a serial grinder firing one agent at a time can't max it.

MCP vs CLI and Tool diversity are described, not graded (like steering — no better/worse end). CLI-first is treated as token-efficient, not a gap.


Cross-model fairness — read this

gnomon is multi-source, and metrics are provider-agnostic where possible:

  • Provider-agnostic: git churn, MCP/CLI tool command, grounding, recovery, steering leverage, compounding (matches CLAUDE.md / AGENTS.md / GEMINI.md / memory/ / docs/adr), and Model mix (rewards using >1 model and routing work off your default — no hard-coded model names).
  • Codex parity fixes: the active model is read from Codex's turn_context (so GPT usage shows up in Model mix instead of reading as model-less), update_plan counts as planning (TodoWrite), and shell reads of skills/<name>/SKILL.md count as skill usage — Codex has no first-class Skill tool, so that's how skills are actually consumed there.
  • Claude-Code-specific signals (still under-read for Codex/Gemini): attributionSkill precision and ToolSearch (part of Token economy). These reflect Claude Code's ecosystem, not universal capability.

Bottom line: scores are most complete for Claude Code. Codex/Gemini profiles are valid but slightly under-read on ToolSearch-style sub-axes. We surface this rather than hide it.


Monthly progression

stats.json["progression"]["monthly"], a Progression section in report.md, and a Your trajectory chart in profile.html: per-month prompts, tool calls, sessions, active days, tool-authored churn, and top model. When a plan's monthly limits cap any single month's volume, the month-over-month slope is the honest signal — not lifetime totals.


Tests

python3 -m unittest discover -s tests -v

Covers the CLI extractor, Codex injected-message filter, compounding-path matcher, and the full compute_aq pillar math.


Privacy

All analysis runs on-device. For accurate code-churn it shells out to your local git (git log --numstat) on the repos it finds. python3 paxel.py makes zero network calls — nothing leaves your machine.

If you run uvx xl-ai-insights, it makes one outbound network call: a POST of summary.json (described above under "What is uploaded") to mirdash after you authenticate. No prompts, no quotes, no project names are ever sent. Running xl-ai-insights is entirely opt-in.

Cursor specifics

No special run needed. Cursor is auto-detected like every other source — python3 paxel.py includes it, python3 paxel.py cursor restricts to it. You don't need to close Cursor first: the SQLite store is opened read-only (mode=ro), nothing is written to it.

Where it reads from (two stores, merged and deduped):

Store Default location Carries
state.vscdb (SQLite) macOS ~/Library/Application Support/Cursor/User/globalStorage/ · Linux ~/.config/Cursor/User/globalStorage/ · Windows %APPDATA%\Cursor\User\globalStorage\ Event stream: per-event timestamps, tool error statuses
agent-transcripts JSONL ~/.cursor/projects/**/agent-transcripts/ Full tool inputs (edit old/new strings → churn), workspace path, subagent sidechains

The same modern session exists in both with complementary data, so gnomon prefers the SQLite copy and backfills workspace path + edit churn from its JSONL twin. JSONL-only sessions (and subagent sidechains, which exist only as JSONL) are kept as-is.

Overrides: --cursor-dir=PATH points at a copied/mounted projects dir (root or the projects subdir both work). The state.vscdb path is fixed per platform — there's no flag for it, so DB-backed sessions are only read from the local Cursor install.

Known caveats (from upstream): workspace slugs with dashes may mis-parse; JSONL-only sessions get a single file-mtime timestamp; ApplyPatch churn counts raw patch lines (slight over-estimate).

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages