Skip to content
dwalleckPublic

About

An experimental TUI for the Agent Client Protocol ecosystem

Resources

Stars

7 stars

Watchers

0 watching

Forks

Latest commit

 

History

2,095 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cyril

The polished TUI for the Agent Client Protocol ecosystem.

What is this?

Cyril is a polished terminal interface for the Agent Client Protocol ecosystem. Run any of 37+ registered agents — Claude, Cursor, Codex, Cline, Goose, Kiro, and more — through a single interface. Beneath the TUI, composable proxy stages add behaviors no agent ships natively: skill systems, transcript audit, organizational permission policies, persistent memory across sessions, multi-client observers. Vendor neutrality is a feature, not a roadmap; stages are how cyril compounds value over time.

Status: Alpha. Today cyril works against Kiro CLI; vendor-neutral agent selection and the proxy-stage layer are in active development. The features and usage documentation below describe the current Kiro-focused implementation.

Features

  • Streaming TUI — ratatui-based interface with real-time markdown rendering (headings, bold, italic, code blocks with syntax highlighting, tables, lists, blockquotes)
  • Cross-platform — runs natively on Linux, macOS, and Windows
  • Slash commands — autocomplete-enabled commands from both the client and the Kiro agent
  • Tool call display — see what the agent is doing in real time with inline diffs
  • Approval prompts — review and approve command execution with Yes/Always/No options
  • Session management — create, load, and resume previous sessions via /chat
  • Agent/model switching — switch agents (/agent) and models (/model) via picker UI
  • MCP management (KAS) — live server status, governance explanations, explicit reconnect/sign-in, and read-only prompt/resource previews
  • Live activity indicator — animated spinner with elapsed time and current tool activity in the toolbar
  • Context bar — visual gauge showing context window usage
  • Opt-in local memory runtime — teach explicit per-project lessons for a fresh session's first ordinary chat prompt, backed by private versioned stores and authenticated health reporting, without making chat depend on memory availability
  • @-file references — reference files in ordinary chat prompts with @path/to/file autocomplete

Prerequisites

  • Kiro CLI installed and authenticated (kiro-cli login), and resolvable on PATH (cyril also checks ~/.local/bin, /usr/local/bin and /opt/homebrew/bin; KAS sessions get their token from it) — Kiro ships native Linux, macOS and Windows binaries; see the platform qualification note below
  • Node.js on PATH for the default KAS launch
  • Rust toolchain (for building from source)

Installation

cargo install cyril

Or build from source:

git clone https://github.com/dwalleck/cyril.git
cd cyril
cargo build -p cyril --release --bins

The build produces target/release/cyril and target/release/cyril-memory-runtime (with .exe suffixes on Windows). Keep both executables in the same directory; Cyril launches the memory runtime by canonical absolute sibling path when [memory] enabled = true.

Usage

Launch the interactive TUI:

cyril

Send a one-shot prompt:

cyril --prompt "Explain what this project does"

Specify a working directory:

cyril -d /path/to/project        # Linux
cyril -d C:\Users\you\project    # Windows

Engines and first run

Normal production builds of the cyril package enable kas and select KAS when engine configuration is absent. The library cyril-core keeps its kas feature opt-in. Explicit v2 selection remains supported:

cyril --agent-engine v2

Or set the engine in ~/.config/cyril/config.toml:

[agent]
engine = "v2"

CLI engine selection still wins over configuration. kas and v3 both select KAS. To build a V2-only binary without the KAS credential-reading code, use an isolated selected-package build:

cargo build -p cyril --no-default-features

That build defaults to v2. Runtime --agent-engine v2 does not remove the credential-reading capability from a normal binary. A workspace build is not proof of exclusion: cyril-workbench enables core KAS through feature unification. See ADR-0002.

On v2, a tool rejected during argument validation is shown as a failed call with its validation error, even when the agent sends only an input chunk and a failed update without a start. Error text supplied in the update's content is displayed when no usable raw-output error is available. Empty or whitespace-only raw strings and error fields do not suppress that content fallback. Nonblank raw errors retain precedence; the original raw output remains unchanged. A chunk alone remains activity, not a fabricated tool result.

