zot/internal/tui
patriceckhart 0c92d6e914 feat: extension system (subprocess + json-rpc, any language)
Phase 1: extensions can register slash commands and push chat
notifications. Tools and event subscriptions land in later phases.

Architecture: each extension is its own subprocess. Zot launches
it on startup, completes a hello/hello_ack handshake over its
stdin/stdout, then routes slash commands the extension registered.
Crash isolation, language agnostic, works with any executable
that can read/write json lines.

What lands here:

- internal/extproto: shared wire-format types (Frame, HelloFromExt,
  RegisterCommandFromExt, CommandResponseFromExt, NotifyFromExt,
  HelloAckFromHost, CommandInvokedFromHost, ShutdownFromHost...).
  Both the host and the SDK marshal/unmarshal the same types.

- internal/agent/extensions: discovery + lifecycle manager.
  - Discover() walks $ZOT_HOME/extensions and ./.zot/extensions
    (project-local first, global second; first wins for duplicates)
  - Spawns each enabled extension, captures stderr to
    $ZOT_HOME/logs/ext-<name>.log
  - Reads frames in a goroutine, dispatches register_command and
    notify, correlates command_response by id
  - Stop() sends shutdown, waits 2s, then SIGTERM/SIGKILL
  - HostHooks abstracts the tui callbacks (Notify/Submit/Insert/Display)

- Interactive bridge: extensions slot into the slash dispatcher
  *after* the built-in catalog, so built-ins always win on conflict.
  Extension-registered commands also flow into the autocomplete
  popup and /help via slashSuggester.SetExtra. NotifyFromExt frames
  render as muted [ext-name] notes above the editor.

- internal/agent/extcmd: `zot ext` CLI.
    list / install <path|git-url> / remove / enable / disable / logs

- pkg/zotext: public Go SDK. Construct an Extension, register
  Command(name, desc, fn), call Run(). Fn returns a Response built
  with Prompt(), Insert(), Display(), Noop(), or Errorf(). Stderr
  via Logf() so stdout stays clean for the protocol.

- examples/extensions/hello: working Go example registering /hello
  and /summon, plus README + extension.json.

- docs/extensions.md: full protocol reference, including a
  ~30-line raw-Python example for users who don't want the SDK.

Tests: internal/agent/extensions/manager_test.go spawns a mock
extension via /bin/sh and exercises the full handshake -> register
-> invoke -> response cycle. Verifies the hello frame ordering,
correlation-by-id, and graceful shutdown.

Verified manually: built and installed the example, drove it via
stdin pipes, confirmed clean handshake + correct frame ordering
and shutdown_ack. Builds vet-clean on darwin / linux / windows.

Editor.Insert exported (was Editor.insert) so the extension hooks
can drop text into the input.
2026-04-19 14:09:43 +02:00
..
editor.go feat: extension system (subprocess + json-rpc, any language) 2026-04-19 14:09:43 +02:00
highlight.go fix ci: portable syscall.Select via x/sys/unix; gofmt pass 2026-04-18 10:55:42 +02:00
image.go fix ci: portable syscall.Select via x/sys/unix; gofmt pass 2026-04-18 10:55:42 +02:00
input.go add collapsible code blocks 2026-04-18 10:30:29 +02:00
markdown.go fix code highlighting 2026-04-18 10:16:06 +02:00
quote_paste_test.go tui: quote drag-dropped file paths in the input editor 2026-04-19 13:01:37 +02:00
render.go tui: quote drag-dropped file paths in the input editor 2026-04-19 13:01:37 +02:00
resize_unix.go fix ci: portable syscall.Select via x/sys/unix; gofmt pass 2026-04-18 10:55:42 +02:00
resize_windows.go initial commit 2026-04-17 20:36:38 +02:00
terminal.go initial commit 2026-04-17 20:36:38 +02:00
theme.go fix ci: portable syscall.Select via x/sys/unix; gofmt pass 2026-04-18 10:55:42 +02:00
view.go tui: align status-bar spacing and indent assistant body 2026-04-19 13:10:16 +02:00