Add Cursor agent transcript adapter#121
Merged
aktasbatuhan merged 1 commit intoJun 8, 2026
Merged
Conversation
The cursor adapter so far reads the IDE chat DB (state.vscdb). Cursor's agent also writes per-session JSONL transcripts to a second, disjoint store — ~/.cursor/projects/<slug>/agent-transcripts/<sid>/<sid>.jsonl — that the IDE DB never sees. This adds that source behind the same NAME="cursor": discover() yields both, scan() dispatches on the entry shape (composer entries carry a composer_id). Transcript-source specifics: - prompts extracted from <user_query> tags; editor-injected context (<attached_files>, <plugin_info>, ...) excluded - human-format <timestamp> tags parsed to UTC ISO; older transcripts carry no tags at all, so scan falls back to file mtime - <manually_attached_skills> entries recorded as pseudo-Skill tool calls (Cursor inlines skill content, so SKILL.md path detection alone goes blind); regular tool_use paths still go through extract_skill_from_args - no tokens/model/tool results exist in this format — those columns stay at defaults - project-dir slugs decode via decode_project_dir (same dash-flattened encoding as Claude Code, minus the leading dash) Also adds cr/Cursor/violet to the display touchpoints (util.py, metrics.py, insights.py, pipeline.py) — the IDE-store adapter landed without them — and refreshes the CONTRIBUTING wishlist (remaining gap is the cursor-agent CLI store at ~/.cursor/chats/, plus hooks). Validated against 126 real transcripts across 5 projects: 0 parse errors, 979 prompts, 9310 tool calls, all project dirs decoded. test_unknown_agent_falls_through_to_raw_slug now uses "windsurf" as its unknown slug — "cursor" stopped being one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fc4c10c to
9dddc1e
Compare
Member
|
Thanks for this, genuinely clean work. I pulled it down and ran it locally and it holds up well:
I just approved the workflow run so CI can go. This was your first PR here so GitHub held it for manual approval, nothing on your side. A few small, non-blocking things if you feel like it. None of these gate the merge:
Once CI is green I'll merge. Thanks again for picking this up. |
aktasbatuhan
approved these changes
Jun 8, 2026
aktasbatuhan
left a comment
Member
There was a problem hiding this comment.
Verified locally and CI is green across all platforms including Windows. Clean two-source design, solid docs. Merging. The optional follow-ups above can be a future PR if you want them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Cursor sessions were invisible to watchmen — the wishlist pencilled a Cursor adapter in as SQLite/
state.vscdbreverse-engineering, but Cursor's agent now writes plain JSONL transcripts under~/.cursor/projects/<slug>/agent-transcripts/, which makes coverage much cheaper than expected.What changed
src/watchmen/adapters/cursor.pyfollowing theNAME/discover()/scan()contract:<user_query>tags; editor-injected context tags (<attached_files>,<plugin_info>, …) excluded from prompt mining<timestamp>tags (Tuesday, Apr 28, 2026, 10:15 AM (UTC+3)) parsed to UTC ISO; older transcripts carry no tags at all, soscan()falls back to file mtime to keep sessions datable<manually_attached_skills>entries recorded as pseudo-Skilltool calls — Cursor inlines the skill content, so the SKILL.md-read detection in_sharedalone would go blind; regulartool_usepaths still go throughextract_skill_from_argsdecode_project_dir(same dash-flattened encoding as Claude Code, minus the leading dash)ADAPTERS;cr/Cursor/ violet added to the display touchpoints (util.py,metrics.py,insights.py,pipeline.py)tests/test_adapter_cursor.py+tests/fixtures/cursor_session.jsonl(12 tests: timestamp parsing incl. noon/midnight/negative/fractional offsets, prompt extraction, skill attribution, mtime fallback, discovery walk, corrupt-file tolerance)test_unknown_agent_falls_through_to_raw_slugnow useswindsurfas its unknown slug —cursorstopped being onestate.vscdb, no hooks" entry replaced with the two real remaining frontiers (IDEstate.vscdbchat history, Cursor 1.7+ hooks host inhooks_setup.py)Testing
uv run pytest tests/— 300 passeduv tool run ruff check src/watchmen tests— cleanuv build— wheel + sdist buildcoderabbit-respond×34,search-company-knowledge×14, …);watchmen ingest+ mission control render the new agent slice end-to-endNotes
agentcolumn already generic).🤖 Generated with Claude Code