The default KAS free spawn runs discovered Node.js plus the extracted acp-server.js directly, with callback authentication: on each KAS token callback Cyril runs kiro-cli chat _ get-kas-token (cyril-zbu7), so kiro-cli resolves and renews the token itself; Cyril reads Kiro's credential store only as a fallback for a kiro-cli that lacks that subcommand (never renewed, so such sessions expire hourly). “Free” means no Kiro wrapper, not credential-free. It ignores --agent-command; an override alone does not select a WSL agent.

If the bundle is missing, the error reports the searched path and instructs kiro-cli acp --agent-engine v3 once to extract it, or KIRO_KAS_SERVER_PATH to select an explicit acp-server.js. Missing Node has its own instruction: install Node.js on PATH or set KIRO_AGENT_PATH to its executable. Credential failures are distinct; authenticate with kiro-cli login. Server/Node overrides do not relocate credentials. There is no silent Wrapper or v2 fallback.

KAS on native platforms

By default, Cyril discovers Kiro's extracted KAS bundles under <data dir>/kas. The stored-token fallback reads <data dir>/data.sqlite3. Native defaults are:

Platform Default Kiro data directory
Linux $XDG_DATA_HOME/kiro-cli when absolute; otherwise ~/.local/share/kiro-cli
macOS ~/Library/Application Support/kiro-cli
Native Windows %LOCALAPPDATA%\Kiro-Cli, falling back to Windows' local-app-data known folder when LOCALAPPDATA is unset or not absolute

KIRO_DATA_DIR overrides bundle discovery only. A value that is nonblank after checking whitespace is used with its original spelling, including leading or trailing spaces; Cyril does not append a kiro-cli or Kiro-Cli directory. An unset, empty, or whitespace-only value uses the native default above. Relative bundle roots resolve from Cyril's launch directory and stay bound there even when the agent runs in a different --cwd workspace. The credential store always keeps its native default location; neither KIRO_DATA_DIR nor KIRO_HOME relocates it.

XDG_DATA_HOME does not redirect macOS or native Windows KAS discovery. macOS also ignores LOCALAPPDATA; native Windows ignores HOME and does not require WSL. Explicit v2 inside WSL requires both engine and command selection, for example cyril --agent-engine v2 --agent-command wsl kiro-cli acp, plus a Kiro login inside WSL. Path translation follows the resolved agent location, not merely Cyril's host OS.

The macOS data-root correction from PR #167 is integrated. Live native macOS/Windows KAS qualification is not claimed here. See the ROADMAP parity table.

Kiro profile roots

Cyril uses one validated Kiro profile root for KAS settings, sidecar reads, global agent configurations and trust writes, and global host hooks. It defaults to <home>/.kiro; KIRO_HOME selects another profile without relocating KAS bundles or credentials.

KIRO_HOME has leading/trailing whitespace trimmed; an unset, empty, or whitespace-only value uses the default. A nonempty value must be absolute on Unix or rooted on Windows (drive-relative paths such as C:profile are rejected). Filesystem roots and paths equal to or containing the user's home are also rejected, including symlink aliases. Rejected overrides produce a warning and fall back to the default profile. Workspace agents still shadow globals, and workspace hooks still merge with global hooks.

MCP sign-in requests and connection errors (KAS)

Cyril handles KAS's _kiro/openExternalUrl callback by displaying a manual “Sign-in requested. Open in browser:” notice in the conversation. Cyril does not launch a browser or copy the URL to the clipboard. Printable quotes, apostrophes, backslashes and Unicode remain unchanged; terminal controls and Unicode bidirectional controls are shown as visible escape sequences.

Main/global MCP notices appear once in the main conversation and reveal its latest content even after browsing older history. A scoped connection error keeps its canonical child/workflow owner and adds one attributed, safely escaped main-conversation presentation; a later workflow claim does not present it again. This does not add a workflow view or a production focus-entry command/key.

