Move to a terminal driven integration to have better interactions
This commit is contained in:
@@ -6,14 +6,20 @@ 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.
|
||||
A Nova extension that launches and tracks Claude Code sessions for a project.
|
||||
**Chat happens in a terminal, not in Nova.** The extension prepares launch
|
||||
commands, lists the project's sessions, and manages the account behind them.
|
||||
|
||||
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.
|
||||
There is no bundled copy of Claude Code, no Node sidecar, no npm dependencies,
|
||||
and no network access of its own.
|
||||
|
||||
> **The extension used to drive the conversation itself** — `claude --print` over
|
||||
> the stream-json protocol, rendered into a Markdown tab, with approvals, plan
|
||||
> review and question answering in Nova panels. That was removed after real use:
|
||||
> modal panels truncate long questions and lose their input when you click away.
|
||||
> The implementation is preserved in commit `89d1a06`; its transport notes and
|
||||
> the editable-proposal flow are the starting point for the IDE bridge. **Do not
|
||||
> reintroduce panel-based chat.**
|
||||
|
||||
## Hard constraints (do not re-derive these)
|
||||
|
||||
@@ -39,16 +45,20 @@ 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:
|
||||
**Claude Code CLI** — the facts this extension relies on:
|
||||
|
||||
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.
|
||||
1. Sessions are stored as `~/.claude/projects/<slug>/<session-id>.jsonl`, where
|
||||
the slug is the project path with `/` and `.` flattened to `-`. This is the
|
||||
CLI's own store, so terminal-started sessions appear in the sidebar too.
|
||||
2. `claude --resume <id>` resumes from any terminal; `--remote-control` enables
|
||||
driving a session from elsewhere. Nothing extra is needed to support either —
|
||||
a launched session is an ordinary CLI session.
|
||||
3. `claude auth status --json` gives structured account state.
|
||||
|
||||
If you ever need to drive the CLI programmatically again, commit `89d1a06`
|
||||
documents the stream-json and control protocols in detail — including that
|
||||
`--permission-prompt-tool stdio` is required and undocumented, without which
|
||||
every permission-requiring tool is silently auto-denied.
|
||||
|
||||
**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
|
||||
@@ -60,15 +70,11 @@ shells out to `claude auth login`. See [authentication](docs/features/authentica
|
||||
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
|
||||
launch.js building and copying the launch command
|
||||
sessions-store.js reading ~/.claude/projects
|
||||
sidebar.js Status and Sessions TreeView providers
|
||||
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
|
||||
```
|
||||
@@ -96,28 +102,29 @@ 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:
|
||||
**Node harness.** The Nova API is stubbable, so the extension can be exercised
|
||||
under Node — including against real session files on disk. This caught several
|
||||
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");
|
||||
const store = require("./Scripts/sessions-store.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`.
|
||||
Point `nova.workspace.path` at a directory that really has sessions, stub
|
||||
`clipboard.writeText` to capture output, and assert on the built command. Stub
|
||||
`showChoicePalette` / `showActionPanel` / `notifications.add` to script UI.
|
||||
`nova.fs.open` needs a working `readline()` — session labels depend on it.
|
||||
|
||||
> 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.
|
||||
Nothing in the current feature set spends tokens: the extension builds command
|
||||
strings and reads files. (If you add code that runs turns, use `--model haiku`
|
||||
and one-line prompts — a verification pass costs a few tens of cents.)
|
||||
|
||||
Cheap checks that need no CLI:
|
||||
|
||||
@@ -128,9 +135,12 @@ 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).
|
||||
- `89d1a06` is the in-editor-chat implementation, kept for the IDE bridge.
|
||||
The terminal-first rewrite on top of it is uncommitted.
|
||||
- Not published. `min_runtime` is declared as `10`.
|
||||
- `nova extension validate` last passed cleanly on the schema; a later run
|
||||
failed only on network reachability from a sandbox, not on the bundle.
|
||||
- **Never run inside Nova yet by this project's tooling** — the user has run
|
||||
earlier builds; verify UI changes in the app.
|
||||
- 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