Files
kilocode/packages/kilo-vscode/AGENTS.md
T

18 KiB

AGENTS.md

This file provides guidance to agents when working with code in this repository.

Product Context

Kilo Code is an open source AI coding agent platform. It ships as a CLI and editor clients that all build on the same backend. This package (packages/kilo-vscode/) is the VS Code extension.

Products and How They Relate

All products are thin clients over the CLI (packages/opencode/, published as @kilocode/cli). The CLI is a fork of upstream OpenCode with Kilo-specific additions (gateway auth, telemetry, migration, code review, branding). It contains the full AI agent runtime, tool execution, session management, provider integrations (500+ models), and an HTTP API server.

Every client spawns or connects to a kilo serve process and communicates via HTTP REST + SSE using the auto-generated @kilocode/sdk.

                        @kilocode/cli  (packages/opencode/)
                     ┌────────────────────────────────┐
                     │  AI agents, tools, sessions,    │
                     │  providers, config, MCP, LSP    │
                     │  Hono HTTP server + SSE         │
                     └──┬──────────┬──────────────────┘
                        │          │
                ┌───────┴──┐ ┌────┴────┐
                │ TUI      │ │ VS Code │
                │ (builtin)│ │Extension│
                └──────────┘ └─────────┘
Product Package What it is How it uses the CLI
Kilo CLI (TUI) packages/opencode/ Interactive terminal UI (SolidJS + OpenTUI) In-process — TUI and server run together
Kilo CLI (kilo run) packages/opencode/ Non-interactive headless mode for scripting In-process — no network socket
Kilo VS Code Extension packages/kilo-vscode/ VS Code extension with sidebar chat + Agent Manager Bundles CLI binary, spawns kilo serve --port 0 as child process

Kilo-Domain Packages

Package Name Role
packages/kilo-vscode/ kilo-code This package. VS Code extension.
packages/kilo-gateway/ @kilocode/kilo-gateway Auth (device flow), AI provider routing (OpenRouter), Kilo API integration (profile, balance, teams)
packages/kilo-ui/ @kilocode/kilo-ui SolidJS component library (40+ components, built on @kobalte/core). Shared by this extension's webview and docs screenshot stories
packages/kilo-telemetry/ @kilocode/kilo-telemetry PostHog analytics + OpenTelemetry tracing for the CLI
packages/kilo-i18n/ @kilocode/kilo-i18n Translation strings (16 languages)
packages/kilo-docs/ @kilocode/kilo-docs Documentation site (Next.js + Markdoc)

Upstream OpenCode Packages (not Kilo-specific)

Package Name Role
packages/opencode/ @kilocode/cli Core CLI — forked from upstream OpenCode. AI agents, tools, sessions, server.
packages/sdk/js/ @kilocode/sdk Auto-generated TypeScript SDK client for the server API. Do not edit src/gen/ by hand.
packages/ui/ @opencode-ai/ui Shared UI primitives
packages/util/ @opencode-ai/util Shared utilities (error, path, retry, slug)
packages/plugin/ @kilocode/plugin Plugin/tool interface definitions

Commands

bun run extension        # Build + launch VS Code with the extension in dev mode
bun run compile          # Type-check + lint + build
bun run watch            # Watch mode (esbuild + tsc)
bun run test             # Run tests (requires pretest compilation)
bun run lint             # ESLint on src/
bun run format           # Run formatter (do this before committing to avoid styling-only changes in commits)

The extension commands also work from the repo root. Pass --insiders to prefer VS Code Insiders, --workspace PATH to open a different folder, --clean to wipe cached state, or --wait to block until VS Code closes. VS Code is auto-detected on macOS, Linux, and Windows; override with --app-path or VSCODE_EXEC_PATH.

Single test: bun run test -- --grep "test name"

CLI Binary

The extension bundles its own CLI binary at bin/kilo — it does NOT use a system-installed CLI. To build it:

bun script/local-bin.ts

Or use --force to rebuild:

bun script/local-bin.ts --force

The script checks for a prebuilt binary in packages/opencode/dist/, builds the CLI if needed, and copies it to bin/kilo.

Architecture

Extension ↔ CLI Backend

The extension is a client of the CLI. Activation creates one shared KiloConnectionService; on its first connection, which autocomplete may prewarm, ServerManager spawns bin/kilo serve --port 0, captures the dynamically assigned port from stdout, and communicates over HTTP + SSE. The current child process is reused unless it exits. A random password is generated and passed via KILO_SERVER_PASSWORD env var for basic auth.

Extension (Node.js)                          CLI Backend (child process)
┌──────────────────────────┐                ┌──────────────────────┐
│ KiloConnectionService    │── HTTP/SSE ──> │ kilo serve --port 0  │
│   ├── ServerManager      │                │   Hono REST API      │
│   ├── HttpClient         │                │   SSE event stream   │
│   └── SSEClient          │                │   Session management │
│                          │                │   AI agent runtime   │
│ KiloProvider (sidebar)   │                └──────────────────────┘
│ KiloProvider (agent mgr) │
│ KiloProvider (open tabs) │
└──────────────────────────┘
  • KiloConnectionService (src/services/cli-backend/connection-service.ts) is created once during extension activation and shared across the sidebar, Kilo editor tabs, and Agent Manager. It owns the current server process, HTTP client, and SSE connection.
  • ServerManager (src/services/cli-backend/server-manager.ts) lazily spawns the CLI binary, reuses its current process, and can start a replacement if that process exits.
  • The sidebar, every Open in Tab Kilo panel, and the Agent Manager chat provider reuse this connection. Multiple KiloProvider instances subscribe to it, with SSE events filtered per-webview via a trackedSessionIds Set. Agent Manager terminals may use additional PTY/WebSocket channels to the same backend, not separate kilo serve processes.
  • Backend state follows where it is allocated, not the worktree shown in a panel. Snapshot repository state uses directory-keyed InstanceState, while trackState is created once in the active Snapshot service closure. For these shared VS Code session paths, its slow-track asked guard spans worktree requests; choosing Continue with snapshots resets asked only when continued tracking returns a snapshot hash.

Builds

Two separate esbuild builds in esbuild.js:

  • Extension (Node/CJS): src/extension.tsdist/extension.js
  • Webview (browser/IIFE): webview-ui/src/index.tsxdist/webview.js AND webview-ui/agent-manager/index.tsxdist/agent-manager.js

Non-Obvious Details

  • Webview uses Solid.js (not React) — JSX compiles via esbuild-plugin-solid
  • Extension code in src/, webview code in webview-ui/src/ with separate tsconfig
  • Tests compile to out/ via compile-tests, not dist/
  • CSP requires nonce for scripts and font-src for bundled fonts — see KiloProvider.ts
  • HTML root has data-theme="kilo-vscode" to activate kilo-ui's VS Code theme bridge
  • Extension and webview have no shared state — communicate via vscode.Webview.postMessage()
  • For editor panels, use AgentManagerProvider pattern with retainContextWhenHidden: true
  • esbuild webview build includes cssPackageResolvePlugin for CSS @import resolution and font loaders (.woff, .woff2, .ttf)
  • Avoid setTimeout for sequencing VS Code operations — use deterministic event-based waits (e.g. waitForWebviewPanelToBeActive())

Extension ↔ Webview Feature Pattern

When adding a new feature that requires data from the CLI backend to be displayed in the webview:

  1. Types (src/services/cli-backend/types.ts): Add response types for the backend data
  2. HTTP Client (src/services/cli-backend/http-client.ts): Add a fetch method to retrieve the data
  3. KiloProvider (src/KiloProvider.ts): Add a fetchAndSend*() method using the cached message pattern, and handle the corresponding request* message from the webview in handleWebviewMessage()
  4. Message Types (webview-ui/src/types/messages.ts): Add *LoadedMessage (extension→webview) and Request*Message (webview→extension) types to the ExtensionMessage / WebviewMessage unions
  5. Context (webview-ui/src/context/): Subscribe to the loaded message outside onMount (to catch early pushes before mount), add retry logic for the request message, expose state via context
  6. Component (webview-ui/src/components/): Consume context, render UI

