Files
claude-nova/CLAUDE.md
T
2026-08-11 12:38:02 -03:00

6.3 KiB

CLAUDE.md

Context for working on this repository. Feature-level detail lives in docs/features/ — 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 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.cryptonot 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 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.

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:

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:

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:

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.