npm.io
0.1.0 • Published 3d agoCLI

polymath-analyzer

Licence
UNLICENSED
Version
0.1.0
Deps
0
Size
250 kB
Vulns
0
Weekly
0

polymath-analyzer

Proprietary — All Rights Reserved. Published for distribution only; see LICENSE. No use, copying, or redistribution without written permission.

A local app for understanding how you work from your own Claude Code and Codex agent logs — parallelism, flow-state, throughput, per-project contributions, and (opt-in) AI-graded judgment criteria.

Three things, in one package:

  1. Deterministic metrics — computed in-process from the JSONL on your disk. Zero LLM, zero network. Facts, not judgments. (Instant and free.)
  2. Local LLM grading (opt-in, --grade) — grades abstraction, taste, delegation, expertise, and frontier tool-use by invoking the local claude -p / codex binary you're already logged into. No API key; it spends your own subscription. Your prompts never leave the machine.
  3. A local web UI (serve) — the same warm "Code tab" view the hosted Polymath app shows, served on a free port, with per-section Share.

What it reads

  • ~/.claude/projects/**/*.jsonl — Claude Code sessions
  • ~/.codex/sessions/**/*.jsonl — Codex sessions

(Override with CLAUDE_PROJECTS_DIR / CODEX_SESSIONS_DIR env vars.)

It separates your real interactive sessions from headless agent runs, auth-ping sessions, and machine-assembled mega-prompts (the klass / klassReason on every parsed session is transparent and auditable), de-dupes resume/fork copies, and then computes everything below over the canonical set.

What it computes

Metric What it is
sessions per-session structural facts: duration, active time, turns, tool counts, models, tokens, modes, renames
parallelism sweep-line concurrency across sessions — max concurrent, minutes spent at each level, per-day overlap, top windows
day-chart per-day Gantt of every conversation's active spans (clipped to local midnight)
flow every 10-minute slot classified in-flow / active-pause / away (rapid rhythm + dictation beat the gaps)
throughput words you actually typed per day (pasted code stripped) → a 1–11 score, plus a proven ceiling
projects GitHub-style per-project days / hours / range
aggregate average throughput over substantial days + flow stats (work window, % in flow, when flow peaks)

Install

npm install polymath-analyzer
# or run the CLI without installing:
npx coding-analyzer

CLI

coding-analyzer                 # human-readable summary of the deterministic profile
coding-analyzer --json          # full profile JSON to stdout
coding-analyzer --out p.json    # write full profile JSON to a file

coding-analyzer projects        # just the projects/contributions view (JSON)
coding-analyzer flow            # just the per-day flow slots + totals (JSON)
coding-analyzer aggregate       # just the corpus aggregate (JSON)

coding-analyzer grade           # OPT-IN: AI-grade your sessions on your OWN
                                #   Claude Code / Codex login (no API key)
coding-analyzer serve           # scrape → (optionally --grade) → open the web UI
coding-analyzer serve --grade   # …including the AI-graded sections

Flags: --no-codex · --idle <min> · --tz <IANA> · --span <hours> · --json · --out <file> · --grade · --adapter <cli|codex> · --model <id> · --max-sessions <n> · --port <n> · --share-url <url> · --open

Example summary:

========== DETERMINISTIC CODING PROFILE ==========
timezone America/Los_Angeles · idle-gap 5m
sessions: 106 interactive (canonical)  ·  1101 agent/excluded  ·  19 resume-copies dropped
active work time: 147.5h   tool calls: 16,017   subagents: 41   queue-ahead: 2271

PARALLELISM
  max concurrent sessions: 6

THROUGHPUT
  proven ceiling (≥3 days): 10/11
  AVG over substantial days: 7/11 — 5960 words/day over 30/50 days

FLOW
  work window: 12:30pm–6:40pm  over 47 active days
  in flow: 63% of active time  ·  86.5h total (1.8h/day)

PROJECTS
  4 projects · 37 active days · 102h

The local app (serve)

coding-analyzer serve            # deterministic only — instant, free
coding-analyzer serve --grade    # + AI-graded sections (spends your subscription)
coding-analyzer serve --grade --open --port 8765

serve scrapes ~/.claude/projects + ~/.codex/sessions, optionally grades, starts a tiny zero-dependency HTTP server on a free port, and prints the clickable http://localhost:<port> URL. The page reproduces the hosted app's Code tab — the same warm palette, cards, contributions heatmap, flow stats, throughput scatter, and (when graded) the criterion cards with verbatim receipts.

The server exposes:

Route What
GET / the bundled static UI
GET /profile.json the computed profile, organized into the share-contract sections
GET /meta.json { version, shareUrl } for the share bar
POST /share proxies the chosen sections to the share endpoint (server-side — no browser CORS)
Local LLM grading (no API key)

Grading is opt-in because it spends your tokens; the deterministic metrics stay instant and free. With --grade, the package invokes the local claude -p (or codex) binary you're already logged into — billed to your Claude / ChatGPT subscription, never an API key. On startup it detects whether a backend is installed and logged in; if not, it prints the exact fix (e.g. run claude to log in) instead of failing obscurely, and serve falls back to deterministic-only.

coding-analyzer grade                       # grade + write graded.json, print means
coding-analyzer grade --adapter codex       # pin the Codex backend
coding-analyzer grade --max-sessions 20     # cap the token/time budget

The five graded criteria — abstraction, taste, delegation, expertise, frontier — are ported from the main Polymath pipeline (same rubric, 1–11 ladder with verbatim quotes, per-conversation ceiling 10). Grading plugs into the analyze() gradeProvider seam (see Library below).