Key patterns:

  • Cached messages (e.g. cachedProvidersMessage, cachedAgentsMessage in KiloProvider): Ensures webview refreshes get data immediately without waiting for a new HTTP round-trip
  • Retry timers (e.g. agentRetryTimer in session context): Handles race conditions where the extension's HTTP client isn't ready when the webview first requests data

Agent Manager

The Agent Manager is a feature within this extension (not a separate product). It opens as an editor tab (Cmd+Shift+M) and provides multi-session orchestration — running multiple independent AI sessions in parallel, each optionally isolated in its own git worktree.

How It Differs From the Sidebar

Aspect Sidebar Agent Manager
Location Activity bar sidebar panel Editor tab (full panel)
Sessions Single session at a time Multiple parallel sessions with tabbed UI
Git isolation Uses workspace root Each session can get its own worktree branch
State No dedicated state file .kilo/agent-manager.json
Terminals None Dedicated VS Code terminal per session
Setup scripts None Configurable .kilo/setup-script runs per worktree
Multi-version Not supported Up to 4 parallel worktrees with the same prompt

Architecture

Agent Manager local worktree sessions use the current shared kilo serve process owned by KiloConnectionService; no session starts its own backend. Their CLI requests pass the worktree path as directory, which resolves directory-scoped backend state. Setup scripts, terminal PTYs, git subprocesses, and a separately opened VS Code window are separate process or extension-host boundaries, not per-worktree kilo serve instances.

Extension-side code lives in src/agent-manager/, webview code in webview-ui/agent-manager/. The webview reuses the sidebar's provider chain and ChatView component, adding a WorktreeModeProvider and a split layout.

Webview UI (kilo-ui)

New webview features must use @kilocode/kilo-ui components instead of raw HTML elements with inline styles. This is a Solid.js component library built on @kobalte/core.

  • Import via deep subpaths: import { Button } from "@kilocode/kilo-ui/button"
  • Available components include Button, IconButton, Dialog, Spinner, Card, Tabs, Tooltip, Toast, Code, Markdown, and more
  • Provider hierarchy in App.tsx: ThemeProvider → I18nProvider → DialogProvider → MarkedProvider → VSCodeProvider → ServerProvider → ProviderProvider → SessionProvider
  • Global styles imported via import "@kilocode/kilo-ui/styles" in index.tsx
  • chat.css is being progressively migrated — when replacing a component with kilo-ui, remove the corresponding CSS rules from it
  • New CSS for components not yet in kilo-ui goes into chat.css grouped by comment-delimited sections (/* Component Name */). Once a kilo-ui equivalent exists, remove the section.
  • Check existing webview usages first: webview-ui/src/ and packages/kilo-ui/src/stories/ show how kilo-ui components are composed. Do not rely only on the component API in isolation.
  • data-component and data-slot attributes carry CSS styling — kilo-ui uses [data-component] and [data-slot] attribute selectors, not class names. Reuse existing component slots where available so shared styles apply consistently.
  • Prefer kilo-ui styles: Always reuse existing kilo-ui CSS variables, tokens, and component styles instead of writing custom CSS. If a style doesn't exist in kilo-ui yet, add it there and reuse it rather than inlining or duplicating styles in the webview.
  • Icons: kilo-ui has 75+ custom SVG icons in packages/ui/src/components/icon.tsx. To list all available icon names: node -e "const c=require('fs').readFileSync('../../packages/ui/src/components/icon.tsx','utf8');[...c.matchAll(/^\\s{2}[\"']?([\\w-]+)[\"']?:\\s*\x60/gm)].map(m=>m[1]).sort().forEach(n=>console.log(n))". Icon names use both hyphenated (arrow-left) and bare-word (brain, console, providers) keys.