Both existing main and focused conversation render branches reserve a fixed right-hand scrollbar column whenever at least two columns are available. Content preparation, wrapping, line counts, scrolling and painting use the same remaining width, so chrome cannot overwrite opaque URL/error text. A zero- or one-column viewport keeps all available content space and omits the scrollbar.

A {success:true} callback response acknowledges delivery for display, not a browser launch, completed authorization or a connected MCP server. Connection errors remain in the conversation independently of later notifications. The callback itself neither stores OAuth credentials nor implements a speculative completion notification. Source-bound qualification and its exact exercised scope are retained in the URL/error evidence, consumer evidence index and earlier repair package.

MCP server management (KAS)

/mcp opens the live MCP panel on KAS; v2 keeps its agent-provided command. The panel distinguishes an unknown inventory from a known empty one, shows connection/authentication/disabled/unrecognized states and explains governance or registry restrictions instead of treating every unavailable server as disconnected. Status updates replace the matching session's inventory without opening a modal unsolicited or changing another session's authority.

Reconnect and authenticate are explicit actions on the selected server. Their acknowledgement is not a connected state: only authoritative status updates resolve the attempt. Passive authorization URLs remain available for manual opening; Cyril does not launch a browser, bypass organization policy, or retry automatically.

Advertised tools and resource templates are inspectable. Prompt arguments are entered explicitly, and prompt/resource fetch results are read-only external data, including visible metadata for unsupported/nontext content. Fetching does not send a model prompt, run a tool, write a file or grant permission. Both MCP form and URL elicitation requests receive a valid decline and a visible attributed notice; interactive acceptance is not advertised.

The current MCP evidence index separates current local qualification and owned App/CLI consumer proof from historical failures and remaining native MCP, final ancestry, Caar and CI acceptance. CLI proof uses controlled termination after its assertions, not normal completion; its privacy claim covers complete McpUpdate summary lines, not every raw diagnostic prefix.

The separate u62f Windows readiness investigation remains open; a green CI run alone does not attribute its earlier failure. Native KAS/auth/host/platform, cache/performance and final MCP startup integration remain separate acceptance obligations, not claims established by the linked owned-consumer proof.

MCP startup waits and diagnostics

New-session creation has its own 90-second response budget. Session loading retains its separate 30-second budget, ordinary control calls retain 10 seconds, and prompt responses have no response-wait deadline. Slow MCP initialization does not stop ready, failure or manual sign-in notices from reaching the conversation while the new-session reply is pending.

On a genuine creation timeout, Cyril reports known server outcomes and explicit OAuth waits. It names silent servers only when the agent supplied a complete, authoritative inventory for the actual scope; otherwise it explicitly reports that no complete startup inventory was supplied. A connecting or pending server has already reported and is not automatically silent. A displayed sign-in URL still does not mean authorization or connection succeeded.

The current startup proof index records the source-complete implementation and local qualification separately from outstanding acceptance. This remains a draft: the latest native attempt stopped before initialize completed with the CLI reporting “not logged in”; native preview/inventory and 80/95-second held-response acceptance, CI and final conformance are not claimed. The CLI message alone does not diagnose global logout, credential expiry or a product defect.

Local memory runtime

Cyril's local memory runtime is disabled by default. It is separate from native KAS memory: upstream KAS defaults are preserved, with possible overlap and costs explicitly accepted. Cyril's /memory does not control native KAS memory. Enable the local runtime in Cyril's ~/.config/cyril/config.toml:

[memory]
enabled = true

Use /memory status to inspect runtime health, store schema versions, and how the current workspace is bound to a project (a Git common dir, or the canonical path outside Git).

Cyril lessons are explicit: the local runtime captures nothing from conversations. /memory teach <text> stores a project lesson (secrets such as password=…, ghp_…, AKIA…, sk-…, and PEM private keys are redacted before storage); /memory list and /memory inspect <id> read them back; /memory teach --replace <id> <text> supersedes one. Active lessons for the bound project are prepended, in a <CYRIL_LESSONS> block, to the first ordinary chat prompt of each fresh session and never shown in the transcript. Registered KAS agent-command prompts do not receive this block; if a command comes first, the lessons remain pending for the next ordinary chat prompt. If the memory runtime is unavailable the prompt goes out without them; chat never waits on memory.

