CLI Design Principles
This page captures cross-cutting design and implementation principles for OAT CLI features.
Use this as the default contract for new CLI commands and modules. Provider- or domain-specific behavior should be layered on top in sub-docs (for example, provider interop docs).
Scope
These principles apply to packages/cli/src/**:
- Command architecture and boundaries
- User interaction and output behavior
- Safety and mutation contracts
- Error semantics and exit codes
- Verification expectations
Architectural Principles
- Keep commands thin: parse args, validate inputs, orchestrate services.
- Keep business logic in modules (
engine/,drift/,manifest/,providers/), not in command handlers. - Use explicit type contracts for command inputs/outputs and adapter behavior.
- Prefer deterministic pure planning stages before execution (compute plan first, then optionally apply).
- Use command-factory exports for top-level commands (
createXCommand(): Command).
Module Placement Rules
- If a command needs dedicated prompts/types/utils, use a command directory (
commands/<name>/). - Keep single-file commands only for trivial read-only behavior.
- Prefer named command files for findability (
sync.ts,sync.test.ts,list.ts,list.test.ts) over genericindex.ts/index.test.ts. - Reserve
index.tsfor command registration boundaries and optional re-export wrappers. - Keep command-specific prompts in the command module; reserve
commands/shared/shared.prompts.tsfor reusable command-level prompt primitives. - Keep domain validation close to the owning domain (
*.types.tsnear command/provider modules). - Keep shared utilities generic; move domain-specific utilities into the owning command/provider area (for top-level command helpers, use
commands/shared/shared.utils.ts).
Example command structure pattern (for findability):
packages/cli/src/
commands/
<top-level-command>/
index.ts
<top-level-command>.types.ts
<top-level-command>.utils.ts
<subcommand>/
<subcommand>.ts
<subcommand>.test.ts
<subcommand>.types.ts
<subcommand>.utils.ts
<nested-subcommand>/
<nested-subcommand>.ts
<nested-subcommand>.test.ts
shared/
shared.prompts.ts
shared.utils.tsImport Path Rules
- Allow only same-directory relative imports:
./... - Disallow parent-directory relative imports:
../... - For anything outside the current directory, use explicit TypeScript aliases.
- Do not use
src/...imports. - Do not use a catch-all alias like
@/*. - CLI alias set:
@app/*,@commands/*,@config/*,@drift/*,@engine/*,@errors/*,@fs/*,@manifest/*,@providers/*,@shared/*,@ui/*,@validation/*.
Runtime Bootstrap and Flags
- Startup order: load runtime config -> validate env/input -> build command context -> register commands.
- Register global flags early and consistently (
--json,--verbose,--scope,--cwd).
UX and Output Principles
- Mutating commands apply by default; use
--dry-runto preview changes safely. - Never require interaction in non-TTY or
--jsonmode. - In interactive mode, prompts should be explicit, reversible where possible, and scoped to actionable choices.
- JSON output must be machine-consumable and stable in structure for automation paths.
- Human output should prioritize actionable state (
what changed,what failed,what to do next).
Logging and Console Rules
- Use the centralized CLI logger for user-facing output and JSON payloads.
- Do not use direct
console.log/console.errorin command handlers (except bootstrap-level fatal fallback). - Keep spinner usage limited to longer operations and auto-disable in non-interactive/
--jsonmode.
Safety Principles
- Treat canonical
.agents/**as source of truth for provider sync workflows. - Destructive operations must be scoped and traceable to known managed state.
- Do not mutate unmanaged filesystem state implicitly.
- Prefer idempotent operations; re-running a command should converge, not compound side effects.
- Keep manifest updates aligned with executed operations only.
Error and Exit Semantics
0: success1: user/actionable error (invalid input, unmet preconditions, declined required action)2: system/runtime error (I/O, unexpected failures, corrupted state)
Error messages should include concrete remediation where possible.
Verification Expectations
For CLI behavior changes:
- Add or update focused unit tests near touched modules.
- Add integration/e2e coverage when behavior spans command -> engine -> filesystem boundaries.
- Verify command wiring and help surfaces for changed command trees.
- Keep adapter contract coverage for provider interface behavior.
- Keep logger/spinner behavior tests aligned with human vs JSON mode behavior.
- Run package checks before merge:
pnpm --filter @open-agent-toolkit/cli testpnpm --filter @open-agent-toolkit/cli lintpnpm --filter @open-agent-toolkit/cli type-check
Related Docs
- CLI Reference:
../reference/cli-reference - Provider sync commands:
../provider-sync/commands - Provider sync manifest and drift:
../provider-sync/manifest-and-drift