A platform tour of KCode v2.10.396: terminal agent, audit engine, and the integrations between
KCode started life as a terminal-native AI coding assistant. Twelve months later, the same binary is also a local-first SAST engine that ships 399 patterns, six framework packs, and a small local LLM as a deterministic-scanner-plus-verifier pipeline. The agent and the auditor share one codebase, one config, and one permission model.
v2.10.396 — released today — closes a multi-day quality push that took typecheck from 851 errors to 0, lint from 527 errors to 0, and the test suite from 25 timeout-shaped failures to 0. Along the way the cleanup surfaced 13 latent runtime bugs that the null-guard noise had been hiding. CI is now green end-to-end on both Linux and Windows lanes.
This post is a tour of what's actually in the repo at this release. If you arrived from the SAST-architecture writeup or the audit-your-auditor week, this is the platform-level companion: how the pieces fit together, what runs locally vs. cloud, and where the boundaries are.
Two products, one binary
KCode is one Bun-compiled binary, ~123 MB, that exposes the same engine through two distinct surfaces:
- The terminal agent.
kcodewith no subcommand starts a TUI built on React 19 + Ink. You get the conversation loop, tool calls, permissions, plans, and live status displays. This is what a developer uses interactively. - The audit engine.
kcode auditruns the SAST pipeline: discover files, run regex + AST patterns, verify candidates with a local LLM, emit Markdown / JSON / SARIF, and optionally generate fixes. CI calls this.
Both consume the same registries: the same tools, the same permission rules, the same model configuration. The audit engine is just a non-interactive entry point that sets up the ConversationManager differently. There's no second runtime to install, no separate license, no separate config file.
The repo measures 1,217 source files under src/ (≈403k lines), with 453 test files. The biggest subsystems are src/core/audit-engine/ at 118 files and 127k lines (the SAST product), and src/core/ at the agent core.
The conversation core
src/core/conversation.ts is the agent loop. The flow it orchestrates per turn:
- The system prompt is built modularly per turn (project context, CLAUDE.md, active skills, tool descriptions, permission state). Each piece can be swapped or omitted; the builder hash decides when to invalidate the prompt cache.
- The request goes through
request-builder.tswhich adapts the same internalMessage[]shape to OpenAI-compatible, Anthropic, xAI, Gemini, Groq, DeepSeek, Together, or Kimi. The system prompt position, the tool-definition format, the cache-control hints, the reasoning-content field — each provider has its own quirks encoded in the builder. - Responses arrive over SSE. A streaming parser extracts text deltas, thinking blocks, tool calls, and usage data. The UI gets text tokens as they arrive; tool calls are buffered until the model marks them complete.
- For each tool call, the permission manager evaluates the configured mode (
ask,auto,plan,deny,acceptEdits), the per-tool rules (glob patterns over command/path/domain), a dedicated Bash-command analyzer (matches against 24+ destructive patterns includingrm -rf /,git reset --hard,dd of=/dev/sd*), and write-path validation against the working directory. - Tools execute through the registry. Read-only tools can be parallelized when permission mode allows; write tools serialize with file-level locks so two parallel agents can't race the same file.
- Post-turn hooks evaluate the scope (did files actually land? did runtime succeed? do the artifacts match the user's request?). The closeout renderer decides whether the model's optimistic summary should be superseded by a scope-grounded one.
All of this is gated by a context manager that watches token usage and triggers compaction (microcompact for tool results, full LLM-based summarization for whole conversations, image-stripping for old vision content) before the request body grows past the model's window.
48 built-in tools, plus MCP
The tool registry exposes 48 first-class tools at startup. The categories:
- Filesystem:
Read,Write,Edit,MultiEdit,Glob,Grep,LS,Rename,GrepReplace. - Execution:
Bash(with the dangerous-pattern guard mentioned above) andTestRunner(auto-detects the framework and runs scoped to the modified files). - Git:
GitStatus,GitLog,GitDiff,GitShow,Worktree. - Code intelligence:
LSP(definitions / references / hover via stdio LSP servers). - Net:
WebFetch(HEAD probe + 5MB cap),WebSearch(DuckDuckGo HTML scrape, no API key),Browser. - Agentic:
Agent(subagent spawn for parallel sub-tasks),Plan(multi-step plan with persistent state),TaskCreate/TaskUpdate. - Multimedia:
ImageGen,Deploy.
Beyond those 48, KCode is a full MCP host. Any MCP server configured in settings exposes its tools and resources to the same registry; the agent treats them like first-class tools and applies the same permission rules.
The audit engine: 399 patterns, 6 packs, an LLM verifier
The audit pipeline at src/core/audit-engine/ is the SAST product. It's documented in detail in the false-positives writeup, but at this release the numbers are:
- 399 patterns total (372 regex + 27 AST via tree-sitter).
- 6 packs: general (290), web (42, with framework sub-packs for Next.js, FastAPI, Express, Django, Rails, Spring, Laravel), embedded (21), ai-ml (8), cloud (6), supply-chain (5).
- 140 fixture files across 68 corpus directories, used for precision/recall regression in the locked CI benchmark.
- Recall on the fixture corpus is at 92.3%, precision at 100%. These numbers are reported by the same harness CI runs against every PR.
The pipeline stages are explicit so each can be swapped: file discovery, regex pass, AST pass, candidate dedupe, optional LLM verifier (off via --skip-verify), report generation (Markdown / JSON / SARIF), optional fixer for patterns marked SAFE-deterministic, and an optional SBOM dependency scan. CI consumers use kcode audit-ci which exits non-zero on confirmed findings and emits SARIF for GitHub Code Scanning.
Permissions are the spine
Permissions in KCode aren't a wrapper around tools — they're the spine of the agent loop. Five modes (ask, auto, plan, deny, acceptEdits), per-tool rules with first-match-wins evaluation (glob over command + path + domain), managed-policy fields for org-level allowlists/denylists, audit logging that survives a crash, and the dangerous-bash analyzer wired straight into the tool-execution gate.
The audit-your-auditor cycle in v2.10.385 caught a textbook example: a registry of 16 destructive bash patterns existed in the repo with full tests, but the actual analyzeBashCommand() never invoked it. Same shape as the Cursor + Railway incident from April 25 — a guardrail that exists but isn't wired. We wired it. Then we added 8 git destructive patterns that weren't on the list (git push --force against main, filter-branch, reflog --expire=now).
Integrations: HTTP API, Web UI, daemon, IDE, mobile, SDKs
The same engine is exposed through multiple integration surfaces, each with its own auth and port:
- HTTP API:
kcode serve --port 10101. Read-only by default. Used by the SDKs and IDE plugins. Token comes from~/.kcode/api-token; token comparison is timing-safe. - Web UI:
kcode webspins up a Bun-served HTTP/WebSocket server with a one-shot fragment-based auth handoff (#auth=<token>). The fragment never hits server logs, browser history, or process-args tables — the client-side bootstrap copies it to localStorage and strips it from the URL. - Bridge daemon:
kcode daemonon port 19100–19199 (auto-bind to first free in range). This is the WebSocket protocol for richer integrations than the read-only HTTP API — bidirectional streaming, permission prompts that round-trip to the requester. - VS Code, JetBrains, Neovim: three IDE extensions, all targeting the HTTP API on 10101. The VS Code variant gets a sidebar chat panel, context menu (Explain / Fix / Test selection), and a terminal integration. The keybinding is
Ctrl+Shift+K. - iOS companion app: SwiftUI client targeting the same HTTP API. SSE streaming, tool-result cards, agent status indicators. Tailscale-friendly so the phone never has to be on the same LAN as the dev machine.
- SDKs:
@kulvex/kcode-sdkfor TypeScript andkcode-sdkfor Python. Both speakPOST /api/promptagainst the HTTP API, with optional SSE streaming. (One of the fixes that landed in this release: the SDK defaults pointed at port 19300 — a leftover from the bridge-daemon era — instead of the actual API port at 10101. Out-of-boxnew KCodeClient()now connects.) - Backend reference: a Bun + Hono + SQLite service under
backend/for OAuth, subscription validation, and Stripe checkout. Run entirely separately onapi.astrolexis.space; KCode itself doesn't depend on it for local-only usage.
The v2.10.396 quality push
Three numbers got moved this week:
bun run typecheck: 851 errors → 0. Most werenoUncheckedIndexedAccessnull-guards, but ~14% were substantive type-shape issues. Going through them surfaced 13 latent runtime bugs the null-guard noise had been hiding. Two examples:/kodi-advisor resetwas a silent no-op for months becauseloadUserSettingsRaw()returns a Promise but three call sites awaited nothing; nine banner events emitted byconversation-post-turnfor partial-implementation warnings were silently dropped because the StreamEvent union didn't include them and the UI's switch had no case for them.bun run lint: 527 errors → 0. Biome auto-fix cleared most (parse-int radix, organize imports, useTemplate, unused imports), with five manual fixes for things auto-fix couldn't:noShadowRestrictedNames(four locals namedescapeshadowing the global), and onenoConstantConditionthat was a real dead-code bug —if (regex.test(x) || true)had been deleting the conditional check for months.bun test: 25 fails → 0. Mostly E2E timeouts at the 5s default — the FakeProvider + ConversationManager flow takes 5–9s on this machine. Bumped the per-test timeout to 30s via a newtest:ciscript (bun test --timeout=30000) that the CI workflow now invokes.
The CI workflow at .github/workflows/build.yml now goes green end-to-end: bun audit → lint → typecheck → test:ci → build (5-platform matrix). No skipped steps, no deferred warnings, no "we'll fix the test runner later".
How distribution works
Five binaries per release: linux-x64, linux-arm64, darwin-x64, darwin-arm64, windows-x64. Each is a Bun-compiled standalone at ≈100–123 MB (the runtime is embedded; there's no Node required on the target machine). The release script generates a latest.json manifest with SHA256 hashes per platform plus binary deltas against the prior three releases (so a small upgrade downloads ≈1 MB instead of 120 MB), copies everything to the kulvex.ai CDN directory served via Cloudflare, then publishes a GitHub Release with the same artefacts as a fallback channel.
The auto-updater inside the running CLI talks to the manifest URL, applies bsdiff deltas where available, and falls back to a full download otherwise. Rollback keeps the previous binary at ~/.kcode/previous-kcode so a bad release can be reverted without re-installing. Install scripts cover Homebrew, a one-line install.sh, and Windows MSI; npm publishing was disabled in v2.10.357 (the 117 MB binary was the wrong shape for npm, and the publish lane had been stuck on a 401/404 mismatch through ~7 token rotations).
What's next
The platform is wide; the work this week was about pulling the rails up to a green CI baseline so future contributions stop being a cleanup tax. The next round of focus is back on the audit engine: more framework packs, the LLM-verifier batching for large codebases, and a tighter integration with the GitHub Code Scanning sink so SARIF emissions surface in the PR timeline directly.
If you want to try v2.10.396 today: install via the one-liner at kulvex.ai/kcode, or grab the binary from the GitHub release page. Local-first, Apache-2.0, no telemetry by default, no account required.
Source code: github.com/AstrolexisAI/KCode. Architecture writeups are tagged KCode on this blog.