|
| 1 | +# AGENTS.md — Codg Plugin System |
| 2 | + |
| 3 | +Guide for AI agents working in `github.com/vcaesar/codg`. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Codg is an AI coding assistant (CLI). This repo is the **plugin system** — hook-based architecture for extending Codg. Inspired by [opencode](https://github.com/anomalyco/opencode/tree/dev/packages/plugin). **Module**: `github.com/vcaesar/codg` · **Go**: 1.26.2 · **Tests**: [testify](https://github.com/stretchr/testify) `require` (only external dep). |
| 8 | + |
| 9 | +## Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +cd plugin && go test ./... # all tests (plugin/ is its own module) |
| 13 | +cd plugin && go test -v ./demo/ # single package |
| 14 | +cd plugin/auth/gemini/standalone && go build -buildmode=plugin -o ~/.codg/plugins/gemini-auth.so . |
| 15 | +``` |
| 16 | + |
| 17 | +No Makefile or linter config. CI: `.github/workflows/go.yml`. |
| 18 | + |
| 19 | +## Layout (STANDALONE — no internal deps) |
| 20 | + |
| 21 | +``` |
| 22 | +plugin/ # own module (plugin/go.mod); plugin.go (core), types.go (syncMap/eventBus/PermissionService), plugin_test.go (~1200 lines) |
| 23 | +├── pkg/ # package codgpkg — shared types: AgentTool, ToolInfo, ProviderOptions |
| 24 | +├── auth/ # gemini, claude (+ standalone/ for .so); codex/gpt stubs |
| 25 | +├── demo/ # hello tool + ShellEnv + SystemPrompt hooks |
| 26 | +├── enhancer/ # tool interception, message transform, error handling |
| 27 | +└── oauthplugin/ # OAuth tokens, provider resolution |
| 28 | +``` |
| 29 | + |
| 30 | +## Key Interfaces |
| 31 | + |
| 32 | +```go |
| 33 | +type Plugin interface { Init(ctx context.Context, input PluginInput) (*Hooks, error) } |
| 34 | +type AgentTool interface { // plugin/pkg (codgpkg) |
| 35 | + Info() ToolInfo |
| 36 | + Run(ctx context.Context, params ToolCall) (ToolResponse, error) |
| 37 | + ProviderOptions() ProviderOptions |
| 38 | + SetProviderOptions(opts ProviderOptions) |
| 39 | +} |
| 40 | +type PermissionService interface { Request(ctx context.Context, opts PermissionRequest) (bool, error) } |
| 41 | +type PluginMetaProvider interface { Meta() PluginMeta } |
| 42 | +``` |
| 43 | + |
| 44 | +## Hooks |
| 45 | + |
| 46 | +Signature: `(input ReadOnly, output *Mutable) error`. Plugins chain by mutating output, ordered by `Priority` (lower first; default 0). All optional. |
| 47 | +- **Short-circuit** on `output.Handled = true`: `OAuthToken`, `ProviderResolve`, `ErrorHandle` |
| 48 | +- **Chaining**: `ChatParams`, `ChatHeaders`, `PermissionAsk`, `ShellEnv`, `ToolExecuteBefore/After`, `SystemPromptTransform`, `ConfigTransform`, `SessionStart/End`, `MessageTransform` |
| 49 | + |
| 50 | +## Plugin Types |
| 51 | + |
| 52 | +| Type | Mechanism | Status | |
| 53 | +| --------- | ------------------------------------ | ------- | |
| 54 | +| `builtin` | Compiled in, registered via `init()` | ✅ | |
| 55 | +| `shared` | `.so` loaded at runtime | ✅ | |
| 56 | +| `exec` | JSON-RPC over stdin/stdout | 🚧 stub | |
| 57 | + |
| 58 | +## Conventions |
| 59 | + |
| 60 | +- **Files**: `<feature>.go` + `<feature>_test.go` (same package, white-box). Every package has a doc comment. |
| 61 | +- **Register**: `plugin.RegisterBuiltin("name", &Plugin{})` in `init()`. Tool names: `plugin_<plugin>_<tool>`. |
| 62 | +- **Hook I/O**: `<Hook>Input` (read-only) / `<Hook>Output` (mutable pointer); trigger `Trigger<Hook>`. |
| 63 | +- **Naming**: PascalCase exported; camelCase unexported consts (e.g., `credCacheTTL`). |
| 64 | +- **JSON tags**: all public types; `omitempty` optional; `json:"-"` for funcs. |
| 65 | + |
| 66 | +### Skeleton |
| 67 | + |
| 68 | +```go |
| 69 | +type Plugin struct { mu sync.RWMutex } |
| 70 | +func (p *Plugin) Meta() plugin.PluginMeta { ... } |
| 71 | +func (p *Plugin) Init(_ context.Context, in plugin.PluginInput) (*plugin.Hooks, error) { |
| 72 | + return &plugin.Hooks{Tools: map[string]plugin.ToolDefinition{...}, Priority: 10}, nil |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +### Rules |
| 77 | + |
| 78 | +- Wrap errors: `fmt.Errorf("desc: %w", err)`. Never ignore returned errors. |
| 79 | +- Tool errors → `ToolResponse{IsError: true}`, not Go errors. Hook errors → logged warnings; others still run. |
| 80 | +- Init map fields before writing (`if output.Env == nil { output.Env = make(...) }`). |
| 81 | +- Concurrency: `syncMap`, `eventBus`, `sync.RWMutex` on auth caches. Plugins init in parallel goroutines. |
| 82 | + |
| 83 | +### Testing |
| 84 | + |
| 85 | +- `t.Parallel()` unless mutating env / package-level state. Use `require` (not `assert`); plain-struct mocks. |
| 86 | +- Save/restore globals (`plugins`, `allTools`, `builtinPlugins`) via `defer`. Env tests: `t.Setenv()`, no `t.Parallel()`. |
| 87 | + |
| 88 | +## Standalone Constraint (Critical) |
| 89 | + |
| 90 | +`plugin/` (incl. `plugin/pkg/`) must have NO dependency on internal Codg packages (config, csync, pubsub, permission). External authors `go get github.com/vcaesar/codg/plugin` without the full tree. If internal types are needed, add standalone equivalents in `plugin/types.go`. |
| 91 | + |
| 92 | +## Known Issues |
| 93 | + |
| 94 | +Stubs: `plugin/auth/codex/codex.go`, `plugin/auth/gpt/`, `pkg/`. gopls: `unusedwrite` in tests (intentional); `stringsbuilder` at `oauthplugin/oauthplugin.go:156`. |
| 95 | + |
| 96 | +## Adding a Plugin / Hook |
| 97 | + |
| 98 | +- **Plugin**: `plugin/myplugin/` → implement `Init()` (+ optional `Meta()`); register in `init()`; tests + package doc. For `.so`: add `standalone/main.go` exporting `CodgPlugin`. |
| 99 | +- **Hook**: define `<Hook>Input/Output` in `plugin.go`; add to `Hooks` struct; add `Trigger<Hook>` (check `Handled` if short-circuiting); test in `plugin_test.go`; update `README.md` tables. |
0 commit comments