Skip to main content

Crate toolpath_cursor

Crate toolpath_cursor 

Source
Expand description

§toolpath-cursor

Derive Toolpath provenance documents from Cursor sessions.

Cursor stores every conversation across two places — a thin JSONL transcript at ~/.cursor/projects/<slug>/agent-transcripts/<id>/<id>.jsonl (the agent’s I/O log, lossy) and a rich SQLite bubble store at ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb (the source of truth). This crate reads the SQLite store (read-only, with content-blob lookup) and maps it to Toolpath documents.

§Overview

  • Reading: open state.vscdb via rusqlite, read ItemTable.composer.composerHeaders for the cross-workspace composer index, then per composer pull cursorDiskKV.composerData:<id> and the cursorDiskKV.bubbleId:<comp>:<bubble> rows. Content- addressed file blobs under composer.content.<hash> are looked up while reading so the IR-builder is pure.
  • Provider: implements toolpath_convo::ConversationProvider, pairing user/assistant bubbles into turns, lifting toolFormerData into ToolInvocation, decoding params / result JSON strings, and resolving edit_file_v2 to real before/after file content via the content store.
  • Derivation: produces toolpath::v1::Path documents through the shared toolpath_convo::derive_path. Edit-tool calls surface as sibling artifacts with the full unified diff as raw.

§Mapping

Cursor sourceToolpath destination
composerData.composerIdConversationView.id, path.id = path-cursor-<first-8>
composerData.modelConfig.modelNameDefault Turn.model
composerData.namepath.meta.title
composerHeader.workspaceIdentifier.uri.fsPathpath.base.uri (as file://…)
composerData.agentBackend ("cursor-agent")producer.name ("cursor")
Bubble type: 1 (user)Step with actor: "human:user"
Bubble type: 2 (assistant)Step with actor: "agent:<model>"
bubble.allThinkingBlocks[*].textTurn.thinking
bubble.toolFormerDataone entry in Turn.tool_uses[]
toolFormerData.tool == 38 (edit_file_v2)ToolCategory::FileWrite + FileMutation with resolved before/after content and unified diff
toolFormerData.tool == 15 (run_terminal_command_v2)ToolCategory::Shell + ToolResult { content: output, is_error: exitCode != 0 }
toolFormerData.tool == 40 (read_file_v2)ToolCategory::FileRead
toolFormerData.tool == 42 (glob_file_search)ToolCategory::FileSearch
bubble.tokenCountTurn.token_usage
bubble.modelInfo.modelNameTurn.model (overrides composer default)
Unknown toolFormerData.tool idspreserved as ToolInvocation { category: None }name + input still carry the call

Provider-specific UI metadata (checkpointId, requestId, capabilityType, richText, Lexical editor DOM, the anysphere.cursor-commits checkpoint store, the AI-authorship SQLite db) is intentionally not smuggled through the IR — those fields don’t belong in a cross-harness representation, and the IR has no Turn.extra slot. They round-trip to nothing.

§Usage

use toolpath_cursor::{CursorConvo, derive::{DeriveConfig, derive_path}};

let manager = CursorConvo::new();
let composer_id = "724686cd-875e-47da-a90b-dbc3e523efb8";
let session = manager.read_session(composer_id)?;
let path = derive_path(&session, &DeriveConfig::default());

§What’s not read

  • ~/Library/Application Support/Cursor/User/globalStorage/anysphere.cursor-commits/ — per-request file snapshots. Their contents duplicate the composer.content.<hash> blobs already keyed in cursorDiskKV.
  • ~/.cursor/ai-tracking/ai-code-tracking.db — per-line AI-vs-human authorship stats. Derivable from the bubble store + git.
  • ~/Library/Application Support/Cursor/User/workspaceStorage/<id>/state.vscdb — per-workspace UI state. The cross-workspace composer.composerHeaders index in the global database covers session listing without it.
  • cursorAuth/* keys — live OAuth tokens. Never read.
  • ~/Library/Application Support/Cursor/logs/ — Chromium / renderer logs.

See docs/agents/formats/cursor.md in the workspace for the full on-disk format reference.

§Part of Toolpath

This crate is part of the Toolpath workspace. See also:

Re-exports§

pub use derive::DeriveConfig;
pub use derive::derive_path;
pub use derive::derive_project;
pub use error::CursorError;
pub use error::Result;
pub use io::ComposerListing;
pub use io::CursorIO;
pub use paths::EnsuredWorkspaceId;
pub use paths::PathResolver;
pub use paths::slug_from_abs_path;
pub use project::CursorProjector;
pub use provider::CursorConvo;
pub use provider::PROVIDER_ID;
pub use provider::session_to_view;
pub use provider::tool_category;
pub use reader::DbReader;
pub use types::BUBBLE_TYPE_ASSISTANT;
pub use types::BUBBLE_TYPE_USER;
pub use types::Bubble;
pub use types::BubbleGrouping;
pub use types::BubbleHeader;
pub use types::CAPABILITY_THINKING;
pub use types::CAPABILITY_TOOL;
pub use types::ComposerData;
pub use types::ComposerHead;
pub use types::ComposerHeaders;
pub use types::CursorSession;
pub use types::CursorSessionMetadata;
pub use types::ModelConfig;
pub use types::ModelInfo;
pub use types::SelectedModel;
pub use types::TOOL_EDIT_FILE_V2;
pub use types::TOOL_READ_FILE_V2;
pub use types::TOOL_RUN_TERMINAL_COMMAND_V2;
pub use types::ThinkingBlock;
pub use types::TokenCount;
pub use types::ToolFormerData;
pub use types::WorkspaceIdentifier;
pub use types::WorkspaceUri;

Modules§

derive
Derive Toolpath documents from Cursor sessions.
error
io
Higher-level filesystem + DB operations over PathResolver and DbReader.
paths
Filesystem layout for Cursor state.
project
Maps a ConversationView back to a CursorSession.
provider
Implementation of toolpath-convo traits for Cursor sessions.
reader
Read-only SQLite access to Cursor’s global state.vscdb.
types
On-disk schema for Cursor’s bubble store. See docs/agents/formats/cursor.md for the full field census.