Keyboard shortcuts

Key Action
Enter Send message
Shift+Enter Newline in input, when the terminal reports the Shift modifier
Tab Accept autocomplete suggestion
Esc Cancel current request
Ctrl+M Toggle mouse capture (off = copy mode)
Ctrl+C / Ctrl+Q Quit

Key presses and held-key repeats are handled; key releases are ignored to prevent duplicate input on Windows. Windows Alt+numpad character entry is unsupported because Crossterm reports those characters only on release. Paste the character instead; Unicode paste remains supported.

Themes and Markdown rendering

Use /theme to preview a bundled palette. Esc cancels the preview and restores the previous colors; Enter applies the selection for the current session. Committed and streaming Markdown, including syntax-highlighted code, use the selected palette without changing the preview/cancel/commit behavior.

Highlighting and Markdown caches identify their output by the concrete style inputs their renderers use, rather than every role in the full theme. Highlighting depends on the resolved syntax palette and fallback text color; Markdown also includes its own formatting colors and render width. Unrelated theme-role changes can reuse cached output, while changes to a used style remain isolated. Adding a rendering dependency to a style input automatically includes it in that input's derived identity.

Display budgets use the released ratatui-core 0.1.2 / ratatui-widgets 0.3.2 physical-cell widths for Markdown tables, code frames and affected popup/status text. Halfwidth sound marks occupy their painted columns; combining marks and original input bytes are preserved. Overlays evict whole painted graphemes intersecting their area at either edge without normalizing bytes, preserving disjoint cells and their styles. The input editor retains character wrapping and its character-cursor contract.

The current diff-reuse integration binding separates this source state and its outstanding qualification from the frozen earlier benchmark and consumer evidence.

Session history and viewport

The selected conversation retains prepared history across redraws and selects the visible physical rows when you browse older messages or return to following live output. Content revisions, viewport width and the selected semantic theme and color mode govern preparation; scrolling and height-only resizing reuse prepared content without changing its wrapping width.

For viewports wider than one column, the conversation reserves the rightmost column for its scrollbar in both follow and browse modes, even when no thumb is needed. At zero or one column it uses the full content width with no indicator.

The restored layout-owner evidence owns the current 506-input qualification, fresh complete 79-case benchmark and both-mode actual-App consumer. The earlier b5 integration and e3 retained-history packet keep their original identities; their native/benchmark results are historical, not substitutes for the restored D1 owner's fresh proof.

Under the supported-controls user approval, the fresh owned Ghostty/Xvfb truecolor/none pair uses separate launches. Main's current supported-native judgment is PASS with explicit limits, combining deterministic exactness, native-export correlation and reviewed PNG appearance. Both complete image sets were reviewed; not every obligation is an unqualified visual PASS. Four actual resize changes follow one baseline/no-op capture. Clipboard toasts obscure the lower-center composer/footer, and native exports omit initial unattached U+0301/U+200B; screenshots alone do not establish exact URL/scalar/RGB/process claims.

Existing state/render/root tests own focus/back; all-four-mode state/render coverage and same-process invalidation/performance evidence remain distinct. No native50,000-history, native child-focus/back, same-process native switching, unsampled-transient or internal-performance result is claimed. Capture-time pending-review receipts remain unchanged beside the later qualified judgment. Fresh isolated reconstruction and independent design comparison now record source-level conformance PASS: 16 changed production files map exactly once to nine approved rows, with no unresolved discrepancies. This review inspected actual restored working production, not the earlier c084 tree; it does not attest qualification, native/performance or CI outcomes. Restored-head CI/platform, Main's final Git/publication and combined acceptance remain PENDING under existing draft PR197, not PR187.

The ordinary-scroll verification disposition records Main's technical adoption of the coverage-preserving cadence instrument; the original burst FAIL and all remaining acceptance limits stay recorded.

Tool call history

Tool rows stay at their first observed position in the conversation. An update without a preceding start creates a row from the fields it supplies, including KAS Tool Load and Subagent Response updates. Later frames for the same call within a session merge into that row rather than adding duplicates, preserving the text before and after it. A new session can reuse a tool ID without changing the previous session's history.

