V0.1.3/m2/chat history capture#5
Open
Vedansi18 wants to merge 51 commits into
Open
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nt sets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ofile Adds selectAbsenceMap helper (hardcore_pro → formal, else → casual) and updates resolveDecisionContent to use it for non-vibe profiles. Updates 6 no-profile tests and 2 priority-override tests to assert casual variants, consistent with selectNonBeginnerVariant's undefined → casual behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 3 hardcore_pro routing tests for remaining formal absence variants, add all 8 non-beginner absence sets to allContent and per-set count tests, add 4 beginner absence sets to C-02 structural validation block with missing imports for ABSENCE_REGRESSION_CHECK_BEGINNER and ABSENCE_SPEC_ACCEPTANCE_BEGINNER. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three changes to buildMjsScript() in TtySelectFn.ts: - Fix _lineCount to split labels by \n and compute per-line visual rows instead of treating the whole label as one long string; this correctly estimates height for multi-line (numbered-steps) option labels - Add wrapping guard: when all options fit within budget (no-overflow) but their total visual lines exceed the option count, recompute _maxItems using the option count as a tight budget ceiling so visually dense option sets always get a viewport rather than a flat list - Pass maxItems to select() so @clack/prompts k() uses _maxItems as the viewport window instead of rows-4; without this, maxItems computation had no effect on the actual rendered UI Applies to Mac, Windows, and Linux new-window paths (all share buildMjsScript). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…word lists Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ded vibeKeyword sets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds formal, casual, and beginner DecisionContent objects for idea_scoping, idea_constraint_check, and idea_user_definition. Includes content existence tests covering shape and non-empty strings. Map wiring deferred to Phase 4. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds formal, casual, and beginner DecisionContent objects for task_ordering, task_sizing, and task_definition_of_done. Includes content existence tests covering shape and non-empty strings. Map wiring deferred to Phase 4. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds formal, casual, and beginner DecisionContent objects for user_feedback_review and iteration_planning. Wires all 8 new signal keys (Groups A, B, C) into ABSENCE_CONTENT, ABSENCE_CONTENT_CASUAL, and ABSENCE_CONTENT_BEGINNER. Adds 24 routing tests covering all 3 registers for all 8 signals, plus Group C content existence tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…INNER Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cture test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This reverts commit 2793678.
… map Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…NG_CASUAL Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 8 new absence signal definitions in signals.ts (scope_creep, context_loss, api_design_review, accessibility, environment_and_secrets, data_validation, ci_pipeline, rate_limiting) - 24 new content set constants across 3 registers (formal, casual, beginner) in options.ts and options-beginner.ts - All 8 keys added to ABSENCE_CONTENT, ABSENCE_CONTENT_CASUAL, ABSENCE_CONTENT_BEGINNER maps - relevantProjectTypes filter on api_design_review, accessibility, rate_limiting signals - AbsenceDetector.ts: Gate 3 now uses per-signal absenceThreshold with profile multiplier; project-type gate added - types.ts: relevantProjectTypes field added to SignalDefinition - auto.ts: projectType extracted from getProject() and passed to detectAbsenceFlags - Tests: routing, content existence, structure, and buildOptionList coverage for all 8 new signals Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Branch 1 of Milestone M2 (v0.1.3/m2/extension-skeleton). Establishes the
src/ext-vscode/ sub-package with an esbuild-driven build pipeline
(ESM source -> CJS bundle for the VS Code host), activates on
onStartupFinished, and ships the four scoped modules:
M1 - Skeleton: package.json (activationEvents, activity-bar container +
placeholder view backed by viewsWelcome so the icon actually renders),
tsconfig.json, esbuild.config.mjs, src/extension.ts entrypoint.
M5 - IPC stub: src/ipc.ts. spawnAuto(prompt, sessionId) and
spawnStop(sessionId) spawn the nexpath CLI as subprocesses and parse
the decision-session JSON payload from stdout, with typed errors
(NexpathBinaryNotFoundError, NexpathMalformedPayloadError) and
configurable binary-path resolution
(opts.binaryPath -> NEXPATH_BIN env -> 'nexpath' on PATH).
The exact stdin envelope vs. Layer C input contract is intentionally
a stub here; Branch 4 (cursor-windsurf-adapters) finalises it.
M11 - Onboarding: src/onboarding.ts. First-launch consent toast persists
the user's choice to globalState; on macOS, additionally shows a
Full-Disk-Access guidance toast that deep-links to the System
Settings privacy pane.
M12 - Icon: media/icon.svg. Y-fork (branching path) representing
"next path" decision points; monochrome currentColor, scalable.
25 unit tests co-located alongside source (8 onboarding, 11 ipc, 6 extension),
runnable via root vitest with vi.mock('vscode') stubs. Sub-package has its
own tsconfig + package-lock; root tsconfig now excludes src/ext-vscode/ so
each side owns its TS build. Both root and sub-package tsc --noEmit are
clean. Full root test suite: 1851 passing + 18 pre-existing unrelated
TtySelectFn Windows-sim failures (carried forward from dev plan §3.0).
Deferred (flagged for follow-up, not blockers for this branch):
- 5 moderate npm-audit warnings in the esbuild -> vite -> vitest dev chain
(dev-only; will be addressed during M5 hardening).
- IPC stdin envelope contract: real wiring lands in Branch 4.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Branch 2 of Milestone M2 (v0.1.3/m2/chat-history-capture). Stacked on M2 Branch 1 (commit 879ed5e). Adds the three scoped modules: M2 - chat-history-watcher.ts: fs.watch on Cursor's state.vscdb and Windsurf's ~/.codeium/windsurf/ dir, debounced (default 250ms), reads ItemTable via injectable readItemTableFn (sql.js by default), diffs against seenSignatures, emits {prompt, rawSessionId, capturedAt, sourcePath, extractorId}. Dependency-injectable throughout (watchFn, readFileFn, readItemTableFn, nowFn) so the unit tests run without sql.js or real fs.watch. M3 - extractors/: four per-version row decoders implementing the ChatHistoryExtractor contract from chat-history-types.ts. - cursor-v2024-q4 (aiService.prompts global key, pre-Composer) - cursor-v2025-q1 (composerData.composerData, Composer era) - cursor-v2025-q2 (cursorAIChatService.chatHistory.<tabId> per-tab keys, current) - windsurf (cascade.* placeholder; real Windsurf decoding lands in Branch 4 alongside windsurfAdapter) Each Cursor extractor handles both `role`/`type` and `content`/`text` field variants seen across minor versions. All four are TODO-flagged for verification against real dumps before Branch 6 publishes — scripts/dump-cursor-state.ts (below) captures those dumps. M4 - pickExtractor in extractors/index.ts: prefix-match each extractor's fingerprintKeys against the observed ItemTable keys, pick the highest match count (ties broken by registry order = newest first). Returns FingerprintResult; unknown schemas surface observedSampleKeys for the "schema unknown" toast hook. scripts/dump-cursor-state.ts: dev-only helper (npx tsx) for capturing state.vscdb fixtures from a machine with Cursor installed. Filters to chat-related key prefixes, optional --redact for sensitive content. Outputs to src/ext-vscode/test-fixtures/state-vscdb-samples/. Sub-package additions: - dependencies: sql.js ^1 (runtime; loaded via dynamic import so wasm boot is lazy). Marked external in esbuild so the .vsix ships node_modules/sql.js rather than inlining it. - devDependencies: tsx ^4 (for running the dump script). 57 new unit tests (sub-package totals: 82 passing across 9 files): cursor-v2024-q4 9 tests cursor-v2025-q1 10 tests cursor-v2025-q2 11 tests windsurf 4 tests extractors/index 12 tests chat-history-watcher 11 tests Verification: root tsc --noEmit clean; sub-package tsc --noEmit clean; sub-package vitest 82/82 pass; full root test suite 1908 passing + 18 pre-existing TtySelectFn Windows-sim failures (carried forward from M1 3.0, unrelated); esbuild bundle still builds out/extension.js. Deferred to follow-up (flagged, not blockers): - Real-dump verification of all 4 extractors (use dump-cursor-state.ts on machines with each Cursor version installed; replace TODO comments in extractors with fixture-driven regression tests). - Windsurf JSON-file decoder (Branch 4). - Wiring the watcher into extension.ts activate() (Branch 3 webview-ui or Branch 4 adapters — depends on UI surface integration). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real-machine inspection on Cursor 3.4.20 (2026-05-15) surfaced three issues with the Branch 2 extractor designs. This commit fixes the verifiable ones, captures redacted fixtures, and documents the still- unknown bits for the next round. Issue 1 — SQLite WAL mode. The dump script previously used sql.js, which only reads the buffer of the main `.vscdb` file. Live Cursor writes go to the sibling `.vscdb-wal` (185 KB while the main file was 4 KB), so sql.js saw "no such table: ItemTable" even though the table exists. Fix: switched the dump script to better-sqlite3 (native, WAL-aware). Copies main + wal + shm siblings to a tmp staging dir before reading so the live Cursor write path is never touched, then runs `PRAGMA wal_checkpoint(TRUNCATE)` on the staged copy for consistency. The PRODUCTION watcher in `chat-history-watcher.ts` still uses sql.js via dynamic import; the same WAL problem will surface when Branch 4 wires the watcher live. Flagged for Branch 4 design — options are: (a) switch the watcher to better-sqlite3 (native binding in .vsix), or (b) implement copy + checkpoint via sql.js. Out of scope for B2. Issue 2 — `cursor-v2025-q1` extractor's fingerprint key was wrong. Community docs said `composerData.composerData`; Cursor 3.4.20 actually uses `composer.composerData`. Updated the key in both the extractor and its tests + the fingerprint test. Open finding: the `composer.composerData` value on a chat-less Cursor 3.4.20 workspace DB is metadata only (selectedComposerIds, migration flags) — not the conversation messages this extractor's decodeRow logic parses for. Logic falls through cleanly (returns [] when the expected `allComposers` field is absent) and the JSDoc now documents that the real Composer message storage location is still TBD and needs a post-chat snapshot to confirm. Issue 3 — `cursor-v2025-q2` extractor's fingerprint prefix (`cursorAIChatService.chatHistory.`) was NOT observed on Cursor 3.4.20. The extractor still ships (in case older versions use it) but the JSDoc now flags this as unverified and points to the dump script for capturing a real fixture before Branch 6 ships. Dump script additions: - Discovers ALL state.vscdb under Cursor's config tree (global + per-workspace) — chat messages live in the workspace DB, not global. - Dumps both `ItemTable` (filtered to chat-related key prefixes) AND `cursorDiskKV` (Cursor 3.x's parallel KV table; currently empty but may hold Composer messages once chats happen). - One output JSON per discovered DB; suffixed with `global` or `workspace-<id>` for traceability. - `--redact` replaces string values > 8 chars with same-length asterisks. Dependencies: - Added better-sqlite3 ^11 + @types/better-sqlite3 ^7 as devDependencies in the sub-package. Dev-only — the production extension bundle is unaffected. Captured fixtures (redacted) — all three DBs from a chat-less Cursor 3.4.20 session, committed for regression testing: - cursor-3-4-20-initial-global.json (9 rows) - cursor-3-4-20-initial-workspace-1778826246907.json (7 rows) - cursor-3-4-20-initial-workspace-empty-window.json (2 rows) Verification: - Sub-package tsc --noEmit clean. - Sub-package vitest 82/82 pass. - Root tsc --noEmit clean. - Full root test suite 1908 passing + 18 pre-existing TtySelectFn carry-forward. Next step (manual, user-driven): submit a real prompt in Cursor's Ask mode AND in Composer mode, then re-run the dump script to capture a chat-bearing snapshot. The new keys / tables that appear will pin down the Composer-mode message storage location, and a follow-up commit will finalise the extractor decode logic against that real data. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the unit-test audit gap surfaced for M2 Branch 2. The dump script
had real business logic (`redactValue`, `shouldKeepItemTable`,
`parseArgs`, `cursorConfigRoot`, `discoverAllStateVscdb`) with zero test
coverage — `redactValue` in particular has data-leak consequences if
buggy.
Extracted the pure / near-pure helpers into a new module:
- `src/cursor-state-dump-helpers.ts` — lives under tsconfig rootDir
so it's typechecked by the sub-package's main `tsc --noEmit` and
auto-picked-up by vitest. Re-exports `KEEP_ITEMTABLE_PREFIXES`,
`shouldKeepItemTable`, `redactValue`, `cursorConfigRoot`,
`discoverAllStateVscdb` (with injectable fs helpers), and
`parseArgs` (returns a tagged-union result instead of calling
`process.exit`, so the error paths are testable).
Co-located tests: `src/cursor-state-dump-helpers.test.ts` — 28 tests
covering:
- `shouldKeepItemTable`: each default prefix matched, unrelated keys
dropped, custom prefix lists, prefix-not-exact match.
- `redactValue`: short-string preservation, long-string redaction,
nested object/array recursion, non-string value preservation, bulk
redact for non-JSON input, JSON-string root, exact 9-char boundary.
- `cursorConfigRoot`: linux / darwin / win32 / unknown-platform paths
and APPDATA fallback.
- `discoverAllStateVscdb`: empty tree, global-only, global + multiple
workspaces, skip workspace dirs missing the DB, injectable fs.
- `parseArgs`: required `--name`, optional `--src` / `--redact`,
`--help` / `-h` signal, missing-value rejection, unknown-argument
rejection.
Script entry-point `scripts/dump-cursor-state.ts` now imports from
`../src/cursor-state-dump-helpers.js` and retains only the I/O
orchestration (file copy to tmp staging dir, better-sqlite3 read, fixture
write). Behaviour is byte-for-byte unchanged — verified by re-running
against the live machine and producing identical row counts to the
previous commit (`3794bc3`).
Sub-package totals:
- Test files: 10 (was 9)
- Tests: 110 passing (was 82) — +28 helpers tests
- Sub-package tsc --noEmit clean
- Root tsc --noEmit clean
- Full root suite: 1936 passing + 18 pre-existing TtySelectFn
Windows-sim failures (M1 §3.0 carry-forward, unrelated)
Closes the only remaining audit gap for M2 Branch 2. No further unit-test
work pending; per the auto-commit rule the branch is now closed pending
push.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Adds the chat-history capture layer of the VS Code extension —
modules M2 (watcher), M3 (per-version extractors), and M4
(schema fingerprint) per dev plan §3 M2 §2.2. The watcher monitors
Cursor's
state.vscdb(and the Windsurf chat-history dir), debouncesfs events, fingerprints the schema, dispatches rows to the appropriate
extractor, and emits normalised
ChatHistoryEvents — without touchingany Layer C file. Also ships a dev-only
dump-cursor-state.tshelper(Option C) for capturing verified fixtures from real Cursor installs.
Stacked on M2 Branch 1 (
v0.1.3/m2/extension-skeleton,commit
879ed5e). Branch 2 needs Branch 1's sub-package skeleton, sothis PR's diff is meaningful only against B1, not against sub-7. See
"PR strategy" below.
Modules covered (per dev plan §3 M2 §2.2)
src/chat-history-watcher.ts+chat-history-types.tsfs.watchon each target with 250 ms debounce; readsItemTablevia injectablereadItemTableFn(sql.js by default);seenSignatures; emits{ prompt, rawSessionId, capturedAt, sourcePath, extractorId }. All deps (watchFn,readFileFn,readItemTableFn,nowFn) injectable for testing.src/extractors/cursor-v2024-q4.ts,cursor-v2025-q1.ts,cursor-v2025-q2.ts,windsurf.tsChatHistoryExtractorcontract. Eachrole/typeandcontent/textfield variants. Windsurf is a deliberate placeholder — real JSON-file decoding lands in B4 alongsidewindsurfAdapter.src/extractors/index.tspickExtractor(observedKeys)— prefix-matches each extractor'sfingerprintKeysagainst the observedItemTablekeys, picks theFingerprintResult(kind:'known'or'unknown'). The'unknown'payload is surfaced by the watcher viaonSchemaUnknownfor the eventual "schema unknown" toast (wiring in B3/B4).scripts/dump-cursor-state.ts+src/cursor-state-dump-helpers.tsItemTable(filtered) +cursorDiskKV, optional--redactfor sensitive content. Helpers live insrc/so they're typechecked + unit-tested.Real-machine inspection findings (Cursor 3.4.20 / Linux)
This branch's commit
3794bc3includes real-data refinements afterrunning the dump script against a live Cursor 3.4.20 install:
.vscdbis 4 KB; live writes go to.vscdb-wal. sql.js cannot read WAL siblings; better-sqlite3 can.User/globalStorage/state.vscdbAND everyUser/workspaceStorage/*/state.vscdb.cursor-v2025-q1key was wrong — community docs saidcomposerData.composerData; real key iscomposer.composerData.cursor-v2025-q1.ts+ tests. Value shape (`allComposers /cursor-v2025-q2prefixcursorAIChatService.chatHistory.NOT observed on Cursor 3.4.20.test-fixtures/state-vscdb-samples/cursor-3-4-20-initial-{global,workspace-*}.json.Files
src/ext-vscode/src/chat-history-types.tssrc/ext-vscode/src/chat-history-watcher.tssrc/ext-vscode/src/chat-history-watcher.test.tssrc/ext-vscode/src/extractors/cursor-v2024-q4.tssrc/ext-vscode/src/extractors/cursor-v2025-q1.tssrc/ext-vscode/src/extractors/cursor-v2025-q2.tssrc/ext-vscode/src/extractors/windsurf.tssrc/ext-vscode/src/extractors/index.tssrc/ext-vscode/src/cursor-state-dump-helpers.tssrc/ext-vscode/scripts/dump-cursor-state.tssrc/ext-vscode/test-fixtures/state-vscdb-samples/*.jsonsrc/ext-vscode/package.jsonsql.jsruntime dep, +better-sqlite3+tsxdevDepssrc/ext-vscode/esbuild.config.mjssql.jsmarked external85 new unit tests added by this branch (sub-package total: 110 across 10 files).
Test plan
Automated