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.vscdbviarusqlite, readItemTable.composer.composerHeadersfor the cross-workspace composer index, then per composer pullcursorDiskKV.composerData:<id>and thecursorDiskKV.bubbleId:<comp>:<bubble>rows. Content- addressed file blobs undercomposer.content.<hash>are looked up while reading so the IR-builder is pure. - Provider: implements
toolpath_convo::ConversationProvider, pairing user/assistant bubbles into turns, liftingtoolFormerDataintoToolInvocation, decodingparams/resultJSON strings, and resolvingedit_file_v2to real before/after file content via the content store. - Derivation: produces
toolpath::v1::Pathdocuments through the sharedtoolpath_convo::derive_path. Edit-tool calls surface as sibling artifacts with the full unified diff asraw.
§Mapping
| Cursor source | Toolpath destination |
|---|---|
composerData.composerId | ConversationView.id, path.id = path-cursor-<first-8> |
composerData.modelConfig.modelName | Default Turn.model |
composerData.name | path.meta.title |
composerHeader.workspaceIdentifier.uri.fsPath | path.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[*].text | Turn.thinking |
bubble.toolFormerData | one 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.tokenCount | Turn.token_usage |
bubble.modelInfo.modelName | Turn.model (overrides composer default) |
Unknown toolFormerData.tool ids | preserved 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 thecomposer.content.<hash>blobs already keyed incursorDiskKV.~/.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-workspacecomposer.composerHeadersindex 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:
toolpath— core provenance typestoolpath-convo— provider-agnostic conversation abstractiontoolpath-claude— Claude Code providertoolpath-codex— Codex CLI providertoolpath-gemini— Gemini CLI providertoolpath-opencode— opencode providertoolpath-pi— Pi providerpath-cli— unified CLI (cargo install path-cli)
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_GLOB_FILE_SEARCH;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
PathResolverandDbReader. - paths
- Filesystem layout for Cursor state.
- project
- Maps a
ConversationViewback to aCursorSession. - provider
- Implementation of
toolpath-convotraits 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.mdfor the full field census.