Files
kit/AGENTS.md
Ed Zynda 623c9fb5ad docs(agents): add BTCA configured resources list to AGENTS.md
Enumerate all 14 external repositories configured in btca.config.jsonc
for easy reference when researching dependencies.
2026-03-30 18:20:43 +03:00

6.4 KiB

KIT Agent Guidelines

Build/Test Commands

  • Build: go build -o output/kit ./cmd/kit
  • Test all: go test -race ./...
  • Test single: go test -race ./cmd -run TestScriptExecution
  • Lint: go vet ./...
  • Format: go fmt ./...

Code Style

  • Imports: stdlib → third-party → local (blank lines between)
  • Naming: camelCase (unexported), PascalCase (exported)
  • Errors: Always check, wrap with fmt.Errorf("context: %w", err)
  • Logging: Use github.com/charmbracelet/log structured logging
  • Types: Prefer any over interface{}
  • JSON: snake_case tags with omitempty where appropriate
  • Context: First parameter for blocking operations

Architecture

  • Multi-provider LLM support via llm.Provider interface
  • MCP client-server for tool integration
  • Builtin servers: bash, fetch, todo, fs
  • Extension system (internal/extensions/): Yaegi-interpreted Go, 13 lifecycle events, custom tools/commands/widgets/overlays/editor interceptors
  • TUI (internal/ui/): Bubble Tea v2 parent-child model (AppModelInputComponent, StreamComponent, etc.)
  • Decoupling pattern: cmd/root.go has converter functions (e.g. widgetProviderForUI()) that bridge internal/extensions/ types to internal/ui/ types — the UI never imports extensions directly
  • Public SDK (pkg/kit/): The public-facing Go SDK for embedding Kit as a library. See rules below.

Public SDK (pkg/kit/) Rules

pkg/kit/ is the public API surface consumed by external Go developers. All exported symbols, types, function names, and godoc comments in this package are part of the SDK contract.

No Dependency Name Leakage

Internal dependency names (e.g. charm.land/fantasy, library-specific jargon) must not appear in:

  • Exported function/method names — use generic terms (LLM, Provider, Message) instead of library names
  • Exported type names — type aliases should use domain names (e.g. LLMMessage, not FantasyMessage)
  • Godoc comments on exported symbols — these are visible in go doc output and pkg.go.dev
  • Struct field names and tags on exported types

Using dependency types directly in function bodies (private implementation) is fine — that's invisible to SDK consumers.

Naming Conventions for SDK Symbols

  • Type aliases re-exporting dependency types: use LLM* prefix (e.g. LLMMessage, LLMUsage, LLMResponse)
  • Conversion helpers: use ConvertToLLM* / ConvertFromLLM* (not the dependency name)
  • Provider queries: use GetLLMProviders (not GetFantasyProviders)
  • When wrapping internal methods, the pkg/kit/ name should be dependency-agnostic even if the internal/ method still uses the old name

Deprecation Pattern

When renaming a public SDK symbol, keep the old name as a deprecated wrapper for one release cycle:

// Deprecated: Use NewName instead.
func OldName() { return NewName() }

Key Patterns

Yaegi (Extension Interpreter) Gotchas

  • No interfaces across boundary: All extension-facing API types must be concrete structs, never interfaces. Yaegi crashes on interface wrapper generation.
  • Function field bug: Named function references assigned to struct fields return zero values across the interpreter boundary. Always use anonymous closure literals:
    // WRONG: ctx.SetEditor(ext.EditorConfig{HandleKey: myHandler})
    // RIGHT: ctx.SetEditor(ext.EditorConfig{HandleKey: func(k, t string) ext.EditorKeyAction { return myHandler(k, t) }})
    
  • Symbol exports: Every new type exposed to extensions must be added to internal/extensions/symbols.go

BubbleTea Integration

  • No prog.Send() from inside Update(): Calling prog.Send() synchronously within a BubbleTea Update() handler deadlocks the event loop. Use go appInstance.NotifyWidgetUpdate() (async goroutine) instead.
  • Height measurement: distributeHeight() in model.go must measure using the same render path as View(). If an interceptor wraps rendering, measure with the wrapper too, or layout will mismatch.
  • Channel-based prompts: Extension prompt calls (PromptSelect, etc.) block on a chan PromptResponse. Extension slash commands run in dedicated goroutines (not tea.Cmd) to avoid stalling BubbleTea's Cmd scheduler.

Extension State Management

  • Thread-safe maps on Runner: Widget/header/footer/editor state lives on the Runner with sync.RWMutex, queried by UI via callbacks
  • Context function fields: The Context struct uses function fields (Print func(string), SetWidget func(WidgetConfig)) wired by closures in cmd/root.go
  • Package-level vars in extensions: Yaegi supports package-level variables captured in closures — this is how extensions maintain state across event callbacks

Unicode in Widget Text

  • Widget content renders through lipgloss.Style.Render() which preserves ANSI escape codes
  • Use rune-based width calculations (len([]rune(s))) not byte length (len(s)) when aligning box-drawing characters or multi-byte symbols

Testing

Interactive TUI Testing with tmux

Use tmux to test Kit interactively without blocking the agent:

tmux new-session -d -s kittest -x 120 -y 40 "output/kit -e examples/extensions/my-ext.go --no-session 2>kit_stderr.log"
sleep 3
tmux capture-pane -t kittest -p          # read screen
tmux send-keys -t kittest '/command' Enter  # send input
tmux kill-session -t kittest              # cleanup

Non-Interactive Kit (Subprocess Spawning)

Extensions can spawn Kit as a subprocess for sub-agent patterns:

kit --quiet --no-session --no-extensions --system-prompt /path/to/prompt.txt --model provider/model "question"

Positional args are the prompt. @file args attach file content. Key flags: --quiet (stdout only, no TUI), --no-session (ephemeral), --no-extensions (prevent recursive loading), --system-prompt (string or file path).

External Repo Research

  • ALWAYS use btca to search external repos (e.g. iteratr, other reference codebases)
  • Never guess or manually search the filesystem for external projects
  • Example: btca ask -r https://github.com/user/repo -q "How does X work?"
  • See .agents/skills/btca-cli/SKILL.md for full btca usage

BTCA Configured Resources

The following external repositories are configured in btca.config.jsonc for research:

  • bubbletea
  • lipgloss
  • bubbles
  • glamour
  • fantasy
  • catwalk
  • crush
  • pi
  • iteratr
  • yaegi
  • acp-go-sdk
  • opencode
  • herald
  • herald-md