Commit Graph

261 Commits

Author SHA1 Message Date
Ed Zynda 626f1105c9 move SDK to pkg/kit, extract shared logic from cmd, relocate main to cmd/kit
Restructure the codebase so the CLI app consumes the SDK rather than
the SDK wrapping CLI internals. This eliminates the circular dependency
(sdk -> cmd -> sdk) and establishes pkg/kit as the canonical API.

Key changes:
- Create pkg/kit/ with InitConfig, SetupAgent, BuildProviderConfig
  extracted from cmd/root.go and cmd/setup.go as parameterized functions
- Move sdk/kit.go -> pkg/kit/kit.go (remove cmd import, use local calls)
- Move sdk/types.go -> pkg/kit/types.go
- Move main.go -> cmd/kit/main.go (standard Go project layout)
- cmd/root.go and cmd/setup.go now delegate to pkg/kit, injecting
  CLI-specific state (quietFlag) via the Quiet field on AgentSetupOptions
- Add setSDKDefaults() for cobra-free SDK usage (viper defaults)
- Fix .gitignore: kit -> /kit (was blocking cmd/kit/ and pkg/kit/)
- Update .goreleaser.yaml, Taskfile.yml, AGENTS.md, contribute/build.sh,
  README.md for new cmd/kit entrypoint and pkg/kit import paths
- Add plans/ with 10 detailed SDK revamp plans and Taskfile.yml
- Delete sdk/ directory entirely
2026-02-27 10:42:27 +03:00
Ed Zynda fb3608326f add unrestricted stdlib symbols to Yaegi interpreter
os/exec lives in yaegi's stdlib/unrestricted package, not the default
stdlib.Symbols. Without it, extensions importing os/exec fail with
'unable to find source'. This is needed for the /run command example
and any extension that spawns subprocesses.
2026-02-27 00:43:31 +03:00
Ed Zynda 6ac8d3983a add SendMessage to extension Context and fix all golangci-lint issues
SendMessage lets extensions inject messages into the conversation and
trigger new agent turns, enabling async patterns like background
subagent execution. It delegates to app.Run() which handles queueing.

CommandDef.Execute now receives Context so commands can access
SendMessage, Print*, and session metadata. The UI layer wraps the
call via runner.GetContext() at the boundary.

Also fixes all 20+ golangci-lint issues across the codebase:
errcheck, modernize (min/max/slices.Contains/SplitSeq/range-over-int),
staticcheck (error string casing), and unused code removal.
2026-02-27 00:41:48 +03:00
Ed Zynda f12950c0b6 btca 2026-02-27 00:26:26 +03:00
Ed Zynda 7a6c0aba61 wire extension slash commands and tools into TUI dispatch pipeline
Extension-registered slash commands are now fully end-to-end:
- Commands appear in autocomplete popup (category: Extensions)
- Commands appear in /help under Extensions section
- Commands dispatch via handleExtensionCommand with argument support
- Command names normalized to /prefix at the cmd layer boundary

Extension-registered tools now show in /tools:
- Agent.GetTools() includes extraTools from extensions
- Previously only core + MCP tools were returned

Also adds RegisterTool and RegisterCommand examples to the
kit extensions init template, and adds .kit/ to .gitignore.
2026-02-27 00:26:06 +03:00
Ed Zynda f42d487214 add Yaegi-based in-process extension system with event handlers, tool/command registration, and styled output
Implement a Pi-style extension system where plain .go files are loaded at
runtime via Yaegi. Extensions register typed event handlers against 13
lifecycle events (tool_call, tool_result, input, before_agent_start, etc.)
using concrete-type-only API methods to avoid Yaegi interface panics.

Key capabilities:
- Tool interception: block calls, modify results (wrapper pattern)
- Input handling: transform or fully handle user input (skip agent)
- System prompt injection via BeforeAgentStartResult
- Custom tool and slash command registration
- Styled output: ctx.Print, PrintInfo, PrintError, PrintBlock
- Legacy hooks.yml compatibility via adapter
- Auto-discovery from ~/.config/kit/extensions/ and .kit/extensions/
- CLI: kit extensions list|validate|init, --no-extensions, -e flags
- 58 unit tests covering runner, loader (Yaegi), wrapper, events
2026-02-27 00:08:48 +03:00
Ed Zynda dd018b65ec add tree-structured session handling and /tree command
Implement pi-style JSONL append-only session management with tree branching:

- TreeManager with id/parent_id tree structure, leaf pointer, and context
  building that walks leaf-to-root for LLM messages
