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

opencode-skill-audit

v0.3.0

Published

Audit trail of skill invocations and file changes, with a live timeline in the opencode sidebar

Readme

skill-audit

CI License: MIT npm Claude Code Codex OpenCode

Deterministic audit trail for Claude Code, Codex and opencode sessions: which observable skills were invoked, when, and which files were changed afterwards. It gives you a quick skill-compliance signal before you read the code.

On opencode it also renders live in the TUI sidebar, and on Claude Code in a live pane, so the audit sits next to the conversation with no command to run.

● Skill audit — f3a91c2e · ~/Dev/Web · 14:02→16:40

  ⚡ superpowers:brainstorming
  ⚡ superpowers:test-driven-development
     14:00  (2)
       14:06 ✎ src/auth/token.ts  Edit
       14:09 ✎ src/auth/token.test.ts  Write
     15:00  (1)
       15:12 ✎ src/auth/session.ts  Edit
  ⚠ (no skill active)
     16:00  (1)
       16:40 ✎ src/index.ts  Edit

● 2 skill runs (2 distinct) · 4 files touched · ⚠ 1 edits outside skill context

The skill leads each block; the hours beneath it show when its work actually happened. Times are your local clock — the log itself stays UTC, so a session stays readable across machines.

Why

You ask an agent to follow a skill. Did it? Reading the transcript to find out is slow, and asking another model to judge costs tokens and is itself non-deterministic.

LLMs are not deterministic. Hook events are. This plugin logs the facts exposed by each host's documented hook API:

  • No LLM judging, no tokens. Everything except the two in-session slash/skill commands runs entirely outside the model.
  • No transcript parsing. Only documented hook payloads, so nothing silently breaks on a transcript format change.
  • One data model across three hosts. The NDJSON log is the only contract, so the CLI reads opencode sessions and the opencode sidebar reads Claude Code sessions.
  • The red flag is a number. ⚠ edits outside skill context counts files changed while no observable skill was active.

Contents

How it works

PostToolUse hooks capture skill-tool calls and file edits. On Codex, a UserPromptSubmit hook also captures explicit $skill-name references, and apply_patch payloads are expanded into one file event per path. On opencode a plugin does the same job through the tool.execute.after hook. Events are appended as NDJSON to ~/.claude/skill-audit/<session_id>.ndjson. All three hosts write to that one directory on purpose, so any viewer can read any host's session. Override the location with SKILL_AUDIT_DIR.

A skill run only claims the edits that keep arriving under it. Once 30 minutes pass with no activity the run closes, and later files are counted as (no skill active) rather than being attributed to whatever skill happened to run that morning. Tune the window with SKILL_AUDIT_IDLE_MINUTES.

The opencode sidebar draws in fixed columns, so it defaults to icons that are always one terminal cell wide (· ✎ !); emoji such as ⚡ and ⚠ render two cells wide in most terminals and shift every row that carries them. Set SKILL_AUDIT_ICONS=emoji to use the emoji set anyway.

Claude Code / Codex ──hooks───▶ logger.sh ──┐
                                            ├─▶ ~/.claude/skill-audit/<sid>.ndjson
opencode ──tool.execute.after──▶ plugin ────┘             │
                                                          ├─▶ skill-audit status/report/watch/list
                                                          ├─▶ opencode sidebar (live)
                                                          └─▶ Claude Code pane (live)

The log is the only contract between the writers and the viewers, so the CLI reads opencode sessions and the opencode sidebar reads Claude Code sessions.

Subagent hook calls use the parent session ID, so delegated edits appear in the same audit.

Install

Claude Code

/plugin marketplace add DepickereSven/skill-audit
/plugin install skill-audit@depickeresven-skill-audit

Restart Claude Code so the hooks load.

Live pane

The plugin also opens a Skill audit pane showing the current session's timeline. It refreshes after every skill and file-edit tool call, and polls the log every two seconds.

