You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracks the JS→Go port. The JS reference (src/sections/*.js, extracted from
Claude Code v2.1.66) was the golden reference for differential testing during
the port and has now been retired to the js-reference branch — the Go
binary is the product. This table is the record of what was ported and where it
deliberately diverges.
Parity was the method, not the destination. It was how the port was kept honest
while the JS tree was still there to diff against; it stopped being a goal the
moment the reference was retired. A 🔀 row is therefore a finished decision, not
an outstanding one — where a whole area has been redesigned rather than ported
(permissions, most visibly) the section says so in prose above its table.
Status legend
Status
Meaning
✅ done
Ported, tested, behaviourally equivalent to the JS reference.
🟡 partial
Ported but narrower than JS (gaps noted).
🔀 divergent
Intentionally differs from JS (a settled Klaudia design choice, including features deliberately removed).
Line range, cat -n format; lines of any length (cut at 2000 bytes on a rune boundary, with a note); notice when the window stops short of EOF; binary (NUL in first 8 KB) → size only; directory → clean error.
Read (image)
05-app-core
tools/read.go
✅ done
Returns ResultImage; flows to vision via toolResultWithImages. Typed from the bytes, not the name; empty, non-image or (after scaling) over 5 MB is refused before sending. Scaled to 2000 px on its longest side on read (pkg/imagefit), so a long session cannot cross the API's stricter many-image limit; the loop also fits any older image once a request holds more than 20, and retries once at a lower limit the API names.
Read (PDF)
05-app-core
tools/read.go + native/pdf
🔀 divergent
Pure-Go text extraction via gopdf (JS rendered pages to images for the model).
Write
05-app-core
tools/write.go
✅ done
Edit
05-app-core
tools/edit.go
✅ done
Exact-match + replace_all.
Glob
05-app-core
tools/glob.go
✅ done
Grep
05-app-core
tools/grep.go + native/search
✅ done
ripgrep-style; pure-Go backend.
Bash
05-app-core
tools/bash.go + sandbox/
✅ done
local / os-confined / container executors.
Bash run_in_background + BashOutput / KillShell
05-app-core
tools/bash.go, tools/shells.go + sandbox
✅ done
Bash run_in_background returns a shell id; BashOutput reads new output incrementally (optional regex filter); KillShell stops it. Session-scoped store; all shells killed at session end. Works across all executors via Executor.Argv.
Code intelligence (LSP)
—
lsp/ + tools/lsp_tools.go
🔀 divergent
New capability with no JS analogue: Diagnostics/Definition/References/Implementation/Hover/DocumentSymbols/Rename/WorkspaceSymbol via language servers detected on PATH + toolchain dirs (gopls/rust-analyzer/tsserver/pyright/clangd). Lazy-spawned, session-scoped, never downloaded.
TodoWrite
05-app-core
tools/todowrite.go + plan/
✅ done
Backed by plan.Store, one per conversation (agent.Turn.Plan; sub-agents get their own). Every frontend renders it from the loop's plan event: a live panel in the TUI (Ctrl+T), a plan event over stream-json, a native plan in ACP. An approved plan is also written to ~/.klaudia/plans/<session-id>.md (as Claude Code's ~/.claude/plans; no plansDirectory override yet), so it survives compaction and --resume. See docs/plan-mode.md.
Task tools (Create/List/Get/Update)
07-app-features
—
🔀 divergent
Retired. They backed a second, independent task store that nothing read — not the TUI, not stream-json, not ACP — so Klaudia had two parallel task systems and neither was authoritative. TodoWrite and plan.Store are the one answer. See docs/plan-mode.md.
NotebookEdit
05-app-core
tools/notebookedit.go
✅ done
AskUserQuestion
05-app-core
tools/askuser.go
✅ done
Interactive via Asker seam. The TUI always appends a "something else" option (the model writes the choices, so its framing can be wrong); picking it — or just typing — opens free text, and the answer goes back verbatim.
ExitPlanMode
05-app-core
tools/exitplan.go
🔀 divergent
Via Planner seam. Takes an optional steps array (a Klaudia extension): approval makes them the plan's checklist, replacing any earlier list, so the approved plan and the step list on screen start as one. See docs/plan-mode.md.
Agent (Task / sub-agents)
07-app-features
tools/agent.go + subagent/
✅ done
Dynamic description from subagent.Builtin(). run_in_background (TUI, headless and stream-json; ACP runs sub-agents in the foreground) and fork (starts from a copy of the conversation, Spawner.SpawnFork).
Memory
07-app-features
tools/memory.go + memory/
🔀 divergent
Auto-memory + recall via .klaudia/memory/.
ToolSearch (deferred loading)
07-app-features
tools/toolsearch.go
✅ done
Deferred tools sent with defer_loading; Context.Reveal records a marker the Anthropic client sends as tool_addition (pkg/api/toolreveal.go).
SlashCommand (model-invoked)
07-app-features
tools/skill.go
🔀 divergent
Covered by the Skill tool (user-defined skills the model can invoke).
Local default tools use lazy Chrome + DDG/Google and rendered markdown; a search or a fetch that comes back as a bot-challenge page (Cloudflare, DataDome, captcha) escalates once to headed Chrome on the persistent ~/.klaudia/browser/chrome-profile, announces why, and closes the window when the page comes through. Permission-gated (allowed in autonomous; denied in plan); the engine is session-owned and Closed on exit. Navigation waits for the DOM to settle (readyState + text-stable, bounded) so JS-rendered content is captured. Google parsing filters Google's own nav/account links (DDG remains the stable default). Anthropic server-side betas still available via agent/webtools.go.
The active mode is a system-prompt clause rebuilt per turn (approving a plan flips it mid-run), appended as a separate block so the cached prefix stays stable. Autonomous adds nothing. Changes emit a permission_mode event for headless frontends.
Parallel tool dispatch
05-app-core
agent/dispatchgroup.go
✅ done
Adjacent tools.ConcurrencySafe calls run together, 5 at a time. Groups are maximal runs so nothing is reordered; a call whose gate or permission check would ask is never grouped (one Approver, one prompt at a time); a group's events are delivered in call order with the head streaming live. Read/Glob/Grep are marked.
Per-message tool-result cap
05-app-core
agent/batchcap.go
🔀 divergent
One turn's combined tool-result text held to 200 KB, on top of the 30 KB per-result cap. Shares are water-filled, not split evenly; images and an existing spill notice are left alone. See docs/compaction.md.
Provider abstraction
03-providers
api/provider.go
🔀 divergent
Multi-provider interface (Anthropic + OpenAI-compatible); JS was Anthropic-centric.
Anthropic Messages (Beta, streaming)
02/03
api/client.go
✅ done
Streamed with an idle watchdog (streamIdleTimeout in config).
OpenAI-compatible Chat Completions
—
api/openai*.go
🔀 divergent
Translation shim; SSE stream:true. Image tool_results translated to image_url content parts. delta.reasoning is counted, not kept.
Model kinds + media tools
—
api/modelkind.go, api/media.go, tools/media.go
🔀 divergent
New capability with no JS analogue: GET /v1/models metadata classifies each model; non-chat ones become Transcribe / Speak / GenerateImage / GenerateMusic, registered per kind the endpoint serves. [models] deny. See docs/models.md.
Prompt caching
03-providers
api/cache.go
✅ done
cache_control breakpoints on tools + system + a rolling conversation tail; usage surfaced as cache_read/creation_input_tokens. Off with api.Tuning.NoPromptCache.
can_use_tool is the JS subtype; ask_user and exit_plan are added, because without them AskUserQuestion and ExitPlanMode were dead over the one channel whose purpose is a client with a user in front of it. The ask also carries tool_use_id, specifier, suggestion and the host change, which the JS payload omits.
Agent Client Protocol (ACP) v1
—
acp/
🔀 divergent
New capability with no JS analogue: --input-format acp serves ACP over stdio for Zed / acp.nvim / JetBrains. Sessions and modes map onto Klaudia's own (ACP session ids are transcript ids; the mode picker is /mode, per thread) and skills become the client's commands; TodoWrite becomes a native plan (from the stored list, not the call's input) and Edit/Write carry diffs. fs/read_text_file is used (unsaved buffers), while client-side writes, terminal/* and session/delete are declined. See docs/acp.md.
One frontend contract (agent.Turn)
—
agent/turn.go
🔀 divergent
TUI, stream-json and ACP all run turns through one struct instead of a per-frontend RunFunc. A capability is a field a frontend sets; the old positional signatures made "does not support it" indistinguishable from "was never wired".
Auto-resumes most recent project session by default; --new-session starts fresh; -r/--resume, --continue, --fork-session remain.
Microcompaction (local)
07-app-features
compaction/
✅ done
Autocompaction (model summary)
07-app-features
compaction/
✅ done
Persisted summaries + resume seeding
—
session/summary.go + agent.OnSummary
🔀 divergent
Compaction summaries persisted to ~/.klaudia/sessions/<encoded-cwd>/<id>.summary.md (the old project-local .klaudia/sessions/ path is still read), plus a compact_boundary line in the transcript; --resume seeds from the last summary and the entries after it (token-saving), --full replays the transcript.
Project knowledge recall (KNOWLEDGE.md)
—
prompt.recalledKnowledge
🔀 divergent
.klaudia/KNOWLEDGE.md injected into the system prompt; curated, distinct from free-form memory.
Permissions
Parity was abandoned here on purpose, so read this section differently from the
rest. The JS permission model — five modes plus allow/deny rule matching — was
ported faithfully, shipped, and then removed once trust/ existed, because
two overlapping gates asking about the same tool call is worse than either one
alone. The rows below record what JS had and what replaced it, not a gap to be
closed. docs/trust.md is the design record.
Feature
JS ref
Klaudia pkg
Status
Notes
5 permission modes
07-app-features
permission/
🔀 divergent
Deliberately three, not five: autonomous/plan/bypassPermissions. default/acceptEdits/dontAsk asked the user to take a stance on tool categories; zones answer that instead.
Allow/deny rule matching
07-app-features
permission/
🔀 divergent
Removed. Replaced by the host gate + session-scoped approvals: /trust grants by what an operation does, not by matching command text. --allowedTools/--disallowedTools went with it.
Persisted rules in config
07-app-features
config/
🔀 divergent
Removed with the rules. [permissions] keeps only mode; there is no [trust] section. Grants are session-scoped by design — a trust decision made for one task should not silently outlive it.
Host-change gate (zones, grants, ledger)
—
hostgate/ + trust/
🔀 divergent
New capability with no JS analogue: classifies a call by what it would change (project / user files / the machine) and asks only when it leaves the working tree.
Lifecycle hooks
07-app-features
hooks/ + agent/hooks.go
🔀 divergent
Five events (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Notification) against Claude Code's thirty-plus; Stop/SubagentStop deliberately absent. Claude Code's stdin field names and exit-2-blocks convention are kept so existing hook scripts run unedited. PreToolUse runs after the host gate, so a hook can narrow but never widen. Hooks declared in a project's config are confirmed once per fingerprinted set; editing the set re-asks. See docs/hooks.md.
MCP & sub-agents
Feature
JS ref
Klaudia pkg
Status
Notes
MCP stdio client
07-app-features
mcp/
✅ done
Global ~/.klaudia/.mcp.json, then .mcp.json + .klaudia/.mcp.json override. Project servers wait for a remembered approval, as Claude Code's project-scope servers do (see docs/hooks.md).
MCP tools wrapped (mcp__*)
07-app-features
mcp/ + cli/
✅ done
Auto-deferred behind ToolSearch.
MCP reconnect/disconnect (/mcp)
—
mcp.Manager + tui
🔀 divergent
Interactive /mcp picker; reconnect swaps the live session into the existing tool wrappers so a crashed server's tools resume, then rebuilds the registries so a server that failed at startup gets its tools at all.
MCP self-healing
—
mcp.Manager.revive
🔀 divergent
A call that fails on the wire probes the server and relaunches it if it has stopped answering. The probe decides, so a bad argument cannot restart a healthy server. Read-only tools are retried; anything else is reported with the ambiguity stated, since the call may have run before the server died. Serialised per server, one attempt per 15s.
MCP HTTP/SSE transports
07-app-features
mcp/mcp.go
✅ done
A server with url uses the streamable HTTP transport (or type:"sse"; with no type, streamable then SSE); command stays stdio. headers are sent on every request, with ${VAR} expansion. SSE is deprecated by the spec — /doctor warns when a server is still on it.
MCP elicitation (form)
—
mcp/elicit.go
🔀 divergent
Protocol 2026-07-28. Form elicitation only, advertised as such, and only in an interactive session; URL mode is declined because a terminal cannot open a link out of band. One question per schema field through the same prompt AskUserQuestion uses; a type that will not coerce cancels rather than guessing. Serialised, because the SDK fulfils one round's requests concurrently and the frontend has a single question slot.
MCP sampling / roots / logging
07-app-features
—
⛔ not planned
All three are deprecated as of protocol 2026-07-28 (SEP-2577). Roots was never populated, so the capability is no longer advertised either.
Built-in sub-agents
07-app-features
subagent/
✅ done
Sub-agent tool allowlists (Type.Filter)
07-app-features
subagent/
🔀 divergent
Explore/Plan get Read/Glob/Grepand Bash, and are held read-only by Type.Mode instead; general-purpose gets the full toolset by design. Withholding the shell also withheld git log, git diff and go doc — the commands research is made of — so the restriction is now the mode, which refuses mutation rather than execution. Not applied over bypassPermissions. See docs/plan-mode.md.
Sub-agent working-tree isolation
—
worktree/ + agent/spawner.go
🔀 divergent
New capability with no JS analogue: a sub-agent whose toolset can write gets its own git worktree under ~/.klaudia/worktrees/, seeded with the parent's uncommitted work (not HEAD) and adopted back with git apply — index untouched, conflicts named to both the user and the model. Explore/Plan share the tree, and now take no worktree at all: a child that cannot write has nothing to isolate. Off with [subagents] worktree = false. See docs/working-tree.md.
TUI & frontends
Feature
JS ref
Klaudia pkg
Status
Notes
Interactive TUI
04/06
tui/
🔀 divergent
Bubble Tea (JS was React+Ink). Single-reader invariant.
Headless -p / --print
08-entry
cli/
✅ done
text / json / stream-json.
stream-json input (embedding frontend)
08-entry
cli/ + streamjson/
✅ done
--input-format stream-json.
@ file autocomplete
06-app-ui
tui/pathmenu.go
✅ done
Live dropdown as you type @… (ranked, ↑↓ to pick, Tab cycles, Esc dismisses). The earlier Tab-only completion was recorded here as a deliberate divergence; it was really invisible — typing @ showed nothing, so the feature was reported as missing. Paths with spaces are inserted quoted, and references outside the working directory complete from the filesystem.
Slash command type-ahead
06-app-ui
tui/input.go
✅ done
Live suggestions as you type /…; Tab completes (unique → fill, many → common prefix).
Bracketed paste / drag-drop
06-app-ui
bubbletea default
✅ done
Bracketed paste on by default (multi-line pastes atomic).
Help bar / keymap / input history
06-app-ui
tui/keymap.go + tui/input.go
✅ done
Up/Down ring-buffer history, Tab completion, key hints; /help generated from one command table.
Scrollback
06-app-ui
terminal-native (inline render)
✅ done
Output is printed into the terminal's own scrollback via tea.Println; no alt-screen and no mouse capture, so scrolling, selection, search and tmux copy mode are the terminal's. /search, /outline, /errors add structured lookup on top.
Interrupt the model (Esc)
06-app-ui
tui/keys.go + per-turn ctx
✅ done
Esc cancels the in-flight turn (and any pending approval); loop-level test confirms cancellation propagates.
Elapsed-time indicator
06-app-ui
tui (turnClock)
🔀 divergent
Live elapsed time while running + "done in"/"interrupted after" when the turn ends. Wall-clock, and paused while the turn waits on the user (permission, question, plan approval): a question left overnight once made the spinner read "thinking… 5h47m". The end line reports such a wait separately, e.g. "done in 1m00s (+5h41m waiting for you)", from a minute up.
Welcome intro
06-app-ui
tui.intro
🔀 divergent
Coloured logo + model/branch + key hints at startup.
Markdown rendering
06-app-ui
tui + glamour
✅ done
Assistant answers stream raw, then render through glamour (headings, bold, syntax-highlighted code) on completion.
Throughput readout
06-app-ui
tui
🔀 divergent
"✓ done in 12.3s · 1.2k written · 98 tok/s" from real Usage data (output tokens; the status bar counts input, cache included, too).
Slash commands (TUI)
Command
Klaudia
Status
Notes
/help, /quit, /exit, /clear
tui/commands.go
✅ done
/model
tui/commands.go
✅ done
/goal
tui/commands.go
✅ done
/memory
tui/commands.go
✅ done
/mcp
tui/commands.go
✅ done
/stats, /status
tui/commands.go
✅ done
/allow, /deny
—
🔀 divergent
Removed with the rule model; /trust is the replacement.
/config, /agents, /context
tui/commands.go
✅ done
Group A (render-only); friendly permission labels.
/mode
tui/commands.go + permission.Mode.Label
✅ done
Interactive picker (or direct set) to change permission mode mid-session. The picker (choice.go) is a live filterable dropdown shared by /model, /theme, /mode and /mcp.
/compact, /add-dir
tui/commands.go + agent.Loop.Compact
✅ done
Group B.
/cost, /permissions
—
🔀 divergent
Not present: /cost was replaced by the per-turn "done in · written · tok/s" line and /stats; /permissions by /mode and /trust.
/plan, /doctor, /diff, /commit, /export
tui/commands.go + doctor/
✅ done
Group C; /commit gated by a y/n confirm. /plan also takes run [N] and stop (plan runs, docs/plan-mode.md D5).
Auth & infra
Feature
JS ref
Klaudia pkg
Status
Notes
API key / env credential
03-providers
anthropicauth/auth.go
✅ done
OAuth keychain session
07-app-features
anthropicauth/auth.go
✅ done
macOS security CLI.
OAuth token refresh
07-app-features
anthropicauth/oauth.go
✅ done
Workstream 1d; refresh on expiry, write-back preserves unknown fields.
Host sandbox confinement
—
sandbox/seatbelt.go, bwrap.go
✅ done
Workstream 1a (divergent: JS had no host confinement).
Container sandbox
—
sandbox/container.go
🔀 divergent
New capability: run/validate code in docker/podman.
Telemetry / analytics
00/07
pkg/agent/trace.go, cli/tracing.go
🔀 divergent
Usage analytics intentionally not ported. OpenTelemetry tracing (GenAI semconv, OTLP when OTEL_EXPORTER_OTLP_* is set) is opt-in and goes only where the user points it.
CLAUDECODE env vars
08-entry
—
⛔ skipped
Only referenced in JS + test-*.sh; Go never needed them.
Auto-updater
00-runtime
—
⛔ skipped
Distributed as a static binary; out of scope.
Skills
Feature
JS ref
Klaudia pkg
Status
Notes
Skill system (.klaudia/skills/*.md)
07-app-features
skill/ + tools/skill.go
✅ done
Frontmatter skills as a Skill tool + TUI /.
JS retirement (done)
All differential-testing dependencies on the JS tree were cleared (the last was
stream-json partial deltas, now emitted behind --include-partial-messages).
The Go build and full test suite pass with the JS tree removed — definitive
proof nothing in it was load-bearing.
The JavaScript reference (src/sections/*.js), its build (build.mjs,
package.json), the pre-native Go sidecar tools (tools/, vendor/,
*.wasm), and the differential test scripts (test-*.sh) were removed from this
branch and preserved on js-reference. Consult them with
git checkout js-reference.
Everything in the tables above is ✅ done, a deliberate 🔀 divergence, a 🟡
partial with noted gaps, or a 🔜 planned post-parity enhancement that does not
require the JS reference.