Repository navigation
Authoring Plugins in Go
Jon edited this page Sep 5, 2026
·
1 revision
Go plugins are compiled binaries located in ~/.ash/plugins/. They provide fast execution, low memory overhead, and strong type safety.
Every Ash plugin must fulfill the following contract:
-
AI Capability Discovery (
--ai-docs):- Output structured JSON defining capabilities, arguments, return schemas, and usage hints.
-
Standard CLI Flags:
-
--version/-v: Print<plugin_name> <version>and exit 0. -
--help/-h: Print human-readable usage instructions and exit 0.
-
-
Structured Logging (
slog):- Inspect
ASH_LOG_FILE,ASH_LOG_FORMAT(jsonortext), andASH_VERBOSE. - Use
log/slogto append structured logs with anEID(Event/Error ID) key.
- Inspect
-
Lifecycle & Signal Trapping:
- Trap
SIGINT/SIGTERMand listen forcontext.Contextcancellation for graceful exit.
- Trap
Ash provides a standard plugin lifecycle package in internal/plugin:
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log/slog"
"ash/internal/plugin"
)
type MyPlugin struct{}
func (p *MyPlugin) Name() string { return "my_plugin" }
func (p *MyPlugin) Version() string { return "1.0.0" }
func (p *MyPlugin) AIDocs() string {
docs := map[string]any{
"Capabilities": []string{"Describe what the plugin does."},
"Arguments": map[string]string{
"--option": "Describe flag options.",
"--ai-docs": "Print AI documentation",
"--version": "Print version",
"--help": "Print help",
},
"Return format": map[string]string{
"status": "success or error",
"result": "the output value",
},
}
b, _ := json.MarshalIndent(docs, "", " ")
return string(b)
}
func (p *MyPlugin) Run(ctx context.Context, args []string, stdout, stderr io.Writer, logger *slog.Logger) int {
logger.Debug("running my_plugin", "args", args, "EID", "myPl01a")
flags := flag.NewFlagSet("my_plugin", flag.ContinueOnError)
opt := flags.String("option", "default", "Sample option")
if err := flags.Parse(args); err != nil {
logger.Error("failed parsing args", "error", err, "EID", "myPl02b")
return 1
}
select {
case <-ctx.Done():
return 130
default:
}
fmt.Fprintf(stdout, "Processed: %s\n", *opt)
return 0
}
func main() {
plugin.Main(&MyPlugin{})
}Place your plugin in its own directory with a dedicated Makefile:
internal/app/ash_bootstrap/plugins_src/my_plugin/
├── Makefile
├── README.md
└── main.go
Example Makefile:
PLUGIN_NAME := my_plugin
BUILD_DIR ?= ../../../../../bin/plugins
OUTPUT_PATH ?= $(BUILD_DIR)/$(PLUGIN_NAME)
.PHONY: all build test lint clean
all: build
build:
@mkdir -p "$(BUILD_DIR)"
@go build -o "$(OUTPUT_PATH)" .
test:
@go test -v ./...
lint:
@go vet ./...
@golangci-lint run .
clean:
@rm -f "$(OUTPUT_PATH)"