# CLAUDE.md Context for working on this repository. Feature-level detail lives in [`docs/features/`](docs/features/README.md) — read the relevant file there before changing a subsystem. ## What this is A Nova extension that puts Claude Code inside Panic's Nova editor: a sidebar for the session, a live Markdown transcript, and tool approvals reviewed in the editor. It **drives the user's own `claude` executable** as a subprocess, speaking the CLI's newline-delimited JSON protocol over stdio. There is no bundled copy of Claude Code, no Node sidecar, no npm dependencies, and no network access of its own. ## Hard constraints (do not re-derive these) **Nova extension API** | Constraint | Consequence | | --- | --- | | No webview / HTML UI for extensions | The chat surface is a Markdown tab, not a rendered view | | Sidebar sections are **TreeView only** | No text fields, forms, or the multi-line box Nova's own Source Control sidebar has | | No WebSocket in the JS runtime | The [IDE bridge](docs/features/ide-bridge.md) needs a Node sidecar | | No terminal API | Cannot create a terminal or send it text | | No diff viewer API | Diffs are rendered text; proposals are editable staged files | | Cannot close tabs programmatically | Never promise to clean up a tab | | Can only read its **own** `IssueCollection` | No access to other extensions' diagnostics | | No multi-line input | Long-form input = open a scratch document | | Commands may only be placed in `editor`, `extensions`, `command-palette`, `text` | No file-browser context menu | | Untitled documents cannot be saved without a dialog | Stage real files when a save gesture matters | The runtime is JavaScriptCore with `fetch`, Streams, `TextEncoder/Decoder`, `setTimeout`, `nova.crypto` — **not Node**. No `require` of npm packages; only relative `require("./x.js")` between `Scripts/` files. Type definitions worth having open: `npm pack @types/nova-editor-node` (it is a single `index.d.ts`, and is more precise than the website). **Claude Code CLI** — see [session transport](docs/features/session-transport.md) for the full protocol. The three facts that cost the most to find: 1. `--permission-prompt-tool stdio` is **required** for permission prompts to reach the host, and is absent from `claude --help`. Without it every tool needing approval is silently auto-denied. 2. One assistant message id arrives multiple times (per completed content block, plus streaming deltas). They must be merged, not appended. 3. A `can_use_tool` control request **must always be answered**, including when the handler throws — the CLI blocks on it and the session hangs otherwise. **Policy** — do not implement claude.ai OAuth in this extension. Anthropic's Agent SDK terms bar third-party products from offering claude.ai login. Sign-in shells out to `claude auth login`. See [authentication](docs/features/authentication.md). ## Layout ``` extension.json manifest: sidebar, commands, config, entitlements Scripts/ main.js Controller: lifecycle, commands, wiring client.js CLI process + stream-json/control protocol session.js conversation model (entries, tools, changed files) permissions.js can_use_tool → approvals, plans, questions, proposals transcript.js Markdown tab rendering sidebar.js four TreeView providers diff.js change reconstruction + unified diff auth.js claude auth status/login/logout cli.js binary resolution, environment, keychain sessions-store.js reading ~/.claude/projects for resume util.js conf, throttle, Signal, runCommand, processWriter docs/features/ one file per capability; read before changing one ``` ## Conventions - 4-space indent, CommonJS, no build step, no dependencies. - Comments explain *why*, especially where a workaround encodes an API limitation. Several exist purely to stop a future reader "simplifying" a constraint back into a bug. - User-visible strings are sentence case and plain English; no jargon from the protocol leaks into the UI (`friendlyToolName`, `modeLabel`). - Config keys are namespaced `claudenova.*`; read them through `util.conf()`, which prefers the workspace value. ## Testing **The extension has never been run inside Nova.** Everything below is verified outside it; a first in-Nova run is still owed. Load it with: ```sh nova extension validate . # manifest + bundle (makes network calls — needs connectivity) nova extension activate . # load into Nova for development ``` Logs (`nova.inDevMode()` output) appear in Nova's Extension Console. **Node harness.** The Nova API is stubbable, so the transport and model can be driven under Node against the *real* CLI. This caught four real bugs and is the highest-value testing move available: ```js global.Process = class { /* wrap child_process.spawn; onStdout/onStderr/onDidExit */ }; global.Range = class { constructor(start, end) { this.start = start; this.end = end; } }; global.nova = { environment: process.env, path, fs, workspace, extension, crypto, inDevMode: () => true, /* … */ }; const { ClaudeClient } = require("./Scripts/client.js"); ``` Then wire `onPermission` to scripted answers and send a prompt. Stub `showChoicePalette` / `showActionPanel` / `showInputPanel` / `notifications.add` to script UI decisions and assert on the resulting `PermissionResult`. > The harness scripts from the build session lived in a temporary scratchpad and > are gone. Recreating them in a `Tests/` folder is the obvious next improvement. **Live tests cost real money** — they run turns on the signed-in account. Use `--model haiku` and one-line prompts; a full verification pass is a few tens of cents. Cheap checks that need no CLI: ```sh for f in Scripts/*.js; do node --check "$f"; done python3 -c "import json; json.load(open('extension.json'))" ``` ## State - Not yet committed. Not yet published. `min_runtime` is declared as `10` (`nova.crypto` is the newest API touched, and it has a fallback). - `nova extension validate` last passed cleanly on the schema; a later run failed only on network reachability from a sandbox, not on the bundle. - Icons are generated by a script (a tapered starburst rendered to PNG with a pure-stdlib encoder); regenerate rather than hand-editing if sizes change.