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

compahook

v1.1.2

Published

Persistent memory layer for Claude Code compaction — improves post-compact context quality

Downloads

952

Readme

compahook

Persistent memory layer for Claude Code's /compact command. Improves post-compact context quality by extracting, scoring, and re-injecting high-signal information that would otherwise be lost during conversation compaction.

Problem

When Claude Code compacts a conversation, the built-in summary can lose important details: architectural decisions, error resolutions, file edit purposes, and explicitly marked context. After compaction, the model may re-ask questions or forget critical constraints.

Solution

Three hooks that work in sequence around the compact cycle:

[Normal work] ─── PostToolUse ──→ logs edits/commands to working-state.jsonl
                                          │
[/compact triggers] ─ PreCompact ──→ reads transcript, scores items,
                                     writes compact-context.json,
                                     outputs compact instructions to stdout
                                          │
[Session resumes] ── SessionStart ─→ reads compact-context.json,
                                     injects structured markdown via
                                     additionalContext

The result: after compaction, the model receives a structured summary of decisions, modified files, unresolved issues, and explicitly marked context — ranked by relevance.

Requirements

  • Node.js >= 18
  • Claude Code CLI installed

Installation

Linux / macOS

npm install -g compahook
compahook install

This installs three commands (compahook-collect, compahook-extract, compahook-restore) into your PATH and registers the hooks in ~/.claude/settings.json.

Windows

npm install -g compahook
compahook install

Same commands — npm creates .cmd shims on Windows that are resolved by cmd.exe when Claude Code spawns hooks (it uses shell: true internally). No additional configuration needed.

Updating

Existing users can update to the latest version:

npm install -g compahook@latest

Verify installation

compahook status

Expected output:

compahook: installed (PostToolUse, PreCompact, SessionStart)

CLI commands

| Command | Description | |---------|-------------| | compahook install | Register hooks in ~/.claude/settings.json | | compahook uninstall | Remove hooks from settings | | compahook status | Check if hooks are installed | | compahook stats | Display performance metrics for current project | | compahook stats --global | Aggregate metrics across all registered projects | | compahook watch [ms] | Live-refreshing metrics dashboard | | compahook watch --global [ms] | Live global metrics across all projects | | compahook reset-metrics | Clear recorded metrics for current project |

How It Works

1. Collector (PostToolUse)

Every time Claude Code uses Write, Edit, MultiEdit, or Bash, the collector appends a one-line JSON entry to <project>/.claude/memory/working-state.jsonl:

{"ts":1706000000,"type":"file_edit","file":"src/auth.js","tool":"Write"}
{"ts":1706000001,"type":"command","cmd":"npm test"}

Automatically prunes to 500 lines (keeps newest 400).

2. Extractor (PreCompact)

When /compact triggers, the extractor:

  1. Reads the conversation transcript (JSONL provided by Claude Code)
  2. Classifies each message: goals, decisions, file edits, commands, errors, markers
  3. Loads previous context and increments cycle ages for carried items
  4. Deduplicates items using type-aware keys with NFKC normalization
  5. Scores items using: recency(position^2) * typeWeight * markerBoost * decay(cycleAge)
  6. Filters stale items below threshold, enforces hard cap (500 items)
  7. Takes the top 30 scored items for output
  8. Merges with recent working-state entries
  9. Writes <project>/.claude/memory/compact-context.json
  10. Outputs natural-language compact instructions to stdout (tells the compactor what to preserve)

3. Restorer (SessionStart)

When the session resumes after compaction, the restorer:

  1. Reads compact-context.json (only if < 5 minutes old — prevents stale injection)
  2. Formats a structured markdown summary (max 4000 chars)
  3. Outputs it as additionalContext which Claude Code injects into the new session

The injected context looks like:

## Session Memory (restored after compaction)
### Current Task
Build REST API with JWT authentication
### Key Decisions
- Using Express with jsonwebtoken (score: 0.95)
- Dual-token strategy: 15min access, 7d refresh (score: 0.90)
### Files Modified This Session
- `src/server.js` (Write)
- `src/auth/middleware.js` (Edit)
### Unresolved Issues
- bcrypt native module failed, switched to bcryptjs
### Important Context
- Token expiry 15min access, 7d refresh
- Rate limiting 5 attempts per minute

