namespace Agent.App.Cli; public enum AppCommand { Serve, Check, ExecutableResolve, ExecutableApprove, ExecutableList, ExecutableRevoke, SandboxResolve, SandboxConfirm, SandboxShow } public sealed record CommandLine( AppCommand Command, string? ConfigPath, string? Language, string? Transport, string? Alias = null, string? ExecutablePath = null, string? RuntimeRoot = null, bool NoRuntime = false, bool Yes = false, string Format = "text") { public static CommandLine Parse(string[] args) { bool implicitServe = args.Length == 0 || args[0].StartsWith("--", StringComparison.Ordinal); int index = implicitServe ? 0 : 1; AppCommand command; string? alias = null; if (implicitServe) command = AppCommand.Serve; else if (args[0] == "serve") command = AppCommand.Serve; else if (args[0] == "check") command = AppCommand.Check; else if (args[0] is "executable" or "sandbox") { if (index >= args.Length) throw new CommandLineException($"Expected a subcommand after '{args[0]}'."); string subcommand = args[index++]; command = (args[0], subcommand) switch { ("executable", "resolve") => AppCommand.ExecutableResolve, ("executable", "approve") => AppCommand.ExecutableApprove, ("executable", "list") => AppCommand.ExecutableList, ("executable", "revoke") => AppCommand.ExecutableRevoke, ("sandbox", "resolve") => AppCommand.SandboxResolve, ("sandbox", "confirm") => AppCommand.SandboxConfirm, ("sandbox", "show") => AppCommand.SandboxShow, _ => throw new CommandLineException($"Unknown subcommand '{args[0]} {subcommand}'.") }; if (command is AppCommand.ExecutableResolve or AppCommand.ExecutableApprove or AppCommand.ExecutableRevoke) { if (index >= args.Length || args[index].StartsWith("--", StringComparison.Ordinal)) throw new CommandLineException($"Executable Alias is required for '{args[0]} {subcommand}'."); alias = args[index++]; } } else throw new CommandLineException("Expected 'serve', 'check', 'executable', or 'sandbox'."); string? config = null; string? language = null; string? transport = null; string? executablePath = null; string? runtimeRoot = null; bool noRuntime = false; bool yes = false; string format = "text"; while (index < args.Length) { string option = args[index++]; switch (option) { case "--yes" when !yes: yes = true; break; case "--no-runtime" when !noRuntime && runtimeRoot is null: noRuntime = true; break; case "--config" when config is null: config = Value(option); break; case "--lang" when language is null: language = Value(option); break; case "--transport" when command == AppCommand.Serve && transport is null: transport = Value(option); if (transport is not ("stdio" or "http")) throw new CommandLineException("Transport must be stdio or http."); break; case "--path" when executablePath is null: executablePath = Value(option); break; case "--runtime-root" when runtimeRoot is null && !noRuntime: runtimeRoot = Value(option); break; case "--format" when command == AppCommand.SandboxShow: format = Value(option); if (format is not ("text" or "json")) throw new CommandLineException("Format must be text or json."); break; default: throw new CommandLineException($"Unknown, duplicate, or invalid option '{option}' for this command."); } } bool management = command is not (AppCommand.Serve or AppCommand.Check); if (management && config is null) throw new CommandLineException("--config is required for executable and sandbox commands."); if (yes && command is not (AppCommand.ExecutableApprove or AppCommand.ExecutableRevoke or AppCommand.SandboxConfirm)) throw new CommandLineException("--yes is valid only for approve, revoke, and confirm."); return new(command, config, language, transport, alias, executablePath, runtimeRoot, noRuntime, yes, format); string Value(string optionName) { if (index >= args.Length) throw new CommandLineException($"Missing value for '{optionName}'."); return args[index++]; } } } public sealed class CommandLineException(string message) : Exception(message);