For successful KAS commands, string-valued rawOutput.output is shown beneath the completed row, including completions that omit kind. Existing output formats keep precedence; non-string output values and the formatted message field are not used as substitutes.

Completed Write calls use the first diff for both the +added -removed summary and the inline body. An unchanged first diff shows no change counts, even if a later diff in the same call changed. The body keeps one context line and at most 20 rows, followed by an ellipsis when truncated.

The tracked call retains one theme-independent derivation for unchanged diff text. Redraws, resizes and palette changes reuse it while applying the current colors; replacing the selected source refreshes the summary and body together. Pending approval comparisons use the same diff renderer without requiring the tool to be completed.

Rejecting a tool with feedback (KAS)

Select the agent's one-time rejection option, then press r to Reject with reason. Enter on the option rejects without feedback, subject to the visible-context checks below.

The feedback editor accepts multiline paste. Ctrl+J inserts a newline, Enter submits, and Esc discards the draft and returns to the same approval choice without answering it. Explicitly reported Shift+Enter also inserts a newline, but many terminals send it as plain Enter; use Ctrl+J reliably. Empty or whitespace-only feedback sends a plain rejection.

Reasons are limited to 4,096 Unicode characters. An insertion that would exceed the limit is refused in full, never silently truncated. An overflow notice is shown when space permits; cramped prompts reserve the target path and editor ahead of that notice. The editor does not alter the chat draft or a queued approval. KAS 0.66.8 and newer receive the reason through permission-response metadata; older KAS may ignore it. The action is not shown for v2 or always-reject options.

Before an approval can answer, its selected choice or feedback editor must be visible, together with an identifiable target path when one is present. If a small window hides that context, Enter leaves the request pending: enlarge the window and continue without losing the feedback draft or chat draft. In cramped reason prompts, the target path takes priority over the character limit and overflow notice. A fully visible target containing a literal … (such as /tmp/report…) or consisting of one visible grapheme (such as a) remains actionable; a genuinely shortened path must retain an identifiable tail.

Reviewing shell commands (KAS)

Shell approvals show the actual joined rawInput.command, not just the tool title. Cyril also shows Cwd: when KAS supplied a string rawInput.cwd; it does not infer a working directory from the workspace or consent metadata. Missing or malformed command data keeps the existing unavailable preview.

The command is not subject to the workflow comparison's 600-code-point cap. Long commands wrap into a bounded, scrollable body of at most 12 rows, including navigation status. Use the preview keys in the table below to inspect every pipeline stage and the supplied cwd before answering. Paging preserves the selected option, rejection draft, and chat draft.

This is a display-only projection: terminal controls and bidi-formatting characters become spaces, while meaningful spaces, newlines, and safe grapheme clusters are preserved. The raw command and permission semantics are unchanged. The preview is not a shell parser or an execution sandbox.

Confirmation additionally requires actual command text in the current drawn viewport, alongside the selected option or feedback editor and any required target path. A cwd-only tail, label-only body, or tiny window cannot answer with Enter. A fully drawn meaningful one-grapheme command remains actionable. Scroll back to command text or enlarge the window; no approval is sent by preview navigation or resizing.

Qualification and its real-KAS versus controlled-peer boundaries are recorded in the cyril-xavt verification receipt.

Reviewing workflow comparisons (KAS)

Workflow approvals keep the complete retained workflow comparison, not a full file diff. Before wrapping, Cyril replaces control and bidi-formatting characters with spaces, collapses whitespace, trims the ends, and retains at most 600 Unicode code points, including the comparison prefix. If more text existed, it appends ...; scrolling cannot recover text beyond that cap.

The logical wrapped step tree has its own 12-row budget, including any ...N more lines omission marker. The entire cleaned comparison is appended after that bounded tree; comparison length does not consume the tree budget. This retains a bounded tree prefix, not every step at every width, and paging cannot recover omitted tree details.

Both share the unchanged body viewport of at most 12 visible rows, including its navigation status. A long comparison is paged rather than cut after four rows. The target path, approval choice or reason editor, and chat input stay outside the scrolling body.