- Auto-discovery by cwd in ~/.kit/sessions/ with session listing
- /tree TUI overlay with ASCII art rendering, filter modes, and navigation
- /fork, /new, /name, /session slash commands for tree operations
- --continue, --resume, --no-session CLI flags
- Default auto-creates a tree session per working directory
2026-02-26 18:47:10 +03:00
Ed Zynda 268f3de34e add tool-specific body rendering with syntax highlighting and truncation
Implement iteratr-style rich tool blocks: side-by-side diffs for Edit
(via go-udiff), syntax-highlighted code blocks for Read (via chroma),
green-tinted write blocks with line numbers and footer, and background-
styled bash output with stderr detection.

All renderers enforce centralized max-line limits (Edit: 20, Read: 20,
Write: 10, Bash: 10) with muted truncation hints.
2026-02-26 18:23:50 +03:00
Ed Zynda 6fdc6f7e5e update KIT banner with KITT red gradient and simplify description 2026-02-26 18:09:26 +03:00
Ed Zynda a3bad94821 unify tool blocks into single header+result rendering
Core tools previously rendered two separate blocks: an 'Executing tool'
header block on ToolCallStartedEvent, then a separate result block on
ToolResultEvent. This merges them into a single unified block that
renders only when the result arrives.

The unified block shows a status icon (checkmark/cross), a friendly
tool display name, inline parameters, and the output body. Border
color indicates success (green) or error (red).
2026-02-26 17:59:45 +03:00
Ed Zynda 3f2a399e47 replace builtin MCP tools with native core tools and custom content blocks
Remove the entire internal/builtin package (bash, fetch, todo, http, fs
servers) and all inprocess/builtin transport support from config and
connection pool.

Add internal/core package with 7 direct fantasy.AgentTool implementations
matching pi's coding agent: bash, read, write, edit, grep, find, ls.
These execute in-process with zero MCP/JSON serialization overhead.

Add internal/message package with crush-inspired custom content blocks:
ContentPart interface with TextContent, ReasoningContent, ToolCall,
ToolResult, and Finish types. Messages carry heterogeneous Parts slices
with type-tagged JSON serialization for persistence and a ToFantasyMessages
bridge for LLM provider integration.

Core tools are always registered on the agent. External MCP servers remain
supported for additional tools, but MCP loading failures are now non-fatal
since core tools guarantee a working baseline.
2026-02-26 17:41:02 +03:00
Ed Zynda 30f368de24 add KITT red ASCII banner to CLI usage output 2026-02-26 17:15:55 +03:00
Ed Zynda 46e1a1d66f docs: add logo and update README tagline 2026-02-26 17:01:58 +03:00
Ed Zynda 7fc94018a9 rename: fork mcphost to kit (github.com/mark3labs/kit)
Rename the entire project from mcphost to kit, including:
- Go module path and all import paths
- SDK type MCPHost -> Kit, file renames mcphost.go -> kit.go
- CLI command name, usage strings, UI labels (KIT in literature)
- Config paths (.mcphost -> .kit), env prefix (MCPHOST_ -> KIT_)
- Data/credential/hooks directory paths
- Remove legacy .mcp config fallbacks
- Session metadata field (mcphost_version -> kit_version)
- MCP client identity name
- Build output, goreleaser binary name
- All documentation, examples, scripts, and test files
2026-02-26 16:59:59 +03:00
Ed Zynda 7483bef1fa refactor: deduplicate setup pipeline and unify non-interactive rendering
Extract shared functions into cmd/setup.go (BuildProviderConfig, SetupAgent,
BuildAppOptions, CollectAgentMetadata, DisplayDebugConfig, SetupCLIForNonInteractive)
eliminating triplicated config/agent/app assembly from root.go, script.go, and
the SDK.

Move the event handler from cmd/script.go into internal/ui/event_handler.go as
CLIEventHandler, shared by both script and --prompt modes. Fix streaming in
non-interactive modes: chunks are now printed to stdout immediately as they
arrive (fmt.Print) instead of being silently buffered until step completion.

The --prompt path switches from RunOnce (no intermediate display) to
RunOnceWithDisplay with CLIEventHandler, gaining streaming, tool call display,
and spinner support that was previously exclusive to script mode.
2026-02-26 16:40:46 +03:00
Ed Zynda c5b75674a3 fix: token usage tracking with fantasy and sticky display across all visual modes
- Fix context percentage: use FinalResponse.Usage (last API call) instead of
  TotalUsage (sum of all tool-calling steps) which overstated context fill level
- Fix token count: display current context window tokens, not cumulative session
  total, so the number and percentage tell a consistent story
- Fix script mode double-counting: app.updateUsage already updates the shared
  tracker before sending StepCompleteEvent, so remove redundant
  UpdateUsageFromResponse call
