mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 03:30:26 +00:00
472 lines
14 KiB
HTML
472 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Capabilities | Kit</title>
|
|
<meta name="description" content="All extension capabilities — lifecycle events, tools, commands, widgets, and more.">
|
|
<link rel="canonical" href="/extensions/capabilities">
|
|
<link rel="stylesheet" href="/assets/index-Di_r5hA0.css">
|
|
<script type="module" src="/assets/index-BbQ_p9l4.js"></script>
|
|
<script type="application/ld+json">{"@context":"https://schema.org","@type":"TechArticle","headline":"Capabilities","description":"All extension capabilities — lifecycle events, tools, commands, widgets, and more.","url":"https://go-kit.dev/extensions/capabilities","isPartOf":{"@type":"WebSite","name":"Kit","url":"https://go-kit.dev"}}</script>
|
|
</head>
|
|
<body>
|
|
<div id="tome-root"></div>
|
|
<div data-pagefind-body style="display:none"><h1>Capabilities</h1>
|
|
# Extension Capabilities
|
|
|
|
## Lifecycle events
|
|
|
|
Extensions can hook into 26 lifecycle events:
|
|
|
|
| Event | Description |
|
|
|-------|-------------|
|
|
| `OnSessionStart` | Session initialized |
|
|
| `OnSessionShutdown` | Session ending |
|
|
| `OnBeforeAgentStart` | Before the agent loop begins |
|
|
| `OnAgentStart` | Agent loop started |
|
|
| `OnAgentEnd` | Agent loop completed |
|
|
| `OnToolCall` | Tool call requested by the model |
|
|
| `OnToolCallInputStart` | LLM began generating tool call arguments (tool name known, args streaming) |
|
|
| `OnToolCallInputDelta` | Streamed JSON fragment of tool call arguments |
|
|
| `OnToolCallInputEnd` | Tool argument streaming complete, before execution begins |
|
|
| `OnToolExecutionStart` | Tool execution beginning |
|
|
| `OnToolOutput` | Streaming tool output chunk (for long-running tools) |
|
|
| `OnToolExecutionEnd` | Tool execution completed |
|
|
| `OnToolResult` | Tool result returned |
|
|
| `OnInput` | User input received |
|
|
| `OnMessageStart` | Assistant message started |
|
|
| `OnMessageUpdate` | Streaming text chunk received |
|
|
| `OnMessageEnd` | Assistant message completed |
|
|
| `OnModelChange` | Model switched |
|
|
| `OnContextPrepare` | Context being assembled for the model |
|
|
| `OnBeforeFork` | Before forking a conversation branch |
|
|
| `OnBeforeSessionSwitch` | Before switching sessions |
|
|
| `OnBeforeCompact` | Before conversation compaction |
|
|
| `OnCustomEvent` | Custom inter-extension event received |
|
|
| `OnSubagentStart` | Subagent spawned by the main agent |
|
|
| `OnSubagentChunk` | Real-time output from subagent (text, tool calls, results) |
|
|
| `OnSubagentEnd` | Subagent completed with final response/error |
|
|
|
|
### Example
|
|
|
|
```go
|
|
api.OnToolCall(func(event ext.ToolCallEvent, ctx ext.Context) {
|
|
ctx.PrintInfo("Calling tool: " + event.Name)
|
|
})
|
|
|
|
api.OnAgentEnd(func(_ ext.AgentEndEvent, ctx ext.Context) {
|
|
ctx.PrintInfo("Agent finished")
|
|
})
|
|
```
|
|
|
|
## Tools
|
|
|
|
Register custom tools that the LLM can invoke:
|
|
|
|
```go
|
|
api.RegisterTool(ext.ToolDef{
|
|
Name: "weather",
|
|
Description: "Get current weather for a location",
|
|
Parameters: map[string]ext.ParameterDef{
|
|
"city": {Type: "string", Description: "City name", Required: true},
|
|
},
|
|
Handler: func(ctx ext.Context, params map[string]any) (string, error) {
|
|
city := params["city"].(string)
|
|
return "Sunny, 72°F in " + city, nil
|
|
},
|
|
})
|
|
```
|
|
|
|
## Commands
|
|
|
|
Register slash commands that users can invoke directly:
|
|
|
|
```go
|
|
api.RegisterCommand(ext.CommandDef{
|
|
Name: "stats",
|
|
Description: "Show context statistics",
|
|
Handler: func(ctx ext.Context, args string) {
|
|
stats := ctx.GetContextStats()
|
|
ctx.PrintInfo(fmt.Sprintf("Tokens: %d", stats.TotalTokens))
|
|
},
|
|
})
|
|
```
|
|
|
|
## Widgets
|
|
|
|
Add persistent status displays above or below the input area:
|
|
|
|
```go
|
|
ctx.SetWidget(ext.WidgetConfig{
|
|
ID: "token-count",
|
|
Position: "bottom",
|
|
Content: ext.WidgetContent{Text: "Tokens: 1,234"},
|
|
})
|
|
|
|
// Update later
|
|
ctx.SetWidget(ext.WidgetConfig{
|
|
ID: "token-count",
|
|
Position: "bottom",
|
|
Content: ext.WidgetContent{Text: "Tokens: 2,456"},
|
|
})
|
|
|
|
// Remove
|
|
ctx.RemoveWidget("token-count")
|
|
```
|
|
|
|
## Headers and footers
|
|
|
|
Persistent content above and below the conversation:
|
|
|
|
```go
|
|
ctx.SetHeader(ext.HeaderFooterConfig{
|
|
Content: ext.WidgetContent{Text: "Project: my-app | Branch: main"},
|
|
})
|
|
|
|
ctx.SetFooter(ext.HeaderFooterConfig{
|
|
Content: ext.WidgetContent{Text: "Plan Mode (read-only)"},
|
|
})
|
|
```
|
|
|
|
## Status bar
|
|
|
|
Custom status bar entries:
|
|
|
|
```go
|
|
ctx.SetStatus("mode", "Planning")
|
|
ctx.RemoveStatus("mode")
|
|
```
|
|
|
|
## Shortcuts
|
|
|
|
Global keyboard shortcuts:
|
|
|
|
```go
|
|
api.RegisterShortcut(ext.ShortcutDef{
|
|
Key: "ctrl+t",
|
|
Description: "Toggle plan mode",
|
|
}, func(ctx ext.Context) {
|
|
// handle shortcut
|
|
})
|
|
```
|
|
|
|
## Overlays
|
|
|
|
Modal dialogs with markdown content:
|
|
|
|
```go
|
|
ctx.ShowOverlay(ext.OverlayConfig{
|
|
Title: "Help",
|
|
Content: "# Keyboard Shortcuts\n\n- **ctrl+t** — Toggle plan mode\n- **ctrl+s** — Save session",
|
|
})
|
|
```
|
|
|
|
## Tool renderers
|
|
|
|
Customize how specific tool calls are displayed in the TUI:
|
|
|
|
```go
|
|
api.RegisterToolRenderer(ext.ToolRenderConfig{
|
|
ToolName: "bash",
|
|
Render: func(name, args, result string, isError bool) string {
|
|
return "$ " + args + "\n" + result
|
|
},
|
|
})
|
|
```
|
|
|
|
## Message renderers
|
|
|
|
Custom rendering for assistant messages:
|
|
|
|
```go
|
|
api.RegisterMessageRenderer(ext.MessageRendererConfig{
|
|
Name: "custom",
|
|
Render: func(content string) string {
|
|
return ">> " + content
|
|
},
|
|
})
|
|
```
|
|
|
|
## Editor interceptors
|
|
|
|
Handle key events and wrap the editor's rendering:
|
|
|
|
```go
|
|
ctx.SetEditor(ext.EditorConfig{
|
|
HandleKey: func(key, text string) ext.EditorKeyAction {
|
|
if key == "escape" {
|
|
return ext.EditorKeyAction{Handled: true}
|
|
}
|
|
return ext.EditorKeyAction{Handled: false}
|
|
},
|
|
})
|
|
```
|
|
|
|
## Interactive prompts
|
|
|
|
Select, confirm, input, and multi-select dialogs:
|
|
|
|
```go
|
|
// Single select
|
|
response := ctx.PromptSelect(ext.PromptSelectConfig{
|
|
Title: "Choose a model",
|
|
Options: []string{"claude-sonnet", "gpt-4o", "llama3"},
|
|
})
|
|
|
|
// Confirm
|
|
confirmed := ctx.PromptConfirm(ext.PromptConfirmConfig{
|
|
Title: "Delete this file?",
|
|
})
|
|
|
|
// Text input
|
|
name := ctx.PromptInput(ext.PromptInputConfig{
|
|
Title: "Enter project name",
|
|
Placeholder: "my-project",
|
|
})
|
|
```
|
|
|
|
## Options
|
|
|
|
Register configurable extension options:
|
|
|
|
```go
|
|
api.RegisterOption(ext.OptionDef{
|
|
Name: "auto-commit",
|
|
Description: "Automatically commit on shutdown",
|
|
DefaultValue: "false",
|
|
})
|
|
```
|
|
|
|
## Subagents
|
|
|
|
Spawn in-process child Kit instances:
|
|
|
|
```go
|
|
result := ctx.SpawnSubagent(ext.SubagentConfig{
|
|
Task: "Analyze the test files and summarize coverage",
|
|
Model: "anthropic/claude-haiku-latest",
|
|
SystemPrompt: "You are a test analysis expert.",
|
|
})
|
|
```
|
|
|
|
### Monitoring subagents spawned by the main agent
|
|
|
|
When the LLM uses the built-in `subagent` tool, extensions can monitor the subagent's activity in real-time using three lifecycle events:
|
|
|
|
```go
|
|
// Subagent started
|
|
api.OnSubagentStart(func(e ext.SubagentStartEvent, ctx ext.Context) {
|
|
// e.ToolCallID — unique ID for this subagent invocation
|
|
// e.Task — the task/prompt sent to the subagent
|
|
ctx.PrintInfo(fmt.Sprintf("Subagent started: %s", e.Task))
|
|
})
|
|
|
|
// Real-time streaming output from subagent
|
|
api.OnSubagentChunk(func(e ext.SubagentChunkEvent, ctx ext.Context) {
|
|
// e.ToolCallID — matches the start event
|
|
// e.Task — task description
|
|
// e.ChunkType — "text", "tool_call", "tool_execution_start", "tool_result"
|
|
// e.Content — text content (for text chunks)
|
|
// e.ToolName — tool name (for tool-related chunks)
|
|
// e.IsError — true if tool result is an error
|
|
switch e.ChunkType {
|
|
case "text":
|
|
// Streaming text output
|
|
case "tool_call":
|
|
// Subagent is calling a tool
|
|
case "tool_execution_start":
|
|
// Tool execution started
|
|
case "tool_result":
|
|
// Tool execution completed (check e.IsError)
|
|
}
|
|
})
|
|
|
|
// Subagent completed
|
|
api.OnSubagentEnd(func(e ext.SubagentEndEvent, ctx ext.Context) {
|
|
// e.ToolCallID — matches start event
|
|
// e.Task — task description
|
|
// e.Response — final response from subagent
|
|
// e.ErrorMsg — error message if subagent failed
|
|
if e.ErrorMsg != "" {
|
|
ctx.PrintError(fmt.Sprintf("Subagent failed: %s", e.ErrorMsg))
|
|
} else {
|
|
ctx.PrintInfo(fmt.Sprintf("Subagent completed: %s", e.Response))
|
|
}
|
|
})
|
|
```
|
|
|
|
This enables building widgets that display real-time subagent activity.
|
|
|
|
## LLM completion
|
|
|
|
Make direct model calls without going through the agent loop:
|
|
|
|
```go
|
|
response := ctx.Complete(ext.CompleteRequest{
|
|
Prompt: "Summarize this in one sentence: " + content,
|
|
})
|
|
```
|
|
|
|
## Themes
|
|
|
|
Register and switch color themes at runtime:
|
|
|
|
```go
|
|
// Register a custom theme
|
|
ctx.RegisterTheme("neon", ext.ThemeColorConfig{
|
|
Primary: ext.ThemeColor{Light: "#CC00FF", Dark: "#FF00FF"},
|
|
Secondary: ext.ThemeColor{Light: "#0088CC", Dark: "#00FFFF"},
|
|
Success: ext.ThemeColor{Light: "#00CC44", Dark: "#00FF66"},
|
|
Warning: ext.ThemeColor{Light: "#CCAA00", Dark: "#FFFF00"},
|
|
Error: ext.ThemeColor{Light: "#CC0033", Dark: "#FF0055"},
|
|
Info: ext.ThemeColor{Light: "#0088CC", Dark: "#00CCFF"},
|
|
Text: ext.ThemeColor{Light: "#111111", Dark: "#F0F0F0"},
|
|
Background: ext.ThemeColor{Light: "#F0F0F0", Dark: "#0A0A14"},
|
|
})
|
|
|
|
// Switch to it
|
|
ctx.SetTheme("neon")
|
|
|
|
// List all available themes
|
|
names := ctx.ListThemes()
|
|
```
|
|
|
|
See [Themes](/themes) for the full theme file format, built-in themes, and color reference.
|
|
|
|
## Custom events
|
|
|
|
Inter-extension communication:
|
|
|
|
```go
|
|
// Emit
|
|
ctx.EmitCustomEvent("my-extension:data-ready", payload)
|
|
|
|
// Listen
|
|
api.OnCustomEvent("my-extension:data-ready", func(data any, ctx ext.Context) {
|
|
// handle event
|
|
})
|
|
```
|
|
|
|
## Bridged SDK APIs
|
|
|
|
Extensions can access powerful internal SDK capabilities that enable advanced features like conversation tree navigation, dynamic skill loading, template parsing, and model resolution.
|
|
|
|
### Tree Navigation
|
|
|
|
Navigate the conversation tree, summarize branches, and implement "fresh context" loops:
|
|
|
|
```go
|
|
// Get a specific node by ID with full metadata and children
|
|
node := ctx.GetTreeNode("entry-id")
|
|
// node.ID, node.ParentID, node.Type ("message"/"branch_summary"/etc)
|
|
// node.Role, node.Content, node.Model, node.Children ([]string)
|
|
|
|
// Get the current branch from root to leaf
|
|
branch := ctx.GetCurrentBranch() // []ext.TreeNode
|
|
|
|
// Get child entry IDs of a node
|
|
children := ctx.GetChildren("entry-id") // []string
|
|
|
|
// Navigate/fork to a different entry in the tree
|
|
result := ctx.NavigateTo("entry-id") // ext.TreeNavigationResult{Success, Error}
|
|
|
|
// Summarize a range of the branch using LLM
|
|
summary := ctx.SummarizeBranch("from-id", "to-id") // string
|
|
|
|
// Collapse a branch range into a summary entry (fresh context primitive)
|
|
result := ctx.CollapseBranch("from-id", "to-id", "summary text")
|
|
```
|
|
|
|
### Skill Loading
|
|
|
|
Load and inject skills dynamically at runtime:
|
|
|
|
```go
|
|
// Discover skills from standard locations
|
|
result := ctx.DiscoverSkills() // ext.SkillLoadResult{Skills, Error}
|
|
// Standard locations: ~/.config/kit/skills/, .kit/skills/, .agents/skills/
|
|
|
|
// Load a specific skill file
|
|
skill, err := ctx.LoadSkill("/path/to/skill.md") // (*ext.Skill, error string)
|
|
// skill.Name, skill.Description, skill.Content, skill.Tags, skill.When
|
|
|
|
// Load all skills from a directory
|
|
result := ctx.LoadSkillsFromDir("/path/to/skills") // ext.SkillLoadResult
|
|
|
|
// Inject a skill as context (pre-loads for next turn)
|
|
err := ctx.InjectSkillAsContext("skill-name") // error string
|
|
|
|
// Inject a skill file directly
|
|
err := ctx.InjectRawSkillAsContext("/path/to/skill.md") // error string
|
|
|
|
// Get all discovered skills
|
|
skills := ctx.GetAvailableSkills() // []ext.Skill
|
|
```
|
|
|
|
### Template Parsing
|
|
|
|
Parse and render templates with variable substitution:
|
|
|
|
```go
|
|
// Parse a template to extract {{variables}}
|
|
tpl := ctx.ParseTemplate("name", "Hello {{name}}, welcome to {{place}}!")
|
|
// tpl.Name, tpl.Content, tpl.Variables ([]string)
|
|
|
|
// Render a template with variable values
|
|
vars := map[string]string{"name": "Alice", "place": "Kit"}
|
|
rendered := ctx.RenderTemplate(tpl, vars) // "Hello Alice, welcome to Kit!"
|
|
|
|
// Parse command-line style arguments
|
|
pattern := ext.ArgumentPattern{
|
|
Positional: []string{"command", "target"}, // $1, $2
|
|
Rest: "args", // $@
|
|
Flags: map[string]string{"--loop": "loop", "-f": "force"},
|
|
}
|
|
result := ctx.ParseArguments("deploy staging --loop 5", pattern)
|
|
// result.Vars["command"] = "deploy"
|
|
// result.Vars["target"] = "staging"
|
|
// result.Flags["--loop"] = "5"
|
|
|
|
// Simple positional argument parsing ($1, $2, $@)
|
|
args := ctx.SimpleParseArguments("deploy staging --force", 2)
|
|
// args[0] = "deploy staging --force" (full input)
|
|
// args[1] = "deploy" ($1)
|
|
// args[2] = "staging" ($2)
|
|
// args[3] = "--force" ($@)
|
|
|
|
// Evaluate model conditionals with wildcards
|
|
matches := ctx.EvaluateModelConditional("claude-*") // bool
|
|
// Patterns: * matches any, ? matches single char, comma = OR
|
|
|
|
// Render content with <if-model> conditionals
|
|
content := `<if-model is="claude-*">Hi Claude<else>Hi there</if-model>`
|
|
rendered := ctx.RenderWithModelConditionals(content) // based on current model
|
|
```
|
|
|
|
### Model Resolution
|
|
|
|
Resolve model fallback chains and query capabilities:
|
|
|
|
```go
|
|
// Resolve a chain of model preferences (tries each until available)
|
|
result := ctx.ResolveModelChain([]string{
|
|
"anthropic/claude-opus-4",
|
|
"anthropic/claude-sonnet-4",
|
|
"openai/gpt-4o",
|
|
})
|
|
// result.Model (selected), result.Capabilities, result.Attempted, result.Error
|
|
|
|
// Get capabilities for a specific model
|
|
caps, err := ctx.GetModelCapabilities("anthropic/claude-sonnet-4")
|
|
// caps.Provider, caps.ModelID, caps.ContextLimit, caps.Reasoning, caps.Streaming
|
|
|
|
// Check if a model is available (provider exists)
|
|
available := ctx.CheckModelAvailable("anthropic/claude-sonnet-4") // bool
|
|
|
|
// Get current provider/model ID
|
|
provider := ctx.GetCurrentProvider() // "anthropic"
|
|
modelID := ctx.GetCurrentModelID() // "claude-sonnet-4"
|
|
```</div>
|
|
</body>
|
|
</html> |