Skip to content
Crystal CodeDocs
ENEnglish

Assemblies and namespaces

The terminal host and the only executable. Owns CLI commands, the terminal host loop (alternate screen, key loop, Ctrl+C), the headless crystal run entry, and the projection of engine events onto CrystalCode.Display. It also owns the terminal surfaces for the engine’s prompts: approval cards and keys, the question overlay, and the saved-session picker. crystal run supplies its own observer, approval prompt, question prompt, and session chooser. It does not own session logic, approval policy, prompts, storage, or tools, and it does not own the frame painter, composer buffer, or transcript log. crystal version and crystal --version print the build identity as plain text and exit. The build section is Crystal Code (this repository’s commit), Crystal (the Crystal library repository’s commit), SDK (the .NET SDK that compiled the executable), and Configuration (the MSBuild configuration stamped on the executable). A blank line separates it from the host section: Runtime (the shared framework hosting the process) and OS (RuntimeInformation.OSDescription plus the operating-system architecture). A line is omitted when that value was not recorded. An empty section is omitted, including the blank line. Commits are the 40-character source revision the SDK appends to the assembly informational version. The assembly version placeholder is left unread. SDK is the NETCoreSdkVersion assembly metadata stamped by Directory.Build.targets for projects in this repository. Configuration is AssemblyConfigurationAttribute. The host passes RuntimeInformation.FrameworkDescription, OSDescription, and OSArchitecture. The description is kept whole and the architecture is appended in lower case. The report does not start a process. The outbound User-Agent stays Crystal Code.

The contract external dotnet tools may reference when a call needs host facts. It defines ToolHostContext, IHostTool, IHostMultimodalTool, and the optional IHostToolApprovalClassifier. ToolHostContext carries the workspace root, the session id, the approval mode, and the extension’s global and project runtime-data directories. An approval classifier receives the model call and a context carrying the workspace root, session id, and approval mode before invocation and returns the call’s risk and authority; the engine merges that proposal with its own classification and it can only raise risk or authority, never lower them. ClassifyApprovalAsync receives user/turn cancellation and an independent two-second timeout. Its entry is dispatched on a worker so blocking before returning a task does not block approval. After timeout or cancellation that classifier instance stays disabled until the external catalog reloads; late work is observed and its result is discarded. In-process code still cannot be forcibly terminated. Null results, exceptions, timeouts, disabled instances, and either invalid classification field fail closed to Privileged + PrivilegedEscalation, merged with the host floor. User/turn cancellation propagates without approval. Classification summaries are authored by the host; a tool’s Summary is not sent to the operator or the reviewer. The engine awaits IApprovalClassifier ClassifyAsync; its default implementation adapts existing synchronous plugin classifiers. Synchronous external classification fails closed when a tool has an asynchronous pre-classifier. It references only Crystal and Crystal.Tools. It does not reference the engine, the display, the executable, or a terminal library. Tools that implement only Crystal’s ITool or IMultimodalTool do not reference it. There is no paired test project; dispatch, approval classification, and load-context identity are tested in CrystalCode.Engine.Tests.

The contract a disk plugin references. It defines IPlugin, contribution lists, the hook and raw hook interfaces, the hook context types, the model and environment views, and IPluginDataDirectory for an extension’s runtime data directories. It references only Crystal and Crystal.Tools. It does not reference the engine, the display, the executable, or a terminal library. There is no paired test project; discovery, the load context, and hook order are tested in CrystalCode.Engine.Tests.

The front-end-neutral class library. Owns session and turn execution, Plan/Work catalogs, approval policy, context compaction, ~/.crystal storage, built-in coding tools, operator tool sets, operator plugins, prompts, in-process plugin contracts, slash commands, and the event stream a front end observes. It never reads a key, writes to the console, or paints. See Engine contract.

Engine tests: workspace fencing, approval classification, compaction selection, session serialization, command behavior, external tool sets, a headless session driven by a scripted model, and the dependency guard. Does not reference the host executable.

The terminal UI library. Owns the alternate-screen frame, input routing, composer, transcript viewport, markdown paint, and queue card. Spectre widgets are rasterized into frame rows. AnsiConsole.Live is not the session shell. Terminal.Gui is a parked package reference for supply-chain review and is not called. The host loop stays in this assembly’s shell types; a later migration to Terminal.Gui would replace that loop inside this project only.

Committed transcript entries may carry a Spectre IRenderable alongside a plain-text fallback. The display engine measures the widget at the current terminal width when its cache is rebuilt, so tables, grids, panels, and other high-level components participate in resize, scrollback, and repaint like text. Non-interactive output renders the same widget directly. The shell progress region owns transient indeterminate progress and elapsed time; static command reports remain committed transcript widgets.

Display tests: layout, chrome, input decoder, scroll policy, composer, paint, transcript log, queue card, and the dependency guard. Does not reference the host executable.

Model adapters. Each adapter implements IChatClient and, when the provider can stream, IStreamingChatClient. Image-capable adapters additionally implement IStreamingMultimodalChatClient. Provider options, wire DTOs, and transport exceptions stay in this assembly. No tools, no UI, no home-directory layout.