- Add sticky usage display in TUI: render in View() layout between stream and
  separator instead of tea.Println so it updates in place
- Add usage display for non-interactive --prompt mode (non-quiet)
- Add SetContextTokens to UsageUpdater interface for separating billing tokens
  (TotalUsage) from context utilization (FinalResponse.Usage)
2026-02-26 16:10:43 +03:00
Ed Zynda 41f1198cb6 fix: extract human-readable text from MCP tool results instead of showing raw JSON
Tool results were displayed as nested JSON (Fantasy wrapper + MCP content
structure). Now extractToolResultText unwraps both layers at the source,
so all UI paths receive clean text. Removes the redundant extraction in
script mode since the agent layer handles it.
2026-02-26 15:41:34 +03:00
Ed Zynda 7244485ce2 fix: script mode streaming display and example script formatting
- Accumulate stream chunks in a buffer and flush through
  DisplayAssistantMessageWithModel at boundaries (tool calls, step
  complete), mirroring the TUI's StreamComponent accumulate-and-flush
  strategy. Text accompanying tool calls now renders identically to
  solo assistant responses.

- Fix example-script.sh: add missing --- frontmatter delimiters and
  convert legacy command/args format to new type+command list format
  so Viper YAML parsing works correctly.

- Fix env-substitution-script.sh: add missing execute permission.
2026-02-26 15:30:39 +03:00
Ed Zynda 89b3adcc64 fix: unify script mode with app layer and fix broken display
Script mode had a duplicated agentic loop (runAgenticLoop/runAgenticStep)
that was copied from root.go during the Bubble Tea refactor but left with
broken streaming display and missing hooks integration. The streaming
callback silently accumulated chunks without rendering, and the final
response was skipped because it assumed streaming had already shown it.

- Refactor app.executeStep to accept a generic eventFn callback instead
  of a *tea.Program, decoupling the agent step from Bubble Tea
- Add app.RunOnceWithDisplay for non-TUI callers that need intermediate
  display events (spinner, tool calls, streaming chunks)
- Replace ~300 lines of duplicated code in script.go with a lightweight
  scriptEventHandler that routes app events to CLI display methods
- Fix agent.GenerateWithLoopAndStreaming to use the streaming path when
  any callbacks are provided (fantasy only exposes callbacks on Stream)
- Fix CLI displayContainer to match TUI output (remove extra padding)
- Remove premature usage display during CLI setup
2026-02-26 14:35:36 +03:00
Ed Zynda 9b430f3883 fix: ESC cancel now actually cancels agent response context
Streaming callbacks were not checking ctx.Err(), so the fantasy library
kept processing after the user cancelled. Additionally, context.Canceled
was surfaced as a StepErrorEvent, printing an error instead of silently
ending the turn.

