Files
kit/AGENTS.md
T
Ed Zynda f0e4e2f757 refactor: remove Fantasy dependency name leakage from public SDK and docs
Rename public SDK symbols to use generic LLM terminology instead of
exposing the internal dependency name (charm.land/fantasy):

Public API renames (with deprecated wrappers for backward compat):
- ConvertToFantasyMessages() → ConvertToLLMMessages()
- ConvertFromFantasyMessage() → ConvertFromLLMMessage()
- GetFantasyProviders() → GetLLMProviders()

New type alias:
- LLMFilePart = fantasy.FilePart (eliminates need for direct fantasy import)
- PromptResultWithFiles() signature now uses LLMFilePart

Internal renames (with deprecated wrappers):
- ModelsRegistry.GetFantasyProviders() → GetLLMProviders()
- TreeManager.GetFantasyMessages() → GetLLMMessages()
- TreeManager.AppendFantasyMessage() → AppendLLMMessage()
- TreeManager.AddFantasyMessages() → AddLLMMessages()
- Message.ToFantasyMessages() → ToLLMMessages()
- FromFantasyMessage() → FromLLMMessage()
- npmToFantasyProvider → npmToLLMProvider
- isProviderFantasySupported() → isProviderLLMSupported()

All internal callers migrated to new names. ~30 comments updated
to remove Fantasy references across pkg/kit/, internal/agent/,
internal/models/, internal/message/, internal/session/.

Documentation updates:
- AGENTS.md: added Public SDK rules section (no dependency leakage,
  naming conventions, deprecation pattern)
- README.md: removed Fantasy references
- pkg/kit/README.md: full rewrite with current API surface
- skills/kit-sdk/SKILL.md: updated examples and type references
- www/pages/providers.md, www/pages/cli/commands.md: updated
2026-03-29 14:01:57 +03:00

6.8 KiB

OpenSpec Instructions

These instructions are for AI assistants working in this project.

Always open @/openspec/AGENTS.md when the request:

  • Mentions planning or proposals (words like proposal, spec, change, plan)
  • Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
  • Sounds ambiguous and you need the authoritative spec before coding

Use @/openspec/AGENTS.md to learn:

  • How to create and apply change proposals
  • Spec format and conventions
  • Project structure and guidelines

Keep this managed block so 'openspec update' can refresh the instructions.

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