Terminal host tests: session renderer, event-to-slash-option mapping, status and tool-list widgets, tool-call and progress text, transcript replay, the session picker, approval cards and keys, the image-marker contract with Display, and crystal run against a scripted model.

Provider adapter tests. One folder per vendor, mirroring CrystalCode.Providers. Does not reference the host executable.

Root identifier is CrystalCode, matching the existing solution. Folder path under a project root equals the namespace after the project name, so engine namespaces read CrystalCode.Engine.Sessions and so on.

CrystalCode (executable):

Folder Owns
Commands Spectre.Console.Cli commands. Bare crystal stays the terminal. space opens the operator space under the data directory. run is the headless task command. version prints the build identity
Run Unattended front end for crystal run: plain-text or JSON-lines log, denied operator prompts, dismissed questions, process-only setting overrides, and exit codes
Terminal Host loop, event projection, session renderer, status and tool-list widgets, progress and tool-call text, transcript replay, session picker, question overlay, slash-option mapping
Terminal/Approvals Approval prompt, card, diff preview, and keys

CrystalCode.Engine (namespaces below are relative to CrystalCode.Engine):

Folder Owns
Configuration Loaded options, defaults, thinking and mode labels, display-case helpers
Events Immutable session events, observer interface, chrome and preference snapshots
Home ~/.crystal paths and file I/O; DiskSettingsStore is the default config.json and providers.json implementation
Home/Interfaces Configuration persistence contracts such as ISettingsStore
Sessions Session (CodingSession), transcript, ledger, streaming turn, slash commands and completions, chat client lifetime, status, usage, and tool-list text, front-end contract (SessionFrontEnd, ISessionChooser)
Approvals Risk, authority, grants, policy, review transcript
Approvals/Interfaces Prompt and reviewer contracts
Compaction Window accounting and summary substitution
Tools Workspace fence and built-in ITool types
Tools/External Operator tool sets (tools.json), exec and isolated ITool / IMultimodalTool loaders, and host-context dispatch
Prompts Caller-owned system text. Built-in Work and Plan identify the assistant as Crystal Code
Skills OpenCode-compatible SKILL.md discovery and catalog
Plugins In-process registry and built-in contributions
Plugins/Interfaces Contribution contracts
Plugins/Providers Built-in DeepSeek, OpenAI-compatible, Responses, Anthropic, Gemini, and Ollama client factories
Version Build identity for crystal version: product commit, Crystal commit, compiling SDK, compile configuration, host runtime, and host operating system. Two plain-text sections

CrystalCode.Display:

Folder Owns
Input Decode ReadKey bursts into keys, paste, and wheel events
Shell Alternate screen, painter, layout, key burst, scroll policy, status and progress chrome
Composer Prompt buffer, keys, slash picker
Cards Queue overlay
Transcript Viewport log, role cards, sequential fallback
Paint Markup, markdown, theme, wrapping

Quick prompts keep the same dependency direction: Engine Home owns QuickPromptStore, bounded UTF-8 file reads, validation, and immutable catalog values; Display Composer owns the isolated quick-prompt picker and preview. The terminal maps catalog values, routes F6 only outside modal prompts, and inserts Fill at the current composer cursor. Send uses CodingSession.SubmitQuickPromptAsync, bypasses slash parsing, and never consumes pending or composer draft images. Ordinary queued submissions bind pending images at enqueue time so later quick sends cannot steal them. Quick queue entries do not request cancellation after tools; explicit queue release and ordinary follow-ups retain their existing interrupt behavior.

Provider types live under CrystalCode.Providers plus one folder per wire family (DeepSeek, OpenAI, Responses, Anthropic, Gemini, and Ollama). Shared OpenAI-compatible Chat Completions request and stream parsing lives in Compatible. Shared direct-HTTP lifecycle and SSE framing for Responses and Messages, Gemini GenerateContent, and Ollama Chat lives in Protocol; each wire codec still owns its request and event semantics. Gemini uses SSE and replays signed response parts so function calls retain thought signatures. Opaque reasoning state is provider-owned: an adapter replays state only in its own format. A block it cannot replay keeps its readable text instead of failing (assistant text, or the protocol’s readable reasoning field where it has one). Ollama uses JSON lines and native tool_name results. Anthropic Messages marks prompt-cache breakpoints (cache_control: ephemeral) on the last system block and the last message content block of every request, so the tool definitions, the system prompt, and the conversation prefix written by the previous round are read from the provider’s prompt cache; the multimodal codec keeps the message breakpoint when image expansion splits a marked text block. Other adapters rely on their providers’ automatic prefix caching and send no cache directives. Chat Completions, Responses, Anthropic Messages, Gemini, and Ollama map an error payload, an event whose type is error, and a JSON body returned where an event stream or JSON lines were expected to provider faults carrying the provider code and message. Gemini uses error.status when error.code is a number. A non-streaming JSON response with an error payload maps the same way even when the HTTP status is 200. Framed Responses and Anthropic error events keep the provider message as the fault text. Session retry then reports the cause instead of treating the truncated stream as retryable. Outbound chat requests send a constant User-Agent of Crystal Code with no version. Provider SDKs are not used.