Add ctx.Err() checks to all streaming callbacks so the fantasy stream
loop breaks immediately on cancel. Introduce StepCancelledEvent so the
TUI flushes partial content and returns to input without an error message.
2026-02-26 13:32:38 +03:00
Ed Zynda 8c140b89c2 fix(ui): uniform background fill on user messages with full-height border
Use three-phase rendering for blocks with background color:
1) content with bg + horizontal padding, 2) Place() for vertical
padding with WithWhitespaceStyle, 3) border applied last so it
extends the full block height. Pass background color through to
glamour markdown renderer so inline text inherits the highlight.
Switch Highlight to Catppuccin Mantle for a subtler recessed look.
2026-02-26 13:20:41 +03:00
Ed Zynda d818eddecb fix(ui): embed vertical padding as content to fix background banding
Lipgloss PaddingTop/PaddingBottom adds raw newlines that don't receive
background color styling. Prepend/append newlines to the content string
instead so they are part of the styled content and get the background.
2026-02-26 12:57:22 +03:00
Ed Zynda 711aefba5e fix(ui): remove opposite border to fix background banding on user messages 2026-02-26 12:54:56 +03:00
Ed Zynda a2c8201ec4 fix(ui): add subtle background fill to user messages
Use theme.Highlight (Catppuccin Surface 1) as a background color on
user message blocks for visual distinction from agent messages.
2026-02-26 12:53:47 +03:00
Ed Zynda ee5f41764b fix(ui): remove borders from agent messages, move user border to left with Primary color
Agent messages now render borderless. User messages use a left border
with theme.Primary (Mauve) instead of a right border with Secondary.
2026-02-26 12:51:44 +03:00
Ed Zynda faea00795e fix(ui): remove width-4 from stream component so agent messages span full width 2026-02-26 12:48:14 +03:00
Ed Zynda 948919d60b fix(ui): make all message blocks full width with no horizontal staggering
All content blocks now span the full container width by default.
Removed PlaceHorizontal floating so user and agent messages no longer
stagger left/right. The align option now only determines which border
gets the accent color (left for agent, right for user).
2026-02-26 12:44:00 +03:00
Ed Zynda 2f8be19c83 fix(ui): align left padding across messages and input components
Remove right padding from content blocks and align input components
with message content by matching the border(1) + paddingLeft(2)
pattern used by renderContentBlock.
2026-02-26 12:38:32 +03:00
Ed Zynda ac6d7f8f53 fix(ui): extend queued message blocks to full width 2026-02-26 12:33:47 +03:00
Ed Zynda d6b710542f fix(ui): left-align queued messages and preserve newlines 2026-02-26 12:32:39 +03:00
Ed Zynda f57aaf31d0 fix(ui): render queued messages as styled content blocks with QUEUED badge
Replace inline text+badge with proper bordered content blocks matching
the overall message styling. Each queued message is rendered with a
right-aligned block, muted border, and a QUEUED badge below the text.
2026-02-26 12:30:51 +03:00
Ed Zynda 38e4aa36e6 chore: fix golangci-lint issues
Modernize range-over-int loops, use tagged switch, replace min/max
if-blocks with builtins, remove unused funcs/fields, delete dead
streaming_display.go, and fix ineffectual assignment in tests.
2026-02-26 12:26:29 +03:00
Ed Zynda 69fba663ef fix(ui): use theme colors for spinner, remove text, persist during streaming
Spinner now uses theme.Primary/Muted/VeryMuted/MutedBorder instead of
hardcoded red. Removed 'Thinking...' label and message parameter from
NewSpinner/ShowSpinner/SpinnerFunc. Spinner keeps running alongside
streaming text and only hides on step complete via Reset().
2026-02-26 12:24:17 +03:00
Ed Zynda ee21408546 fix(ui): collapse multi-line queued messages to single line preview 2026-02-26 12:11:47 +03:00
Ed Zynda 4d9bf8a276 fix(ui): resolve queue deadlock and anchor queued messages to input
Fix a deadlock where submitting a message while the agent is streaming
locks the app. App.Run() and App.ClearQueue() were calling prog.Send()
synchronously from within Bubble Tea's Update() loop, blocking when the
internal event channel was full from streaming events.

Run() now returns the queue depth instead of sending events, and
ClearQueue() no longer sends events. The UI layer updates state directly.

Additionally, queued messages are now rendered with a "queued" badge
between the separator and input, anchored in the managed region until
the agent picks them up. Previously they were printed to scrollback
immediately and only a count badge was shown.
2026-02-26 12:05:29 +03:00
Ed Zynda acc8843ec0 chore: add Pi coding agent repo as btca research resource 2026-02-26 11:34:39 +03:00
Ed Zynda d405bc12d2 fix(ui): print startup messages before Bubble Tea takes over
tea.Println from Init() gets pushed above the fold because the View
fills the terminal immediately. Print startup info to stdout before
program.Run() so messages are visible when the TUI appears.
2026-02-26 10:14:32 +03:00
Ed Zynda cd4b5ab851 fix(ui): use tea.Sequence for startup messages to preserve ordering
tea.Batch runs commands concurrently, so multiple tea.Println calls
would either race (dropping messages) or render on one line. Use
tea.Sequence to emit each startup message one at a time.
2026-02-26 10:10:52 +03:00
Ed Zynda 18bedca479 fix(ui): combine startup messages into single tea.Println to prevent race
Multiple tea.Println commands in a tea.Batch can race, causing the
model-loaded message to be dropped. Combine all startup info into a
single system message so only one tea.Println is needed.
2026-02-26 10:08:16 +03:00
Ed Zynda 26b40aa613 fix(ui): restore startup info messages in interactive Bubble Tea TUI
The Bubble Tea refactor stopped calling SetupCLI for interactive mode,
which dropped the model-loaded, loading-message, and tool-count info
that used to appear on startup. Emit those messages from AppModel.Init()
via tea.Println to match the old behaviour.
2026-02-26 10:01:31 +03:00
Ed Zynda c1b3fa1dfc add .iteratr and .opencode tool configuration directories 2026-02-26 09:55:53 +03:00
Ed Zynda 4f96acc217 fix(ui): execute slash commands in TUI instead of sending them to the LLM
The Bubble Tea refactor only wired /quit, /clear, and /clear-queue in
InputComponent; the remaining commands (/help, /tools, /servers, /usage,
/reset-usage) fell through as submitMsg and were forwarded to app.Run()
as regular prompts.

