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

pi-logger

v2.0.3

Published

A telemetry logging extension for the pi agent harness.

Readme

pi-logger

A telemetry logging extension for pi. It intercepts lifecycle events and writes them to a JSONL file in a local .pi/logs/ directory for debugging and visualization.

Built for personal use to understand how an AI coding agent works internally — what it does, how long things take, where tokens go, and how the agent's decision-making flows from turn to turn.

What It Captures

Subscribes to 21 of 32 available pi extension events, organized by priority:

| Priority | Events | Purpose | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | | P0 (core) | session_start/shutdown, agent_start/end/settled, turn_start/end, message_start/end, tool_execution_start/end, after_provider_response | Trace hierarchy, timing, token/cost metrics | | P1 (strong) | input, before_agent_start, session_before_compact, session_compact, model_select, tool_call, tool_result | User intent, context utilization, audit trail | | P2 (nice) | thinking_level_select, session_tree, user_bash | Config changes, navigation, user commands |

Events intentionally skipped: message_update and tool_execution_update (too high-frequency — token-by-token), before_provider_request (sensitive payload), context (too large), and transitional events already captured by start/shutdown pairs.

Output

Each session produces a JSONL file in .pi/logs/:

session-2025-01-15T10-30-00-000Z-abc12345.jsonl

Every line is a JSON object:

{
    "ts": 1736935800000,
    "model": "anthropic/claude-sonnet-4-20250514",
    "cwd": "/home/user/project",
    "event_type": "tool_execution_end",
    "trace_id": "f1e2d3c4b5a69780",
    "span_id": "a1b2c3d4e5f60789",
    "parent_span_id": "9876543210fedcba",
    "payload": {
        "tool_call_id": "tc_001",
        "tool_name": "bash",
        "is_error": false,
        "result_summary": "total 123\n-rw-r--r--  1 user  ..."
    }
}

All events carry trace_id, span_id, and parent_span_id for trace correlation — even non-span events like input, model_select, and agent_settled.

session_id and session_file are not stored per-event since each JSONL file already represents one session (encoded in the filename).

Span Hierarchy

Events that represent span boundaries include trace_id, span_id, and parent_span_id to build a trace tree:

session (root)
├── agent_run
│   ├── turn [0]
│   │   ├── message (assistant)
│   │   │   ├── tool [read]
│   │   │   ├── tool [edit]
│   │   │   └── tool [bash]
│   │   └── message (tool_result)
│   └── turn [1]
│       └── message (assistant)
└── agent_settled

Message Content

message_start and message_end events capture the LLM's actual output — text, thinking blocks, and tool call counts:

{
    "event_type": "message_end",
    "payload": {
        "role": "assistant",
        "usage": {
            "input": 1558,
            "output": 32,
            "reasoning": 20,
            "totalTokens": 1590
        },
        "content": {
            "text": "Here's the fix for the bug in parser.js: ...",
            "thinking": "The user asked about the parser. Let me check the error ...",
            "tool_use_count": 2
        }
    }
}

Text and thinking content are truncated to 2000 characters each. This lets you see what the agent actually said and thought without bloating the log files.

Metrics Snapshots

Every 5 turns, a metrics_snapshot event is emitted with running totals:

{
    "event_type": "metrics_snapshot",
    "payload": {
        "agent_run_count": 3,
        "turn_count": 17,
        "avg_agent_run_duration_ms": 12400,
        "avg_turn_duration_ms": 3200,
        "tokens": {
            "input": 45000,
            "output": 12000,
            "cache_read": 8000,
            "cache_write": 2000,
            "reasoning": 3500
        },
        "cost": { "total": 0.045 },
        "tool_call_count": 23,
        "tool_error_count": 1,
        "tool_error_rate": 0.0435,
        "tool_counts": { "read": 8, "edit": 6, "bash": 7, "write": 2 },
        "compaction_count": 0,
        "http_request_count": 17,
        "http_error_count": 0,
        "http_rate_limit_count": 0
    }
}

Token usage is normalized across providers — Anthropic (input_tokens), OpenAI (prompt_tokens), and lmstudio (input, output, cacheRead, cacheWrite, reasoning, totalTokens) are all mapped to a common shape.

Installation

Auto-discovery (recommended)

The extension is already in .pi/extensions/pi-logger/ — pi will auto-discover it when you run pi from this project directory.

For global use, copy or symlink:

mkdir -p ~/.pi/agent/extensions/pi-logger
cp .pi/extensions/pi-logger/*.js ~/.pi/agent/extensions/pi-logger/

One-off test

pi -e .pi/extensions/pi-logger/index.js

Configuration

| Environment Variable | Default | Description | | -------------------- | ------------------------- | -------------------------------- | | PI_OBS_LOG_DIR | .pi/logs/ (project dir) | Directory for JSONL output files |

Dashboard Ideas

The JSONL output is designed to feed three dashboard views:

Metrics View

  • Total cost, token usage (input/output/cache)
  • Average turn duration, average agent run duration
  • Tool call count by tool, tool error rate
  • Compaction count by reason (manual/threshold/overflow)
  • HTTP error rate, rate-limit hit count
  • Model usage distribution
  • Input source distribution (interactive vs RPC vs extension)

Logs View

  • User prompts with timestamps
  • Tool calls with arguments and results
  • LLM responses with token usage and cost
  • Session events (start/shutdown/compaction)
  • Security audit trail (bash commands, file writes)
  • Error log (failed tool calls, HTTP errors)

Traces View

  • Hierarchical span tree for each agent run
  • Click into any span to see timing and details
  • Filter by tool name, error status, duration thresholds
  • Compare traces across turns to spot patterns

Architecture

pi-logger/
├── index.js      # Extension entry — subscribes to 21 events, routes to modules
├── tracer.js     # Span ID generation, trace correlation, span lifecycle
├── writer.js     # Buffered JSONL writer with auto-flush and redaction
└── metrics.js    # In-memory metric accumulators, periodic snapshots
  • Buffered writes: Events are batched (50 events or 5s interval) to minimize disk I/O.
  • Redaction: Sensitive fields (api_key, authorization, etc.) are redacted. Strings over 2000 chars are truncated.
  • Flush on shutdown: All buffered data is flushed on session_shutdown.
  • Zero dependencies: Only uses node:fs and node:path built-ins.
  • Universal trace context: Every event carries trace_id/span_id/parent_span_id, including non-span events like input and model_select.
  • Provider-agnostic metrics: Token usage is normalized across Anthropic, OpenAI, and lmstudio field shapes.

Limitations

  • Per-tool duration is approximate (exact start timestamps are lost between tool_execution_start and tool_execution_end events).
  • Parallel tool execution: tool_execution_end fires in completion order, not source order. Trace correlation via tool_call_id handles this.
  • No built-in log rotation by file size (sessions rotate by default).
  • No real-time dashboard — this is a data collector; build your own viewer on top of the JSONL files.