Architecture#

  • Language: Go 1.25+, 100% Go
  • Size: 33 packages, ~294 Go files, ~100K LOC
  • Entry point: cmd/gokin/main.go (Cobra CLI)
  • Orchestrator: internal/app/app.go (~1200 LOC)

Package Structure#

internal/
├── app/          # Orchestrator (builder, queue, signals, message_processor)
├── agent/        # Multi-agent system (delegation, reflection, tree planning)
├── tools/        # AI tool registry (54 tools, 10 tool sets)
├── ui/           # TUI on Bubble Tea (themes, progress, command palette)
├── client/       # API clients (Gemini, Gemini OAuth, Anthropic, Ollama)
├── config/       # Configuration (YAML, 22+ sections, provider registry)
├── chat/         # Session and history management
├── permission/   # Permission system (RiskLow/Medium/High)
├── plan/         # Task planning and execution
├── context/      # Context management and summarization
├── memory/       # Memory, learning, persistence
├── security/     # Security (sandbox, command/SSH validation)
├── semantic/     # Semantic search with embeddings
├── mcp/          # Model Context Protocol (external servers)
├── router/       # Smart request routing
├── format/       # Formatting utilities (Duration, Bytes)
├── fileutil/     # Atomic file transactions
├── hooks/        # Pre/post-tool hooks
├── auth/         # OAuth authorization (Gemini)
├── update/       # Self-update system with rollback
├── audit/        # Audit logging
├── cache/        # Result caching
├── ratelimit/    # API rate limiting
├── watcher/      # File change monitoring
├── tasks/        # Background tasks (bash shells)
├── commands/     # Slash commands
├── logging/      # Structured logging (slog)
├── git/          # Git integration
├── highlight/    # Syntax highlighting
├── ssh/          # SSH validation
├── robustness/   # Error recovery, circuit breaker
└── undo/         # Undo stack

Key Dependencies#

LibraryVersionPurpose
Cobrav1.10.2CLI framework
Bubble Teav1.3.10TUI framework (charmbracelet)
genai SDKv1.42.0Google Gemini integration
Glamourv0.10.0Markdown rendering in terminal
LipglossTUI styling
fsnotifyFile watching

Design Patterns#

Registry#

Tool registration and lookup by name. Lazy loading via ToolEntry.

// internal/tools/registry.go
type Registry struct {
    tools map[string]Tool
}

Builder#

DI container for assembling application dependencies.

// internal/app/builder.go — selectToolSets, client creation, wiring

Factory#

Client creation based on provider/model.

// internal/client/factory.go
func NewClient(ctx, cfg, modelID) (Client, error)

Strategy#

Delegation strategy and MCP transport.

// internal/agent/delegation.go
type DelegationStrategy struct { ... }

Observer#

Progress callbacks and tool activity notifications.

Command#

Slash commands with metadata (category, priority, platform, requires).

Snapshot-then-async#

Serialize data under lock, write to file without lock:

dm.mu.Lock()
snapshot, _ := dm.save()   // Serialize under lock
dm.mu.Unlock()
go dm.writeSnapshot(snapshot)  // Async write without lock

Concurrency#

RWMutex#

All client setters/getters are protected by sync.RWMutex:

// Setters — under Lock()
func (c *Client) SetModel(m string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.model = m
}

// Getters — under RLock()
func (c *Client) GetModel() string {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.model
}

Snapshot-then-async#

Mutable data is copied under lock, writes happen asynchronously.

Semaphore#

Concurrency limiting (up to 5 agents, up to 5 tools, up to 3 token count requests).

WaitGroup#

Parallel execution of plan steps and MCP connections.

Channels#

  • done channels with doneOnce.Do(close(done)) for safe shutdown
  • cmdDone before panic recovery to prevent hanging

Creating a Custom Tool#

1. Implement the Tool interface#

// internal/tools/my_tool.go
package tools

type MyTool struct {
    workDir string
}

func (t *MyTool) Name() string {
    return "my_tool"
}

func (t *MyTool) Description() string {
    return "Description of my tool"
}

func (t *MyTool) Declaration() *genai.FunctionDeclaration {
    return &genai.FunctionDeclaration{
        Name:        "my_tool",
        Description: "Description of my tool",
        Parameters: &genai.Schema{
            Type: genai.TypeObject,
            Properties: map[string]*genai.Schema{
                "input": {
                    Type:        genai.TypeString,
                    Description: "Input parameter",
                },
            },
            Required: []string{"input"},
        },
    }
}

func (t *MyTool) Execute(ctx context.Context, args map[string]any) (ToolResult, error) {
    input, _ := args["input"].(string)
    // ... logic ...
    return ToolResult{Output: "result"}, nil
}

func (t *MyTool) Validate(args map[string]any) error {
    if _, ok := args["input"].(string); !ok {
        return fmt.Errorf("input is required")
    }
    return nil
}

2. Register in the registry#

// internal/tools/registry.go — add to DefaultRegistry or DefaultLazyRegistry

3. Add to a tool set#

// internal/tools/registry.go — add to the appropriate toolSetDefinitions

4. Add the declaration#

// internal/tools/declarations.go — add FunctionDeclaration

5. Assign a safety level#

// internal/tools/safety.go — add to the appropriate level

Code Style#

CategoryStyle
Exported namesCamelCase: NewRegistry, Tool
YAML/JSON tagssnake_case: yaml:"api_key"
Receivers1-2 chars: r *Registry, c *Config
Mutexesmu sync.RWMutex — setters under Lock, getters under RLock
Errorsfmt.Errorf("context: %w", err)
Tests_test.go in the same package

Development Commands#

# Compile all packages
go build ./...

# Static analysis
go vet ./...

# Tests with race detection
go test -race -coverprofile=coverage.out ./...

# Dependencies
go mod download && go mod tidy

# Build binary
go build -o gokin ./cmd/gokin

# Build with version
go build -ldflags "-X main.version=1.0.0" -o gokin ./cmd/gokin

CI/CD#

  • Automated tests on every PR
  • Cross-platform builds: linux/darwin/windows × amd64/arm64
  • Releases on v* tags
  • Coverage via codecov

Key Files#

FileLOCPurpose
app/app.go~1200Main orchestrator, message processing loop
app/builder.goDI container, selectToolSets
app/message_processor.goModel message processing
agent/agent.goAgent struct, Run(), execution, delegation
agent/delegation.goDelegationStrategy, rules, scoring
agent/tree_planner.goTask decomposition into tree
tools/registry.goRegistry, ToolSet, FilteredGeminiTools
tools/executor.goTool call execution, streaming
tools/declarations.goAll 54 tool declarations
client/factory.goNewClient(), pool, fallback
client/model_profiles.goModelProfile for Ollama
config/provider_registry.go7 providers, models, validation

Contributing#

  1. Fork the repository
  2. Create a branch: git checkout -b feature/my-feature
  3. Ensure it compiles: go build ./...
  4. Static analysis: go vet ./...
  5. Tests: go test -race ./...
  6. Commit and PR
GitHub MIT License © Gokin Contributors