Skip to main content

Inspect Claude Code local transcripts, reminders, and hook-visible lifecycle data.

Project description

neo

neo

License: MIT Python Status MCP

Website · Dashboard surface · Security notes

Local workflow forensics for Claude Code. neo indexes text found on disk, transcripts, retained telemetry rows, hook-visible lifecycle events, and memory artifacts into a SQLite database, then exposes them through a dashboard and an MCP server running on the same machine.

What It Does

  • Reads local Claude Code artifacts directly from ~/.claude/ and ~/.neo/ instead of relying on export surfaces that strip evidence
  • Separates measured, estimated, and inferred claims so row counts stay distinct from heuristics and anomaly labels
  • Exposes a local operator surface through a browser dashboard, terminal commands, and an MCP server registered inside Claude Code
  • Makes local overhead visible by surfacing reminder injections, sidechains, compaction churn, retained telemetry rows, and other traces the UI does not foreground

Install

Recommended — pipx:

pipx install neo-harnesster
neo

With pip:

pip install neo-harnesster
neo

Directly from the repo:

git clone https://github.com/asuramaya/neo.git
cd neo
python3 neo.py

Any of the above runs setup + ingest + dashboard in one step and opens http://127.0.0.1:7777.

Restart Claude Code after the first run so the installed hooks begin capturing events.

CLI

neo                        # setup + ingest + dashboard
neo --setup                # install hooks + register MCP server
neo --ingest               # ingest data only
neo --dashboard            # dashboard only
neo --dashboard --no-open  # serve without opening a browser
neo --port 8888            # custom port
neo-tokens                 # data accounting in the terminal
neo-states diagram         # state machine diagram

Live event stream:

tail -f ~/.neo/harness_log.jsonl

MCP Surface

neo runs as a single shared daemon per host. That one process serves the dashboard and the MCP protocol over HTTP at http://127.0.0.1:7777/mcp, guarded by a per-host bearer token. neo --setup registers it in ~/.claude.json as an http MCP server (not a per-session stdio subprocess), so every Claude Code session connects to the same URL — one neo process per host, no per-session fan-out, no version skew. On the next session start, Claude Code connects automatically and the dashboard opens locally.

Setup also installs a systemd --user service (neo.service) that keeps the daemon running — auto-start on login, restart on crash. Where systemd --user is unavailable, start it manually with neo-mcp --http. See Daemon below.

The server exposes 12 tools:

Tool What it returns
status setup health + the live process registry across sessions (roles, code versions, version_skew flag)
summary top-level row counts across all tables
data_accounting full measured/estimated accounting
tokens_report compact accounting totals with the measured-vs-estimated basis
query_reminders reminder rows with file + line provenance
query_sessions session list across projects with agent / compaction counts
query_agents subagent genealogy and messages
query_probe_events raw hook events, filterable by type
query_telemetry retained telemetry rows
query_memory_files persistent memory files per project
state_model inferred state-model labels
correlations cross-signal patterns (hook timeline, totals by type)

Tools that read hook events filter out neo's own MCP traffic by default (include_self=false) so observer overhead does not contaminate the picture.

Daemon

neo --setup writes a systemd --user unit at ~/.config/systemd/user/neo.service and enables + starts it. The unit runs the shared daemon (neo-mcp --http), which owns the dashboard, periodic ingest, and the HTTP /mcp endpoint. If multiple neo processes start, they elect a single owner via a per-host lock; followers serve queries against the shared DB and take over if the owner dies.

systemctl --user status neo      # check the daemon
systemctl --user stop neo        # stop it
systemctl --user disable neo     # stop it auto-starting on login
neo-mcp --http                   # run the daemon manually (no systemd)

Evidence Model

neo labels its claims on purpose:

  • measured — reminder rows, sessions, agents, tasks, memory files, telemetry rows, hook events; hidden-context share, data multiplier, and API call counts when transcripts carry API-reported token usage
  • estimated — hidden-context share and data multiplier only when no token usage is recorded, where they fall back to on-disk transcript byte sizes
  • inferred — state-model labels and anomaly interpretation from local timing + lifecycle patterns

For exact billable token numbers, use /usage inside Claude Code. neo does not fabricate token totals.

What It Captures

