npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agent-optic

v0.6.1

Published

Zero-dependency, local-first library for reading AI assistant session data from provider home directories

Readme

agent-optic

Reads local assistant history directories and returns structured JSON — sessions, costs, timesheets, work patterns.

Zero-dependency, local-first TypeScript library for reading session data from provider directories such as ~/.claude/, ~/.codex/, ~/.pi/, ~/.copilot/, Cursor's local app storage, Claude Desktop, and OpenCode Desktop.

Security Warning: Provider home directories contain highly sensitive data — API keys, source code, credentials, and personal information may be present in plaintext session files. This library is designed with privacy as the primary concern. See SECURITY.md.

Try it

bunx --silent agent-optic sessions

Features

  • Zero runtime dependencies
  • No network access
  • Privacy by default — strips tool results and thinking blocks
  • Multi-tier session loading — index, meta, detail, and transcript streaming
  • Agent-first CLI contract — stable JSON envelope + JSONL streaming + machine-readable errors
  • Bounded multi-provider observation — one versioned snapshot of session facts and provider health
  • Bun-nativeBun.file(), Bun.Glob

Cost data & staying fully local

agent-optic computes token counts and USD cost entirely locally, with no network access (see Security). The tradeoff: its model pricing is only as current as the installed version, so brand-new models can show as unpriced until you upgrade.

If you don't need strict locality and want the broadest agent coverage with continuously-updated pricing, ccusage is a good complement — it prices token usage against the community-maintained LiteLLM table and reads the same on-disk session logs (Claude Code, Codex, pi-agent, and ~15 other CLIs). Note it fetches pricing over the network unless you pass --offline, which is why it is not fully local. A pattern that works well: use ccusage for authoritative multi-provider cost and agent-optic for the transcript/prompt detail, joined on session id.

Install

Requires Bun ≥ 1.0. This package ships TypeScript source and a Bun shebang for the CLI; it does not run under Node.

bun add agent-optic

Examples

The examples/ directory contains standalone scripts that show what your session data unlocks. Run any of them with bun examples/<name>.ts.

Cost per Feature

Match sessions to git branches and calculate what each feature costs in tokens and USD.

bun examples/cost-per-feature.ts --repo /path/to/repo
Cost per Feature / Branch
==========================================================================================
Feature/Branch                      Sessions      Tokens    Est. Cost     Commits
------------------------------------------------------------------------------------------
feat/auth-system                          8       2.3M        $4.12           5
fix/memory-leak                           3       890K        $1.55           2
refactor/api-client                       5       1.1M        $2.08           3

Match Git Commits

Correlate git commits with sessions by timestamp proximity — find which session produced each commit.

bun examples/match-git-commits.ts --days 7

Timesheet

Generate a weekly timesheet grouped by day and project, with gap-capped hours.

bun examples/timesheet.ts
Timesheet: 2026-02-10 → 2026-02-14
==========================================================================================
Day   Date        Project                           Hours   Sessions    Prompts
------------------------------------------------------------------------------------------
Mon   2026-02-10  agent-optic                        2.3          4         18
                  my-app                               1.1          2          8
Tue   2026-02-11  agent-optic                        3.5          6         32
------------------------------------------------------------------------------------------
      TOTAL                                            6.9         12         58

Model Costs

Compare token usage and costs across model families.

bun examples/model-costs.ts
Model Usage & Costs: 2026-01-13 → 2026-02-12
====================================================================================================
Model                    Sessions     Input    Output   Cache W   Cache R    Est. Cost
----------------------------------------------------------------------------------------------------
opus-4-5-20250514              12     4.2M      1.1M     3.8M      2.1M       $98.42
sonnet-4-5-20250929            45     8.1M      2.3M     6.2M      4.5M       $42.15

Prompt History

Export sampled prompts grouped by project as JSON — pipe to an LLM for categorization or analysis.

# Pipe to your preferred LLM CLI
bun examples/prompt-history.ts --from 2026-01-01 | your-llm-cli "categorize these prompts by intent"

Session Digest

Compact session summaries as JSON — first prompt, branch, model, token counts, cost, duration.