While choosing an approval or editing a rejection reason:

Key Preview action
PageUp / PageDown (no modifiers) Move by the current visible content page
Shift+Up / Shift+Down (Shift only) Move by one row
Shift+Home / Shift+End (Shift only) Jump to the first / last window

Terminal shortcuts may intercept these keys before Cyril receives them. In Ghostty 1.3.1, the observed defaults consumed Shift+Home/Shift+End; the configured native proof forwarded them with this example configuration:

keybind = shift+home=unbind
keybind = shift+end=unbind

Other modifier combinations are not preview shortcuts. Ordinary Up/Down still select an approval or move in the reason editor; ordinary Home/End retain their editor behavior. Preview navigation does not answer the request. Entering or leaving the reason editor preserves the preview position without altering the reason or chat draft; the next queued request starts at the top with its own fresh reason editor.

Resizing rewraps the retained text and clamps the current window. The next navigation key uses the new visible capacity. When only one content row fits, the navigation hint moves to the popup border rather than taking that row. With no usable body-text cells there is no scrolling target: enlarge the window. The visible-context confirmation checks described above still apply.

These limits are distinct: a workflow-plan Text/raw-text fallback has 12 total rows including any omission marker; an ordinary Text/raw-text preview keeps five content rows plus its marker; an ordinary Diff preview keeps 20 diff lines plus its marker. Workflow comparison pagination does not expand ordinary diffs or their cached content.

The independent-tree cutover, final repaired PR #188 content and retained-path window reconciliation now have current 497-input workspace qualification and one Main-accepted complete 24-specimen composer-geometry owned-consumer PASS. The verification record is the sole actual-result owner: it separates current fresh local gates from historical 488-input proof, preserves failures and causal/exit-code limits, and owns review applicability. Public receipts are mapped by the current evidence index into the current evidence archive. The current ordinary source review found no actionable defects (correct, confidence 0.94) in the assembled five-file main-relative approval delta and affected cache/chat, key-dispatch and workflow interactions; this is static review, not runtime or CI acceptance. The workspace descriptor is not a Git tree or committed head. Main still owns completion of the held current-main merge, final-head binding, affected proof retention or requalification, final-head review applicability and publication. Remaining exact CI, platform/native and common-provider obligations stay unwaived; ordinary local auth-fixture coverage is not live Kiro, real-account or native-glyph acceptance.

Host hooks (KAS)

KAS is the default engine in normal production builds. In the default KAS hooks mode, [agent] kas_hooks = "host", Cyril loads command hooks from the workspace's .kiro/hooks/*.json and ~/.kiro/hooks/*.json.

For example, a .kiro/hooks/shell.json hook can select tools tagged shell:

{
  "version": "v1",
  "hooks": [{
    "name": "shell-hook",
    "trigger": "PreToolUse",
    "matcher": "shell",
    "action": {"type": "command", "command": "echo shell hook"}
  }]
}

For PreToolUse and PostToolUse, matching follows KAS's selector-plus-ID-regex rules:

  • Matchers without any of \ ^ $ ( ) [ ] { } | + act as tool selectors. Literals match exact supplied tool IDs or tags, such as shell, read, @builtin, and @mcp; source tags are not inferred from ID prefixes. * matches zero or more characters and ? matches one, across the whole ID or tag. Other characters are literal in these wildcard patterns, and * alone is valid.
  • Exact ID selectors also recognize execute_bash/execute_pwsh and control_bash_process/control_pwsh_process as aliases in both directions.
  • Independently, a valid matcher retains legacy unanchored Rust-regex matching against the tool ID only. For example, fs_.* still matches fs_write. Regex-only forms such as ^mcp_.*$ never match tags.

On these two triggers a configured matcher requires a tool ID, even if tags are supplied. Missing or empty tags leave ID matching available. A malformed toolTags value is logged and its non-string members are dropped; the request is still answered, because KAS runs none of a tool's hooks when the list call fails.