Scoring System

Items are ranked by a composite score:

| Factor | Formula | |--------|---------| | Recency | (position / total)^2 — recent items score higher | | Type weight | decision: 1.0, error: 1.5, goal: 0.85, file_edit: 0.7, command: 0.5, read: 0.3 | | Marker boost | 1.5x multiplier for messages containing IMPORTANT:, REMEMBER:, NOTE:, CRITICAL: | | Cycle decay | exp(-cycleAge / halfLife) — items decay ~63% per 10 compaction cycles |

v2 Scoring Pipeline (v1.1.0+)

The extractor now includes additional processing:

  • Deduplication: Type-aware deduplication with NFKC normalization prevents duplicate entries
  • Hard cap: Maximum 500 preserved items with score-based truncation
  • Cycle age tracking: Items carry their age across compaction cycles for decay calculation
  • Threshold filtering: Stale carried items below minScoreThreshold are pruned (fresh items always pass)

Configuration

Create <project>/.claude/memory/config.json to override defaults:

{
  "maxContextSize": 4000,
  "maxItems": 30,
  "maxWorkingStateLines": 500,
  "pruneKeepLines": 400,
  "recencyExponent": 2,
  "markerKeywords": ["IMPORTANT:", "REMEMBER:", "NOTE:", "CRITICAL:", "TODO:", "FIXME:"],
  "markerBoost": 1.5,
  "stalenessMinutes": 5,
  "decayHalfLife": 10,
  "minScoreThreshold": 0.001,
  "maxPreservedItems": 500,
  "enableTelemetry": true,
  "typeWeights": {
    "decision": 1.0,
    "error": 1.5,
    "goal": 0.85,
    "file_edit": 0.7,
    "command": 0.5,
    "marker": 1.0,
    "read": 0.3,
    "generic": 0.2
  }
}

All fields are optional — unspecified values use defaults.

Storage

Per-project memory is stored in <project>/.claude/memory/:

| File | Purpose | |------|---------| | working-state.jsonl | Rolling log of file edits and commands | | compact-context.json | Structured context from last compaction | | config.json | Optional per-project configuration | | metrics.json | Performance metrics and token savings tracking | | telemetry.jsonl | Pipeline telemetry logs (when enableTelemetry: true) |

Global state is stored in ~/.claude/:

| File | Purpose | |------|---------| | compahook-projects.json | Registry of active projects (max 200, self-pruning) |

These files are created automatically. Add .claude/memory/ to your .gitignore.

Running Tests

npm test

Runs 48 unit tests covering scorer, parser, collector, extractor, and restorer.

Monitoring & Metrics

compahook tracks its own performance and token savings in real time. Every hook invocation records latency, classification stats, and injection size to <project>/.claude/memory/metrics.json.

View current project metrics

compahook stats

Sample output:

compahook metrics (session: 1h 23m)
──────────────────────────────────────────
Compact cycles:        3
Total items tracked:   187
Items preserved:       90 (top 30 per cycle)
Noise filtered:        97 items dropped below threshold

Token budget:
  Injected:            1,068 tokens (across 3 cycles)
  Est. waste prevented: ~7,500 tokens
  Net saving:          ~6,432 tokens

Hook latency (avg):
  Collector:           1.2ms
  Extractor:           5.8ms
  Restorer:            0.3ms

Collector activity:
  File edits logged:   42
  Commands logged:     15
  Total invocations:   57

Score distribution (cumulative):
  file_edit:     48 items
  command:       31 items
  decision:      12 items
  marker:        8 items
  goal:          6 items
  error:         3 items

Top scores (last cycle): 1.2279, 0.7347, 0.7, 0.6667, 0.4535
Score range: 0 - 1.2279 (avg: 0.2731)

Live monitoring

compahook watch        # refreshes every 2s
compahook watch 5000   # custom interval (ms)

Displays the same metrics dashboard with live refresh. Press Ctrl+C to stop.

Reset metrics

compahook reset-metrics

Clears all recorded metrics for the current project. Useful at the start of a benchmarking session.

Global monitoring (cross-project)