# Pipe to your preferred LLM CLI
bun examples/session-digest.ts --days 7 | your-llm-cli "which sessions were the most productive?"

Retrospective

Dump the current session as JSON so the agent can look at its own data and propose take-aways. The "Retrospective Knowledge Capture" pattern: decouple flagging (individual, in-the-moment) from solving (team, on a cadence). Works with any supported provider — Claude Code, Claude Desktop, Codex, Copilot, Cursor, OpenCode, Pi.

# Auto-detects session id from CLAUDE_CODE_SESSION_ID / CODEX_COMPANION_SESSION_ID
# Then ask the agent: "Look at the data for the current session. What take-aways could we make?"
bun examples/retrospective.ts

# Pipe to any LLM CLI
bun examples/retrospective.ts | your-llm-cli "what could have gone better in this session?"

# Explicit session + provider (claude | codex | openai | pi | copilot | cursor | claude-desktop | opencode)
bun examples/retrospective.ts --provider codex --session <id>
bun examples/retrospective.ts --provider copilot --session <id>
bun examples/retrospective.ts --provider cursor --session <id>
bun examples/retrospective.ts --provider claude-desktop --session <id>
bun examples/retrospective.ts --provider opencode --session <id>

The output includes prompts, assistant summaries, tool-call breakdown, files touched, and cost — enough for the agent to spot redirects, wasted tool calls, and missing context. A common follow-up is to file each take-away as a labelled GitHub issue (gh issue create -l agent-retrospective) so the team can review them weekly and convert them into changes to skills, agent instructions (CLAUDE.md / AGENTS.md / .cursorrules / etc.), tests, or tooling.

Work Patterns

Aggregated work pattern metrics as JSON — hour distribution, late-night/weekend counts, longest and most expensive sessions.

# Pipe to your preferred LLM CLI
bun examples/work-patterns.ts | your-llm-cli "analyze my work patterns and suggest improvements"

Commit Tracker

Post-commit hook that records AI usage per commit to .ai-usage.jsonl.

# Install as post-commit hook
bun examples/commit-tracker.ts install

# Backfill last 30 days of commits
bun examples/commit-tracker.ts init

# Run manually for the latest commit
bun examples/commit-tracker.ts run

# Uninstall
bun examples/commit-tracker.ts uninstall

Each commit gets a JSONL record linking it to the sessions that were active at commit time:

{"commit":"7c5a457","timestamp":"2026-02-13T21:12:13.000Z","branch":"main","author":"kristoffer","session_ids":["019c9aea-484d-7200-87fd-07a545276ac4"],"tokens":{"input":194,"output":1638,"cache_read":1534390,"cache_write":83203},"cost_usd":1.33,"models":["claude-opus-4-6"],"messages":89,"files_changed":2}

Session matching uses timestamp proximity + branch preference. If the commit was authored by a known AI agent (Cursor, GitHub Copilot SWE, Devin), ai_tool is added automatically.

Annotate Commits

Writes AI cost data as git notes on each commit in .ai-usage.jsonl. Uses refs/notes/ai (compatible with git-ai tooling).

bun examples/annotate-commits.ts
bun examples/annotate-commits.ts --push  # also push notes to origin
# View inline in git log
git log --show-notes=ai

# Fetch notes from a remote
git fetch origin refs/notes/ai:refs/notes/ai

Each note has a human-readable line followed by a JSON section:

AI: $1.33 | out: 2K | cache: 1.5M | sessions: 1 | claude-opus-4-6
---
{"schema":"agent-optic/1.0","sessions":[...],"tokens":{...},"cost_usd":1.33,"models":["claude-opus-4-6"],"branch":"main"}

Branch Report

Generates a self-contained HTML report of token usage and cost per branch from .ai-usage.jsonl.

bun examples/branch-report.ts > report.html
bun examples/branch-report.ts path/to/.ai-usage.jsonl > report.html

Pipe Match

Generic stdin matcher — pipe in any JSON with timestamps, match against sessions.

# Match GitHub PRs to sessions that produced them
gh pr list --json createdAt,title | bun examples/pipe-match.ts --field createdAt

