The claude-code provider spawns the bundled `claude` CLI via
@cline/sdk → ai-sdk-provider-claude-code → @anthropic-ai/claude-agent-sdk.
On macOS, when that subprocess is launched from inside the VS Code extension
host it performs IDE auto-connect, which shells out to `osascript`
(`tell application …`) to identify/activate the editor — raising the macOS
"control other applications" (Apple Events / Automation) permission prompt.
There is no AppleScript/osascript anywhere in the JS dependency tree (the only
match is the build-only `vite`); the Apple Event is sent by the spawned `claude`
binary, which contains 26 osascript calls. Every one is user-action-gated
(clipboard image, screenshot, terminal-setup, Claude-Desktop deep link, browser
detection) except the IDE auto-connect path, which is the only one that fires
without user action when launched inside an IDE — exactly the reported symptom
(appears on use, harmless when declined, since we never use the connection).
Fix: spawn the agent with the macOS IDE integration disabled via the provider's
`options.env` passthrough — `CLAUDE_CODE_AUTO_CONNECT_IDE=0` hard-returns out of
the binary's auto-connect gate (`mK("0") === true`), and
`CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL=1` skips the extension-install deep link.
`options.env` is seeded with the full `process.env` so the default environment
inheritance is unchanged (login under HOME, proxy/CA vars preserved) — only the
two switches are added. The env flows end to end into the spawned child
(verified through the provider's getBaseProcessEnv merge and the agent SDK's
spawn). runEditTurn is the sole agent-spawn site, so this covers every flow.
Unit test asserts the agent is constructed with the two switches set and the
rest of the environment preserved. 250 unit + typecheck + build green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vscode-cowriting-plugin
Non-shippable proof-of-concept (Feature #2 of Epic #1): a standalone VS Code
extension that drives @cline/sdk
— validating Approach A (own coauthoring extension on the Cline SDK, no fork).
What it does
Registers one command, Cowriting: Show Cline SDK Info, which loads
@cline/sdk and shows the SDK build version plus the agent's builtin tool
catalog (a pure, key-free SDK call) in a notification and the
"Cowriting (Cline SDK)" output channel.
Features shipped so far: F2 region-anchored threads (Feature #4), F3 live human/Claude attribution (Feature #6), F4 propose/accept diff flow (Feature #12), F5 cross-rung sidecar contract (Feature #14), F6 diff-view toggle (Feature #17), F10 interactive review — write left / review right (Feature #29), and F11 — the preview toolbar as the primary interaction surface (Feature #43).
Architecture
- CommonJS extension bundled with esbuild (
src/extension.ts→out/extension.cjs). @cline/sdkis ESM-only (Node ≥22) and usescreateRequire(import.meta.url), so it is not bundled — it is shipped innode_modulesand loaded at runtime via dynamicimport()from the vscode-freesrc/cline.ts.
Run it (F5)
npm installnpm run build- Press F5 (or Run → "Run Extension") to launch the Extension Development Host.
It opens the committed
sandbox/playground as its workspace (not the repo itself — VS Code won't open one folder in two windows, #8); start withsandbox/playground.md. - In the new window: Cmd/Ctrl+Shift+P → "Cowriting: Show Cline SDK Info".
F2 — Region-anchored threads (Feature #4)
Attach durable, region-anchored discussion threads to any document. Threads
render in the native VS Code Comments gutter and persist as git-native
sidecars under .threads/<doc-path>.json (plain, diffable JSON — no server).
- Create: select text → run "Cowriting: Add Coauthoring Thread on Selection" (or the Comments gutter "+").
- Reply / Resolve: use the native Comments reply box and the thread's Resolve/Reopen actions.
- Survives edits, reload, and external change (
git pull): a hybrid anchor (durable content fingerprint + live offset tracking) re-resolves the thread. If the anchored text can't be confidently re-found, the thread is shown as orphaned at its last-known line — never silently moved.
Design: vscode-cowriting-plugin-content/specs/coauthoring-inner-loop.md. No live
@cline/sdk turn and no credentials are involved in F2.
F3 — Live human/Claude attribution (Feature #6)
As you and Claude coauthor, every span in the buffer carries an author: human edits render with a subtle left border, Claude-authored spans with a background tint. Text that predates tracking stays plain — the honest record. Edits that touch a boundary (split, merge, partial overwrite) are handled char-precisely.
Attribution is persisted git-natively in the same .threads/ sidecars
(attributions[], sharing the same anchors fingerprints as F2 threads). On
reload, fingerprints re-resolve spans against the current document; spans that
can't be confidently re-found are orphaned (status-bar count + "Cowriting
Attribution" output channel) rather than silently moved or discarded.
Commands
Cowriting: Ask Claude to Edit Selection— select text → enter an instruction → a live@cline/sdkturn runs on the built-inclaude-codeprovider (rides your local Claude Code Pro/Max login; the extension stores no credentials). As of F4 the turn ends in a proposal (see below); accepted text lands as a Claude-attributed span.Cowriting: Toggle Attribution— show/hide attribution decorations.cowriting.applyAgentEdit(palette-hidden) — the single machine-edit ingress seam. Tests drive this directly so CI requires no LLM.
Design: vscode-cowriting-plugin-content/specs/coauthoring-attribution.md.
F4 — Propose/accept diff flow (Feature #12)
Claude's edits arrive as pending proposals — propose-by-default, the
document never changes without your say-so. A proposal renders two ways at
once: an amber tint on the target range, and a "Claude proposes" comment
thread showing a fenced diff of current → proposed text with two actions:
- ✓ Accept Proposal — applies the replacement through the
applyAgentEditseam, so it lands Claude-attributed (F3) — and the proposal disappears. - ✗ Reject Proposal — the document is untouched; the proposal disappears.
Pending proposals persist git-natively in the same sidecar (proposals[],
sharing the F2/F3 anchors fingerprints), survive reload, and re-anchor as
surrounding text changes. If the target text itself changes, the proposal
goes stale (status-bar count, accept disabled — never applied by guess);
undo the change and it becomes decidable again. cowriting.proposeAgentEdit
(palette-hidden) is the programmatic propose ingress E2E drives — no LLM in
CI.
Design: vscode-cowriting-plugin-content/specs/coauthoring-propose-accept.md.
Live smoke: docs/MANUAL-SMOKE-F4.md.
F5 — Cross-rung format + round-trip (Feature #14)
The .threads/ sidecar is the published cross-rung contract: any rung of
the ladder (this editor → Gitea substrate → rfc-app) reads and writes the same
record per the contract; git push/pull is the transport, no re-homing ever.
- Normative contract (INV-14):
vscode-cowriting-plugin-content/specs/coauthoring-sidecar-contract.md— format changes land there first, then schema, then code. - Machine-checkable half:
schemas/coauthoring-sidecar.schema.json; validate any sidecar from any rung withnode scripts/validate-sidecar.mjs <file>. The unit suite validates every serialized artifact — contract drift fails CI. - Identity crosses rungs:
Provenance.email(git's own join key — populated from the workspace git config when available) andagent.onBehalfOf(who the machine acted for). - Writers preserve unknown fields (INV-15): rewriting a sidecar never destroys another rung's data — unknown keys survive, after known keys, sorted.
- Newer-major sidecars are read-only (INV-16): the editor renders what it understands, warns once, and writes nothing (the store refuses as a backstop).
- Deterministic merge (INV-17):
src/mergeArtifacts.ts— union-by-id, documented tie-breaks, every resolved divergence surfaced inconflicts. - The round-trip is proven, not asserted:
scripts/crossrung-reply.mjsis a self-contained conforming foreign writer (the Gitea-rung stand-in); host E2E drives editor-thread → stand-in reply → external-change → the reply renders in the thread.
Design: vscode-cowriting-plugin-content/specs/coauthoring-cross-rung-format.md.
F6 — Diff-view toggle (Feature #17, #19)
Ctrl+Alt+D (the same chord on macOS — not Cmd; or Cowriting: Toggle
Diff View) flips the focused document into a native vscode.diff against a
coauthoring baseline — the readonly baseline on the left, your live,
editable document on the right (so you keep writing inside the diff; toggling
again closes it). The diff answers "what did I change?" in one keystroke
instead of git archaeology.
- Works on any file (#19): any document you can edit — a file inside or outside the workspace folder, or an untitled scratch buffer. Only a non-text-editor focus warns. (Untitled buffers diff in-memory; saving makes the baseline persist.)
- Machine-factored baseline (INV-18): the baseline initializes when a doc
is first seen and advances automatically at every machine landing (every
successful
applyAgentEditseam apply — INV-9). So text Claude landed never shows as a change; everything the diff shows is operator-authored by construction — no attribution filtering. - Pin on demand: Cowriting: Pin Diff Baseline to Now resets the
baseline to the current buffer for a deliberate "review my next pass" epoch;
the diff tab title names the epoch (
opened/Claude landed/pinned). - Pure view, repo-free (INV-19): the baseline snapshot lives in VS Code
global extension storage, keyed by a hash of the document URI, never
the repo —
.threads/, the cross-rung contract (INV-14..17), andSCHEMA_VERSIONare untouched. Storage-unavailable degrades to in-memory baselines + one warning. - No LLM in CI: host E2E (
test/e2e/suite/diffView.test.ts) drives the same programmatic seam ingress (propose + accept) the F4 suite uses.
Design: vscode-cowriting-plugin-content/specs/coauthoring-diff-view.md.
Live smoke: docs/MANUAL-SMOKE-F6.md.
F7 — Rendered track-changes preview (Feature #21)
Ctrl+Alt+R (or Cowriting: Open Track-Changes Preview) opens a
read-only webview beside a Markdown editor that renders the document and
marks what changed since the F6 baseline — the "track changes" / "suggesting
mode" altitude rather than a raw-text split-diff:
- Prose additions are highlighted (
<ins>), deletions struck (<del>), refined to the word. - Code and mermaid fences are diffed whole (atomic, INV-23): a changed or added one renders fully with a small "changed" badge; a removed one renders struck. Mermaid fences render as diagrams (mermaid runs in the webview). Intra-diagram node/edge diffing is deferred (#22).
- It updates live as you and Claude edit (debounced), and re-bases when Claude lands an edit (baseline advances, INV-18) or you pin — so accepted text drops its marks. It reuses the F6 baseline and adds no persistence (pure read-only, INV-20).
- The webview is sealed (INV-21): local bundled assets only, strict CSP with a per-load nonce, no network/CDN, no LLM. Mermaid is bundled into the webview asset only, never the extension-host bundle.
- The render engine (
src/trackChangesModel.ts) is a pure, vscode-free function (INV-22), unit-tested with no editor and no webview; host E2E (test/e2e/suite/trackChangesPreview.test.ts) drives the same programmatic propose/accept seam with no LLM.
F7 is markdown-only; for any other file (incl. code), use F6's diff toggle
(Ctrl+Alt+D). The webview's visual rendering (mermaid, theming) is verified by
the manual smoke, not the sealed-sandbox E2E.
Design: vscode-cowriting-plugin-content/specs/coauthoring-rendered-preview.md.
Live smoke: docs/MANUAL-SMOKE-F7.md.
F9 — Authorship view in the preview (Feature ~#27)
The rendered preview (F7) gains a second mode, switched by a [ Track changes | Authorship ] toggle in its header. Authorship mode re-renders the current
document with each span colored by its F3 author — Claude (blue) vs you (green),
inline and char-precise — with a legend. Unlike track-changes (which diffs against
the F6 baseline, and so hides Claude's text once the baseline advances past a
landing), authorship reads F3 attribution directly, so Claude's contributions stay
visible. Code/mermaid fences carry a block-level author badge (atomic). Read-only,
sealed webview, no new persistence (INV-26..28).
Design: docs/superpowers/specs/2026-06-11-authorship-preview-design.md.
Live smoke: docs/MANUAL-SMOKE-F9.md.
F8 — Out-of-workspace authoring (Feature #25)
"Ask Claude to Edit Selection" (and F2 threads / F3 attribution / F4 propose-accept) now work on any document the editor shows — saved in the workspace folder, saved outside it, or untitled — matching the already-universal F6 diff and F7 preview. Authoring is no longer gated to in-workspace files, and the commands are live even with no folder open.
Persistence is hybrid (one SidecarStore abstraction, routed per-document):
- an in-workspace file keeps its committable
.threads/<repo-rel>.jsonsidecar, byte-for-byte unchanged (INV-2) — the only home the F5 cross-rung contract ever sees; - an out-of-workspace file or untitled buffer stores its coauthoring
artifact in VS Code global storage keyed by
sha256(uri)(the same home and key F6's baseline uses, INV-19/24); untitled buffers are in-memory only (lost on reload/save).
A global-storage artifact is not a committed file, so it is never cross-rung-shareable (INV-25), and renaming/moving the file orphans its artifact (the key is the URI hash) — both stated as design contract, not discovered later. Routing leaves the in-workspace path untouched, so rollback is a plain PR revert with zero data migration.
Design: vscode-cowriting-plugin-content/specs/coauthoring-out-of-workspace.md.
Live smoke: docs/MANUAL-SMOKE-F8.md.
F10 — Interactive review: write left / review right (Feature #29)
A clean, zero-annotation editor on the left; the rendered preview on the
right as the single interactive review surface. The editor carries no
attribution tint, no in-editor proposal threads, and no diff — all review lives
in the preview, toggled by the Annotations switch in its header (on by
default). Open it via Ctrl+Alt+R, the editor title-bar button, or
right-click a markdown file in the Explorer / its editor tab →
"Open Cowriting Review Panel" (#41) — the right-click acts on the clicked
document, opening it first if needed.
In the on-state the preview shows green = human / blue = Claude /
strikethrough = deleted, and surfaces each of Claude's pending F4 proposals as
a blue cw-proposal block with ✓ / ✗ buttons: ✓ accepts (the
replacement lands Claude-attributed via the seam and the baseline advances past
it), ✗ rejects (the block vanishes, the document untouched). With no preview
open, a status-bar indicator shows the pending-proposal count and opens the
review when clicked. Toggle Annotations off for clean rendered markdown.
Read-only, sealed webview, no new persistence (INV-32..34).
F6's two-pane diff and F9's authorship view are retained only as data layers (the baseline the preview diffs against; the F3 attribution that colors it) — they are no longer separate user surfaces.
Design: vscode-cowriting-plugin-content/specs/coauthoring-interactive-review.md.
Live smoke: docs/MANUAL-SMOKE-F10.md.
F11 — Preview toolbar as the primary interaction surface (Feature #43)
The review preview's header toolbar becomes the cockpit for the inner loop. Beside the existing Annotations switch it gains two controls:
- ⌖ Pin baseline — pins the previewed document's review baseline to now and
clears the change-marks (homes the previously-orphaned
pinDiffBaselinecommand, which is reachable from the palette again too). - ✦ Ask Claude… — one adaptive button. Its label flips on the preview's
selection: Edit Selection when text is selected in the rendered preview,
Edit Document otherwise. Clicking it opens a host input box for the
instruction (the LLM turn and prompt stay host-side — the sealed webview gains
no LLM/credential surface), then surfaces the result as F4 proposals — one
for a selection (mapped back to its source block-union), or one per changed
hunk for a whole-document rewrite (
diffToHunks), each independently ✓/✗-able.
The pure render layer emits data-src-start/data-src-end on every block in
both modes (INV-36); the webview's only mapping duty is walking a selection
to its nearest data-src ancestor. Right-clicking a markdown tab → Open Review
Preview is the minimal gateway making the surface reachable end to end (#41/#42
expand it). Edit controls are disabled on a non-authorable doc (reading stays
allowed). No new model, no new persistence — pin via the F6 store, edits via the
F4 propose/accept seam with F3 attribution (INV-35..37).
Design: docs/superpowers/specs/2026-06-12-f11-preview-toolbar-interaction-surface.md.
Live smoke: docs/MANUAL-SMOKE-F11.md.
Develop
npm run watch— rebuild on change.npx vitest run— unit suite (SDK driver, schema, store, anchorer, thread mutations, attribution split/merge).npm run test:e2e—@vscode/test-electronhost E2E (create → reply → resolve → persist → reload → re-anchor → orphan; drivescowriting.applyAgentEditdirectly — no LLM required).npm run smoke:live— scripted live-turn smoke test for F3; requires Claude Code installed and signed in. Seedocs/MANUAL-SMOKE-F3.md.npm run typecheck— type-check without emit.