First version
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user