kit-extensions
A guide and workflow for building custom Go-based extensions for the Kit framework.
Install
mkdir -p .claude/skills/kit-extensions && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/11233" && unzip -o skill.zip -d .claude/skills/kit-extensions && rm skill.zipInstalls to .claude/skills/kit-extensions
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Guide for creating Kit extensions. Use when the user asks to build, create, or modify a Kit extension, add a custom tool, slash command, widget, keyboard shortcut, editor interceptor, tool renderer, or hook into any Kit lifecycle event.Key capabilities
- →Build custom slash commands
- →Add custom tools
- →Create UI widgets
- →Hook into editor lifecycle
How it works
It provides a framework for creating single-file Go extensions that hook into Kit's lifecycle events.
Inputs & outputs
When to use kit-extensions
- →Build custom slash commands
- →Add new tools to the Kit environment
- →Create UI widgets for the editor
- →Hook into editor lifecycle events
About this skill
Kit Extensions Development Guide
Kit extensions are single-file Go programs interpreted at runtime by Yaegi. They hook into Kit's lifecycle, register custom tools and slash commands, display widgets, intercept editor input, render tool output, register and switch color themes, and more.
Extensions can be distributed via git repositories using kit install. Repos can contain single extensions or collections of multiple extensions.
Extension Structure
Every extension must export a package main with an Init(api ext.API) function:
//go:build ignore
package main
import "kit/ext"
func Init(api ext.API) {
// Register event handlers, tools, commands, etc.
}
The //go:build ignore tag prevents go build from compiling the file directly.
Extension Locations
Extensions are auto-loaded from these directories:
~/.config/kit/extensions/*.go(global, single files)~/.config/kit/extensions/*/main.go(global, subdirectories).kit/extensions/*.go(project-local, single files).kit/extensions/*/main.go(project-local, subdirectories)
Or loaded explicitly:
kit -e path/to/extension.go
kit --extension path/to/extension.go
Import Path
Extensions import the Kit API as "kit/ext". The full standard library is available plus os/exec for subprocess spawning.
API Overview
The Init function receives an ext.API object for registering handlers, and event handlers receive an ext.Context with runtime capabilities.
Lifecycle Events
Kit provides 30 lifecycle events. Each handler receives an event struct and a Context.
Session Events
// Fired when session is loaded/created.
api.OnSessionStart(func(e ext.SessionStartEvent, ctx ext.Context) {
// e.SessionID string
})
// Fired when Kit is shutting down. Use for cleanup.
api.OnSessionShutdown(func(e ext.SessionShutdownEvent, ctx ext.Context) {
// No fields.
})
Agent Turn Events
// Before agent starts processing. Can inject system prompt or text.
api.OnBeforeAgentStart(func(e ext.BeforeAgentStartEvent, ctx ext.Context) *ext.BeforeAgentStartResult {
// e.Prompt string
// Return nil to pass through.
// Return &ext.BeforeAgentStartResult{SystemPrompt: &s} to augment system prompt.
// Return &ext.BeforeAgentStartResult{InjectText: &s} to inject text before prompt.
return nil
})
// Agent loop has started.
api.OnAgentStart(func(e ext.AgentStartEvent, ctx ext.Context) {
// e.Prompt string
})
// Agent finished responding. Carries per-turn aggregates so observer-style
// extensions don't need to maintain parallel bookkeeping.
api.OnAgentEnd(func(e ext.AgentEndEvent, ctx ext.Context) {
// e.Response string
// e.StopReason string — "error" (on failure), "completed" (when LLM returns
// empty stop reason), or the raw LLM provider value passed through
// (e.g. "stop", "length" (max output tokens hit), "tool-calls", "content-filter").
// To detect errors, check e.StopReason == "error".
// Do NOT compare against "completed" for success — instead check != "error".
//
// Per-turn aggregates (computed by Kit's runtime):
// e.ToolCallCount int — total tool invocations this turn
// e.ToolNames []string — tool names in call order (duplicates preserved)
// e.LLMCallCount int — LLM round-trips / tool-loop iterations
// e.InputTokensDelta int — sum of input tokens across LLM calls this turn
// e.OutputTokensDelta int
// e.CacheReadTokensDelta int
// e.CacheWriteTokensDelta int
// e.CostDelta float64 — USD cost (zero when pricing unknown / OAuth)
// e.DurationMs int64 — wall-clock duration AgentStart→AgentEnd
})
// Per-LLM-call usage — fires after each provider round-trip with token + cost
// deltas attributed to that specific call. A single turn typically produces
// multiple LLMUsageEvents (one per tool-loop iteration). Use this for accurate
// budget enforcement that needs to react between calls instead of waiting
// for the turn to finish.
api.OnLLMUsage(func(e ext.LLMUsageEvent, ctx ext.Context) {
// e.InputTokens, e.OutputTokens int
// e.CacheReadTokens, e.CacheWriteTokens int
// e.Cost float64 — USD; zero when pricing unknown / OAuth
// e.Model, e.Provider string — model used for THIS call
// (may differ across calls if SetModel was called)
// e.StepNumber int — zero-based step index in this turn
// e.FinishReason string — "stop" / "tool_calls" / "length" / ...
// e.RequestID string — optional provider correlation id (may be empty)
})
Tool Events
// Before a tool executes. Can block the call.
api.OnToolCall(func(e ext.ToolCallEvent, ctx ext.Context) *ext.ToolCallResult {
// e.ToolName string
// e.ToolCallID string
// e.Input string — JSON-encoded parameters
// e.Source string — "llm" or "user"
// Return nil to allow.
// Return &ext.ToolCallResult{Block: true, Reason: "..."} to block.
return nil
})
// Tool execution started (informational only).
api.OnToolExecutionStart(func(e ext.ToolExecutionStartEvent, ctx ext.Context) {
// e.ToolName string
})
// Tool execution ended (informational only).
api.OnToolExecutionEnd(func(e ext.ToolExecutionEndEvent, ctx ext.Context) {
// e.ToolName string
})
// After a tool returns. Can modify the result.
api.OnToolResult(func(e ext.ToolResultEvent, ctx ext.Context) *ext.ToolResultResult {
// e.ToolName string
// e.Input string
// e.Content string
// e.IsError bool
// Return nil to pass through.
// Return &ext.ToolResultResult{Content: &s} to replace content.
// Return &ext.ToolResultResult{IsError: &b} to change error status.
return nil
})
Tool Call Input Streaming Events
These events fire during the LLM's tool argument generation phase, before the tool call is fully parsed and before OnToolCall fires. They enable UIs to show tool activity immediately rather than waiting for the full argument JSON to finish streaming.
// Fires when the LLM begins generating tool call arguments.
// The tool name is known but the full argument JSON is still streaming.
api.OnToolCallInputStart(func(e ext.ToolCallInputStartEvent, ctx ext.Context) {
// e.ToolCallID string — stable ID for correlating tool lifecycle events
// e.ToolName string — name of the tool being called
// e.ToolKind string — "execute", "edit", "read", "search", "agent"
ctx.PrintInfo("Tool starting: " + e.ToolName)
})
// Fires for each streamed fragment of tool call arguments.
// Useful for live-previewing artifact content or showing a progress indicator.
api.OnToolCallInputDelta(func(e ext.ToolCallInputDeltaEvent, ctx ext.Context) {
// e.ToolCallID string
// e.Delta string — JSON fragment of tool arguments
})
// Fires when tool argument streaming is complete, before the tool call
// is parsed and execution begins. Transition UI from "generating args"
// to "executing".
api.OnToolCallInputEnd(func(e ext.ToolCallInputEndEvent, ctx ext.Context) {
// e.ToolCallID string
})
Full tool lifecycle order: OnToolCallInputStart → OnToolCallInputDelta (repeated) → OnToolCallInputEnd → OnToolCall → OnToolExecutionStart → OnToolOutput (optional, repeated) → OnToolExecutionEnd → OnToolResult
Input Events
// User submitted input. Can handle or transform it.
api.OnInput(func(e ext.InputEvent, ctx ext.Context) *ext.InputResult {
// e.Text string
// e.Source string — "interactive", "cli", "script", "queue"
// Return nil to pass through to agent.
// Return &ext.InputResult{Action: "handled"} to consume without sending to agent.
// Return &ext.InputResult{Action: "transform", Text: "new text"} to rewrite.
return nil
})
Streaming Events
api.OnMessageStart(func(e ext.MessageStartEvent, ctx ext.Context) {})
api.OnMessageUpdate(func(e ext.MessageUpdateEvent, ctx ext.Context) {
// e.Chunk string — streaming text chunk
})
api.OnMessageEnd(func(e ext.MessageEndEvent, ctx ext.Context) {
// e.Content string — full message content
})
Model Events
api.OnModelChange(func(e ext.ModelChangeEvent, ctx ext.Context) {
// e.NewModel string
// e.PreviousModel string
// e.Source string — "extension" or "user"
})
// Extended-thinking effort level changed.
api.OnThinkingLevelChange(func(e ext.ThinkingLevelChangeEvent, ctx ext.Context) {
// e.NewLevel, e.PreviousLevel string — off, none, minimal, low, medium, high
// e.Source string — "user" (/thinking or shift+tab) or "model_fallback"
// ("model_fallback" = automatic downgrade because the newly selected
// model does not support the previous level)
})
UI Events
Interactive TUI only; these do not fire in headless, ACP, or script mode.
// Terminal resized. Also fires once at startup with the initial size.
api.OnTerminalResize(func(e ext.TerminalResizeEvent, ctx ext.Context) {
// e.Width, e.Height int
})
// UI entered or left the working state.
api.OnTurnStateChange(func(e ext.TurnStateChangeEvent, ctx ext.Context) {
// e.State, e.Previous string — "working" or "idle"
})
OnTurnStateChange is a superset of OnAgentStart/OnAgentEnd: it also
covers work that never reaches the agent loop (shell commands run with !) and
fires on every path back to idle, including cancellation and error. Use it to
drive a spinner or turn timer; use OnAgentStart/OnAgentEnd when you
specifically care about agent turns and their token usage.
Context Filtering
// Before messages are sent to the LLM. Can filter, reorder, or inject messages.
api.OnCon
---
*Content truncated.*
When not to use it
- →When Kit is not the target environment
Prerequisites
Limitations
- →Requires Go
- →Dependent on Kit API
How it compares
It allows for deep integration into the editor lifecycle, unlike standard plugin systems.
Compared to similar skills
kit-extensions side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| kit-extensions (this skill) | 0 | 2mo | Review | Advanced |
| command-development | 16 | 9mo | Review | Intermediate |
| skill-forge | 11 | 9mo | Review | Intermediate |
| codex-skill | 12 | 5mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
command-development
anthropics
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
skill-forge
WilliamSaysX
Automated skill creation workshop with intelligent source detection, smart path management, and end-to-end workflow automation. This skill should be used when users want to create a new skill or convert external resources (GitHub repositories, online documentation, or local directories) into a skill. Automatically fetches, organizes, and packages skills with proactive cleanup management.
codex-skill
feiskyer
Use when user asks to leverage codex, gpt-5, or gpt-5.1 to implement something (usually implement a plan or feature designed by Claude). Provides non-interactive automation mode for hands-off task execution without approval prompts.
agent-factory
alirezarezvani
Claude Code agent generation system that creates custom agents and sub-agents with enhanced YAML frontmatter, tool access patterns, and MCP integration support following proven production patterns
subagent-driven-development
davila7
Use when executing implementation plans with independent tasks in the current session
peekaboo
openclaw
Capture and automate macOS UI with the Peekaboo CLI.