SessionStart, UserPromptSubmit and Stop have no tool to test, so, as in KAS, a valid matcher on them selects every invocation. On any trigger, an omitted or empty matcher selects every invocation, and an invalid matcher is warned about at load and never matches. Matchers compile as Rust regexes, so JavaScript-only syntax that KAS accepts, such as lookaround or backreferences, is invalid here.

Tag matching does not change hook execution authority or approval behavior. A pre-tool command hook exiting with code 2 blocks the tool. Other triggers keep their existing execution behavior. kas_hooks = "kas" remains the separate KAS-owned registry/execution mode; this Host-mode repair does not change it.

Reviewing a branch (KAS)

/review, run from the repository root, reviews changes with Kiro's workflow engine. The form picks the Review target with ←/→:

  • auto — HEAD against its upstream, main or master, plus uncommitted changes;
  • vs <branch> — what a PR would contain, <branch>...HEAD (Tab moves to the branch field, ←/→ picks the branch);
  • uncommitted changes — HEAD against the working tree;
  • the commit at HEAD — that commit alone.

Every target ends at the checked-out HEAD, because the reviewers read the working tree. The scope is a row of checkboxes: one per top-level directory the diff touches, plus each touched root file, all checked unless the repository's [review] scope says otherwise. Tab moves between the target, the base branch and the paths; on the paths ←/→ moves and Space toggles, with the file count following. Unchecking every path is refused ("select at least one path"). The form also shows the expected cost (about 50–65 model sessions and 40–50 minutes) and what the run may do. Esc backs out without writing anything; Enter starts it. An empty diff for the chosen target and paths stops there with "nothing to review".

Arguments prefill the form, which still asks for Enter: /review [auto | uncommitted | head | base [<branch>]] [-- <path>…]. The cursor starts on the first field the arguments left out.

Starting installs the cyril-review recipe and its four agents into ~/.kiro/workflows and ~/.kiro/agents, rewriting only files that differ. It refuses if the workspace defines an agent with one of those names. Results go to a new .code-review/<YYYYMMDD-HHMMSS>-<hex>/ directory, whose parent ignores itself in git. While the review runs, cyril answers its step sessions' permission requests itself:

  • reads only inside the workspace;
  • writes only inside the run directory;
  • shell only for the run's own cyril crtool step calls.

Everything else is denied, without a prompt, and listed in denied.log.

/review resume continues a failed or paused run, also after restarting cyril: with no argument the newest run the agent reports as failed or paused, or a run directory name or workflow id. It shows the stored run read-only and continues after Enter (failed runs are retried, paused runs resumed). It never gathers or checks again, and refuses a run started by a different cyril binary or version.

/review cancel stops the review at any phase. Before the workflow exists it abandons the launch, killing a running check; once the workflow exists it withdraws the run's authorization at once and cancels the workflow, and says whether the agent confirmed it.

A repository can commit settings for every reviewer in .cyril/config.toml:

[review]
check_cmd = "cargo clippy --workspace --all-targets -- -D warnings"
check_timeout_s = 900          # default 1800
context_file = "docs/review-context.md"   # or: context = "..."
scope = ["crates", "docs"]     # default: everything; no spaces in paths

The settings are read fresh on every /review. An unknown key, a wrong type, setting both context and context_file, or a path outside the repository refuses the review with an error naming the problem. The form shows the check as will run: <command>, or no check configured; it never guesses a check. The check runs once, after gathering and before the workflow starts ("review: running check…"). A failing or timed-out check is recorded for the reviewers and the review goes on. A command that cannot start stops the review before any workflow exists. Esc during the check abandons the launch and kills the check command's own process (not processes it started in the background). context_file is read when the review starts and its contents go to the verifiers. The chat stays usable throughout, and /workflow status <id> shows progress. When the run ends, one message lists the verdict counts, the top ten findings and the path to report.md.

Slash commands

Local commands (handled by Cyril):

