mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-28 14:23:41 +02:00
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.
41 lines
1.2 KiB
Go
41 lines
1.2 KiB
Go
// hello — a tiny zot extension that registers /hello and /summon.
|
|
//
|
|
// Build it:
|
|
//
|
|
// cd examples/extensions/hello
|
|
// go build -o hello .
|
|
//
|
|
// Then drop it next to its extension.json under
|
|
// $ZOT_HOME/extensions/hello/, or run `zot ext install ./hello`
|
|
// from this directory.
|
|
package main
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/patriceckhart/zot/pkg/zotext"
|
|
)
|
|
|
|
func main() {
|
|
ext := zotext.New("hello", "1.0.0")
|
|
|
|
// /hello [name] — submits a friendly prompt to the agent.
|
|
ext.Command("hello", "say hello (optional name)", func(args string) zotext.Response {
|
|
who := strings.TrimSpace(args)
|
|
if who == "" {
|
|
return zotext.Prompt("Greet me with a short, slightly absurd compliment.")
|
|
}
|
|
return zotext.Prompt("Greet " + who + " with a short, slightly absurd compliment.")
|
|
})
|
|
|
|
// /summon — pushes a notice into the chat without involving the
|
|
// model. Useful for pretending we did something important.
|
|
ext.Command("summon", "show a tongue-in-cheek summon notice", func(args string) zotext.Response {
|
|
ext.Notify("info", "the daemon stirs in its cage.")
|
|
return zotext.Display("a wisp of incense curls past your terminal.")
|
|
})
|
|
|
|
if err := ext.Run(); err != nil {
|
|
ext.Logf("fatal: %v", err)
|
|
}
|
|
}
|