Intercept all recognized slash commands in AppModel.Update before they
reach the app layer, and add print helpers that emit formatted output
via tea.Println. Also create a UsageTracker for interactive mode so
/usage and /reset-usage work correctly.
2026-02-26 09:55:20 +03:00
Ed Zynda 80d5300feb remove all tool calling permission checks and approval infrastructure
Strip the three-layer permission system (hook-based blocking, user approval
dialogs, PostToolUse output suppression) so every tool call executes
unconditionally. This removes ~1100 lines across 14 files including the
--approve-tool-run and --no-hooks CLI flags, ToolApprovalHandler/Func types,
PreToolUse/UserPromptSubmit/PostToolUse/Stop hook firing, HookBlockedEvent,
ToolApprovalNeededEvent, the ApprovalComponent UI, and all associated tests.
2026-02-26 09:47:10 +03:00
Ed Zynda 0c3d240519 fix(ui): print user messages, tool calls, and tool results immediately via tea.Println
The bubbletea refactor was accumulating all messages in StreamComponent
and only printing Response.Content.Text() on step completion, causing
user messages, tool calls, and tool results to be missing from output.

Now only agent streaming text stays live in StreamComponent. Everything
else is printed immediately to scrollback:
- submitMsg: prints rendered user message
- ToolCallStartedEvent: flushes stream text, prints tool call
- ToolResultEvent: prints tool result, restarts spinner
- HookBlockedEvent: prints blocked notice
- ResponseCompleteEvent: prints assistant text (non-streaming mode)
- StepCompleteEvent: flushes remaining stream text
2026-02-26 09:29:19 +03:00
Ed Zynda cf2c026182 refactor(ui): trim CLI to non-TUI helpers only
Remove methods that belonged to the old interactive loop and have no
external callers: HandleSlashCommand, SlashCommandResult, IsSlashCommand,
DisplayHelp, DisplayTools, DisplayServers, ClearMessages, UpdateUsage,
DisplayUsageStats, ResetUsageStats. Slash command dispatch is now owned
by InputComponent in the Bubble Tea TUI; usage tracking is handled
directly by the app layer.

Retain all display methods still needed by non-interactive paths
(cmd/root.go session replay, cmd/script.go, factory.go). Also drop the
now-unused "strings" import. cli.go shrinks from 509 to 329 lines.
2026-02-26 02:32:12 +03:00
Ed Zynda 4450945b4f test(ui): add 48 unit tests for InputComponent, StreamComponent, and ApprovalComponent (TAS-34) 2026-02-26 02:25:42 +03:00
Ed Zynda 977f6c75bd test(ui): add 23 unit tests for AppModel state machine (TAS-33)
Cover state transitions (input->working->approval->input), StepError->input,
ESC cancel flow (single tap sets canceling, double tap calls CancelCurrentStep,
timer expiry resets), queue badge updates, window resize/distributeHeight, and
tea.Println cmd emission on StepCompleteEvent/StepErrorEvent.
2026-02-26 02:22:13 +03:00
Ed Zynda 00771cf291 test(app): add AgentRunner interface and 15 unit tests for App
Introduce AgentRunner interface in options.go so tests can supply stub
agents without a real LLM (backward-compatible; *agent.Agent satisfies it).

Fix three pre-existing deadlock bugs in app.go where sendEvent() was called
while a.mu was held (Run, ClearQueue, drainQueue); the sendEvent comment
explicitly forbids this.

Add app_test.go with 15 tests covering: single/queued Run, FIFO drain
ordering, CancelCurrentStep, cancel-during-approval (ctx unblocks
ToolApprovalFunc), ClearQueue, Close (WaitGroup, idempotent, root-ctx
cancel), step error recovery, ClearMessages, and QueueLength.
2026-02-26 02:17:29 +03:00
Ed Zynda 04a0689775 test(app): add MessageStore unit tests (TAS-31)
17 tests covering Add, Replace, GetAll, Clear, copy isolation,
session.Manager bridge (in-memory, no disk I/O), and race-detector
concurrency smoke test.
2026-02-26 02:09:41 +03:00
Ed Zynda c15ff612b8 refactor(ui): move knightRiderFrames to stream.go, remove unused Spinner variants
- Move knightRiderFrames() from spinner.go to stream.go (its natural owner
  as StreamComponent is now the primary TUI inline spinner)
- Remove unused dotFrames, dotFPS vars and NewThemedSpinner() constructor
- Remove color field from Spinner struct and simplify run() accordingly
- spinner.go shrinks from 150 to 76 lines; Spinner struct retained for
  script.go and cli.go non-TUI paths
2026-02-26 02:07:48 +03:00