You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note: This RFC's scope has been narrowed by a follow-up Epic (apps/editor, linked below once
filed). Phase 1 (extracting packages/ui) is no longer needed — packages/design-system and packages/markdown already exist as publishable workspace packages. The Claude backend is CLI
subprocess (claude -p, reusing the local claude login) rather than the Agent SDK, so each user
— including anyone besides me who runs the app — authenticates with their own subscription with
zero token wiring. Phase 3 (live two-way sync draft store, terminal-MCP leg) is deferred; v1 is a
single-window editor with one-shot, selection-scoped AI edits instead.
What's the Motivation? 🤔
The Markdown editor built under apps/web/src/theme/Markdown (with section/MarkdownEditor
and the dev-only /articles/write page) should grow into a personal desktop writing tool
with Claude built in — draft an article, ask Claude to revise/extend it, and see changes live.
A browser page is the wrong host for this. It cannot spawn a local CLI, cannot run the Node-based
Claude Agent SDK, and cannot hold long-lived local state — so an in-browser version needs a local
"bridge" process plus WebSocket/SSE/CORS plumbing purely to work around the sandbox. A desktop
app removes that constraint entirely: its own Node process runs the CLI/SDK directly and owns
the draft, so the desired hybrid two-way sync collapses to one process with one source of truth and
simple IPC to the UI.
This RFC proposes building that desktop app, reusing the site's design system, and retiring the
web writing page (and the bridge idea) in its favour.
What are the requirements? ❓
Reuse the existing markdown editor UI so the tool matches the site's design system.
Claude integrated on the user's own Claude subscription — no separate API billing, and no
shared/bundled credential when someone other than me runs the app.
Live two-way sync: edits in the editor and edits from Claude both update the same draft. (Narrowed for v1 — see note above: one-shot selection edits instead.)
Optional: drive the draft from a terminal Claude Code session (get/set the draft). (Deferred.)
The existing web app must stay green throughout (build + 100% coverage on its touched files).
Primary development on Windows 11; keep a cross-platform path open.
Secrets (the subscription token) never bundled or committed.
What are our options? 💡
Integration surface
A — Browser page + local bridge. A dev-only Vite plugin owning draft state, exposing WebSocket
(editor sync), SSE (streamed completions), and an HTTP MCP endpoint. Rejected — substantial
plumbing that exists only to defeat the browser sandbox.
B — Desktop app. Node process hosts the Claude integration and the draft directly. Chosen.
Desktop framework (given the Claude tooling is a JS library/CLI)
Axis
Electron (chosen)
Tauri
Claude on subscription
Runs (or spawns claude) from the Node main process, zero glue
No Node core → needs a Node sidecar, or fall back to spawning the claude CLI
Bundle / RAM
~80–150 MB, heavier
~5–15 MB (advantage erodes once a Node sidecar is added)
Design-system reuse
Guaranteed Chromium renderer, pixel-identical to the site
WebView2/WebKit → possible CSS drift on macOS
Repo fit
Stays 100% in the pnpm+TS+Vite+Vitest toolchain
Adds Rust + cargo to the monorepo/CI
Claude backend
Subprocess the claude CLI (chosen for v1) — inherits the CLI login with zero token
wiring; every user (me or anyone else running the app) authenticates via their own claude login
and uses their own subscription automatically. Non---bareclaude -p reads OAuth/keychain.
Agent SDK on subscription — @anthropic-ai/claude-agent-sdk, authenticated with a CLAUDE_CODE_OAUTH_TOKEN from claude setup-token; more programmatic control (streaming,
structured messages) but needs a per-user token-generation step. Kept as a future option if the
CLI-subprocess approach turns out too limited.
Anthropic API key — separate pay-per-token billing; not wanted.
UI reuse
Shared design-system package (already done) — packages/design-system and packages/markdown are already extracted, publishable workspace packages; no extraction phase
needed.
Fresh desktop UI — rejected, diverges from the site and duplicates components.
Alias hack — point desktop at apps/web/src/theme; rejected (reaches into another app's src).
Proposed solution 🟢
Electron app (apps/editor) whose Node main process owns file I/O and the claude CLI subprocess
bridge; a React renderer consumes @soroush.tech/design-system and @soroush.tech/markdown
directly (both already exist as workspace packages); typed IPC keeps them in sync.
apps/editor (Electron, electron-vite)
renderer (React) ── imports ──▶ @soroush.tech/design-system, @soroush.tech/markdown
│ IPC (file:*, claude:editSelection)
▼
main (Node)
├─ file store (New/Open/Save/Save As via native dialogs)
└─ claude bridge spawns `claude -p` per edit request (stdin = selection, prompt = instruction)
Full breakdown, acceptance criteria, and file layout are tracked in the follow-up Epic (linked
below once filed) rather than duplicated here.
Risks & mitigations 🚨
Electron/IPC glue is hard to reach 100% coverage. Mitigate: unit-test pure logic (file
handlers with fs/dialog injected, the Claude bridge's argv/output parsing); take the same
e2e-only carve-out apps/web already uses for pages/, backed by Playwright-Electron smoke
tests for the wiring itself.
Subprocess invocation quirks on Windows.claude -p piping large selections via stdin, PATH
resolution for the claude binary from an Electron main process, and quoting on PowerShell.
Mitigate: pipe text via stdin (not argv) to sidestep escaping, resolve the binary explicitly
rather than relying on inherited shell PATH.
Unwanted context bleed. Running claude -p from a repo directory would load that project's CLAUDE.md/hooks/MCP config into every edit request. Mitigate: always run from a neutral,
per-app scratch working directory, with --permission-mode dontAsk and empty --allowedTools so
no tool use is possible regardless.
Electron footprint / signing / auto-update. Accept the larger bundle; defer code-signing for
personal use.
Scope creep. Live two-way sync and the terminal-MCP leg are deferred, not built into v1.
What's the Motivation? 🤔
The Markdown editor built under
apps/web/src/theme/Markdown(withsection/MarkdownEditorand the dev-only
/articles/writepage) should grow into a personal desktop writing toolwith Claude built in — draft an article, ask Claude to revise/extend it, and see changes live.
A browser page is the wrong host for this. It cannot spawn a local CLI, cannot run the Node-based
Claude Agent SDK, and cannot hold long-lived local state — so an in-browser version needs a local
"bridge" process plus WebSocket/SSE/CORS plumbing purely to work around the sandbox. A desktop
app removes that constraint entirely: its own Node process runs the CLI/SDK directly and owns
the draft, so the desired hybrid two-way sync collapses to one process with one source of truth and
simple IPC to the UI.
This RFC proposes building that desktop app, reusing the site's design system, and retiring the
web writing page (and the bridge idea) in its favour.
What are the requirements? ❓
shared/bundled credential when someone other than me runs the app.
(Narrowed for v1 — see note above: one-shot selection edits instead.)
What are our options? 💡
Integration surface
(editor sync), SSE (streamed completions), and an HTTP MCP endpoint. Rejected — substantial
plumbing that exists only to defeat the browser sandbox.
Desktop framework (given the Claude tooling is a JS library/CLI)
claude) from the Node main process, zero glueclaudeCLIClaude backend
claudeCLI (chosen for v1) — inherits the CLI login with zero tokenwiring; every user (me or anyone else running the app) authenticates via their own
claude loginand uses their own subscription automatically. Non-
--bareclaude -preads OAuth/keychain.@anthropic-ai/claude-agent-sdk, authenticated with aCLAUDE_CODE_OAUTH_TOKENfromclaude setup-token; more programmatic control (streaming,structured messages) but needs a per-user token-generation step. Kept as a future option if the
CLI-subprocess approach turns out too limited.
UI reuse
packages/design-systemandpackages/markdownare already extracted, publishable workspace packages; no extraction phaseneeded.
apps/web/src/theme; rejected (reaches into another app's src).Proposed solution 🟢
Electron app (
apps/editor) whose Node main process owns file I/O and theclaudeCLI subprocessbridge; a React renderer consumes
@soroush.tech/design-systemand@soroush.tech/markdowndirectly (both already exist as workspace packages); typed IPC keeps them in sync.
Full breakdown, acceptance criteria, and file layout are tracked in the follow-up Epic (linked
below once filed) rather than duplicated here.
Risks & mitigations 🚨
handlers with
fs/dialoginjected, the Claude bridge's argv/output parsing); take the samee2e-only carve-out
apps/webalready uses forpages/, backed by Playwright-Electron smoketests for the wiring itself.
claude -ppiping large selections via stdin, PATHresolution for the
claudebinary from an Electron main process, and quoting on PowerShell.Mitigate: pipe text via stdin (not argv) to sidestep escaping, resolve the binary explicitly
rather than relying on inherited shell PATH.
claude -pfrom a repo directory would load that project'sCLAUDE.md/hooks/MCP config into every edit request. Mitigate: always run from a neutral,per-app scratch working directory, with
--permission-mode dontAskand empty--allowedToolssono tool use is possible regardless.
personal use.
Resources and benchmarks 🔗
soroush-tech/coreissues [Task] Markdown editor compound (theme/Markdown) #267–[Task] Markdown toolbar icons (theme/Icon) #275 (theme/Markdown,CodeBlock, Quote, toolbar icons).
packages/packages.md,packages/design-system/design-system.md,packages/markdown/markdown.md,.claude/skills/Electron/SKILL.md.issues/epic-apps-monorepo-restructure.md.claudeCLI (-p/--print,--output-format json,--permission-mode), electron-vite,Electron Forge, Playwright.