Add core TUI support for handling sudo password prompts when executing
bash commands that require elevated privileges.
- Detect sudo commands and check if credentials are cached (sudo -n)
- Show modal password prompt with masked input (• characters) when needed
- Pipe password via stdin using sudo -S -p '' (no password in command string)
- Password flows through context callbacks, never stored in session history
- Add PasswordPromptHandler to agent and SDK event system
- Add password prompt overlay to TUI with 🔐 icon and hidden input
- Include tests for sudo command detection and rewriting
The password is never persisted to disk - it only exists in memory
during execution and is piped directly to sudo via stdin.
- Add editor interceptor via OnSessionStart so !{...} expansions
appear in the user message block on screen
- Restrict OnInput handler to non-interactive sources (CLI, script,
queue) to avoid double expansion
- Extract expand() helper and hoist regex to package level
- Update doc comments and examples
- Send sendChatAction("typing") every 4s while agent is processing,
started on AgentStart and stopped on AgentEnd/SessionShutdown
- configPath() now checks project-local .kit/ first, then falls back
to ~/.config/kit/kit-telegram.json for cross-project portability
- Add fsnotify-based file watcher that auto-reloads extensions on .go
file changes in autoloaded dirs with 300ms debounce
- Add /reload-ext built-in command (alias /re) for manual reload
- Add Agent.SetExtraTools() so extension tools update on reload
instead of being baked in at agent creation time
- Run reload async via tea.Cmd to avoid prog.Send() deadlock when
extension handlers call ctx.Print() during SessionStart/Shutdown
- Wire watcher lifecycle into cmd/root.go with graceful shutdown
Add two test files that auto-discover and validate every single-file
extension in examples/extensions/:
- all_extensions_load_test.go: Verifies all 32 extensions load into the
Yaegi interpreter without errors (syntax, imports, Init signature).
- all_extensions_sanity_test.go: Six generalized sanity checks:
- Lifecycle: SessionStart → SessionShutdown round-trip
- CommandSanity: non-empty names/descriptions, no spaces/leading slash,
non-nil Execute, no duplicates
- ToolSanity: non-empty names/descriptions, at least one executor,
valid JSON parameters, no duplicates
- ZeroValueEvents: all 22 event types fired as zero-value structs
- WidgetSanity: non-empty IDs, consistent keys, valid placements
- IdempotentLifecycle: repeated SessionStart/SessionShutdown
Shared extensionFiles() helper auto-discovers extensions so new files
are automatically covered.
Move the extension testing package from internal/extensions/test to
pkg/extensions/test to make it publicly importable by external extension
authors.
Changes:
- Moved test package files to pkg/extensions/test/
- Updated all imports from internal/ to pkg/ path:
- README.md
- examples/extensions/tool-logger_test.go
- examples/extensions/extension_test_template.go
- skills/kit-extensions/SKILL.md
- www/pages/extensions/testing.md
- pkg/extensions/test/README.md
- pkg/extensions/test/harness.go
The test package is now available for external import as:
github.com/mark3labs/kit/pkg/extensions/test
All tests pass with race detector.
Add comprehensive testing utilities for Kit extensions:
- internal/extensions/test/: New test package with:
- harness.go: Test harness for loading extensions into Yaegi
- mock.go: Mock context that records all context interactions
- assert.go: 20+ assertion helpers (AssertBlocked, AssertWidgetSet, etc.)
- harness_test.go: 18 comprehensive test examples
- README.md: Complete documentation with usage examples
- internal/extensions/test_api.go: Helper function for creating test API objects
- examples/extensions/tool-logger_test.go: 14 tests demonstrating real extension testing
- examples/extensions/extension_test_template.go: Copy-and-paste template for extension authors
- .gitignore: Allow internal/extensions/test/ directory
All 93 tests pass including race detector.
Add RegisterTheme, SetTheme, and ListThemes to the extension Context,
allowing extensions to create custom themes at runtime and switch
between them. Uses ThemeColor/ThemeColorConfig concrete structs (no
interfaces) for Yaegi safety.
Include neon-theme.go example extension demonstrating the API.
- Copy Telegram relay extension from ../kit-telegram
- Add README with quickstart, commands, API reference, and architecture
- Update examples/extensions/README.md with Integrations section and details
Adds an extension that starts language servers on demand and surfaces
diagnostics after file edits, following crush's LSP integration pattern.
Hooks into the edit tool lifecycle to diff pre/post diagnostics, display
a persistent widget, and expose lsp_diagnostics/lsp_hover tools plus
/lsp and /lsp-check slash commands.
Implement 4-phase subagent system enabling LLM and extensions to spawn,
manage, and orchestrate child Kit instances for parallel task execution.
- Phase 1: SDK API with SpawnSubagent() for extensions
- Phase 2: spawn_subagent core tool for LLM usage
- Phase 3: Session hierarchy with ParentSessionID tracking
- Phase 4: Provider pooling for concurrent model access
New files:
- internal/extensions/subagent.go: SpawnSubagent implementation
- internal/core/subagent.go: Core tool definition
- internal/models/pool.go: Provider pool for concurrency
- examples/extensions/subagent-test.go: Test extension
- openspec/subagent-support.md: Design specification
Drop the --prompt/-p flag entirely. Non-interactive mode is now
triggered by passing positional arguments:
kit "Explain this"
kit @file.go "Review this" --json
kit @a.go @b.go --quiet
Updated extension examples (kit-kit.go, subagent-widget.go) to pass
the prompt as a positional arg. Updated AGENTS.md and README.md.
- ctx.SuspendTUI(callback): releases terminal for interactive subprocesses
(vim, shell, htop), automatically restores TUI when callback returns.
Uses BubbleTea v2 ReleaseTerminal/RestoreTerminal.
- api.RegisterMessageRenderer(config) + ctx.RenderMessage(name, content):
named render functions for branded/styled extension output. Renderers
receive content and terminal width, return ANSI-styled strings.
- ctx.ReloadExtensions(): hot-reloads all extensions from disk. Emits
SessionShutdown to old extensions, reloads source, emits SessionStart
to new. Event handlers, commands, renderers, shortcuts update immediately.
TUI command list refreshes via WidgetUpdateEvent. Extension tools are
NOT updated (baked into agent at creation, documented limitation).
New example extensions: interactive-shell.go, branded-output.go, dev-reload.go
- RegisterShortcut(ShortcutDef, handler) for global keyboard shortcuts
that fire across all non-modal app states (after ctrl+c, before
component dispatch). Handlers run in goroutines for safe blocking calls.
- ToolContext with IsCancelled/OnProgress for rich tool execution;
ExecuteWithContext on ToolDef takes priority over simple Execute.
- Source field on ToolCallEvent (currently "llm", forward-compatible
with future user-initiated tool calls).
- Fix missing //go:build ignore on context-inject.go.
- Update plan-mode.go to register ctrl+alt+p shortcut.
Add three new extension events that allow extensions to gate destructive
session operations and compaction:
- OnBeforeFork: fires before branching in the tree selector; handler can
cancel with reason (e.g. dirty-repo guard)
- OnBeforeSessionSwitch: fires before /new resets the session branch;
handler can cancel with reason
- OnBeforeCompact: fires before context compaction (auto or manual);
handler receives token stats and IsAutomatic flag, can cancel
Includes SDK hook registry (beforeCompact), extension bridge, UI
callbacks threaded through AppModelOptions, and two example extensions:
- confirm-destructive.go: git dirty check + fork confirmation
- compact-notify.go: compaction notification + auto-compact gating
Extensions can now register an OnContextPrepare handler that fires after
the context window is built from the session tree and before messages are
sent to the LLM. Handlers receive ContextMessage entries with positional
indices and can filter, reorder, or inject messages. Original messages
referenced by index preserve tool calls, reasoning, and other complex
parts. New context-inject example extension demonstrates injecting a
local .kit/context.md file as an ephemeral system message every turn.
Extensions can now provide a Complete function on CommandDef that supplies
argument suggestions. When the user types a command name followed by a space,
the input popup switches to argument-completion mode, calling Complete with
the partial text and displaying matching suggestions.
Add a --json flag that outputs structured JSON (response, model, usage,
messages with typed parts) when used with --prompt. Update kit-kit and
subagent-widget extensions to use --json for cleaner subprocess output
parsing instead of raw text heuristics.
- Add ctx.SetUIVisibility() to toggle built-in TUI chrome (startup
message, status bar, separator, input hint) from extensions
- Add ctx.GetContextStats() returning accurate API-reported token counts
instead of text-based heuristic; fix event ordering so extension
handlers see up-to-date conversation state
- Add compact tool body renderers for compact mode: Read/Edit/Write/Ls
show one-line summaries, Bash shows first 3 lines instead of full
20-line syntax-highlighted output
- Add minimal.go example extension using UIVisibility + GetContextStats
Port of Pi Pi (meta-agent with parallel expert subprocesses) to Kit's
extension system. Includes expert grid widget, query_experts tool,
custom footer, tool renderer, and orchestrator system prompt injection.
Also updates AGENTS.md with Yaegi gotchas, BubbleTea patterns, testing
recipes, and extension architecture notes.
Fixes golangci-lint issues: modernize min/max in overlay.go, replace
deprecated GetExtRunner() with new GetExtensionContext() SDK method,
remove broken --model flag from expert subprocess.
The interceptor now stays installed for the entire vim session, handling
both normal and insert modes. Esc switches from insert back to normal,
and /vim toggles the entire interceptor on/off.
Extensions can now intercept key events and wrap the editor's rendered
output via ctx.SetEditor/ctx.ResetEditor, enabling vim-like modal
editing, custom key bindings, and visual decorators.
Key fixes during development:
- Yaegi requires closure wrappers for struct function fields (bare
function references return zero values across the interpreter boundary)
- SetEditor/ResetEditor use async NotifyWidgetUpdate to avoid deadlocking
BubbleTea's event loop when called from HandleKey callbacks
- distributeHeight now uses renderInput() to account for interceptor
Render wrapper in height calculations
Add ctx.ShowOverlay() API that displays modal dialogs with optional
scrollable content, markdown rendering, action buttons, and configurable
positioning. Follows the same channel-based blocking pattern as prompts,
with full Yaegi compatibility via concrete structs.
Extensions can now override how tool calls are displayed in the TUI via
API.RegisterToolRenderer(). Supports custom display name, border color,
background color, header parameter formatting, body rendering, and
optional markdown processing of custom body output.
Extensions can now place persistent header (above stream) and footer
(below status bar) regions via ctx.SetHeader/SetFooter. Single-instance
per slot, reuses WidgetContent/WidgetStyle types and WidgetUpdateEvent
for notifications. Includes thread-safe Runner storage, SDK methods,
UI rendering with height distribution, and example extension.
Extensions can now show modal prompts to the user via ctx.PromptSelect,
ctx.PromptConfirm, and ctx.PromptInput. Prompts render inline below the
separator (replacing the input area) and use channel-based sync so the
extension blocks until the user responds. Extension slash commands run in
dedicated goroutines to avoid stalling BubbleTea's Cmd scheduler.
Add a declarative widget system that lets extensions place persistent
content above or below the input area. Widgets survive across agent
turns and are updated via ctx.SetWidget/ctx.RemoveWidget from any
event handler.
All types are concrete structs (Yaegi-safe, no interfaces cross the
interpreter boundary). Widget state lives on the Runner with mutex
protection, and WidgetUpdateEvent triggers BubbleTea re-renders.