Diff Rendering Performance

  • Preserve hunk-bounded unified patch data through Changes/review detail flows and pass patch-derived FileDiffMetadata to Pierre when available. Do not eagerly render Pierre from complete before/after contents based only on changed-line counts: a tiny patch in a large source file can otherwise parse and render the entire file while the user sees a placeholder.
  • Pierre workers can offload highlighted updates, but they do not make an expensive synchronous initial render safe. Keep initial rendering hunk-bounded, and keep patch parsing behind deferred visibility/activation where session-switch responsiveness depends on it.
  • When changing diff scheduling, verify both rapid session switching and fast scrolling through a review. Improving one by shifting work into the other is a regression, not an optimization.

Docs Screenshot Stories

When adding or updating Storybook stories for screenshots used by docs, make the story content match the docs page closely before replacing the docs image. Do not replace screenshots from VSCode Legacy docs tabs or sections.

Generated screenshot baselines live under packages/kilo-docs/public/img/screenshot-tests/ and are referenced from docs as /docs/img/screenshot-tests/.... If a generated VS Code visual-regression screenshot is used in docs, add the docs usage to the DOCS map in tests/visual-regression.spec.ts and keep tests/visual-regression.spec.mts in sync while that file exists.

Debugging

  • Extension logs: "Extension Host" output channel (not Debug Console)
  • Webview logs: Command Palette → "Developer: Open Webview Developer Tools"
  • In Chrome/VS Code performance traces, associate CPU ProfileChunk events to their Profile.id target before attributing work to a thread. v8:ProfEvntProc is a profile delivery thread, not evidence that application work ran off the webview main thread.
  • All debug output must be prepended with [Kilo New] for easy filtering

Naming Conventions

  • All VSCode commands must use kilo-code.new. prefix (not kilo-code.)
  • All view IDs must use kilo-code.new. prefix, except the sidebar view which uses kilo-code.SidebarProvider to preserve user sidebar position when upgrading from the legacy extension

Kilocode Change Markers

This package is entirely Kilo-specific — kilocode_change markers are NOT needed in any files under packages/kilo-vscode/. The markers are only necessary when modifying shared upstream opencode files.

Process Spawning (Windows)

On Windows, any spawn/execFile/exec call that does not set windowsHide: true will flash a cmd.exe console window at the user. To prevent this, never import spawn, execFile, or exec from child_process directly. Use the wrappers in src/util/process.ts instead — they enforce windowsHide: true automatically:

import { spawn, exec } from "../util/process"

The spawn wrapper covers long-lived processes (e.g. kilo serve). The exec wrapper covers short commands (e.g. git, tar). If you need the raw callback form of execFile for some reason, pass windowsHide: true explicitly in the options object.

Style

Follow monorepo root AGENTS.md style guide:

  • Prefer const over let, early returns over else
  • Single-word variable names when possible
  • Avoid try/catch, avoid any type
  • ESLint enforces: curly braces, strict equality, semicolons, camelCase/PascalCase imports

File Size Caps (maxLines)

Large files in src/agent-manager/ have maxLines caps enforced by tests/unit/agent-manager-arch.test.ts. Do not raise these caps. If adding a feature would exceed a cap, extract logic into a vscode-free helper module and call it from the provider. See fork-session.ts and format-keybinding.ts for examples of this pattern.

Markdown Tables

Do not pad markdown table cells for column alignment. Use | content | with single spaces, not | content | with extra padding. Padding creates spurious diffs. Markdown files are excluded from prettier (via .prettierignore) to prevent auto-reformatting of tables.

Committing

  • Before committing, always run bun run format so commits don't accidentally include formatting/styling-only diffs.