# Match GitHub issues
gh issue list --json createdAt,title | bun examples/pipe-match.ts --field createdAt

# Match any timestamped JSON
echo '[{"timestamp":"2026-02-10T14:00:00Z","title":"Deploy v2.1"}]' | bun examples/pipe-match.ts

# Works with any JSON — work items, calendar events, deploys, etc.
cat events.json | bun examples/pipe-match.ts

Quick Start

import { createHistory } from "agent-optic";

const ch = createHistory({ provider: "claude" });

// List today's sessions (fast — reads only history.jsonl)
const sessions = await ch.sessions.list();

// List with metadata (slower — reads session files for branch/model/tokens)
const withMeta = await ch.sessions.listWithMeta();

// Get full session detail (projectPath is optional for codex/openai)
const detail = await ch.sessions.detail(sessionId);

// Stream transcript entries (projectPath is optional for codex/openai)
for await (const entry of ch.sessions.transcript(sessionId)) {
  console.log(entry.message?.role, entry.timestamp);
}

// Daily summary (sessions + tasks + plans + todos + project memory)
const daily = await ch.aggregate.daily("2026-02-09");

// Project summaries
const projects = await ch.aggregate.byProject({ from: "2026-02-01", to: "2026-02-09" });

// Estimate cost of a session
import { estimateCost } from "agent-optic";
const cost = estimateCost(withMeta[0]); // USD

// Collect one bounded, versioned observation across provider stores
import { collectSessionObservation } from "agent-optic";
const observation = await collectSessionObservation({
  providers: ["pi", "claude", "codex"],
  sinceMs: 24 * 60 * 60 * 1000,
  privacy: "shareable",
  maxSessions: 12,
});

API

The public package surface is intentionally small: createHistory, core types, privacy profiles, pricing helpers, and a few date/project helpers. Low-level readers, parsers, and JSONL utilities remain internal.

createHistory(config?)

const ch = createHistory({
  provider: "claude",                // "claude" | "codex" | "openai" | "pi" | "copilot" | "cursor" | "claude-desktop" | "opencode"
  providerDir: "~/.claude",          // default: provider-specific home directory
  privacy: "local",                  // "local" | "shareable" | "strict" | Partial<PrivacyConfig>
});

openai is currently an alias of Codex-format local history and defaults to ~/.codex.

pi reads from ~/.pi/agent/sessions/ — Pi has no history.jsonl, so sessions are discovered by scanning the directory tree. Pi sessions include all user prompts, transcript start/end timestamps, lastFileActivity, lastPrompt, userPromptCount, a coarse activityKind, privacy-safe lifecycle evidence (lastMessageRole, lastMessageStopReason, and lastMessageTimestamp), and totalCost from accumulated message costs. Lifecycle roles and stop reasons are closed vocabularies; unrecognized strings are not exposed. Pi date filters use actual transcript activity, not only the filename date, so a session that started earlier but continued today is still returned. Consumers must treat lifecycle evidence as an observation, not authority to steer or close a session.

copilot reads from ~/.copilot/session-state/ — sessions are discovered from workspace.yaml files, token totals from session.shutdown events in events.jsonl.

cursor reads from ~/Library/Application Support/Cursor/User/workspaceStorage/*/state.vscdb — sessions are discovered from aiService.generations. These sessions are marked dataCompleteness: "prompt-only" with sourceCapabilities: ["prompt", "project", "timestamps"].

claude-desktop reads from ~/Library/Application Support/Claude/local-agent-mode-sessions/**/local_*.json and sibling audit.jsonl files when present. Sessions with audit logs expose full transcript/tool-stream semantics and are marked dataCompleteness: "full"; metadata-only records fall back to dataCompleteness: "prompt-only".