Source Location What
session transcripts ~/.claude/projects/*.jsonl full conversations including system reminders
subagents and sidechains ~/.claude/projects/*/subagents/ spawned transcripts and context copies
compaction events ~/.neo/harness_log.jsonl PostCompact hook events from the probe
telemetry ~/.claude/telemetry/ telemetry rows currently retained on disk
memory files ~/.claude/projects/*/memory/ persistent context seeded by instances
tasks ~/.claude/tasks/ task state across sessions
hook events ~/.neo/harness_log.jsonl tool use, notifications, session lifecycle

What It Cannot Capture

  • server-side reasoning or model internals that never land on disk
  • the contents of any secondary channel that is not persisted locally
  • whether absent telemetry rows were uploaded, deleted, or never written locally
  • system prompt assembly inside the compiled binary
  • HTTPS request and response bodies without a proxy

Export Boundary

The /export command in Claude Code strips system reminders. The raw JSONL files in ~/.claude/projects/ retain them. neo reads the raw files.

That boundary is the whole reason the project exists.

Project Layout

src/neo/
  app.py            setup, ingest, threaded HTTP server, systemd daemon install
  mcp_server.py     HTTP MCP server (/mcp on the shared daemon); owner election + process registry
  db.py             SQLite ingest + query layer
  tokens.py         visible vs hidden channel accounting
  states.py         inferred state model + anomaly labels
  harness_probe.py  hook script copied into ~/.neo/ by setup
  dashboard.html    single-file local dashboard
neo.py              repo-clone launcher shim
test.py             smoke tests

All data lives in ~/.neo/neo.db. The dashboard binds to 127.0.0.1 only.

Hooks

neo installs async hooks for 20 Claude Code event types:

PreToolUse PostToolUse PostToolUseFailure Notification SessionStart SessionEnd Stop SubagentStart SubagentStop PreCompact PostCompact UserPromptSubmit InstructionsLoaded PermissionRequest PermissionDenied TaskCreated TaskCompleted FileChanged CwdChanged ConfigChange

Security

  • dashboard and /mcp endpoint bind to 127.0.0.1 only and validate local Host headers
  • the /mcp endpoint requires a per-host bearer token stored in ~/.neo/
  • POST /api/ingest requires a same-origin browser request
  • ~/.neo/ is created with private permissions where the OS allows it
  • neo does not transmit your data anywhere
  • hooks run async and do not block Claude Code operation
  • setup installs a systemd --user service (neo.service) that auto-starts the local daemon on login and restarts it on crash; remove it with systemctl --user disable --now neo (see Daemon)
  • no dependencies beyond Python stdlib

Requirements

  • Python 3.10+
  • Claude Code installed (~/.claude/settings.json must exist)

Upgrade Note

If you used the project under its old harnesster name, the first run of neo or python3 neo.py migrates ~/.harnesster/ to ~/.neo/ and renames harnesster.db to neo.db. Hook commands in settings.json are rewritten automatically.

The harnesster command remains as a forwarding shim.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

neo_harnesster-0.2.1.tar.gz (62.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

neo_harnesster-0.2.1-py3-none-any.whl (62.6 kB view details)

Uploaded Python 3

File details

Details for the file neo_harnesster-0.2.1.tar.gz.

File metadata

  • Download URL: neo_harnesster-0.2.1.tar.gz
  • Upload date:
  • Size: 62.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for neo_harnesster-0.2.1.tar.gz
Algorithm Hash digest
SHA256 2e6c4c0692ec4ca39bfb2fa4599fc7b1c573dade5fa7c332615b72465e580177
MD5 7c8a1ab7f9615a03add1f8456c5c3ecd
BLAKE2b-256 3b80cc9e14f7bb5db5f8ab5ceba6b43402bc0d61230ee25a2d0a787aa3e898ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for neo_harnesster-0.2.1.tar.gz:

Publisher: publish.yml on asuramaya/neo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file neo_harnesster-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: neo_harnesster-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 62.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for neo_harnesster-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a6fbc96782d046deac86b673a1990887bf8042e39c496b0e6d49584b3fe0a0e7
MD5 c9c0d7ec8607ff909d2d5949331ae052
BLAKE2b-256 55bdf9bf32085e771aaaa1fd21bb2a9ad9493b8b79456b1bfe03ef205f8336f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for neo_harnesster-0.2.1-py3-none-any.whl:

Publisher: publish.yml on asuramaya/neo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page