Command Description
/help Show available commands
/new Start a new session
/load <id> Load a session by ID
/clear Clear the chat
/mode <id> Switch agent mode
/model [id] Switch model (opens picker if no ID given)
/theme Preview a palette; Enter applies it for this session, Esc cancels
/memory status Show local memory runtime health, store versions, and project binding
/memory teach <text> Store an explicit lesson for the bound project
/memory teach --replace <id> <text> Supersede one lesson with new text
/memory list List active project lessons (newest first)
/memory inspect <id> Show one lesson, active or replaced
/review Review changes with a multi-agent workflow (KAS); /review resume, /review cancel
/mcp Inspect/manage MCP servers and read-only previews (KAS; v2 retains its agent command)
/quit Quit

V2 agent commands (forwarded to Kiro via ACP; examples, not a KAS parity guarantee):

Command Description
/agent Switch agent (picker)
/chat Resume a previous session (picker)
/compact Compact conversation history
/context Show context/token usage breakdown
/knowledge Manage knowledge bases
/mcp Show configured MCP servers
/plan Switch to planning agent
/prompts Select from available prompts (picker)
/tools Show available agent tools
/usage Show billing and usage info

V2 retains its existing command execution and selection-option requests. Each advertisement is the agent's complete command list: a command it no longer lists is withdrawn from autocomplete and dispatch, and a re-advertised command takes its new behaviour. Advertised commands never replace Cyril's builtins.

KAS agent commands: when KAS advertises a custom agent, skill or saved prompt (custom-agent, skill, prompt), Cyril registers it for ordinary ACP prompt submission. For example, if advertised, /general-task-execution "inspect foo" starts a prompt turn rather than a V2 command RPC. The invocation is sent as exactly one original text block, preserving the slash, quotes, and whitespace as typed, including commands with no arguments. As in KAS, the command name ends at the first whitespace of any kind, so a tab or newline after the name still invokes the command.

Recognized KAS command prompts receive no automatic local lesson or @file expansion text: those additions would change KAS's command arguments. First-prompt local lessons remain pending for ordinary chat instead. Ordinary chat and unregistered slash fallback keep their existing context/file behavior. If a turn is already busy, the invocation follows normal steering behavior.

Unsupported KAS command types are not registered. The exact workflow commands workflow-run, workflow-status, workflow-cancel, and workflow-resume also remain excluded; Cyril retains its own workflow control plane.

Project structure

crates/
  cyril/          # TUI application (binary)
  cyril-core/     # Protocol logic, path translation, session state
  cyril-review/   # Native crtool (gather, facts) and the review check command
docs/
  kiro-acp-protocol.md  # Comprehensive Kiro ACP protocol reference

Core library consumers can construct a safe executable prefix with cyril_core::review::CrtoolPrefix::current(dialect). Obtain the dialect from BridgeHandle::review_shell() before splitting a KAS bridge handle; it reflects the host shell already resolved for that bridge, not an OS-based guess. The constructor canonicalizes the executable, normalizes Windows drive/UNC spelling, and refuses shell-active characters rather than attempting fallback quoting. This API constructs a command prefix; it does not add a /review command or execute a review.

The internal cyril crtool subcommands (gather, facts, merge, shard, ballots, collate, finalize, comments) are the native crtool that the review workflow runs. They need Git on PATH, run from the repository root (anywhere else is refused), and are hidden from --help. docs/crtool-contract.md specifies their outputs: the same text the Python crtool.py gives the models, value-equal JSON, and the same exit codes (3 for an empty diff, 2 for any other error). Every run is stamped with the cyril version, and a run stamped by another version is refused.

cyril_review::ASSETS embeds the canonical review workflow (cyril-review.workflow.json) and its four cyril-review-* agents, and cyril_review::read_findings reads a finished run's findings.json, keeping a missing file and a corrupt one as distinct errors. The experiment's code-review-max recipe and cr-* agents are generated from the same sources by experiments/code-review-workflow/build_recipe.py.

cyril_review::run_check runs a repository's check command once for a review, on the caller's Tokio runtime, and writes facts/diagnostics.txt and facts/diagnostics-raw.txt. It is not a crtool subcommand. It reports clean, failed (with the exit code), timed out or cancelled; a timeout or cancellation kills the command and keeps the output collected so far.

License

MIT

About

An experimental TUI for the Agent Client Protocol ecosystem

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages