diff --git a/specs/terminal-gui-cli-v2.md b/specs/terminal-gui-cli-v2.md
new file mode 100644
index 0000000..7f1609a
--- /dev/null
+++ b/specs/terminal-gui-cli-v2.md
@@ -0,0 +1,985 @@
+# Terminal.Gui.Cli Library Specification
+
+> Definitive implementation spec for `gui-cs/Terminal.Gui.cli`: a `Terminal.Gui` library that lets applications expose Views as scriptable CLI commands with typed JSON output, POSIX exit codes, and AI-agent discoverability.
+
+## 0. Repo Identity
+
+`gui-cs/Terminal.Gui.cli` is a new gui-cs library repo for the NuGet package `Terminal.Gui.Cli`. This repo/package casing split is normative: the repo slug uses lowercase `cli`; the package, assembly, and namespace use the `Terminal.Gui.Cli` identifier with the final segment spelled `Cli`. Before writing library code, scaffold the repo by cloning the structure and maintenance model of `gui-cs/Editor` (the Terminal.Gui.Editor repo):
+
+- `develop` is the integration branch; `main` is the stable release branch.
+- Copy and adapt `specs/constitution.md`; it is the highest-authority engineering document.
+- Copy and adapt `CLAUDE.md`, `AGENTS.md`, `.editorconfig`, `Directory.Build.props`, `Directory.Build.targets`, solution naming, and test conventions.
+- Copy and adapt CI/CD workflows from Editor: `ci.yml`, `release.yml`, `prepare-release.yml`, and `finalize-release.yml`.
+- Keep Editor's zero-warning policy, xUnit v3 executable tests, develop prerelease versioning, main/tag stable releases, and release-PR workflow.
+
+Repository/package identity:
+
+| Concern | Value |
+|---------|-------|
+| GitHub repo | `gui-cs/Terminal.Gui.cli` |
+| Package ID | `Terminal.Gui.Cli` |
+| Root namespace | `Terminal.Gui.Cli` |
+| Primary TFM | `net10.0` |
+| Dependencies | `Terminal.Gui` only |
+| License | MIT |
+| AOT | `true` |
+
+## 1. Problem Statement
+
+Many Terminal.Gui apps need the same hosting layer: parse command-line args, resolve an alias, initialize Terminal.Gui, run a View, serialize a typed result, emit predictable exit codes, and provide enough self-description for humans and AI agents. `clet` proved this pattern with 18 commands and 500+ tests, but the infrastructure should be a reusable library rather than app-specific hosting code.
+
+`Terminal.Gui.Cli` supplies that reusable layer. A consumer creates a `CliHost`, registers command instances, and calls `RunAsync`. The library owns framework flags, Terminal.Gui lifecycle, result writing, JSON envelope shape, exit-code mapping, help metadata, and OpenCLI introspection. Consumers own their concrete commands and any domain policies.
+
+## 2. Design Principles
+
+1. **The library owns Terminal.Gui lifecycle.** Commands receive an initialized `IApplication`; commands never call `Application.Create()`, `app.Init()`, or `Application.Init()`.
+2. **No reflection; NativeAOT from day one.** Commands self-describe via properties. JSON serialization uses source generation. Registration is explicit.
+3. **Registry stores instances.** Commands are constructed by the consumer and registered as instances, supporting DI, conditional registration, and plugins.
+4. **Three-tier option model.** Framework flags are hard-coded in `ArgParser`; consumer globals are declared in `CliHostOptions.GlobalOptions` and land in `CommandRunOptions.Extensions`; per-command options are declared by `CommandOptionDescriptor` and land in `CommandRunOptions.CommandOptions`.
+5. **JSON envelope is the stable wire contract.** `{ schemaVersion, status, value?, code?, message? }` is controlled by the library and append-only within schema v1.
+6. **Exit codes are library-controlled.** Commands return `CommandResult`; the library maps status/error code to POSIX-conventional process codes.
+7. **AI discoverability is first-class.** `llms.txt`, `agent-guide`, and `--opencli` are normative parts of the model.
+8. **No general-purpose CLI ambitions.** This library is for Terminal.Gui-backed interactive commands, not a replacement for System.CommandLine.
+
+## 3. Engineering Constitution
+
+`specs/constitution.md`, cloned and adapted from `gui-cs/Editor`, is canonical. It must include these Terminal.Gui.Cli-specific rules:
+
+| Rule | Requirement |
+|------|-------------|
+| C1 | Only `CliHost` calls Terminal.Gui lifecycle APIs and disposes `IApplication`. |
+| C2 | Public API changes require this spec to be updated in the same PR. |
+| C3 | No reflection-based command discovery or runtime code generation. |
+| C4 | Source-generated JSON only for library-owned JSON types. |
+| C5 | All correctness tests run in parallel unless a test explicitly opts out for a process-global. |
+| C6 | Commands never call `Environment.Exit`; return `CommandResult`. |
+| C7 | Schema v1 is append-only within library major version 1.x. |
+| C8 | Zero warnings in Debug and Release. |
+
+The constitution must also document two narrow file-layout exceptions proven by the clet prototype in PR #176: `CommandResult` and `CommandResult` live together in `CommandResult.cs`, and `ICliCommand` lives in `ICliCommandGeneric.cs`. Do not use angle brackets in filenames: `<` and `>` are invalid on Windows and awkward in POSIX shells. The `Generic` suffix is the established convention for this single generic-interface companion file.
+
+## 4. Public API Surface
+
+All public API lives in namespace `Terminal.Gui.Cli`. XML comments below are normative.
+
+### 4.1 Command model
+
+```csharp
+namespace Terminal.Gui.Cli;
+
+/// The two kinds of CLI commands the library knows about.
+public enum CommandKind
+{
+ /// An interactive command that returns a typed value.
+ Input,
+
+ /// An interactive or headless command that does not return a typed result value.
+ Viewer
+}
+
+/// Outcome status of a command run.
+public enum CommandStatus
+{
+ /// The command completed successfully.
+ Ok,
+
+ /// The user or caller cancelled the command.
+ Cancelled,
+
+ /// The command failed.
+ Error,
+
+ /// The command completed but produced no result.
+ NoResult
+}
+
+/// Metadata descriptor for a per-command option.
+public sealed record CommandOptionDescriptor (
+ string Name,
+ string? ShortName,
+ Type ValueType,
+ string Description,
+ bool Required,
+ string? DefaultValue);
+
+/// Non-generic result for dispatch and output formatting.
+public readonly record struct CommandResult (
+ CommandStatus Status,
+ object? Value,
+ string? ErrorCode,
+ string? ErrorMessage);
+
+/// Typed result returned by input commands.
+public readonly record struct CommandResult (
+ CommandStatus Status,
+ T? Value,
+ string? ErrorCode,
+ string? ErrorMessage);
+```
+
+`CommandResult` and `CommandResult` must remain readonly record structs in one file because `ICliCommand` bridges the typed result to the non-generic dispatch result by copying the same status, value, error code, and error message fields.
+
+### 4.2 Command interfaces
+
+```csharp
+using Terminal.Gui.App;
+
+namespace Terminal.Gui.Cli;
+
+/// A CLI command backed by Terminal.Gui. Implemented by consumer apps and built-ins.
+public interface ICliCommand
+{
+ /// The canonical alias shown in help and OpenCLI output.
+ string PrimaryAlias { get; }
+
+ /// All aliases that resolve to this command. Must include .
+ IReadOnlyList Aliases { get; }
+
+ /// Human-readable one-line command description.
+ string Description { get; }
+
+ /// The command kind.
+ CommandKind Kind { get; }
+
+ /// The CLR type of the value written to the JSON envelope, or .
+ Type ResultType { get; }
+
+ /// Per-command options accepted by this command.
+ IReadOnlyList Options { get; }
+
+ /// Whether this command consumes positional arguments.
+ bool AcceptsPositionalArgs => false;
+
+ ///
+ /// Validates the --initial value before Terminal.Gui starts. The default permits any value;
+ /// commands override this method when they need command-specific validation.
+ ///
+ bool TryValidateInitial (string initial, CommandRunOptions options) => true;
+
+ /// Runs the command after the host has initialized Terminal.Gui.
+ Task RunAsync (
+ IApplication app,
+ string? initial,
+ CommandRunOptions options,
+ CancellationToken cancellationToken);
+}
+
+/// Typed command that returns a value.
+public interface ICliCommand : ICliCommand
+{
+ /// Runs the command and returns a typed result.
+ new Task> RunAsync (
+ IApplication app,
+ string? initial,
+ CommandRunOptions options,
+ CancellationToken cancellationToken);
+
+ async Task ICliCommand.RunAsync (
+ IApplication app,
+ string? initial,
+ CommandRunOptions options,
+ CancellationToken cancellationToken)
+ {
+ CommandResult result = await RunAsync (app, initial, options, cancellationToken);
+ return new (result.Status, result.Value, result.ErrorCode, result.ErrorMessage);
+ }
+}
+
+///
+/// Viewer command. Viewers can be interactive TUI commands or headless content commands,
+/// but they are invoked through the viewer path and default to fullscreen when a TUI is used.
+///
+public interface IViewerCommand : ICliCommand
+{
+ ///
+ /// Renders content to stdout without launching the TUI. Called when --cat is set.
+ /// Return null to indicate --cat is not supported and normal TUI dispatch should continue.
+ ///
+ Task RenderCatAsync (
+ CommandRunOptions options,
+ TextWriter stdout,
+ CancellationToken cancellationToken) => Task.FromResult (null);
+}
+```
+
+### 4.3 Registry
+
+```csharp
+namespace Terminal.Gui.Cli;
+
+/// Manages alias-to-command lookup.
+public interface ICommandRegistry
+{
+ /// Registers a command instance.
+ ///
+ /// Thrown when PrimaryAlias is not present in Aliases, or any alias is already registered.
+ ///
+ void Register (ICliCommand command);
+
+ /// Resolves an alias case-insensitively.
+ bool TryResolve (string alias, out ICliCommand? command);
+
+ /// All registered commands in registration order.
+ IReadOnlyCollection All { get; }
+}
+
+/// Default case-insensitive, duplicate-rejecting command registry.
+public sealed class CommandRegistry : ICommandRegistry
+{
+ ///
+ public void Register (ICliCommand command);
+
+ ///
+ public bool TryResolve (string alias, out ICliCommand? command);
+
+ ///
+ public IReadOnlyCollection All { get; }
+}
+```
+
+`PrimaryAlias` matching is case-insensitive. Duplicate alias detection is case-insensitive. Registration failure throws `InvalidOperationException`.
+
+### 4.4 Run options and global option descriptors
+
+```csharp
+namespace Terminal.Gui.Cli;
+
+/// Parsed options bag passed to commands.
+public sealed class CommandRunOptions
+{
+ /// Pre-fill value for the View.
+ public string? Initial { get; init; }
+
+ /// Title override for TUI chrome. --prompt/-p is an alias for --title/-t.
+ public string? Title { get; init; }
+
+ /// Whether to emit the JSON envelope instead of plain text.
+ public bool JsonOutput { get; init; }
+
+ /// Cancel after this duration.
+ public TimeSpan? Timeout { get; init; }
+
+ /// Force fullscreen. Input commands otherwise default to inline.
+ public bool Fullscreen { get; init; }
+
+ /// Render supported viewer content to stdout instead of launching the TUI.
+ public bool Cat { get; init; }
+
+ /// Write successful command output to this file instead of stdout.
+ public string? OutputPath { get; init; }
+
+ /// Constrain inline height.
+ public int? Rows { get; init; }
+
+ /// Positional arguments after the alias.
+ public IReadOnlyList Arguments { get; init; } = [];
+
+ /// Per-command option values keyed by long option name without dashes.
+ public IReadOnlyDictionary CommandOptions { get; init; }
+ = new Dictionary ();
+
+ /// Consumer-registered global option values keyed by long option name without dashes.
+ public IReadOnlyDictionary> Extensions { get; init; }
+ = new Dictionary> ();
+
+ /// Gets the last value for a single-value consumer extension, parsed by .
+ public T? GetExtension (string key, Func parser, T? defaultValue = default);
+
+ /// Gets all values for a repeatable consumer extension.
+ public IReadOnlyList GetExtensionList (string key);
+
+ /// Returns true when a consumer extension flag or value is present.
+ public bool HasExtension (string key);
+}
+
+/// Describes a consumer-defined global option.
+public sealed record GlobalOptionDescriptor (
+ string Name,
+ string? ShortName,
+ string Description,
+ bool IsFlag,
+ bool Repeatable = false);
+```
+
+### 4.5 Host
+
+```csharp
+using System.Reflection;
+
+namespace Terminal.Gui.Cli;
+
+/// The main entry point. Owns parsing, dispatch, Terminal.Gui lifecycle, and output.
+public sealed class CliHost
+{
+ /// Creates a host, applies configuration, creates its registry, and registers built-ins.
+ public CliHost (Action? configure = null);
+
+ /// The command registry owned by this host. Register consumer commands before RunAsync.
+ public ICommandRegistry Registry { get; }
+
+ /// Parses args, dispatches a command, writes output, and returns a process exit code.
+ public Task RunAsync (
+ string[] args,
+ CancellationToken cancellationToken = default,
+ TextWriter? stdout = null,
+ TextWriter? stderr = null);
+}
+
+/// Configuration options for .
+public sealed class CliHostOptions
+{
+ /// Application name shown in help, version output, and OpenCLI.
+ public string ApplicationName { get; set; } = "app";
+
+ /// Version string shown in --version and OpenCLI. Null uses 0.0.0.
+ public string? Version { get; set; }
+
+ /// Custom help provider. Null uses .
+ public IHelpProvider? HelpProvider { get; set; }
+
+ /// Maximum characters accepted by --initial. Default is 64 KiB.
+ public int MaxInitialChars { get; set; } = 64 * 1024;
+
+ /// Agent guide embedded resource name or literal markdown. Null disables agent-guide.
+ public string? AgentGuide { get; set; }
+
+ /// True when is an embedded resource name; false when literal content.
+ public bool AgentGuideIsResource { get; set; } = true;
+
+ /// Assembly used to resolve embedded resources. Null falls back to .
+ public Assembly? ResourceAssembly { get; set; }
+
+ /// Consumer-defined global options parsed into .
+ public List GlobalOptions { get; } = [];
+
+ /// Replaces a library built-in command before it is registered.
+ /// Thrown when is not a replaceable built-in alias.
+ /// Thrown when the same built-in alias is replaced more than once.
+ public void ReplaceBuiltInCommand (string alias, ICliCommand replacement);
+}
+```
+
+`CliHost` constructs and owns its `CommandRegistry`. It registers built-ins during construction after applying options: `help` is always registered unless replaced; `agent-guide` is registered only when `AgentGuide` is non-null unless replaced. When `AgentGuideIsResource` is true, `CliHost` resolves `AgentGuide` from `ResourceAssembly ?? Assembly.GetEntryAssembly()` during construction and passes the resolved markdown string to `AgentGuideCommand`; missing assembly or missing resource throws `InvalidOperationException`. When `AgentGuideIsResource` is false, `AgentGuide` is already literal markdown. `ReplaceBuiltInCommand` supports reserved aliases `help` and `agent-guide`; a replacement for `help` must include `help` in `Aliases`, and a replacement for `agent-guide` must include `agent-guide` in `Aliases`.
+
+### 4.6 Parser
+
+`ArgParser` is public because the proven API contract includes direct parser tests and consumers may validate argument behavior without running a TUI. It remains a small, data-driven parser, not a general CLI framework.
+
+```csharp
+namespace Terminal.Gui.Cli;
+
+/// Data-driven parser for framework flags, consumer globals, and per-command options.
+public sealed class ArgParser
+{
+ /// Creates a parser with registered consumer globals and an --initial limit.
+ public ArgParser (List globalOptions, int maxInitialChars = 64 * 1024);
+
+ /// Parses command-line arguments, optionally validating against a resolved command.
+ public ParseResult Parse (string[] args, ICliCommand? command = null);
+
+ /// Parses duration strings accepted by --timeout: ms, s, m, h.
+ public static bool TryParseTimeout (string input, out TimeSpan timeout);
+
+ /// Represents the result of parsing arguments.
+ public sealed class ParseResult
+ {
+ /// True if parsing succeeded.
+ public bool Success { get; init; }
+
+ /// Error message when parsing failed.
+ public string? Error { get; init; }
+
+ /// The command alias, when this is not a root flag.
+ public string? Alias { get; init; }
+
+ /// The parsed initial value.
+ public string? Initial { get; init; }
+
+ /// The parsed options bag.
+ public CommandRunOptions? Options { get; init; }
+
+ /// Root flag detected before command dispatch.
+ public RootFlag? RootFlag { get; init; }
+
+ /// Creates a failed parse result.
+ public static ParseResult Fail (string error);
+ }
+
+ /// Root flags that exit without command dispatch.
+ public enum RootFlag
+ {
+ /// Root --help or -h.
+ Help,
+
+ /// Root --version.
+ Version,
+
+ /// Root --opencli.
+ OpenCli
+ }
+}
+```
+
+### 4.7 Help and built-in commands
+
+```csharp
+using System.Reflection;
+using Terminal.Gui.App;
+
+namespace Terminal.Gui.Cli;
+
+/// Pluggable help rendering.
+public interface IHelpProvider
+{
+ /// Renders root-level help. Return null to use generated fallback text.
+ string? GetRootHelp (ICommandRegistry registry);
+
+ /// Renders per-command help. Return null to use generated fallback text.
+ string? GetCommandHelp (ICliCommand command);
+}
+
+/// Generates help text from registry metadata.
+public sealed class MetadataHelpProvider : IHelpProvider
+{
+ ///
+ public string? GetRootHelp (ICommandRegistry registry);
+
+ ///
+ public string? GetCommandHelp (ICliCommand command);
+}
+
+/// Reads embedded markdown resources for root, command, and agent help.
+public sealed class EmbeddedMarkdownHelpProvider : IHelpProvider
+{
+ /// Creates a provider that reads markdown resources from .
+ public EmbeddedMarkdownHelpProvider (Assembly resourceAssembly);
+
+ ///
+ public string? GetRootHelp (ICommandRegistry registry);
+
+ ///
+ public string? GetCommandHelp (ICliCommand command);
+
+ /// Reads an embedded markdown resource by exact manifest resource name.
+ public string? GetMarkdownResource (string resourceName);
+}
+
+/// Interactive TUI markdown help viewer, with --cat support for ANSI stdout.
+public sealed class HelpCommand : IViewerCommand
+{
+ /// Creates a help command that lazily reads command metadata from .
+ public HelpCommand (ICommandRegistry registry, IHelpProvider helpProvider);
+
+ ///
+ public string PrimaryAlias { get; }
+
+ ///
+ public IReadOnlyList Aliases { get; }
+
+ ///
+ public string Description { get; }
+
+ ///
+ public CommandKind Kind { get; }
+
+ ///
+ public Type ResultType { get; }
+
+ ///
+ public IReadOnlyList Options { get; }
+
+ ///
+ public bool AcceptsPositionalArgs { get; }
+
+ ///
+ public Task RunAsync (IApplication app, string? initial, CommandRunOptions options, CancellationToken cancellationToken);
+
+ ///
+ public Task RenderCatAsync (CommandRunOptions options, TextWriter stdout, CancellationToken cancellationToken);
+}
+
+/// Non-interactive viewer command that prints the consumer's agent guide.
+public sealed class AgentGuideCommand : IViewerCommand
+{
+ /// Creates an agent guide command from resolved markdown content.
+ public AgentGuideCommand (string markdown);
+
+ ///
+ public string PrimaryAlias { get; }
+
+ ///
+ public IReadOnlyList Aliases { get; }
+
+ ///
+ public string Description { get; }
+
+ ///
+ public CommandKind Kind { get; }
+
+ ///
+ public Type ResultType { get; }
+
+ ///
+ public IReadOnlyList Options { get; }
+
+ ///
+ public Task RunAsync (IApplication app, string? initial, CommandRunOptions options, CancellationToken cancellationToken);
+
+ ///
+ public Task RenderCatAsync (CommandRunOptions options, TextWriter stdout, CancellationToken cancellationToken);
+}
+```
+
+`HelpCommand` uses `MarkdownRenderer` for ANSI output and `Terminal.Gui.Views.Markdown` for interactive TUI mode. `AgentGuideCommand` is headless: it returns the guide markdown as the command value so plain output prints the text and `--json` wraps it in the envelope.
+
+### 4.8 Output and JSON
+
+```csharp
+namespace Terminal.Gui.Cli;
+
+/// The stable wire format for CLI output.
+public sealed class JsonEnvelope
+{
+ /// Wire schema version. Always 1 for library major version 1.x.
+ public int SchemaVersion { get; init; } = 1;
+
+ /// Status string: ok, cancelled, error, or no-result.
+ public string Status { get; init; } = "ok";
+
+ /// Result value. Omitted when null.
+ public object? Value { get; init; }
+
+ /// Error code. Omitted when null.
+ public string? Code { get; init; }
+
+ /// Error message. Omitted when null.
+ public string? Message { get; init; }
+
+ /// Creates an ok envelope.
+ public static JsonEnvelope Ok (object? value = null);
+
+ /// Creates a cancelled envelope.
+ public static JsonEnvelope Cancelled ();
+
+ /// Creates an error envelope.
+ public static JsonEnvelope Error (string code, string message);
+
+ /// Creates a no-result envelope.
+ public static JsonEnvelope NoResult ();
+
+ /// Serializes using the source-generated JSON context.
+ public string ToJson ();
+}
+
+/// Formats command results to stdout, stderr, or an output file.
+public static class ResultWriter
+{
+ /// Writes and returns false when output file creation fails.
+ public static bool Write (CommandResult result, bool jsonOutput, TextWriter stdout, TextWriter stderr, string? outputPath = null);
+}
+
+/// Generates an OpenCLI JSON document from registry metadata.
+public static class OpenCliWriter
+{
+ /// Generates OpenCLI JSON for the registered commands and framework options.
+ public static string Generate (ICommandRegistry registry, CliHostOptions options);
+}
+```
+
+`OpenCliWriter` hand-builds JSON with a shared string-escape helper. It must escape command aliases, descriptions, option names, short names, app names, versions, and metadata values.
+
+The JSON source-generation context is internal, not public API:
+
+```csharp
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui.Cli;
+
+[JsonSourceGenerationOptions (
+ PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
+[JsonSerializable (typeof (JsonEnvelope))]
+internal partial class CliJsonContext : JsonSerializerContext
+{
+}
+```
+
+### 4.9 Utilities
+
+```csharp
+namespace Terminal.Gui.Cli;
+
+/// POSIX-conventional exit codes.
+public static class ExitCodes
+{
+ /// Success.
+ public const int Ok = 0;
+
+ /// Successful command execution with no result.
+ public const int NoResult = 1;
+
+ /// Usage error: bad command, bad option, or output-file creation failure.
+ public const int UsageError = 2;
+
+ /// Validation error, equivalent to sysexits EX_DATAERR.
+ public const int ValidationError = 65;
+
+ /// I/O error, equivalent to sysexits EX_IOERR.
+ public const int IoError = 74;
+
+ /// Cancelled, equivalent to 128 + SIGINT.
+ public const int Cancelled = 130;
+
+ /// Maps a command result to a process exit code.
+ public static int FromResult (CommandResult result);
+}
+
+/// Maps CLR types to stable wire-format type names.
+public static class TypeNames
+{
+ /// Returns the wire-format name for .
+ public static string WireName (Type type);
+}
+
+/// Strips dangerous terminal escape sequences from untrusted content.
+public static class TerminalEscapeSanitizer
+{
+ /// Sanitizes user-supplied content before it reaches a terminal driver.
+ public static string? Sanitize (string? input);
+
+ /// Sanitizes rendered ANSI, preserving only SGR CSI sequences generated by trusted renderers.
+ public static string SanitizeRenderedOutput (string renderedAnsi);
+}
+
+/// Markdown-to-ANSI helper for help and viewer output.
+public static class MarkdownRenderer
+{
+ /// Renders markdown as ANSI to and sanitizes rendered output.
+ public static void RenderToAnsi (string markdown, TextWriter output);
+}
+```
+
+`MarkdownRenderer` wraps `Terminal.Gui.Views.Markdown.RenderToAnsi()` and then applies `TerminalEscapeSanitizer.SanitizeRenderedOutput` before writing to the target `TextWriter`. The required Terminal.Gui version is listed in the Terminal.Gui Dependency Floor section.
+
+### 4.10 InputCommandRunner
+
+```csharp
+using Terminal.Gui.App;
+using Terminal.Gui.ViewBase;
+using Terminal.Gui.Views;
+
+namespace Terminal.Gui.Cli;
+
+/// Shared boilerplate for input commands that wrap a control in RunnableWrapper.
+public static class InputCommandRunner
+{
+ /// Configures, runs, and maps the result from an input command wrapper when raw result and output value differ.
+ public static Task> RunAsync (
+ IApplication app,
+ RunnableWrapper wrapper,
+ CommandRunOptions options,
+ string defaultTitle,
+ CancellationToken cancellationToken,
+ Func> resultMapper,
+ bool addEnterBinding = true)
+ where TControl : View, new();
+
+ /// Configures and runs a wrapper whose raw result is already the output value.
+ public static Task> RunAsync (
+ IApplication app,
+ RunnableWrapper wrapper,
+ CommandRunOptions options,
+ string defaultTitle,
+ CancellationToken cancellationToken,
+ bool addEnterBinding = true)
+ where TControl : View, new();
+}
+```
+
+Use the three-type-parameter overload when the wrapper's raw result must be mapped to a different output value. Use the two-type-parameter overload when `TRawResult` and `TValue` are the same and the wrapper result can be returned directly.
+
+`InputCommandRunner` applies these defaults before `app.RunAsync`: `Title = options.Title ?? defaultTitle`, `Width = Dim.Fill()`, `BorderStyle = LineStyle.Rounded`, `Border.Thickness = new Thickness (0, 1, 0, 0)`, and Enter key binding to `Command.Accept` when requested. Consumers override using standard Terminal.Gui lifecycle, especially `wrapper.Initialized`; the library must not add custom styling callbacks.
+
+## 5. CLI Grammar
+
+```text
+ [--help|-h]
+ --version
+ --opencli
+ [positional...] [framework-options] [consumer-global-options] [per-command-options]
+ --help|-h|help
+ help [alias] [--cat]
+ agent-guide [--json]
+```
+
+There is no `list` command. Human listing is `--help`; structured listing is `--opencli`.
+
+### 5.1 Framework command flags
+
+| Flag | Short | Value | Target property | Behavior |
+|------|-------|-------|-----------------|----------|
+| `--json` | `-j` | none | `JsonOutput=true` | Write JSON envelope. |
+| `--initial` | `-i` | string | `Initial` | Pre-fill command value; max 64 KiB by default. |
+| `--title` | `-t` | string | `Title` | Override TUI title. |
+| `--prompt` | `-p` | string | `Title` | Exact alias for `--title` / `-t`. |
+| `--timeout` | none | duration | `Timeout` | Positive number with `ms`, `s`, `m`, or `h`. |
+| `--fullscreen` | `-f` | none | `Fullscreen=true` | Force fullscreen app model. |
+| `--cat` | none | none | `Cat=true` | Ask viewer to render to stdout without TUI. |
+| `--output` | `-o` | path | `OutputPath` | Write successful command output to a newly-created file. |
+| `--rows` | `-r` | positive int | `Rows` | Constrain inline height. |
+
+### 5.2 Root-only framework flags
+
+These are intercepted before command dispatch and do not populate `CommandRunOptions`.
+
+| Flag | Short | Behavior |
+|------|-------|----------|
+| `--help` | `-h` | Write root help and exit 0. |
+| `--version` | none | Write app name/version and exit 0. |
+| `--opencli` | none | Emit OpenCLI JSON and exit 0. |
+
+Supported syntax: `--option value`, `--option=value`, the short forms of framework command flags documented above, and `--` to end option parsing. Short bundling (`-jf`) is not supported.
+
+### 5.3 Consumer global options
+
+Consumers add `GlobalOptionDescriptor` values to `CliHostOptions.GlobalOptions`. Matching is case-insensitive by long name or one-character short name. For flags, `Extensions[name]` contains one empty string per occurrence. For repeatable value options, all values are appended. For non-repeatable value options, the last value wins.
+
+### 5.4 Per-command options
+
+A token not matched as a framework flag or consumer global is accepted as a per-command option only if it matches the resolved command's `Options` by `Name` or `ShortName`. Values are strings. Unknown options fail with exit code 2. Required/default semantics are metadata the command must enforce; the parser validates presence and value consumption but does not coerce to `ValueType`.
+
+### 5.5 Help interception
+
+` --help`, ` -h`, and ` help` are intercepted by `CliHost.RunAsync` before `ArgParser.Parse()`. This is required because `--help` is not a per-command option and the parser would otherwise reject it. The command must already be registered; unknown aliases return usage error.
+
+## 6. Execution Pipeline
+
+```mermaid
+sequenceDiagram
+ participant Main as Program.Main
+ participant Host as CliHost
+ participant Parser as ArgParser
+ participant Registry as CommandRegistry
+ participant TG as Terminal.Gui
+ participant Command as ICliCommand
+ participant Writer as ResultWriter/OpenCliWriter
+
+ Main->>Host: RunAsync(args, ct, stdout, stderr)
+ Host->>Host: intercept --help|-h|help
+ Host->>Parser: Parse(args)
+ Parser-->>Host: ParseResult
+
+ alt parse failed
+ Host-->>Main: write stderr, exit 2
+ else root --help / --version / --opencli
+ Host->>Writer: write root output
+ Host-->>Main: exit 0
+ else command dispatch
+ Host->>Registry: TryResolve(alias)
+ Registry-->>Host: command or null
+ Host->>Parser: Parse(args, command)
+ Parser-->>Host: validated CommandRunOptions
+ Host->>Host: validate positional args and --initial
+ Host->>Host: create linked CTS(user token + timeout)
+
+ alt options.Cat && command is IViewerCommand && RenderCatAsync returns result
+ Host->>Command: RenderCatAsync(options, stdout, ct)
+ Host->>Writer: ResultWriter.Write(result)
+ Host-->>Main: ExitCodes.FromResult(result)
+ else normal path
+ Host->>TG: ConfigurationManager.Enable(All), fallback to None on failure
+ Host->>TG: set Application.AppModel
+ Host->>TG: Application.Create(); app.Init()
+ Host->>Command: RunAsync(app, initial, options, ct)
+ Command-->>Host: CommandResult
+ Host->>TG: dispose IApplication
+ Host->>Writer: ResultWriter.Write(result)
+ Host-->>Main: ExitCodes.FromResult(result)
+ end
+ end
+```
+
+`CliHost` handles dispatch inline; there is no separate `CommandDispatcher` class. `OperationCanceledException` maps to `CommandStatus.Cancelled`. Other command or TG initialization exceptions map to `CommandStatus.Error` with code `io`.
+
+## 7. Exit Codes, JSON, and Type Names
+
+### 7.1 Exit codes
+
+| Constant | Value | Meaning | Mapping |
+|----------|-------|---------|---------|
+| `Ok` | 0 | Success | `CommandStatus.Ok` |
+| `NoResult` | 1 | Successful no-result | `CommandStatus.NoResult` |
+| `UsageError` | 2 | Usage or generic command error | parse errors, unknown command, output path failure, unknown error code |
+| `ValidationError` | 65 | EX_DATAERR | `CommandStatus.Error` with `validation` or `input-too-large` |
+| `IoError` | 74 | EX_IOERR | `CommandStatus.Error` with `io` |
+| `Cancelled` | 130 | 128 + SIGINT | `CommandStatus.Cancelled` |
+
+### 7.2 JSON envelope
+
+| Status | Required fields | Omitted fields |
+|--------|-----------------|----------------|
+| `ok` | `schemaVersion`, `status`, optional `value` | `code`, `message` |
+| `cancelled` | `schemaVersion`, `status` | `value`, `code`, `message` |
+| `error` | `schemaVersion`, `status`, `code`, `message` | `value` |
+| `no-result` | `schemaVersion`, `status` | `value`, `code`, `message` |
+
+Fields use camelCase. Null fields are omitted. Plain-text `Ok` output writes values directly; `JsonArray` writes one item per line; `JsonNode` writes JSON; errors write `error: {code}: {message}` to stderr.
+
+### 7.3 TypeNames mapping
+
+| CLR type | Wire name |
+|----------|-----------|
+| `string` | `string` |
+| `int`, `long`, `short` and nullable forms | `int` |
+| `decimal`, `double`, `float` and nullable forms | `decimal` |
+| `bool` and nullable form | `bool` |
+| `DateTime`, `DateOnly` and nullable forms | `date` |
+| `TimeOnly` and nullable form | `time` |
+| `TimeSpan` and nullable form | `duration` |
+| `JsonArray` | `array` |
+| `JsonObject` | `object` |
+| `JsonNode` | `json` |
+| `void` | `none` |
+| other | `Type.Name` |
+
+## 8. AI Discovery Model
+
+### 8.1 `llms.txt`
+
+Consumers SHOULD ship `llms.txt` in the repo root and, when applicable, at `https:///llms.txt`. It is a short orientation document for agents: what the tool does, install instructions, quick start, key flags, and pointers to `agent-guide` and `--opencli`. The library does not generate it.
+
+### 8.2 `agent-guide`
+
+When `CliHostOptions.AgentGuide` is set, `CliHost` registers `AgentGuideCommand` under `agent-guide`. Behavior:
+
+| Invocation | Behavior |
+|------------|----------|
+| ` agent-guide` | Prints the guide text to stdout, exit 0. |
+| ` agent-guide --json` | Writes JSON envelope with guide text as `value`, exit 0. |
+| no configured guide | Command is absent; invoking it is unknown command, exit 2. |
+
+When `AgentGuideIsResource` is true, the value is an embedded resource name resolved from `CliHostOptions.ResourceAssembly ?? Assembly.GetEntryAssembly()`. Consumers should set `ResourceAssembly = typeof (Program).Assembly` for explicit, testable resolution. When false, `AgentGuide` is literal markdown content.
+
+### 8.3 `--opencli`
+
+Every host supports root `--opencli`. It emits OpenCLI JSON with:
+
+- `opencli: "0.1"`
+- app `info.title` and `info.version`
+- all registered commands in registry order
+- aliases, descriptions, options, and exit codes
+- metadata entries `kind` (`input`/`viewer`) and `resultType` (`TypeNames.WireName`)
+- recursive framework options
+
+Agents should call `--opencli` once per session and cache the result.
+
+## 9. Project Structure
+
+```text
+Terminal.Gui.Cli.slnx
+Directory.Build.props
+Directory.Build.targets
+.editorconfig
+CLAUDE.md
+AGENTS.md
+specs/
+ constitution.md
+ terminal-gui-cli-v2.md
+src/
+ Terminal.Gui.Cli/
+ Terminal.Gui.Cli.csproj
+ Abstractions/
+ CommandKind.cs
+ CommandStatus.cs
+ CommandOptionDescriptor.cs
+ CommandResult.cs
+ ICliCommand.cs
+ ICliCommandGeneric.cs (contains ICliCommand)
+ IViewerCommand.cs
+ ICommandRegistry.cs
+ CommandRunOptions.cs
+ Registry/
+ CommandRegistry.cs
+ Hosting/
+ ArgParser.cs
+ CliHost.cs (dispatch is inline; no CommandDispatcher.cs)
+ CliHostOptions.cs
+ ExitCodes.cs
+ GlobalOptionDescriptor.cs
+ InputCommandRunner.cs
+ Commands/
+ HelpCommand.cs
+ AgentGuideCommand.cs
+ Help/
+ IHelpProvider.cs
+ MetadataHelpProvider.cs
+ EmbeddedMarkdownHelpProvider.cs
+ MarkdownRenderer.cs
+ Output/
+ CliJsonContext.cs
+ JsonEnvelope.cs
+ OpenCliWriter.cs
+ ResultWriter.cs
+ TypeNames.cs
+ Security/
+ TerminalEscapeSanitizer.cs
+ Properties/
+ AssemblyInfo.cs
+tests/
+ Terminal.Gui.Cli.Tests/
+ Terminal.Gui.Cli.IntegrationTests/
+ Terminal.Gui.Cli.SmokeTests/
+examples/
+ Terminal.Gui.Cli.ExampleApp/
+```
+
+## 10. Test Strategy
+
+Port `tests/Terminal.Gui.Cli.Tests` from clet PR #176 as the baseline public API contract. These tests validate parser, registry, JSON envelope, exit codes, type names, sanitizer, host dispatch, help interception, OpenCLI, and consumer global option flow.
+
+| Tier | Project | Scope | Notes |
+|------|---------|-------|-------|
+| Unit | `Terminal.Gui.Cli.Tests` | Parser, registry, result writer, JSON, OpenCLI, sanitizer, type names, host paths not requiring real TUI interaction | No Terminal.Gui driver initialization except where unavoidable. |
+| Integration | `Terminal.Gui.Cli.IntegrationTests` | `CliHost` end-to-end with `Application.Create()`, cancellation, timeout, `InputCommandRunner`, interactive help rendering | Full parallel; no process-global mutation unless isolated by collection. |
+| Smoke | `Terminal.Gui.Cli.SmokeTests` | Spawn `examples/Terminal.Gui.Cli.ExampleApp`; verify `--help`, `--version`, `--opencli`, `agent-guide`, JSON output, exit codes | OS matrix, Release build. |
+
+Write fresh tests for `HelpCommand`, `AgentGuideCommand`, `ReplaceBuiltInCommand`, `CliHostOptions.ResourceAssembly`, `EmbeddedMarkdownHelpProvider`, `MarkdownRenderer`, and smoke tests tied to the new repo/example app. Tests run with `dotnet run --project tests/`.
+
+## 11. CI/CD
+
+Clone from `gui-cs/Editor` and adapt names.
+
+| Workflow | Trigger | Responsibilities |
+|----------|---------|------------------|
+| `ci.yml` | PRs and branch pushes | Restore, build Debug/Release as appropriate, format verification, unit/integration/smoke tests on ubuntu/macos/windows, AOT publish example app. |
+| `release.yml` | `develop` push and `v*` tags | Compute version, Release build/test, pack, NuGet push with skip-duplicate, GitHub release artifacts for tags. |
+| `prepare-release.yml` | manual dispatch | Compute next beta/rc/stable version, create release branch, open release PR to `main`. |
+| `finalize-release.yml` | release PR merged | Create annotated tag/GitHub Release, delete release branch, open main-to-develop back-merge PR. |
+
+Versioning follows Editor: base `` in `Directory.Build.props`; develop builds append GitHub run number to a `-develop` base; tag builds strip leading `v`; `` pins the Terminal.Gui dependency and can be overridden in CI.
+
+## 12. Scope Boundaries
+
+| Out of scope | Reason |
+|--------------|--------|
+| Concrete consumer commands such as clet's `select`, `md`, `edit`, or config tools | Consumers own domain commands. |
+| FileAccessPolicy / file access settings | clet-specific AI-agent threat model. |
+| MarkdownContentResolver | clet-specific file and stdin resolution. |
+| InputCletRunner | clet-specific styling wrapper around `InputCommandRunner`. |
+| CletStyling / scheme names | App-specific visual policy. |
+| General-purpose markdown file browser command | Library provides help/agent guide primitives, not file browsing. |
+| DI container | Consumers construct commands however they want. |
+| Reflection/source-generated command discovery | Explicit registration is the contract. |
+| Logging abstraction | Consumers may add logging around their commands. |
+| Environment-variable option fallback or short-option bundling | Not required for proven API; keep parser small. |
+| Owning consumer config files | Consumers set `ConfigurationManager.AppName`; library only enables/falls back during dispatch. |
+
+## 13. Terminal.Gui Dependency Floor
+
+| Dependency | Required by | Minimum version | Requirement |
+|------------|-------------|-----------------|-------------|
+| `Terminal.Gui.Views.Markdown` View | `HelpCommand` interactive mode | `2.4.1-develop.11` or later | Display markdown help in a fullscreen Terminal.Gui viewer. |
+| `Terminal.Gui.Views.Markdown.RenderToAnsi()` | `MarkdownRenderer`, `HelpCommand --cat` | `2.4.1-develop.11` or later | Render markdown to ANSI for stdout output before sanitizer pass-through. |
+
+`Directory.Build.props` must pin `TerminalGuiVersion` to a version that contains both APIs. CI should fail fast if the pinned package no longer exposes them.