The Claude Code Skill audit pane docked beside the transcript

  • Opening: it opens by itself at session start once the terminal is 144 columns or wider. Narrower terminals hold it back until you run /skill-audit-pane, which opens it at any width.
  • Placement: in fullscreen from 110 columns it docks beside the transcript, 40 columns wide (drag the edge to change it; your width is kept), and shows the full timeline. Press a ▼/▶ marker to collapse or expand a run or an hour. Elsewhere it sits above the prompt in a compact form: the header and the latest run.
  • Other panes: panes from other plugins become tabs beside it (click a tab, or ctrl+x tab). Only one is shown at a time.
Showing and hiding the pane

| Command | What | |-----------------------|-----------------------------------------------------------------------------| | /skill-audit-pane | Close the pane when it is open, open it (at any width) when it is not | | Esc or the close mark | Close the pane for this session only; the next session opens it again |

Closing with /skill-audit-pane is remembered in the plugin's own store (a JSON file under your Claude Code configuration directory), so new sessions keep it closed until you run /skill-audit-pane again, which opens it and turns auto-open back on. Hiding never stops recording: the shell hooks keep writing the log, and the CLI and the pane read it back the moment you open it again.

The pane is display only. The shell hooks keep writing the log through logger.sh, so recording works the same on Claude Code builds without plugin function hooks.

Migrating from a manual setup? Remove any logger.sh entries from the hooks block of ~/.claude/settings.json first, or every event is logged twice.

Codex

codex plugin marketplace add DepickereSven/skill-audit
codex plugin add skill-audit@depickeresven-skill-audit

Start a new Codex session after installation. Codex asks you to review and trust plugin-bundled hooks before they run. Plugins are available in Codex CLI and the ChatGPT desktop app's Codex surface, but not in the IDE extension. See the official plugin and hook documentation.

Invoke the bundled Codex skill with $skill-audit.

opencode

opencode plugin opencode-skill-audit --global

The plugin is published on npm as opencode-skill-audit.

Restart opencode so the plugin loads. It registers two things: a server hook that logs skill, edit, write, multiedit, apply_patch and patch tool calls, and a sidebar section that renders the current session's timeline live.

The opencode sidebar rendering a live skill-audit timeline beside a conversation

Click the header to collapse the section, or a skill row to fold its files away.

opencode discovers skills natively, including from .claude/skills/ and .agents/skills/, so skills you already use are logged without moving them. To get the in-session report as well, link the bundled skill into a directory opencode scans:

ln -sf "$PWD/skills/skill-audit" ~/.config/opencode/skills/skill-audit

CLI on your PATH (recommended)

The viewer works from any terminal. Link the installed script somewhere on your PATH.

Claude Code:

ln -sf ~/.claude/plugins/cache/*/skill-audit/*/scripts/skill-audit ~/.local/bin/skill-audit

Codex:

ln -sf ~/.codex/plugins/cache/*/skill-audit/*/scripts/skill-audit ~/.local/bin/skill-audit

You can also clone this repository and link scripts/skill-audit directly.

~/.local/bin is not on every system's PATH. Check with command -v skill-audit. If it prints nothing, add the directory in your shell profile:

export PATH="$HOME/.local/bin:$PATH"

Claude Code statusline segment (optional)

Plugins cannot modify your statusline. Add this to your own statusline script to get a live ⚡2 ✎5 tdd segment:

sid=$(echo "$input" | jq -r '.session_id // empty')
audit_log="$HOME/.claude/skill-audit/$sid.ndjson"
if [ -n "$sid" ] && [ -s "$audit_log" ]; then
  audit=$(jq -rs '
    [ .[] | select(.kind=="skill") ] as $s
    | ([ .[] | select(.kind=="file") | .path ] | unique | length) as $f
    | "⚡\($s|length) ✎\($f)"
      + (if ($s|length) > 0 then " " + ($s[-1].name | split(":") | last) else "" end)
  ' "$audit_log" 2>/dev/null)
  [ -n "$audit" ] && parts="$parts | $audit"
fi

Verify the install

Hooks that never fire look exactly like a session with no skill usage, so confirm once after installing.

  1. In a new session on the host you installed, invoke any skill and edit one file. Invoking this plugin's own skill is enough:

    • Claude Code: /skill-audit
    • Codex: $skill-audit
    • opencode: watch the sidebar section appear
  2. Check that a log exists and is growing. Look in ~/.claude/skill-audit, or in the directory you set as SKILL_AUDIT_DIR:

    ls -la ~/.claude/skill-audit/
  3. Read it back from any terminal:

    skill-audit list      # sessions, newest first
    skill-audit status    # counts + recent timeline for the newest session

If list prints no session logs in ..., nothing was written. Go to Troubleshooting.

Usage

CLI

Works from any terminal, on logs from any host. None of these cost tokens.

| Command | What | |----------------------------|---------------------------------------------------| | skill-audit status [sid] | Compact counts and recent timeline | | skill-audit report [sid] | Full timeline | | skill-audit watch [sid] | Live view, refreshed every two seconds. q quits | | skill-audit list | Recent sessions, newest first | | skill-audit --help | Usage summary |

status, report and watch all take an optional session ID. Without one they use the most recently modified log, which is the wrong session if you run several at once. Get the ID from skill-audit list and pass it explicitly.

Inside a session

| Host | Command | What | Tokens | |-------------|------------------------|----------------------------------------------------------------------|-----------:| | Claude Code | /skill-audit-pane | Show or hide the live pane (details) | 0 | | Claude Code | ! skill-audit status | Run the CLI in the session. Queues while the model is busy | 0 | | Claude Code | /skill-audit | Print the full report in the transcript | Model turn | | Codex | $skill-audit | Print the full report in the transcript | Model turn | | opencode | skill-audit skill | Print the full report, once linked (opencode) | Model turn |

Live views

| Where | What | |---------------------|---------------------------------------------------------------------------| | Claude Code pane | Opens at session start from 144 columns; /skill-audit-pane at any width | | opencode sidebar | Always on beside the conversation. Click the header to collapse it | | skill-audit watch | Any terminal, any host |

Configuration

Set these in the environment of the host (and of your shell, for the CLI). All are optional.

| Variable | Default | What | |----------------------------|-------------------------|-------------------------------------------------------------------------------------| | SKILL_AUDIT_DIR | ~/.claude/skill-audit | Where logs are written and read | | SKILL_AUDIT_IDLE_MINUTES | 30 | Idle minutes after which a skill run stops claiming edits | | SKILL_AUDIT_ICONS | one-cell icons | emoji uses ⚡ / ⚠ in the pane and sidebar (two cells wide in most terminals) |

The ⚠ edits outside skill context counter is the compliance red flag: files changed while no observable skill was active.

Data format

One NDJSON file per session, one event per line, appended in chronological order:

{"ts":"2026-07-10T14:05:11Z","kind":"skill","name":"superpowers:test-driven-development","args":"","cwd":"/Users/me/proj","source":"tool"}
{"ts":"2026-07-10T14:06:40Z","kind":"file","tool":"apply_patch","path":"/Users/me/proj/src/auth/token.ts","cwd":"/Users/me/proj"}

| Field | On | Meaning | |-----------|---------|-------------------------------------------------------------------------------| | ts | both | UTC timestamp, YYYY-MM-DDThh:mm:ssZ | | kind | both | skill or file, the only two event kinds | | cwd | both | Session working directory as reported by the host | | name | skill | Skill identifier, e.g. superpowers:test-driven-development | | args | skill | Arguments passed to the skill tool, empty string when there were none | | source | skill | tool for an observed skill tool call, prompt for a Codex $skill-name | | turn_id | skill | Codex turn identifier, present only when the host supplies one | | tool | file | Tool that made the edit: Edit, Write, MultiEdit, apply_patch, etc. | | path | file | Absolute path of the changed file (relative paths are resolved against cwd) |

Within a Codex turn, a repeated (turn_id, name) skill pair is written once, so a skill named several times in one prompt does not inflate the counts.

The format is open, so you can build other viewers on top. Prune old logs with:

find ~/.claude/skill-audit -name '*.ndjson' -mtime +30 -delete

Troubleshooting

Nothing is logged / no session logs in ...

  • Did you restart the host after installing? Hooks and plugins load at startup, and an existing session keeps running without them.
  • Confirm the plugin is installed: claude plugin list, codex plugin list, or check the plugin array in ~/.config/opencode/opencode.json.
  • On Codex, hooks only run after you review and trust them, so accept the prompt.
  • Is jq installed? logger.sh exits silently without it, by design: the hook must never block a session. Check with command -v jq.

Every event appears twice. A manual logger.sh entry is still in the hooks block of ~/.claude/settings.json alongside the plugin's. Remove the manual one.

The CLI shows an empty or unrelated session. Without an argument the viewer picks the most recently modified log, which is the wrong one when sessions run in parallel. Run skill-audit list and pass the ID: skill-audit report <sid>.

The CLI finds nothing but the logs exist. Writer and viewer disagree about the directory. If you set SKILL_AUDIT_DIR for the host, export it for your shell too. Otherwise the CLI looks in ~/.claude/skill-audit.

skill-audit: command not found. The symlink is missing or its directory is not on PATH. See CLI on your PATH.

The Claude Code pane no longer opens at session start. You closed it with /skill-audit-pane, which is remembered across sessions. Run /skill-audit-pane again to show it and turn auto-open back on. If you never hid it, your terminal is likely narrower than 144 columns; the command opens it at any width.

A skill ran but is missing from the timeline. Expected in some cases. See Honest limitations.

Honest limitations

  • Invocation is not compliance. The log proves that a skill was explicitly selected or exposed as a tool event, not that the result followed every instruction.
  • Codex automatic skill loading is not a hook event today. Explicit $skill-name references are captured. Skills that Codex chooses automatically are not. No transcript parsing is used to fill that gap.
  • Prompt-sourced entries are syntax-level evidence. Codex supplies plain prompt text to the hook, so a lower-case dollar-prefixed token can be logged even if it does not resolve to an installed skill. The NDJSON source: "prompt" field distinguishes these entries.
  • Only observable file tools are captured. Claude Edit/Write/MultiEdit/NotebookEdit, Codex apply_patch, and opencode edit/write/multiedit/apply_patch/patch edits are logged. Files created indirectly by shell commands are not visible as separate file events.
  • opencode agents and subagents are not logged. They have no equivalent on the other two hosts, so logging them would add an event kind only one host can emit. The audit keeps one data model across all three.
  • Session-start injected skills are context, not skill tool calls, and do not appear.
  • Concurrent sessions: the newest-log default can pick the wrong session. Pass the session ID explicitly after using skill-audit list.

Uninstall

Claude Code:

claude plugin uninstall skill-audit@depickeresven-skill-audit

Codex:

codex plugin remove skill-audit@depickeresven-skill-audit

opencode has no removal subcommand. Delete the "opencode-skill-audit" entry from the plugin array in ~/.config/opencode/opencode.json (or the project-local opencode.json), and remove the skill symlink if you made one:

rm -f ~/.config/opencode/skills/skill-audit

Then restart the host. Finally, clean up the CLI symlink and the logs, which no uninstall touches:

rm -f ~/.local/bin/skill-audit
rm -rf ~/.claude/skill-audit

Development

The hooks and the CLI viewer are plain bash (scripts/) with no build step. The opencode plugin and sidebar are TypeScript (src/) built with Bun.

| Path | What | |-------------------|----------------------------------------------------------------------------| | scripts/ | logger.sh hook target, the skill-audit CLI, sync-versions.mjs | | src/ | opencode plugin (index.ts), sidebar (tui.ts), shared log and view code | | hooks/ | Claude Code shell hooks (hooks.json) and the live pane (claude.tsx) | | types/ | $.state contract for the Claude Code pane | | test/ | Bun tests, fixtures, and test/format-contract.sh | | .claude-plugin/ | Claude Code plugin manifest and marketplace entry | | .codex-plugin/ | Codex plugin manifest | | .agents/ | Codex marketplace manifest | | skills/ | The skill-audit skill used by Codex and opencode | | commands/ | The /skill-audit slash command for Claude Code | | .opencode/ | Local opencode workspace for testing the plugin during development |

bun install
bun run check     # format:check + lint + typecheck (src and test) + tests
bun run build     # dist/index.js (plugin) and dist/tui.js (sidebar)

| Script | What | |----------------------------|----------------------------------------------------------| | bun run format | Prettier, write mode | | bun run lint | ESLint over src and test | | bun run typecheck | tsc --noEmit for src | | bun test | Bun test suite in test/ | | bun run check | Everything CI runs | | bun run test:claude | Validate and test the Claude Code pane (needs claude) | | bun run typecheck:claude | Type-check the pane (needs .claude-plugin/types/) | | bun run check:versions | Assert package.json and both plugin.json files agree | | bun run sync:versions | Rewrite the plugin manifests from package.json |

Tests live in test/. The Claude Code pane is the exception: its tests (hooks/*.test.tsx) run in Claude Code's mod sandbox, which has no Node or Bun, so bun test is rooted at test/ (bunfig.toml). test:claude copies the pane's files into a scratch folder and runs claude plugin validate and claude plugin test there. src/core.ts and src/view.ts must stay free of node:* imports and process because the pane imports them; test/core.test.ts checks this. typecheck:claude reads the API types the engine writes to .claude-plugin/types/ once it has loaded the plugin from a local folder. That folder is gitignored and not available in CI, so neither pane check runs in CI.

test/format-contract.sh pins the rendered CLI output against fixtures, so a change to the timeline format has to be updated there deliberately. That output is the contract the sidebar and any third-party viewer rely on.

Releasing

Two workflows, chained.

.github/workflows/ci.yml runs four jobs: it checks that both plugin manifests carry the same version as package.json, runs bun run check on Bun 1.2.0 and on the latest Bun, builds the bundles, and packs the npm tarball to confirm it ships every entry point package.json exports. It runs on pull requests and on pushes to main, not on every branch, so a pull request is never tested twice.

.github/workflows/publish.yml starts only when a CI run on main finishes successfully. It checks out that exact commit and asks npm whether the version in package.json already exists:

  • Already on npm. Nothing is released. This is what an ordinary push to main does.
  • Not on npm. It publishes with provenance over OIDC, tags the commit, and opens a GitHub release with notes generated from the merged commits.

So a version bump landing on main is the release, and it can only happen after CI has gone green on that commit.

To cut one:

npm version patch --no-git-tag-version   # or minor / major
git commit -am "chore: release $(node -p 'require("./package.json").version')"
git push

--no-git-tag-version matters. The workflow creates the v<version> tag itself, and it aborts the release if that tag already exists while npm has never seen the version. The bump still runs scripts/sync-versions.mjs and stages .claude-plugin/plugin.json and .codex-plugin/plugin.json alongside it, keeping both manifests on the same version as package.json, which CI fails the build over if they ever drift.

Use the publish workflow's manual trigger (workflow_dispatch) with dry run enabled to rehearse a publication without releasing anything.

Requirements

  • macOS or Linux
  • bash and jq for the CLI viewer and the Claude Code / Codex hooks
  • opencode >= 1.14 for the plugin and its sidebar

License

MIT © Sven Depickere