7.9 KiB
CLI Command Architecture Plan
Goal
Redesign the yaak-cli command structure to use a resource-oriented <resource> <action>
pattern that scales well, is discoverable, and supports both human and LLM workflows.
Command Architecture
Design Principles
- Resource-oriented: top-level commands are nouns, subcommands are verbs
- Polymorphic requests:
requestcovers HTTP, gRPC, and WebSocket — the CLI resolves the type viaget_any_requestand adapts behavior accordingly - Simple creation, full-fidelity via JSON: human-friendly flags for basic creation,
--jsonfor full control (targeted at LLM and scripting workflows) - Runtime schema introspection:
request schemaoutputs JSON Schema for the request models, with dynamic auth fields populated from loaded plugins at runtime - Destructive actions require confirmation:
deletecommands prompt for user confirmation before proceeding. Can be bypassed with--yes/-yfor scripting
Commands
# Top-level shortcut
yaakcli send <id> [-e <env_id>] # id can be a request, folder, or workspace
# Resource commands
yaakcli workspace list
yaakcli workspace show <id>
yaakcli workspace create --name <name>
yaakcli workspace create --json '{"name": "My Workspace"}'
yaakcli workspace create '{"name": "My Workspace"}' # positional JSON shorthand
yaakcli workspace update --json '{"id": "wk_abc", "name": "New Name"}'
yaakcli workspace delete <id>
yaakcli request list <workspace_id>
yaakcli request show <id>
yaakcli request create <workspace_id> --name <name> --url <url> [--method GET]
yaakcli request create --json '{"workspaceId": "wk_abc", "url": "..."}'
yaakcli request update --json '{"id": "rq_abc", "url": "https://new.com"}'
yaakcli request send <id> [-e <env_id>]
yaakcli request delete <id>
yaakcli request schema <http|grpc|websocket>
yaakcli folder list <workspace_id>
yaakcli folder show <id>
yaakcli folder create <workspace_id> --name <name>
yaakcli folder create --json '{"workspaceId": "wk_abc", "name": "Auth"}'
yaakcli folder update --json '{"id": "fl_abc", "name": "New Name"}'
yaakcli folder delete <id>
yaakcli environment list <workspace_id>
yaakcli environment show <id>
yaakcli environment create <workspace_id> --name <name>
yaakcli environment create --json '{"workspaceId": "wk_abc", "name": "Production"}'
yaakcli environment update --json '{"id": "ev_abc", ...}'
yaakcli environment delete <id>
send — Top-Level Shortcut
yaakcli send <id> is a convenience alias that accepts any sendable ID. It tries
each type in order via DB lookups (short-circuiting on first match):
- Request (HTTP, gRPC, or WebSocket via
get_any_request) - Folder (sends all requests in the folder)
- Workspace (sends all requests in the workspace)
ID prefixes exist (e.g. rq_, fl_, wk_) but are not relied upon — resolution
is purely by DB lookup.
request send <id> is the same but restricted to request IDs only.
Request Send — Polymorphic Behavior
send means "execute this request" regardless of protocol:
- HTTP: send request, print response, exit
- gRPC: invoke the method; for streaming, stream output to stdout until done/Ctrl+C
- WebSocket: connect, stream messages to stdout until closed/Ctrl+C
request schema — Runtime JSON Schema
Outputs a JSON Schema describing the full request shape, including dynamic fields:
- Generate base schema from
schemars::JsonSchemaderive on the Rust model structs - Load plugins, collect auth strategy definitions and their form inputs
- Merge plugin-defined auth fields into the
authenticationproperty as aoneOf - Output the combined schema as JSON
This lets an LLM call schema, read the shape, and construct valid JSON for
create --json or update --json.
Implementation Steps
Phase 1: Restructure commands (no new functionality)
Refactor main.rs into the new resource/action pattern using clap subcommand nesting.
Existing behavior stays the same, just reorganized. Remove the get command.
- Create module structure:
commands/workspace.rs,commands/request.rs, etc. - Define nested clap enums:
enum Commands { Send(SendArgs), Workspace(WorkspaceArgs), Request(RequestArgs), Folder(FolderArgs), Environment(EnvironmentArgs), } - Move existing
Workspaceslogic intoworkspace list - Move existing
Requestslogic intorequest list - Move existing
Sendlogic intorequest send - Move existing
Createlogic intorequest create - Delete the
Getcommand entirely - Extract shared setup (DB init, plugin init, encryption) into a reusable context struct
Phase 2: Add missing CRUD commands
workspace show <id>workspace create --name <name>(and--json)workspace update --jsonworkspace delete <id>request show <id>(JSON output of the full request model)request delete <id>folder list <workspace_id>folder show <id>folder create <workspace_id> --name <name>(and--json)folder update --jsonfolder delete <id>environment list <workspace_id>environment show <id>environment create <workspace_id> --name <name>(and--json)environment update --jsonenvironment delete <id>
Phase 3: JSON input for create/update
Both commands accept JSON via --json <string> or as a positional argument (detected
by leading {). They follow the same upsert pattern as the plugin API.
-
create --json: JSON must includeworkspaceId. Must NOT includeid(or use empty string""). Deserializes into the model with defaults for missing fields, then upserts (insert). -
update --json: JSON must includeid. Performs a fetch-merge-upsert:- Fetch the existing model from DB
- Serialize it to
serde_json::Value - Deep-merge the user's partial JSON on top (JSON Merge Patch / RFC 7386 semantics)
- Deserialize back into the typed model
- Upsert (update)
This matches how the MCP server plugin already does it (fetch existing, spread, override), but the CLI handles the merge server-side so callers don't have to.
Setting a field to
nullremoves it (forOption<T>fields), per RFC 7386.
Implementation:
- Add
--jsonflag and positional JSON detection tocreatecommands - Add
updatecommands with required--jsonflag - Implement JSON merge utility (or use
json-patchcrate)
Phase 4: Runtime schema generation
- Add
schemarsdependency toyaak-models - Derive
JsonSchemaonHttpRequest,GrpcRequest,WebsocketRequest, and their nested types (HttpRequestHeader,HttpUrlParameter, etc.) - Implement
request schemacommand:- Generate base schema from schemars
- Query plugins for auth strategy form inputs
- Convert plugin form inputs into JSON Schema properties
- Merge into the
authenticationfield - Print to stdout
Phase 5: Polymorphic send
- Update
request sendto useget_any_requestto resolve the request type - Match on
AnyRequestvariant and dispatch to the appropriate sender:AnyRequest::HttpRequest— existing HTTP send logicAnyRequest::GrpcRequest— gRPC invoke (future implementation)AnyRequest::WebsocketRequest— WebSocket connect (future implementation)
- gRPC and WebSocket send can initially return "not yet implemented" errors
Phase 6: Top-level send and folder/workspace send
- Add top-level
yaakcli send <id>command - Resolve ID by trying DB lookups in order: any_request → folder → workspace
- For folder: list all requests in folder, send each
- For workspace: list all requests in workspace, send each
- Add execution options:
--sequential(default),--parallel,--fail-fast
Crate Changes
- yaak-cli: restructure into modules, new clap hierarchy
- yaak-models: add
schemarsdependency, deriveJsonSchemaon model structs (current derives:Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)