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

cursor-acp-enriched

v0.4.0

Published

Recovers missing tool call arguments from Cursor's ACP events by reading the local SQLite store.db

Readme

cursor-acp-enriched

Recovers missing tool call arguments from Cursor's ACP (Agent Client Protocol) events by reading the local SQLite store.db.

The problem

Cursor's ACP stream emits tool_call notifications with an empty rawInput field — you can see that a tool was called but not what arguments it received (which file was read, which command was run, etc.).

Cursor does write the full tool call data — including arguments — to a local SQLite database. Recent Cursor builds use a flat layout:

~/.cursor/acp-sessions/<sessionId>/store.db

Older sessions still live under the legacy hashed layout, which this package also resolves transparently:

~/.cursor/chats/<hash>/<sessionId>/store.db

This package reads whichever database is present for a given session and recovers the missing arguments, using the toolCallId as the join key between ACP events and store.db blobs.

Installation

npm install cursor-acp-enriched

Quick start

import { CursorToolEnricher } from 'cursor-acp-enriched';

// sessionId comes from the ACP session notification
const enricher = new CursorToolEnricher(sessionId);

// toolCallId comes from the ACP tool_call event
const result = await enricher.enrich(toolCallId);

if (result) {
  console.log(result.toolName); // e.g. "Read"
  console.log(result.args); // e.g. { path: "/foo/bar.ts" }
  console.log(result.result); // e.g. "export default function main() {}"
  console.log(result.richResult); // structured data from Cursor (see below)
}

// With a polling timeout (retries until blob appears or timeout expires)
const result = await enricher.enrich(toolCallId, { timeoutMs: 2000 });

// Limit result length (default is 50 000 characters)
const result = await enricher.enrich(toolCallId, { maxResultLength: 5000 });

API reference

new CursorToolEnricher(sessionId, options?)

Creates an enricher for the given Cursor session.

| Option | Type | Default | Description | | ------------------ | -------- | ----------- | ----------------------------------- | | cursorDir | string | ~/.cursor | Override the Cursor data directory | | defaultTimeoutMs | number | 1000 | Default retry window for enrich() |

enricher.enrich(toolCallId, options?)

Looks up the tool call in store.db. Returns EnrichedToolCall | null.

If the blob isn't found immediately and timeoutMs > 0, retries with exponential backoff (50ms → 100ms → 200ms…) until the timeout is reached. Useful when the blob may not be written yet.

| Option | Type | Default | Description | | ----------------- | -------- | -------- | ------------------------------------------------- | | timeoutMs | number | | Overrides defaultTimeoutMs for this call | | maxResultLength | number | 50_000 | Truncates the recovered result to this many chars |

enricher.close()

Clears the cached store.db path. The next enrich() call will re-discover it.

findSessionStorePath(sessionId, options?)

Low-level helper. Scans ~/.cursor/chats/*/ for a subdirectory matching sessionId and returns the full path to store.db. Throws SessionNotFoundError if not found.

Types

interface EnrichedToolCall {
  toolCallId: string;
  toolName: string;
  args: Record<string, unknown>;
  result: string | null; // tool output; null when no tool-result blob was found
  richResult: Record<string, unknown> | null; // structured data from highLevelToolCallResult; null when absent
}

richResult — structured tool output

Cursor's store.db stores a providerOptions.cursor.highLevelToolCallResult field on tool-role blobs containing rich structured data that goes beyond the plain-text result. The enricher extracts this as richResult.

Examples of what each tool provides:

| Tool | richResult contains | | ----------- | ---------------------------------------------------- | | Grep / Glob | workspaceResults — file paths and matching lines | | WebSearch | references — URLs, titles, and content chunks | | StrReplace | diffString — before/after diff | | Write | diffString — full file diff | | Task | conversationSteps — subagent conversation activity | | Shell | exit code and full output (when Cursor includes it) |

richResult is null when the tool blob has no highLevelToolCallResult (e.g. older Cursor versions, or tools that don't provide it).

const result = await enricher.enrich(toolCallId);
if (result?.richResult) {
  // Grep example
  const workspaceResults = result.richResult.workspaceResults;

  // WebSearch example
  const references = result.richResult.references;

  // StrReplace/Write example
  const diff = result.richResult.diffString;
}

How it works

  1. Path discovery — scans ~/.cursor/chats/<hash>/<sessionId>/store.db to find the database for the session
  2. Blob correlation — opens the database read-only in WAL mode, iterates all rows in the blobs table; finds the type === "tool-call" entry for the given toolCallId (for args), the paired type === "tool-result" entry (for output), and the providerOptions.cursor.highLevelToolCallResult field (for structured rich data)
  3. Retry — if the blob isn't present yet (Cursor may not have flushed it), polls with exponential backoff up to the configured timeout

Limitations

  • macOS and Linux only (no Windows support in v1)
  • Depends on Cursor's internal store.db schema, which may change across Cursor versions
  • Read-only: does not write to or modify the database

License

MIT