opencode reads full session data from ~/.local/share/opencode/storage/{session,message,part}/ when present, including prompts, assistant text, tool calls, token totals, and costs. It also reads ~/Library/Application Support/ai.opencode.desktop/*.dat as a desktop notification/model-selection fallback; fallback-only records are marked dataCompleteness: "metadata-only".

Not every installed AI app has readable local history. In this install, ChatGPT.app conversation files under ~/Library/Application Support/com.openai.chat are binary/encrypted .data blobs; Gemini and Continue only showed skill bundles, not session history.

Sessions

| Method | Speed | Reads | Returns | |--------|-------|-------|---------| | sessions.list(filter?) | Fast | history.jsonl only | SessionInfo[] | | sessions.listWithMeta(filter?) | Medium | + reads session files for metadata | SessionMeta[] | | sessions.detail(id, project?) | Slow | Full session parse | SessionDetail | | sessions.transcript(id, project?) | Streaming | Full session file | AsyncGenerator<TranscriptEntry> | | sessions.count(filter?) | Fast | history.jsonl only | number |

For codex, openai, pi, cursor, claude-desktop, and opencode, project is optional because project/cwd is resolved from session metadata. Consumers should check dataCompleteness / sourceCapabilities before assuming a full transcript, tool calls, tokens, or assistant summaries are available; Claude Desktop and OpenCode records vary depending on whether their transcript stores exist.

dataCompleteness values:

  • full or omitted: regular provider data with full transcript semantics.
  • prompt-only: local store exposes prompts and metadata, but not the full assistant transcript/tool stream.
  • metadata-only: local store exposes timestamps/project/model metadata, but not prompt or transcript text.

Other Data

ch.projects.list()                    // ProjectInfo[]
ch.projects.memory(projectPath)       // ProjectMemory | null
ch.tasks.list({ date: "2026-02-09" })     // TaskInfo[]
ch.todos.list({ date: "2026-02-09" })     // TodoItem[]
ch.plans.list({ date: "2026-02-09" })     // PlanInfo[]
ch.skills.list()                      // string[]
ch.skills.read("skill-name")         // string (SKILL.md content)
ch.stats.get()                        // StatsCache | null

Aggregations

ch.aggregate.daily("2026-02-09")                          // DailySummary
ch.aggregate.dailyRange("2026-02-01", "2026-02-09")       // DailySummary[]
ch.aggregate.byProject({ from: "2026-02-01" })            // ProjectSummary[]
ch.aggregate.toolUsage({ date: "2026-02-09" })            // ToolUsageReport
ch.aggregate.estimateHours(sessions)                       // number (gap-capped)

Cost Estimation

import { estimateCost, getModelPricing, normalizeModelName, detectAgentFromCommit, MODEL_PRICING } from "agent-optic";

// Estimate cost for a session (requires SessionMeta — use listWithMeta)
const session = (await ch.sessions.listWithMeta())[0];
const cost = estimateCost(session); // USD

// Look up pricing for a model
const pricing = getModelPricing("claude-opus-4-6");
// { input: 5, output: 25, cacheWrite: 6.25, cacheRead: 0.5 } per million tokens

// Normalize a model name for pricing lookup
// Strips provider prefixes, date suffixes, speed qualifiers, and version dots
normalizeModelName("anthropic/claude-opus-4.6-fast"); // → "claude-opus-4-6"
normalizeModelName("us.anthropic.claude-sonnet-4-6-20260101:thinking"); // → "claude-sonnet-4-6"

// Detect AI agent from a git commit's author email or username
// Returns "cursor" | "github-copilot" | "devin" | undefined
detectAgentFromCommit("[email protected]"); // → "cursor"
detectAgentFromCommit(undefined, "copilot-swe-agent[bot]"); // → "github-copilot"

Filters

// Date filter (all methods)
{ date: "2026-02-09" }                    // Single day
{ from: "2026-02-01", to: "2026-02-09" }  // Range
{ from: "2026-02-01" }                    // From date to today

// Session filter (extends DateFilter)
{ date: "2026-02-09", project: "my-app" } // Filter by project name or full path

Privacy Profiles

| Profile | Strips | |---------|--------| | local (default) | Tool results, thinking blocks | | shareable | + home-rooted paths inside prompt and transcript text | | strict | + prompt text, emails, credential patterns, IPs |

Project identity fields remain available for local correlation and can contain absolute paths. Privacy profiles minimize content; they do not make arbitrary output safe to publish without review.

// Use a built-in profile
const ch = createHistory({ provider: "claude", privacy: "strict" });

// Or customize
const ch = createHistory({
  provider: "claude",
  privacy: {
    redactPrompts: false,
    stripToolResults: true,
    stripThinking: true,
    excludeProjects: ["/work/secret-project"],
  },
});

CLI

# Agent-friendly list (JSONL stream)
bunx --silent agent-optic sessions --provider codex --format jsonl

# Detail for one session
bunx --silent agent-optic detail 019c9aea-484d-7200-87fd-07a545276ac4 --provider openai

# Transcript stream (limit + selected fields)
bunx --silent agent-optic transcript 019c9aea-484d-7200-87fd-07a545276ac4 --provider openai --format jsonl --limit 50 --fields timestamp,message

# Complete local scan with bounded, cursor-addressable evidence
bunx --silent agent-optic evidence 019c9aea-484d-7200-87fd-07a545276ac4 --provider pi --terms "Blockbuster,Tiny Place" --max-matches 8 --max-chars 4000

# One bounded observation across provider stores
bunx --silent agent-optic observe --providers pi,claude,codex --since 24h --privacy shareable --max-sessions 12 --max-prompts 5 --max-prompt-chars 600

# One provider on an exact historical date, with an overridden store
bunx --silent agent-optic observe --provider pi --provider-dir /path/to/.pi --date 2026-07-14 --raw

# Tool usage report
bunx --silent agent-optic tool-usage --provider codex --from 2026-02-01 --to 2026-02-26

# Daily summary
bunx --silent agent-optic daily --date 2026-02-09

# Recent Claude Code sessions (default sort uses transcript mtime when available)
bunx --silent agent-optic sessions --provider claude --date 2026-02-09 --limit 5 --fields sessionId,project,timeRange,lastFileActivity

# Raw output without envelope
bunx --silent agent-optic sessions --provider claude --date 2026-02-09 --raw

--format json returns a stable envelope (schemaVersion, command, provider, generatedAt, data) by default. Use --raw for data-only output and --format jsonl for one JSON object per line. sessions defaults to --sort recent (newest of lastFileActivity, timeRange.end, timeRange.start) before applying --limit; use --sort mtime|start|end|recent to be explicit. Use --since 24h / --since 90m / --since 7d for a rolling session window. --fields selects top-level fields and fails fast if a requested field is unknown.

Common agent commands:

  • sessions [session-id?] list sessions (or filter to one ID)
  • detail <session-id> full parsed session
  • transcript <session-id> transcript stream/output
  • evidence <session-id> scan the complete transcript and return bounded matches, prompts, paths, and tool names
  • observe return agent-optic.observation/v1 session facts plus per-provider available, absent, or error status
  • tool-usage aggregated tool analytics

observe canonicalizes the openai alias to codex, so requesting both scans the shared Codex store once. Its v1 session projection and source-capability vocabulary are explicit: future SessionMeta fields or capabilities do not silently enter the wire contract. With no date, range, or rolling window, the query records the effective local date it scanned. completeness reports observed and returned session counts plus truncation, while capabilities describes the contract-level evidence available. availability describes provider-store health, not whether matching sessions exist. --provider-dir is accepted only when the effective observation contains one provider.

Validation

Run the tests and the manual Pi lifecycle extraction eval:

bun test
bun evals/pi-lifecycle/run.ts

The eval replays raw Pi JSONL and writes a comparative, privacy-checked receipt to evals/pi-lifecycle/out/receipt.json.

Architecture

src/
  index.ts              # Public API exports
  agent-optic.ts        # Main factory: createHistory()
  pricing.ts            # Model pricing data and cost estimation
  types/                # Type definitions (one file per domain)
  readers/              # Per-provider file readers (Claude, Codex, Pi, Copilot, Cursor, Claude Desktop, OpenCode)
  parsers/              # Session parsing, tool categorization, content extraction
  aggregations/         # Daily/project/tool summaries, time estimation
  collectors/           # Bounded single-session evidence and multi-provider observations
  privacy/              # Redaction engine, privacy profiles, credential detection
  utils/                # Dates, paths, providers
  cli/                  # CLI entry point
examples/               # Standalone scripts showing what the data unlocks

License

MIT