Share — per-section selection

Every section in the UI has a checkbox; tick the ones you want and hit Share selected in the footer bar. The browser sends only the ticked keys to the local server, which forwards exactly this contract to the share endpoint:

POST <shareUrl>
Content-Type: application/json

{
  "source": "coding-analyzer",
  "version": "<pkg version>",
  "name": "<optional user-provided name>",
  "sharedAt": "<ISO8601>",
  "sections": { "<key>": <that section's data> }
}

key ∈ { howYouWork, snapshot, workstyle, parallelism, lixiThroughput, topDays, abstraction, taste, delegation, expertise, frontier, operate } — only ticked keys appear in sections. The endpoint defaults to http://localhost:3100/api/coding-share (dev) and is overridable with --share-url <url> or PS_SHARE_URL. TODO: set the production share URL before shipping. The local server does the POST server-side, so the browser never hits CORS.

How the bundled UI is produced / rebuilt

The UI sources live in ui/ (index.html, styles.css, app.js — vanilla JS, no React/Tailwind; the warm palette tokens and Card/Chip primitives are hand-ported to plain CSS). npm run build runs tsc then scripts/build-ui.mjs, which esbuild-bundles ui/app.jsdist/ui/app.js (minified, IIFE, zero runtime deps) and copies the HTML/CSS. The server serves dist/ui/, which ships inside the published package. To rebuild only the UI: npm run build:ui.

Library

import { analyze } from "polymath-analyzer";

const profile = await analyze({
  codex: true,            // include ~/.codex/sessions (default)
  idleGapMin: 5,          // gap that breaks a session into active intervals
  tz: "America/Los_Angeles",
  spanHourMin: 4,         // "substantial day" threshold for the throughput avg
});

console.log(profile.concurrency.maxConcurrency);
console.log(profile.throughput.ceiling);          // 1–11, proven on ≥3 days
console.log(profile.flow.totals.flowFraction);
console.log(profile.projects.projects);

Every building block is exported too, so you can compose your own pipeline:

import {
  readAllSessions, toSessionRecords, buildConcurrency, buildDayChart,
  markDuplicates, computeFlow, computeProjects, computeAggregate,
  proseWords, throughputScore, throughputCeiling,
} from "polymath-analyzer";

const raw = await readAllSessions();
const interactive = raw.filter((s) => s.klass === "interactive");
const records = toSessionRecords(interactive);
await markDuplicates(records);
const canon = records.filter((r) => !r.duplicateOf);
const conc = buildConcurrency(interactive, canon);
const flow = await computeFlow(canon);
// …
Local LLM grading via analyze()'s gradeProvider

analyze() is deterministic by default and never calls a model. The grading seam is the optional gradeProvider: pass one and analyze() attaches a graded block of per-criterion means. The package ships LocalGradeProvider, which implements that seam by grading on your local Claude Code / Codex CLI:

import { analyze, LocalGradeProvider } from "polymath-analyzer";

const provider = new LocalGradeProvider({
  adapter: "cli",       // or "codex"; omit for auto (first installed + logged-in)
  model: "sonnet",      // grading model (cli only; codex ignores it)
  maxSessions: 20,      // cap the token/time budget
});

const profile = await analyze({ gradeProvider: provider });
profile.graded;         // { abstraction: { mean, n }, taste: { mean, n }, … }
provider.detail();      // richer per-criterion distributions + per-session takes (UI)

Or supply your own gradeProvider (any object with criteriaMean(records)) to fold in grades from a different pipeline.

const profile = await analyze({
  gradeProvider: { criteriaMean: () => ({ taste: { mean: 8.2, n: 40 } }) },
});
Serving programmatically
import { analyze, LocalGradeProvider, buildWebProfile, startServer } from "polymath-analyzer";

const provider = new LocalGradeProvider();
const profile = await analyze({ gradeProvider: provider });
const web = buildWebProfile(profile, provider.detail());
const { url } = await startServer({ profile: web, version: "0.2.0", shareUrl: "https://…/api/coding-share" });
console.log(url);

Project aliases

A session's project is its working directory, so the same project shows up under several paths (parent-folder runs, old names). Pass aliases to canonicalize:

await analyze({
  aliases: [
    { name: "Polymath", match: ["/polymath-society", "/polymath"], note: "merged old name" },
  ],
});

Without aliases, the leaf directory name is used.

Configuration

Option (lib) Flag (CLI) Env Default
codex --no-codex true
idleGapMin --idle 5
tz --tz CODING_TZ America/Los_Angeles
spanHourMin --span 4
--port free OS-assigned
--share-url PS_SHARE_URL http://localhost:3100/api/coding-share
--adapter CODING_ANALYZER_ADAPTER auto (clicodex)
CLAUDE_BIN / CODEX_BIN resolved from PATH
CLAUDE_PROJECTS_DIR ~/.claude/projects
CODEX_SESSIONS_DIR ~/.codex/sessions

Privacy

On-device by default. The deterministic library reads local files and returns plain objects — no telemetry, no network, no model inference.

Grading (--grade) runs on the local CLI you're logged into (claude -p / codex exec) — your prompts are read from disk and graded on your own subscription; nothing is sent to a third party. The only outbound network call the package ever makes is POST /share, and only for the exact sections you tick, to the endpoint you configure.

Requirements

Node.js ≥ 18, ESM only. Grading additionally needs Claude Code or Codex installed and logged in (claude / codex login).

License

MIT