A powerful, extensible AI coding agent CLI with multi-provider support, built-in tools, and a rich extension system.
- Multi-Provider LLM Support: Anthropic, OpenAI, Google Gemini, Ollama, Azure OpenAI, AWS Bedrock, OpenRouter, and more
- Built-in Core Tools: shell (configurable shell, bash by default, with interactive sudo password prompt), read, write, edit, grep, find, ls, subagent - no MCP overhead
- Named Agents: Reusable subagent presets defined in markdown with per-agent tool allowlists, advertised to the LLM for delegation. A built-in status bar above parent activity shows active runs and recent results
- Smart @ Attachments: Binary files auto-detected via MIME type, MCP resources via
@mcp:server:uri - MCP Integration: Connect external MCP servers for expanded capabilities
- Extension System: Write custom tools, commands, widgets, and UI modifications in Go
- Theming: 22 built-in color themes (KITT, Catppuccin, Dracula, Nord, etc.) with runtime switching, persistence, and custom theme files
- Model Persistence: Model and thinking level selections are automatically saved and restored across sessions
- Prompt Templates: Create reusable prompt templates with shell-style argument substitution
- Interactive TUI: Rich terminal interface powered by Bubble Tea with streaming, syntax highlighting, and custom rendering
- Session Management: Tree-based conversation history with branching support
- Non-Interactive Mode: Script-friendly positional args with JSON output
- GitHub Integration: Scaffold a GitHub Actions workflow with
kit github installto run Kit as a collaborator/reviewer on/kitcomments - ACP Server: Run Kit as an Agent Client Protocol agent over stdio
- Sessions: with the daemon running, a plain
kitis already a detachable session in the directory you ran it from (tmux-style:Ctrl+]ddetach,sswitch,cnew,n/pcycle);kit session attachpicks which session to return to, across this machine and every paired host, andkit session lslists them - Remote Sessions:
kit daemonon one machine; pair a client once withkit daemon pair+kit remote --pair <code>(accept/reject on the host), then connect any time withkit remote --host <name>— sessions can be detached (Ctrl+] d) and reattached from the in-client session list, or shared by several clients at once (tmux-style mirrored view) — end-to-end encrypted iroh transport, revocable public-key credentials, clipboard image paste from the client machine, systemd service support - Go SDK: Embed Kit in your own applications with full agent lifecycle events (30+ event types) and behavior-modifying hooks
curl -fsSL https://raw.githubusercontent.com/mark3labs/kit/master/install.sh | bashThe script downloads the latest release binary for your platform, verifies its
SHA-256 checksum, and installs it to ~/.local/bin (or another writable
directory on your system). Options:
# Install a specific version into a custom directory
curl -fsSL https://raw.githubusercontent.com/mark3labs/kit/master/install.sh | bash -s -- --version v0.111.0 --bin-dir /usr/local/bin| Option | Description |
|---|---|
--bin-dir <dir> |
Install directory (env: KIT_BIN_DIR) |
--version <tag> |
Release tag to install, default is latest (env: KIT_VERSION) |
--no-provider-check |
Skip the LLM provider API key reminder |
npm install -g @mark3labs/kit
# or
bun install -g @mark3labs/kit
# or
pnpm install -g @mark3labs/kitnix profile install github:mark3labs/kit --no-write-lock-fileThe flake ships no lock file: its only inputs are nixpkgs and the pinned
release, and an unlocked nixpkgs is what keeps the build working as kit asks
for newer Go. Nix cannot create a lock file inside the read-only source it
fetched, so an install without --no-write-lock-file fails with cannot write modified lock file.
This installs the newest tagged release, built from source for linux and macOS on amd64 and arm64. To install a specific release instead, name its tag:
nix profile install github:mark3labs/kit/v0.111.0 --no-write-lock-fileRun nix develop in a clone to hack on kit with a Nix-provided toolchain: it
puts go (the version go.mod asks for), gopls, and golangci-lint on
$PATH; build the binary yourself with go build ./cmd/kit. See
Development for details.
go install github.com/mark3labs/kit/cmd/kit@latestgit clone https://github.com/mark3labs/kit.git
cd kit
go build -o kit ./cmd/kit# Start interactive session
kit
# Run a one-off prompt
kit "List files in src/"
# Attach files as context
kit @main.go @test.go "Review these files"
# Continue the most recent session
kit --continue
# Model and thinking level selections are automatically persisted
# across sessions and restored on next launch
# Use specific model
kit --model anthropic/claude-sonnet-latest# Get JSON output for scripting (stdout carries only the JSON envelope,
# so it pipes straight into jq; banners and warnings go to stderr)
kit "Explain main.go" --json | jq -r .response
# Quiet mode (final response only)
kit "Run tests" --quiet
# Ephemeral mode (no session file)
kit "Quick question" --no-sessionThe standard CLI/TUI and non-interactive runs execute enabled tool calls without
a built-in per-call approval prompt by default. Shell commands run with the
process account's permissions. The write and edit tools can change files without
using the shell. Custom extensions and SDK hooks can block calls. --quiet,
--json, and --no-session do not add isolation or approval.
Repository files, fetched content, MCP results, and issue/PR text can influence model instructions. With untrusted input, use a dedicated restricted account or an isolated runtime. Limit filesystem mounts and network access, and provide only the credentials needed for the task. A working directory is not a filesystem access boundary. Kit does not automatically provide an OS sandbox.
ACP server mode is an exception: its default approval mode is
ask, subject to read-only tool exemptions and remembered session decisions.
Approval prompts are not an OS sandbox or a complete defense against malicious
input. This guidance does not add a gate that blocks destructive commands.
Kit can run as an ACP (Agent Client Protocol) agent server, enabling ACP-compatible clients (such as OpenCode) to drive Kit as a remote coding agent over stdio.
# Start Kit as an ACP server (communicates via JSON-RPC 2.0 on stdin/stdout)
kit acp
# With debug logging to stderr
kit acp --debugThe ACP server exposes Kit's full capabilities — LLM execution, tool calls (shell, read, write, edit, grep, etc.), and session persistence — over the standard ACP protocol (protocol version 1). Sessions are persisted to Kit's normal JSONL session files.
Supported ACP features:
- Sessions:
session/new,session/load(replays the history),session/resume,session/list,session/close,session/cancel - Working directory: each session uses the
cwdthe client sends for project discovery (AGENTS.md, skills,.kit.yml) and as the base directory of the file and shell tools - MCP servers: servers the client passes in
mcpServers(stdio, HTTP, SSE) are connected in addition to the ones in Kit's config - Tool approval: before edits, commands, subagents and MCP/extension tools, Kit asks the client with
session/request_permission(allow/reject, once or always). Set the default withkit acp --approval ask|auto_edit|auto; clients change it per session with theapprovalconfig option - Editor integration: when the client supports them, file reads and writes go through
fs/read_text_file/fs/write_text_file(so the agent sees unsaved buffers), and shell commands run in client terminals (terminal/*) that are shown in the tool call - Config options:
approval,modeland, for reasoning models,thinking_level, set withsession/set_config_option. Initial thinking uses normal configuration precedence, including per-model defaults, unless explicitly overridden - Slash commands: prompt templates and skills are sent as
available_commands_update;/name argsexpands them - Plans: the model publishes its plan with the
update_plantool, shown as an ACP plan - Prompt content: text, images, embedded resources, and resource links
- Streaming: agent messages, thoughts, and tool calls (with kind, file locations, and diffs for edits)
- Auth errors: missing provider credentials give the
auth_requirederror with steps to fix them
To check an ACP build end to end, run python3 scripts/acp_smoke_test.py (set MODEL=provider/model to pick a model).
Kit looks for configuration in the following locations (in order of priority):
- CLI flags
- Environment variables (with
KIT_prefix) ./.kit.yml/./.kit.yaml/./.kit.json(project-local)~/.kit.yml/~/.kit.yaml/~/.kit.json(global)
Create ~/.kit.yml:
model: anthropic/claude-sonnet-latest
max-tokens: 4096
temperature: 0.7
stream: true
thinking-level: off # model-specific effort; off omits the field
no-core-tools: false # set to true to disable all built-in core tools
exclude-core-tools: # List of core tools to exclude, mutually exclusive to `include-core-tools`
#include-core-tools:
# - "shell" # List of core tools to exclude, mutually exclusive to `exclude-core-tools`
shell: "bash" # shell the shell tool runs commands through, plus
# its own leading arguments, e.g. "busybox ash". Set
# this on images that do not ship bash
shell-timeout: 120 # default per-call timeout in seconds
shell-max-timeout: 600 # ceiling a single call may request
codemode: # one "codemode" tool runs JavaScript that calls the
enabled: true # other tools; intermediate results stay out of context
mcp-exposure: direct # direct | codemode (scripts only) | deferred | model-only
# Images read by the read tool. PNG, JPEG, GIF and WebP files are sent to the
# model as viewable images instead of as text. An image above either limit is
# scaled down before it is sent, and re-encoded as JPEG when lossless encoding
# cannot meet the byte budget.
image-max-edge: 1568 # largest width or height in pixels
image-max-bytes: 3500000 # largest encoded payload in bytes
image-no-resize: false # set to true to reject an oversized image instead
# Skills — all keys are optional
no-skills: false # set to true to disable all skill loading
skill: # explicit skill files/dirs (disables auto-discovery)
- /path/to/skill.md
skills-dir: "" # scan this directory directly for skills (overrides auto-discovery)
skill-disable: # hide skills from the model catalog by name (still usable via /<name>)
- some-skill
# Named agents
no-agents: false # set to true to disable named agent discovery (built-ins and definition files)All of the above keys can also be set programmatically via the SDK
(kit.Options.MaxTokens, Options.Temperature, Options.ThinkingLevel, etc.)
without touching config files — see SDK options.
/thinking lists the current model's catalog choices. Kit preserves their exact
spelling and order, including xhigh, max, and future named efforts. off
omits the effort field; it does not guarantee that the model stops reasoning.
none is a literal provider effort. Explicit CLI and SDK requests for an
unsupported effort on a graded model return an error before a request is sent.
The TUI reports any suggested substitution.
When metadata is missing, the default ladder is off, none, minimal, low,
medium, high, xhigh. Unknown-capability OpenAI-compatible endpoints also
accept explicit effort strings; the remote provider decides whether they are
valid. Token-budget adapters retain their finite controls. If an adapter cannot
encode a selected effort, Kit returns an error instead of silently dropping it.
Custom endpoints can declare an authoritative list in YAML or JSON:
customModels:
jundot-qwen:
name: Qwen3.8-27B
baseUrl: http://localhost:8000/v1
apiModelName: Qwen3.8-27B-oQ6e-fp16-mtp
reasoning: true
reasoningLevels: [low, medium, xhigh]
limit:
context: 262144
output: 32768A non-empty list requires reasoning: true. Entries must be non-blank strings;
off is reserved. Duplicates are removed in order. Invalid models are excluded
with a configuration warning. An omitted or empty list leaves capabilities
unknown. params.thinkingLevel and modelSettings set defaults, not capability
lists. No model-name inference or effort translation is applied.
export ANTHROPIC_API_KEY="sk-..."
export OPENAI_API_KEY="sk-..."
export KIT_MODEL="openai/gpt-4o"Add external MCP servers to .kit.yml:
mcpServers:
filesystem:
type: local
command: ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed"]
environment:
LOG_LEVEL: "info"
allowedTools: ["read_file", "write_file"]
search:
type: remote
url: "https://mcp.example.com/search"
pubmed:
type: remote
url: "https://pubmed.mcp.example.com"
noOAuth: true # skip OAuth for public servers that don't require auth
builds:
type: remote
url: "https://builds.mcp.example.com"
tasksMode: always # async task execution — see MCP Tasks below# Model and provider
--model, -m Model to use (provider/model format)
--provider-api-key API key for the provider
--provider-url Base URL for provider API
--provider-wire Wire protocol for auto-routed providers (openai, openai-compat, anthropic, google)
--tls-skip-verify Skip TLS certificate verification
# Session management
--session, -s Open specific JSONL session file
--continue, -c Resume most recent session for current directory
--resume, -r Interactive session picker
--no-session Ephemeral mode, no persistence
# Behavior (non-interactive: pass prompt as positional arg)
--quiet Suppress all output (non-interactive only)
--json Output response as JSON (non-interactive only)
--no-exit Enter interactive mode after prompt completes
--max-steps Maximum agent steps (0 for unlimited)
--stream Enable streaming output (default: true)
--compact Enable compact output mode
--auto-compact Auto-compact conversation near context limit (reactive compact-and-retry on provider context-overflow errors is always on)
# Extensions and tools
--extension, -e Load additional extension file(s) (repeatable)
--no-extensions Disable all extensions
--no-core-tools Disable all built-in core tools (shell, read, write, edit, grep, find, ls, subagent)
--include-core-tools
--exclude-core-tools Mutually exclusive lists of core tool names to include or not to include in agent
--codemode Enable code mode (default on; --codemode=false disables): the model can run JavaScript that calls the other tools
--shell Shell the shell tool runs commands through, e.g. "/bin/dash" or "busybox ash" (default "bash")
--prompt-template Load a specific prompt template by name
--no-prompt-templates Disable prompt template loading
# Skills
--skill Load skill file or directory (repeatable)
--skills-dir Scan this directory directly for skills (overrides auto-discovery)
--skill-disable Hide a skill from the model catalog by name (repeatable); still usable via /<name>
--no-skills Disable skill loading (auto-discovery and explicit)
--no-agents Disable named agent discovery (built-ins and definition files)
# Generation parameters
--max-tokens Maximum tokens in response (default: 8192, auto-raised up to 32768 for models with larger known output limits)
--temperature Randomness 0.0-1.0 (default: 0.7)
--top-p Nucleus sampling 0.0-1.0 (default: 0.95)
--top-k Limit top K tokens (default: 40)
--stop-sequences Custom stop sequences (comma-separated)
--frequency-penalty Penalize frequent tokens 0.0-2.0 (default: 0.0)
--presence-penalty Penalize present tokens 0.0-2.0 (default: 0.0)
--thinking-level Model-specific reasoning effort (default: off; /thinking lists choices)
# System
--config Config file path (default: ~/.kit.yml)
--system-prompt System prompt text or file path
--debug Enable debug logging# Authentication (OAuth for anthropic/openai/copilot, API key for everything else)
kit auth login [provider] # Start OAuth flow (e.g., anthropic) or prompt for an API key
kit auth login groq --api-key ... # Store an API key without prompting
kit auth login [provider] --set-default # Set provider's default model as system default
kit auth logout [provider] # Remove credentials for provider
kit auth status # Check authentication status
# Stored keys ($XDG_CONFIG_HOME/.kit/credentials.json) win over env vars; env vars stay the fallback.
# No key yet? Kit still starts — use /connect inside the TUI to add one.
# GitHub Copilot login (experimental; requires active Copilot subscription)
kit auth login copilot
kit --model copilot/gpt-5.5 "Hello"
# Model database
kit models [provider] # List available models (optionally filter by provider)
kit models --all # Show all providers (not just LLM-compatible)
kit update-models [source] # Update model database (from models.dev, URL, file, or 'embedded')
# Extension management
kit extensions list # List discovered extensions
kit extensions validate # Validate extension files
kit extensions init # Generate example extension template
kit install <git-url> # Install extensions from git repositories
kit install -l <git-url> # Install to project-local .kit/git/ directory
kit install -u <git-url> # Update an already-installed package
kit install --uninstall <pkg> # Remove an installed package
# Skills
kit skill list # List discovered skills with scope and spec warnings
kit skill validate <path> # Validate a skill against the agentskills.io spec
kit skill install # Install the Kit skills (kit-extensions, kit-sdk) via skills.sh
# GitHub integration
kit github install # Scaffold .github/workflows/kit.yml (run Kit on '/kit' comments)
kit github install --model anthropic/claude-sonnet-4-5-20250929
kit github install --force # Overwrite an existing workflow file
kit github install --no-secret # Skip the offer to set the provider secret via the gh CLI
# ACP server
kit acp # Start as ACP agent (stdio JSON-RPC)
kit acp --debug # With debug logging to stderr
# Detachable sessions (survive closing the terminal; Ctrl+] is the prefix)
kit daemon service install # Start the daemon once; after this, plain `kit` is detachable
cd ~/project && kit # A detachable session, right here — Ctrl+] d to leave it working
kit session attach # Pick a live session (local or paired host), or start one
kit session attach 3 # Attach straight to session 3
kit session attach --new # Skip the picker, start a new session
kit session ls # List live sessions
kit session ls --all # Include sessions on every paired host
kit session rename 3 work # Name session 3
kit session kill 3 # Confirm, then stop session 3
kit --no-daemon # Run in this terminal, daemon or no daemon
# Remote sessions (host)
kit daemon # Host sessions for local and paired clients
kit daemon pair # One-time pairing: show code, confirm here
kit daemon status # Endpoint, paired clients, active sessions
kit daemon service install # Install + start the systemd user service
# Remote sessions (client)
kit remote --pair A1B2C3D4 # Pair with a host and save it under a name
kit session attach --host homelab # Attach to the paired host
kit session attach --all # Pick across paired hosts without starting a local daemon
kit session rename --host homelab 3 work
kit session kill --host homelab --yes 3While attached, use Ctrl+] , to edit the session name, or Ctrl+] k to
kill the session with confirmation. /name <name> updates the live-session
picker name. /kill asks you to enter /kill again to confirm; /kill cancel
cancels the request. Killing a session retains its saved conversation history.
The old kit attach, kit ls, and kit remote --host attachment commands
remain as deprecated aliases.
Kit ships with 22 built-in color themes that control all UI elements. Switch at runtime:
/theme dracula
/theme catppuccin
/theme tokyonight
Theme selections are automatically saved and restored on next launch (stored in ~/.config/kit/preferences.yml). This persistence also applies to model and thinking level selections — all are saved together and restored on startup.
Drop a .yml file in ~/.config/kit/themes/ (user) or .kit/themes/ (project):
# ~/.config/kit/themes/my-theme.yml
primary:
light: "#8839ef"
dark: "#cba6f7"
success:
light: "#40a02b"
dark: "#a6e3a1"Built-in themes: kitt, catppuccin, dracula, tokyonight, nord, gruvbox, monokai, solarized, github, one-dark, rose-pine, ayu, material, everforest, kanagawa, amoled, synthwave, vesper, flexoki, matrix, vercel, zenburn
Extensions are Go source files that run via Yaegi interpreter. They can add custom tools, slash commands, widgets, keyboard shortcuts, themes, and intercept lifecycle events.
//go:build ignore
package main
import "kit/ext"
func Init(api ext.API) {
api.OnSessionStart(func(_ ext.SessionStartEvent, ctx ext.Context) {
ctx.SetFooter(ext.HeaderFooterConfig{
Content: ext.WidgetContent{Text: "Custom Footer"},
})
})
}Usage:
kit -e examples/extensions/minimal.goLifecycle Events: OnSessionStart, OnSessionShutdown, OnBeforeAgentStart, OnAgentStart, OnAgentEnd, OnLLMUsage, OnToolCall, OnToolCallInputStart, OnToolCallInputDelta, OnToolCallInputEnd, OnToolExecutionStart, OnToolOutput, OnToolExecutionEnd, OnToolResult, OnInput, OnMessageStart, OnMessageUpdate, OnMessageEnd, OnModelChange, OnThinkingLevelChange, OnTerminalResize, OnTurnStateChange, OnMCPToolsReady, OnContextPrepare, OnBeforeFork, OnBeforeSessionSwitch, OnBeforeCompact, OnCustomEvent, OnSubagentStart, OnSubagentChunk, OnSubagentEnd
OnAgentEnd carries per-turn aggregates (ToolCallCount, ToolNames, LLMCallCount, InputTokensDelta, OutputTokensDelta, CostDelta, DurationMs) so observers don't need to maintain parallel bookkeeping. OnLLMUsage fires after each LLM provider call with token + cost deltas attributed to that specific call/model — use it for accurate budget enforcement between calls instead of waiting for the turn to finish.
OnTurnStateChange is a superset of OnAgentStart/OnAgentEnd: it also covers work that never reaches the agent loop (shell commands) and fires on every path back to idle, including cancellation and error — use it for spinners and turn timers, and OnAgentStart/OnAgentEnd when you specifically care about agent turns. OnTerminalResize and OnTurnStateChange fire in the interactive TUI only.
Custom Components:
- Tools: Add new tools the LLM can invoke
- Commands: Register slash commands (e.g.,
/mycommand) - Options: Register configurable extension options
- Session State: Last-write-wins key-value store via
ctx.SetState/GetState/DeleteState/ListState, persisted to a per-session sidecar file outside the conversation tree - Widgets: Persistent status displays above/below input
- Headers/Footers: Persistent content above/below the conversation (rendered at full width without truncation — size with
ctx.GetTerminalSize()) - Status Bar: Custom status bar entries
- Shortcuts: Global keyboard shortcuts
- Overlays: Modal dialogs with markdown content
- Tool Renderers: Customize how tool calls display
- Message Renderers: Custom rendering for assistant messages
- Editor Interceptors: Handle key events and wrap rendering
- Interactive Prompts: Select, confirm, input, and multi-select dialogs
- Subagents: Spawn in-process child Kit instances
- LLM Completion: Direct model calls via
Complete() - Themes: Register, switch, and read color themes via
RegisterTheme,SetTheme,ListThemes,GetTheme(read the active palette to paint widgets in the user's theme) - Custom Events: Inter-extension communication via
EmitCustomEvent
Bridged SDK APIs (NEW): Extensions can now access internal SDK capabilities:
- Tree Navigation: Navigate conversation history (
GetTreeNode,GetCurrentBranch,NavigateTo), summarize branches (SummarizeBranch), and implement fresh context loops (CollapseBranch) - Skill Loading: Dynamically load and inject skills at runtime (
LoadSkill,DiscoverSkills,InjectSkillAsContext) - Template Parsing: Parse and render templates with
{{variables}}(ParseTemplate,RenderTemplate), parse CLI-style arguments (ParseArguments,SimpleParseArguments), and evaluate model conditionals (EvaluateModelConditional,RenderWithModelConditionals) - Model Resolution: Resolve model fallback chains (
ResolveModelChain), query model capabilities (GetModelCapabilities,CheckModelAvailable), and extract provider/model ID (GetCurrentProvider,GetCurrentModelID) - Model Pricing: Per-million-token costs on
ModelCapabilities.PricingandModelInfoEntry.Pricing— checkPricing.Knownbefore rendering a cost, since local and OpenAI-compatible models report no pricing - Usage & Environment: Session tokens and cost via
GetSessionUsage(includingIsOAuth, since subscriptions report$0cost), terminal dimensions viaGetTerminalSize, reasoning effort viaGetThinkingLevel, and the composed system prompt with its configured source viaGetSystemPrompt
See the examples/extensions/ directory:
minimal.go- Clean UI with custom footerauto-commit.go- Auto-commit on shutdownbookmark.go- Bookmark conversationsbranded-output.go- Branded output renderingbridge-demo.go- Bridged SDK API demo (tree navigation, skills, templates, model resolution)compact-notify.go- Notification on compactionconfirm-destructive.go- Confirm destructive operationscontext-inject.go- Inject context into conversationsconversation-manager.go- NEW Tree navigation, branch summarization, and fresh context loopscustom-editor-demo.go- Vim-like modal editordev-reload.go- Development live-reloadheader-footer-demo.go- Custom headers and footersinline-bash.go- Inline bash executioninteractive-shell.go- Interactive shell integrationkit-kit.go- Kit-in-Kit (sub-agent spawning)lsp-diagnostics.go- LSP diagnostic integrationnotify.go- Desktop notificationsoverlay-demo.go- Modal dialogspermission-gate.go- Permission gating for toolspirate.go- Pirate-themed personalityplan-mode.go- Read-only planning modeproject-rules.go- Project-specific rulesprompt-demo.go- Interactive prompts (select/confirm/input)prompt-templates.go- NEW Frontmatter-driven templates with model switching and skill injectionprotected-paths.go- Path protection for sensitive filesstatus-footer.go- NEW Configurable status bar footer (model, context, cache, cost, clock, turn timing)subagent-monitor.go- NEW Live subagent dashboard: per-frameRenderstrip,RefreshHzanimation, theme-aware colors, and a realShowOverlaymodal for the full transcript (ctrl+alt+s)subagent-widget.go- Multi-agent orchestration with status widgetsubagent-test.go- Subagent testing utilitiessummarize.go- Conversation summarizationtool-logger.go- Log all tool callsneon-theme.go- Custom theme registration and switchingtool-renderer-demo.go- Custom tool call renderingusage-budget.go- Per-call usage callback (OnLLMUsage), session state, and enrichedOnAgentEndper-turn reportwidget-status.go- Persistent status widgets
Also see .kit/extensions/go-edit-lint.go (in this repo) for a project-local extension example that runs gopls and golangci-lint on Go file edits.
Auto-discovery (loads automatically):
/usr/share/kit/extensions/*.go(system-wide single files)/usr/share/kit/extensions/*/main.go(system-wide subdirectory extensions)~/.config/kit/extensions/*.go(user single files)~/.config/kit/extensions/*/main.go(user subdirectory extensions).kit/extensions/*.go(project-local single files).kit/extensions/*/main.go(project-local subdirectory extensions)~/.local/share/kit/git/(global git-installed packages).kit/git/(project-local git-installed packages)
The system-wide directory serves packaged installs (rpm, deb, ...). Set
KIT_SYSTEM_EXTENSIONS_DIR to use other directories (separated by the platform
list separator: : on Unix, ; on Windows), or set it empty to disable
system-wide discovery. Packagers can change the
compiled-in default with
-ldflags "-X github.com/mark3labs/kit/internal/extensions.SystemExtensionsDir=/opt/kit/extensions".
Explicit loading:
kit -e path/to/extension.go
kit -e ext1.go -e ext2.go # Multiple extensionsDisable auto-load:
kit --no-extensionsKit provides a testing package to help you write unit tests for your extensions.
pkg/extensions/test holds the harness and the assertions; pkg/extensions
holds the event, result, and config types the harness speaks in. Both are
public, so these tests build from your own repository:
package main
import (
"testing"
"github.com/mark3labs/kit/pkg/extensions"
"github.com/mark3labs/kit/pkg/extensions/test"
)
func TestMyExtension(t *testing.T) {
harness := test.New(t)
harness.LoadFile("my-ext.go")
// Emit events and verify behavior
_, err := harness.Emit(extensions.SessionStartEvent{SessionID: "test"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Verify the extension printed something
test.AssertPrinted(t, harness, "session started")
}Available assertions:
AssertBlocked(),AssertNotBlocked()— Verify tool blockingAssertWidgetSet(),AssertWidgetText()— Verify widget contentAssertPrinted(),AssertPrintedContains()— Verify outputAssertToolRegistered(),AssertCommandRegistered()— Verify registration
See examples/extensions/tool-logger_test.go for a complete example with 14 test cases covering tool calls, input handling, and session lifecycle.
Create reusable prompt templates with shell-style argument substitution. Templates are loaded from ~/.kit/prompts/*.md and .kit/prompts/*.md.
Example template (~/.kit/prompts/review.md):
---
description: Review code for issues
---
Review the following code for bugs and security issues.
Focus on $1 specifically.Usage:
/review error handling
Argument placeholders:
$1,$2, etc. — Individual arguments$@or$ARGUMENTS— All arguments (zero or more)$+— All arguments (one or more required; error if none given)${@:2}— Arguments from position 2 onwards${@:1:3}— 3 arguments starting at position 1
Placeholders inside fenced code blocks (```) and inline code spans are ignored.
Disable templates with --no-prompt-templates or load a specific template with --prompt-template <name>.
Define reusable subagent presets as markdown files. Named agents are advertised to the LLM in the subagent tool description, so the main agent can delegate tasks to the right specialist by name — with a preset system prompt, model, tool allowlist, temperature, and timeout.
Discovery locations (highest to lowest precedence):
<project>/.agents/agents/*.md— project-local (cross-client convention)<project>/.kit/agents/*.md— project-local (Kit-specific)~/.config/kit/agents/*.md— user-level ($XDG_CONFIG_HOMEaware)- Built-in agents:
general(full tool access) andexplore(read-only:read,grep,find,ls)
The filename (minus .md) is the agent name. Higher-precedence definitions override lower ones, so a project can replace — or disable — a built-in or user-level agent.
Example (.agents/agents/code-reviewer.md):
---
description: Reviews code for quality and best practices # required
model: anthropic/claude-sonnet-4 # optional model override
tools: [read, grep, find, ls] # optional tool allowlist
temperature: 0.1 # optional
timeout: 300 # optional, seconds
hidden: false # optional: resolvable but not advertised
disabled: false # optional: remove this agent (and anything it shadows)
---
You are in code review mode. Focus on correctness, security, and
maintainability. Report findings with file paths and line references.The LLM invokes it through the subagent tool's agent parameter; explicit model / system_prompt / timeout_seconds arguments override the agent's presets. An agent without a tools: list gets the default subagent tool set (everything except subagent, preventing recursion); with a tools: allowlist the subagent is restricted to exactly those tools.
From the Go SDK:
agents := k.GetAgents() // discovered definitions (built-ins included)
result, err := k.Subagent(ctx, kit.SubagentConfig{
Prompt: "Map out the session persistence flow",
Agent: "explore", // preset prompt + read-only tools
})Disable discovery entirely with --no-agents, the no-agents config key (.kit.yml), KIT_NO_AGENTS=true, or Options.NoAgents in the SDK.
Inspect retained child output and tool activity in the TUI with /subagents. Press Ctrl+Alt+A to open a subagent run picker, then Enter to inspect the selected run. The subagent status bar shows this shortcut when space permits. Use Left/Right to switch sibling runs, Up/Down or Page Up/Page Down to scroll, Esc to return to the parent, and Ctrl+K to stop an active run. The conversation view is read-only and refreshes once per second.
The status bar shows active runs with blinking lights and finished results for 10 seconds. Older retained runs remain available in the picker. The view uses in-memory history, not saved session files.
Command migration: /subagent-sessions and /agents have been removed; use /subagents instead. Extensions can no longer override /subagents. The subagent-monitor.go example now opens its overlay with /subagent-monitor (or Ctrl+Alt+S).
From the SDK, use k.SubagentRuns() for active and retained finished runs, or k.GetSubagentRun(runID) for one snapshot. k.RunningSubagents() continues to list only active runs. History retains up to 100 finished runs plus all active runs and up to 500 event records per run.
See the subagent documentation for controls, history limits, and SDK examples.
Kit can run as an automated collaborator/reviewer inside GitHub Actions. The
kit github install command scaffolds a workflow that triggers when someone
comments /kit ... on an issue or pull request review, runs the agent
non-interactively in the runner, and lets it respond.
kit github installThis writes .github/workflows/kit.yml. By default the command prompts for the
model (pre-filled with a sensible default); pass --model to skip the prompt.
If the gh CLI is detected on your PATH and the
provider API key is present in your environment, you'll be offered the option to
store it as a repository secret automatically.
The generated workflow:
- Triggers only on
issue_commentandpull_request_review_comment(types: [created]). - Runs only when the comment begins with the
/kitcommand token. - Restricts triggers to repository owners, members, and collaborators (via
author_association). - Uses least-privilege
permissionsandpersist-credentials: false. - Authenticates git/PR operations with the built-in
secrets.GITHUB_TOKENand the provider via a repository secret (e.g.ANTHROPIC_API_KEY).
CI trust: Trigger restrictions control who can start a run. They do not make all issue/PR content trusted or confine tool calls. Use an isolated, disposable runner, minimal token permissions, and only required credentials. Do not expose unrelated secrets or host access. See Tool execution and trust.
After committing the workflow and setting the provider secret, comment
/kit <your request> on any issue or pull request to trigger Kit.
The generated workflow uses the bundled mark3labs/kit composite
action, which installs the Kit binary and runs kit github run. That command
reads the triggering event, enforces permissions, reacts with an emoji, runs the
agent against the issue thread or pull request, posts the response as a comment,
and — if the agent changed files — pushes a kit-agent[bot] branch and opens a
pull request.
| Flag | Description |
|---|---|
--model |
Provider/model to write into the workflow |
--force |
Overwrite an existing workflow file |
--no-secret |
Skip the offer to set the provider secret via the gh CLI |
Kit uses a tree-based session model that supports branching and forking conversations.
- Default:
~/.kit/sessions/<cwd-path>/<timestamp>_<id>.jsonl - Path separators in the working directory are replaced with
--(e.g.,/home/user/projectbecomeshome--user--project) - Each line is a session entry (messages, tool calls, extension data)
- Supports branching from any message to explore alternate paths
# Resume most recent session for current directory
kit --continue
kit -c
# Interactive session picker
kit --resume
kit -r
# Open specific session file
kit --session path/to/session.jsonl
kit -s path/to/session.jsonl
# Ephemeral mode (no file persistence)
kit --no-sessionDuring an interactive session, use these slash commands:
| Command | Description |
|---|---|
/name [name] |
Set or display the session's display name |
/session |
Show session info (path, ID, message count) |
/resume |
Open the session picker to switch sessions |
/export [path] |
Export session as JSONL (auto-generates path if omitted) |
/import <path> |
Import and switch to a session from a JSONL file |
/share |
Upload session to GitHub Gist and get a shareable viewer URL |
/tree |
Navigate the session tree |
/fork |
Fork to new session from an earlier message |
/new |
Start a fresh session |
| Shortcut | Description |
|---|---|
Ctrl+V |
Paste an image from the clipboard — shows an inline low-res thumbnail preview (tmux/zellij-safe); over kit remote it pastes from the local machine's clipboard |
Ctrl+U |
Clear all pending image attachments |
Ctrl+X e |
Open $VISUAL/$EDITOR to compose or edit your prompt |
Ctrl+X s |
Steer — inject a system-level instruction mid-turn |
Ctrl+] |
Session prefix in any daemon-hosted session (kit with a daemon running, kit attach, kit remote): d detach · s switch · c new · n/p cycle · w across hosts |
ESC ESC |
Cancel the current operation (tool call or streaming) |
↑ / ↓ |
Navigate prompt history |
| Mouse drag | Select text in the conversation. Kit copies it on release and keeps it highlighted until the next key or click, or until the text changes. On Linux and the BSDs it also sets the primary selection for middle-click paste in other windows |
Embed Kit in your Go applications:
package main
import (
"context"
"log"
kit "github.com/mark3labs/kit/pkg/kit"
)
func main() {
ctx := context.Background()
// Create Kit instance with default configuration
host, err := kit.New(ctx, nil)
if err != nil {
log.Fatal(err)
}
defer func() { _ = host.Close() }()
// Send a prompt
response, err := host.Prompt(ctx, "What is 2+2?")
if err != nil {
log.Fatal(err)
}
println(response)
}Kit-controlled HTTP requests use User-Agent: KIT by default. Set a per-instance
identity with either API:
host, err := kit.New(ctx, &kit.Options{UserAgent: "MyApp/1.0"})
// Or use functional options:
host, err = kit.NewAgent(ctx, kit.WithUserAgent("MyApp/1.0"))Empty means KIT, not an opt-out. Valid values are preserved without trimming,
including spaces. HTTP control characters (including CR, LF, NUL and DEL) are
rejected before construction or login does network work. This setting has no
CLI flag, environment variable, or config-file key.
Each instance keeps its own identity. It passes to model changes, temporary
completions and subagents, including isolated/bare children and provider changes.
It covers built-in model requests (including streams), Ollama setup, remote MCP
and their OAuth requests, and provider token refresh. The identity replaces
conflicting user-agent headers from model defaults, catalog headers, per-call
headers and MCP configuration, case-insensitively. Other headers stay unchanged.
Custom provider factories receive ProviderConfig.UserAgent and must enforce it
on their own requests; Kit does not intercept arbitrary factory traffic.
Two inference paths keep their fixed compatibility identities, even with an SDK override. Their token endpoints still use the configured Kit identity. These exceptions follow existing source requirements; they were not live-tested.
| Inference path | Fixed user agent |
|---|---|
| Codex | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 |
| Copilot | GitHubCopilotChat/0.31.0 |
Standalone kit.LoginProvider has a separate kit.LoginOptions.UserAgent field
(default KIT), independent of any Kit instance or supplied credential manager.
Standalone CLI OAuth and model-catalog downloads use KIT.
This policy does not change global HTTP defaults. It does not cover subprocesses (shell, git/gh/curl, installers, npm or MCP server processes), extension/application code, browser navigation, or remote services acting for Kit. Third-party cloud credential discovery (metadata services or default credential chains) inherits the identity only when it uses the HTTP client Kit supplies; model endpoint requests use the configured identity. A custom transport can rewrite headers after Kit's boundary and is responsible for its own policy.
host, err := kit.New(ctx, &kit.Options{
Model: "ollama/llama3",
SystemPrompt: "You are a helpful bot",
ConfigFile: "/path/to/config.yml",
MaxSteps: 10,
Streaming: ptr(true), // *bool: nil = unset (default true), &false = off
Quiet: true,
// Generation parameters (override env/config/per-model defaults)
MaxTokens: 16384, // 0 = auto-resolve (env → config → per-model → 8192 floor)
ThinkingLevel: "medium", // model-specific; see SupportedThinkingLevels
Temperature: ptr(float32(0.2)), // pointer so 0.0 != unset; nil = provider default
TopP: nil, // nil = leave provider/per-model default
TopK: nil,
FrequencyPenalty: nil,
PresencePenalty: nil,
// Provider configuration (override env/config without reaching into viper)
ProviderAPIKey: "sk-...", // "" = use config / provider env var
ProviderURL: "https://proxy.internal/v1", // "" = provider default
TLSSkipVerify: false, // only takes effect when true
// Session options
SessionPath: "./session.jsonl", // Open specific session
Continue: true, // Resume most recent session
NoSession: true, // Ephemeral mode
// Tool options
Tools: []kit.Tool{...}, // Replace default tool set entirely
ExtraTools: []kit.Tool{...}, // Add tools alongside defaults
DisableCoreTools: true, // Disable all built-in core tools; also controllable via
// --no-core-tools flag, KIT_NO_CORE_TOOLS env var,
// or no-core-tools: true in .kit.yml
CoreToolList []string, // List of core tool names to register. If empty (default), include all.
// DisableCoreTools has precedence.
// Configuration
SkipConfig: true, // Skip .kit.yml files (viper defaults + env vars still apply)
// Compaction — proactive check before turns near the context limit.
// Reactive compaction (compact + replay once on provider context-overflow
// errors) is always on, independent of this setting.
AutoCompact: true,
Debug: true, // Debug logging
})Generation & provider fields (added in v0.55+) let SDK consumers configure
Kit entirely in-code without viper.Set() workarounds or shipping a .kit.yml.
Precedence is Options > KIT_* env vars > .kit.yml > per-model defaults
(modelSettings / customModels) > provider-level defaults. Sampling params
are pointer types so explicit 0.0 is distinguishable from "leave alone"; a
non-zero MaxTokens suppresses automatic right-sizing the same way --max-tokens
does on the CLI.
For simple programmatic setups, kit.NewAgent offers an ergonomic
functional-options front door over kit.New. Streaming is enabled by
default; pass kit.WithStreaming(false) to opt out.
host, err := kit.NewAgent(ctx,
kit.WithModel("anthropic/claude-sonnet-4-5-20250929"),
kit.WithSystemPrompt("You are a helpful assistant."),
kit.WithMaxTokens(8192),
kit.WithThinkingLevel("medium"),
kit.Ephemeral(), // in-memory session, no persistence
)Available options: WithModel, WithSystemPrompt, WithStreaming,
WithMaxTokens, WithThinkingLevel, WithTools, WithExtraTools,
WithProviderAPIKey, WithProviderURL, WithProvider, WithConfigFile,
WithDebug, WithDebugLogger, and Ephemeral. For advanced configuration not covered by
the helpers (custom MCP config, in-process MCP servers, session backends, MCP
task tuning) construct an Options value explicitly and call kit.New.
An embedded Kit discovers .kit.yml, AGENTS.md, skills, extensions and
named agents on the host, as the CLI does. It also writes session files and
enables the core file and shell tools. kit.NewIsolatedAgent turns all of
this off. The opt-in options (WithConfig, WithContextFiles, WithSkills,
WithExtensions, WithAgents, WithSessions, WithCoreTools) turn each
feature back on:
host, err := kit.NewIsolatedAgent(ctx,
kit.WithModel("anthropic/claude-sonnet-4-5-20250929"),
kit.WithCoreTools("read", "grep"), // only these core tools
)kit.Isolated() is the same preset as an Option for kit.NewAgent. See
Isolated agents.
Each kit.New / kit.NewAgent call owns an isolated configuration store,
so constructing multiple Kit instances in the same process is safe: setting the
model, thinking level, or generation parameters on one never affects another,
and runtime mutators (SetModel, SetThinkingLevel) only touch the owning
instance. This makes subagent spawning and multi-Kit embedding race-free with
no external synchronization required.
When a remote MCP server returns 401, Kit runs the full OAuth flow (dynamic
client registration → PKCE → token exchange → persistence) but delegates the
user-facing step — showing the authorization URL and receiving the callback —
to an MCPAuthHandler that you pass explicitly via Options.MCPAuthHandler.
If nil, OAuth is disabled and the authorization-required error surfaces to the
caller; the SDK never auto-opens a browser or binds a localhost port.
// CLI/TUI apps: opens the system browser + prints status to stderr.
authHandler, _ := kit.NewCLIMCPAuthHandler()
defer authHandler.Close()
host, _ := kit.New(ctx, &kit.Options{
MCPAuthHandler: authHandler,
})
// Custom UX: reuse the SDK's port + callback server, supply your own
// presentation via OnAuthURL (TUI modal, QR code, web redirect, etc.).
// h, _ := kit.NewDefaultMCPAuthHandler()
// h.OnAuthURL = func(server, authURL string) { myUI.Show(server, authURL) }
//
// Full control (web apps, daemons): implement kit.MCPAuthHandler yourself —
// no localhost binding, no side effects.Tokens are persisted to $XDG_CONFIG_HOME/.kit/mcp_tokens.json by default; swap
in a custom MCPTokenStoreFactory for encrypted, DB-backed, or in-memory
storage. See the SDK options docs for
the full matrix.
Kit advertises MCP task support
during initialize, so cooperating MCP servers can respond to tools/call
with a taskId instead of blocking the connection. Kit then polls
tasks/get / tasks/result until the task reaches a terminal state, and
best-effort tasks/cancels on context cancellation.
Defaults are safe — a server that doesn't advertise task capability runs
synchronously, exactly as before. Opt in per server via tasksMode in
.kit.yml (auto | never | always) or programmatically through the SDK:
host, _ := kit.New(ctx, &kit.Options{
MCPTaskMode: map[string]kit.MCPTaskMode{
"build-server": kit.MCPTaskModeAlways,
},
MCPTaskTimeout: 15 * time.Minute,
MCPTaskProgress: func(p kit.MCPTaskProgress) {
log.Printf("%s: %s", p.TaskID, p.Status)
},
})
tasks, _ := host.ListMCPTasks(ctx, "build-server")
_, _ = host.CancelMCPTask(ctx, "build-server", tasks[0].TaskID)See the configuration docs and SDK options → MCP Tasks for the full surface.
Create custom tools with automatic schema generation — no external dependencies needed:
type SearchInput struct {
Query string `json:"query" description:"Search query"`
}
searchTool := kit.NewTool("search", "Search the codebase",
func(ctx context.Context, input SearchInput) (kit.ToolOutput, error) {
return kit.TextResult("Found: ..."), nil
},
)
host, _ := kit.New(ctx, &kit.Options{
ExtraTools: []kit.Tool{searchTool}, // adds alongside built-in tools
})Use kit.NewParallelTool for tools safe to run concurrently. Binary data (images, audio, etc.) in ToolOutput.Data is automatically forwarded to the LLM when MediaType is set. See the SDK docs for full details on struct tags, ToolOutput fields, and ToolCallIDFromContext.
| Helper | Description |
|---|---|
kit.TextResult(content) |
Successful text result |
kit.ErrorResult(content) |
Error result (LLM sees it as a tool error) |
kit.ImageResult(content, data, mediaType) |
Image result with binary data (e.g. "image/png") |
kit.MediaResult(content, data, mediaType) |
Non-image media result (e.g. "audio/mpeg") |
kit.ToolOutput{
Content: "result text", // text returned to the LLM
IsError: false, // true = LLM sees this as an error
Data: pngBytes, // optional binary data (images, audio)
MediaType: "image/png", // MIME type for binary Data
Metadata: map[string]any{}, // opaque metadata for hooks/UI (not sent to LLM)
}unsub := host.OnToolCall(func(e kit.ToolCallEvent) {
println("Calling tool:", e.ToolName)
})
defer unsub()
unsub2 := host.OnToolResult(func(e kit.ToolResultEvent) {
if e.IsError {
println("Tool failed:", e.ToolName)
}
})
defer unsub2()
unsub3 := host.OnMessageUpdate(func(e kit.MessageUpdateEvent) {
print(e.Chunk)
})
defer unsub3()
response, err := host.Prompt(
ctx,
"List files in current directory",
)// Multi-turn conversations retain context automatically
host.Prompt(ctx, "My name is Alice")
response, _ := host.Prompt(ctx, "What's my name?")
// Sessions are persisted automatically to JSONL files.
// Access session info:
path := host.GetSessionPath()
id := host.GetSessionID()
// Clear conversation history
host.ClearSession()Session persistence is configured via Options:
host, _ := kit.New(ctx, &kit.Options{
SessionPath: "./my-session.jsonl", // Open specific session
Continue: true, // Resume most recent session
NoSession: true, // Ephemeral mode
})For multi-tenant hosts (chatbots, per-user agents, web services), the SDK
lets you swap skills and AGENTS.md-style context files after Kit
construction. Every mutation recomposes the system prompt and applies it to
the agent so the next turn picks up the new instructions — no restart needed.
// Programmatic skill (no file on disk required).
host.AddSkill(&kit.Skill{
Name: "polite-french",
Description: "Respond in French and always greet the user.",
Content: "Always reply in French. Open every response with 'Bonjour'.",
})
// Or load one from disk.
host.LoadAndAddSkill("/var/skills/refund-policy.md")
// Per-user AGENTS.md content pulled from a database.
host.AddContextFileContent(
fmt.Sprintf("session://%s/AGENTS.md", userID),
rulesFromDB,
)
// Tear down session-specific state on logout.
host.RemoveSkill("polite-french")
host.RemoveContextFile(fmt.Sprintf("session://%s/AGENTS.md", userID))
// Hide a skill from the model catalog without unloading it (still usable
// via /<name>); EnableSkill reverses it.
host.DisableSkill("refund-policy")
host.EnableSkill("refund-policy")
// Or replace the whole set atomically.
host.SetSkills(activeSkillsForUser)
host.SetContextFiles(activeContextForUser)Skills dedupe by Name, context files dedupe by Path (which can be any
opaque identifier — it doesn't have to be a real filesystem path). All
mutators and readers (GetSkills, GetContextFiles) are safe to call
concurrently from multiple goroutines. See the SDK overview docs
for the full reference.
Native Go tools can also be added and removed on a live host, mirroring the runtime MCP-server and skill APIs. This is useful for progressive disclosure (dynamically loading a domain toolset only when the model asks for it) and for multi-tenant hosts that need to swap tool catalogs without rebuilding the host.
// Add tools that persist for the session.
host.AddTools(crmTools...)
// Drop a domain when it is no longer needed.
if err := host.RemoveTools("crm_search_contacts", "crm_create_deal"); err != nil {
log.Printf("remove tools: %v", err)
}
// Replace the entire extra-tool set wholesale.
host.SetExtraTools(activeTools...)
// Read back the currently registered extra tools.
extra := host.GetExtraTools()AddTools replaces existing extra tools by name (last-write-wins) and appends
new ones. RemoveTools is atomic: if any supplied name is not currently
registered, it returns an error and leaves the tool set unchanged. Core tools
and MCP tools are unaffected. Mutations take effect on the next LLM step.
Spawn Kit as a subprocess for multi-agent orchestration:
kit "Analyze codebase" \
--json \
--no-session \
--no-extensions \
--quiet \
--model anthropic/claude-haiku-3-5-20241022Parse the JSON output:
{
"response": "Final assistant response text",
"model": "anthropic/claude-haiku-3-5-20241022",
"stop_reason": "end_turn",
"session_id": "a1b2c3d4e5f6",
"usage": {
"input_tokens": 1024,
"output_tokens": 512,
"total_tokens": 1536,
"cache_read_tokens": 0,
"cache_creation_tokens": 0
},
"messages": [
{
"role": "assistant",
"parts": [
{"type": "text", "data": "..."},
{"type": "tool_call", "data": {"name": "...", "args": "..."}},
{"type": "tool_result", "data": {"name": "...", "result": "..."}}
]
}
]
}Test the TUI non-interactively:
# Start Kit in detached tmux session
tmux new-session -d -s kittest -x 120 -y 40 \
"kit -e ext.go --no-session 2>kit.log"
# Wait for startup
sleep 3
# Capture screen
tmux capture-pane -t kittest -p
# Send input
tmux send-keys -t kittest '/command' Enter
# Cleanup
tmux kill-session -t kittest# Build
go build -o output/kit ./cmd/kit
# Run tests
go test -race ./...
# Run specific test
go test -race ./cmd -run TestScriptExecution
# Lint
go vet ./...
# Format
go fmt ./...cmd/kit/ - CLI entry point (main.go)
cmd/ - CLI command implementations (root, auth, models, etc.)
pkg/kit/ - Go SDK for embedding Kit
internal/app/ - Application orchestrator (agent loop, message store, queue)
internal/agent/ - Agent execution and tool dispatch
internal/auth/ - OAuth authentication and credential storage
internal/acpserver/ - ACP (Agent Client Protocol) server
internal/clipboard/ - Cross-platform clipboard operations
internal/compaction/ - Conversation compaction and summarization
internal/config/ - Configuration management
internal/core/ - Built-in tools (shell, read, write, edit, grep, find, ls)
internal/extensions/ - Yaegi extension system
internal/kitsetup/ - Initial setup wizard
internal/message/ - Message content types and structured content blocks
internal/models/ - Provider and model management
internal/session/ - Session persistence (tree-based JSONL)
internal/skills/ - Skill loading and system prompt composition
internal/tools/ - MCP tool integration
internal/ui/ - Bubble Tea TUI components
examples/extensions/ - Example extension files
npm/ - NPM package wrapper for distribution
- Anthropic - Claude models (native, prompt caching, OAuth)
- OpenAI - GPT models
- Copilot - GitHub Copilot models (
copilot, requires active Copilot subscription) - Google - Gemini models
- Ollama - Local models
- Azure OpenAI - Azure-hosted OpenAI
- AWS Bedrock - Bedrock models
- Google Vertex - Claude on Vertex AI
- OpenRouter - Multi-provider router
- Vercel AI - Vercel AI SDK models
- Custom - Any OpenAI-compatible endpoint via
--provider-url - Auto-routed - Any provider from models.dev database
- App-supplied (Go SDK) - Your own inference backend under any provider name, via
kit.RegisterProviderorOptions.Providers(see pkg/kit/README.md)
Use custom/custom when pointing Kit at any OpenAI-compatible endpoint with --provider-url:
kit --provider-url "http://localhost:8080/v1" "Hello"This automatically defaults to custom/custom without needing to specify a model. The custom provider routes through the openaicompat provider and supports:
- Zero cost tracking (input/output = 0)
- 262K context window, 65K output limit
- Reasoning and temperature support
- Optional
CUSTOM_API_KEYenvironment variable or--provider-api-keyflag
Any provider in the models.dev database can be used as
provider/model without a dedicated native integration. Kit auto-routes the
request through the matching wire protocol based on the provider's npm package
(or per-model override), using its api URL as the base:
Providers with an api URL but an unrecognized npm package fall back to the
OpenAI-compatible wire. Because routing follows the wire protocol, aggregator/proxy
providers work across all of their models — including Claude, GPT, and Gemini
routes:
kit --model opencode/claude-haiku-4-5 "Hello" # → Anthropic wire
kit --model opencode/gpt-5 "Hello" # → OpenAI wire
kit --model opencode/gemini-3.5-flash "Hello" # → Google wire
kit --model opencode/grok-4.6 "Hello" # → OpenAI Responses wireWhen an aggregator mixes wires inside one catalog without publishing a hint for
every family, Kit pins the family itself. opencode's gpt-* and grok-* models
answer only on the OpenAI Responses API — their /chat/completions route
returns HTTP 500/503 — so Kit routes them to the Responses wire automatically.
Declare or patch providers in your config file's providers section — fix a
wire protocol the database gets wrong, or define an internal gateway the
database doesn't know about:
# ~/.kit.yml
providers:
minimax: # patch a known provider
wire: anthropic
corp-llm: # declare a new one
wire: anthropic
baseUrl: https://llm.internal.corp/api
apiKeyEnv: [CORP_LLM_KEY]
headers:
X-Team: platformAccepted wire values: openai (Responses API), openai-compat (chat
completions), anthropic, google. Unset fields inherit database values.
For one-off use, --provider-wire with --provider-url routes any
provider/model through the declared wire without a config entry.
provider/model # Standard format
anthropic/claude-sonnet-latest
openai/gpt-4o
ollama/llama3
google/gemini-2.0-flash-exp# Anthropic Claude
claude-opus-latest → claude-opus-4-6
claude-sonnet-latest → claude-sonnet-4-6
claude-haiku-latest → claude-haiku-4-5
claude-4-opus-latest → claude-opus-4-6
claude-4-sonnet-latest → claude-sonnet-4-6
claude-4-haiku-latest → claude-haiku-4-5
claude-3-7-sonnet-latest → claude-3-7-sonnet-20250219
# OpenAI GPT
o1-latest → o1
o3-latest → o3
o4-latest → o4-mini
gpt-5-latest → gpt-5.4
gpt-5-chat-latest → gpt-5.4
gpt-4-latest → gpt-4o
gpt-4 → gpt-4o
gpt-3.5-latest → gpt-3.5-turbo
gpt-3.5 → gpt-3.5-turbo
# Google Gemini
gemini-pro-latest → gemini-2.5-pro
gemini-flash → gemini-2.5-flash
gemini-pro → gemini-2.5-pro
gemini-2-flash → gemini-2.0-flash
gemini-2-pro → gemini-2.5-proAn alias applies only when the target model is available for the provider you
name, so anthropic/claude-sonnet-latest resolves while a provider that does
not carry that model reports a normal unknown-model error.
Contributions are welcome! Please see the contribution guide for guidelines.