Monitor compahook performance across all projects on the machine:

compahook stats --global

Sample output:

compahook global metrics (4 projects)
──────────────────────────────────────────────────
Compact cycles:        12
Total items tracked:   487
Items preserved:       360
Noise filtered:        127

Token budget (all projects):
  Injected:            4,320 tokens
  Est. waste prevented: ~30,000 tokens
  Net saving:          ~25,680 tokens

Hook latency (avg across all):
  Collector:           1.1ms
  Extractor:           4.9ms
  Restorer:            0.4ms

Per-project breakdown:
  /home/user/project-alpha
    cycles: 5  saved: ~10,240 tokens  edits: 38
  /home/user/project-beta
    cycles: 4  saved: ~8,720 tokens  edits: 25
  /home/user/project-gamma
    cycles: 3  saved: ~6,720 tokens  edits: 19

Live global monitoring:

compahook watch --global         # refresh every 2s
compahook watch --global 5000    # custom interval

How it works: Each time a hook fires, the project path is registered in ~/.claude/compahook-projects.json. The registry is self-managing:

  • Capped at 200 entries — evicts least-recently-seen when full
  • Auto-prunes on read — removes entries whose metrics.json no longer exists
  • Staleness eviction — drops entries not seen in 30+ days

The file stays under 20KB permanently regardless of how many projects you work on.

What's tracked

| Metric | Source | Description | |--------|--------|-------------| | Compact cycles | Extractor | Number of times /compact has run | | Items classified | Extractor | Total transcript messages parsed and scored | | Items preserved | Extractor | Items that made the top-N cut | | Noise filtered | Extractor | Items dropped below threshold | | Tokens injected | Restorer | Characters injected / 4 (approximate) | | Waste prevented | Extractor | Conservative estimate: ~2500 tokens saved per cycle | | Hook latency | All hooks | Execution time per hook invocation | | Type distribution | Extractor | Breakdown by decision, error, goal, file_edit, etc. | | Score distribution | Extractor | Min/max/avg scores and top 10 from last cycle | | Collector activity | Collector | File edits vs commands logged |

Metrics recording never blocks hook execution — all recording is wrapped in try/catch with silent failure.

Benchmarking

npm run benchmark

Measures extractor quality against a fixed transcript with 15 ground-truth facts:

  • Coverage: % of ground-truth facts captured
  • Precision: % of extracted items that are substantive
  • Size efficiency: facts per 1000 characters of output
  • Latency: extractor execution time

Uninstalling

npm uninstall -g compahook

This automatically removes hooks from ~/.claude/settings.json (via the preuninstall lifecycle script) without affecting other hook configurations.

To remove hooks without uninstalling the package:

compahook uninstall

Security

compahook is hardened against common filesystem and input attacks:

| Control | Implementation | |---------|---------------| | Path traversal | validateCwd() rejects null bytes, non-absolute paths, over-length paths | | Symlink attacks | isSymlink() check before all file writes | | Stdin DoS | 1MB size cap on all stdin handlers | | File permissions | 0o600 for files, 0o700 for directories | | Atomic writes | Temp file + rename for metrics, settings, and context | | TOCTOU races | open → fstat → read → close pattern in restorer | | Prototype pollution | Schema validation with whitelisted keys in config | | Processing caps | 5000 item limit, 1MB line limit, 10MB file size cap | | Transcript validation | Type, size, path, and file-type checks before parsing | | Command matching | Exact Set-based lookup for hook command names |

Debug logging is available via COMPAHOOK_DEBUG=1 (outputs to stderr to avoid corrupting hook stdout).

Platform Notes

| Concern | Status | |---------|--------| | Settings.json location | os.homedir() + '/.claude/settings.json' — works on all platforms | | Hook command resolution | Claude Code uses shell: true — resolves npm .cmd shims on Windows natively | | Path separators | All paths use path.join() — correct separators per OS | | Line endings | Parser uses crlfDelay: Infinity; all splits use .trim() to handle \r | | Shebangs | Irrelevant on Windows — npm .cmd shims call node directly |

Zero Dependencies

compahook uses only Node.js built-in modules (fs, path, os, readline). No external dependencies.

License

MIT