Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR-0017: CLI Command Implementation Details

Status: accepted | Date: 2026-01-19

References: RFC-0002

Context

RFC-0002 defines the structural contract for govctl CLI commands: resource-first organization, universal CRUD verbs, lifecycle operations, and output format control. However, RFC-0002 intentionally defers implementation details to preserve flexibility while maintaining a stable interface contract.

Implementation requires decisions on:

  • Exact flag syntax and naming conventions
  • Array field editing notation (how to add/remove items)
  • Help text formatting and structure
  • Error message templates and exit codes
  • Terminal capability detection (colors, interactive prompts)
  • Confirmation prompt behavior
  • Progress indicators for long-running operations

Without documented conventions, implementers would make inconsistent choices, leading to:

  • Flags named differently across commands (–filter vs –query)
  • Array editing syntax that varies by resource type
  • Inconsistent help text structure
  • Error messages with different formats
  • Terminal behavior that breaks in non-interactive contexts

This ADR establishes implementation conventions that realize RFC-0002’s structural contract with consistent UX.

Decision

We adopt the following implementation conventions:

1. Flag Syntax and Naming

Long flags: Always use double-dash with kebab-case

  • --output, --filter, --dry-run, --force

Short flags: Single letter aliases for common flags only

  • -o for --output
  • -f for --force
  • -n for --limit (per ADR-0019, aligns with head/tail convention)

No short flag for --dry-run: Safety flags benefit from explicit long form.

No short flags for domain-specific options to avoid collision:

  • --filter has no short form
  • --rfc-id has no short form

Flag values:

  • Space-separated: --output json (preferred)
  • Equals accepted: --output=json (clap auto-supports)
  • No colons: avoid --output:json

2. Field Editing Commands

govctl uses distinct verbs for different mutation operations. Each verb is clear and discoverable via --help.

Scalar fields: Use set

govctl rfc set RFC-0001 title "New Title"
govctl work set WI-001 description "Updated description"
govctl adr set ADR-001 status accepted

Array fields - append: Use add

govctl work add WI-001 refs RFC-0001
govctl rfc add RFC-0001 owners @alice
govctl work add WI-001 acceptance_criteria "New criterion"

Array fields - remove: Use remove

govctl work remove WI-001 refs RFC-0001
govctl rfc remove RFC-0001 owners @bob

Checklist status: Use tick

govctl work tick WI-001 acceptance_criteria "Tests pass" -s done
govctl work tick WI-001 acceptance_criteria "Docs" -s pending

Pattern matching for array operations: Same semantics as ADR-0007

  • Default: case-insensitive substring match
  • Use --exact for exact match
  • Use --regex for pattern match
  • Use --at N for index-based access

Rationale: Distinct verbs (set, add, remove, tick) are more intuitive and discoverable than a unified edit command with prefix notation. Users immediately understand what each command does.

3. Help Text Structure

All commands follow this template:

USAGE:
    govctl <resource> <verb> [OPTIONS] <ARGS>

ARGS:
    <required>     Description
    [optional]     Description

OPTIONS:
    -o, --output <FORMAT>    Output format [default: table] [possible: json, yaml, toml, plain]
    --filter <EXPR>          Filter results (KEY=VALUE)
    --dry-run                Preview changes without writing
    -h, --help               Print help

EXAMPLES:
    govctl rfc list draft
    govctl rfc get RFC-0001 -o json
    govctl rfc set RFC-0001 title "New Title"

Ordering:

  1. Universal flags first (output, filter, dry-run)
  2. Resource-specific flags second
  3. Help/version last

4. Error Messages and Exit Codes

Exit codes:

  • 0 - Success
  • 1 - General error (validation, not found, invalid transition)
  • 2 - Usage error (wrong arguments, unknown flag)

Error format:

error[CODE]: <message>
  --> <location>
  |
  | <context>

Example:

error[E0102]: RFC not found: RFC-9999
  --> gov/rfc/RFC-9999
  |
  | Run 'govctl rfc list' to see available RFCs

Diagnostic codes: Use existing govctl error taxonomy (E0xxx, W0xxx).

5. Terminal Capability Detection

Color output:

  • Auto-detect TTY with atty or is-terminal crate
  • Respect NO_COLOR environment variable
  • Respect --color <always|never|auto> flag (future)

Interactive prompts:

  • Only show in TTY mode
  • Skip if --force flag provided
  • Skip if stdin is not TTY

Progress indicators:

  • Use indicatif crate for long operations (>2s expected)
  • Show progress for: render, check, bulk operations
  • Suppress if --quiet or non-TTY

6. Confirmation Prompts

Destructive operations require confirmation unless --force:

  • delete (clauses, work items)
  • deprecate (RFCs, clauses)
  • supersede (RFCs, ADRs, clauses)

Prompt format:

Delete work item WI-2026-01-19-001? [y/N]

Behavior:

  • Default to “no” (capital N)
  • Accept: y, Y, yes, Yes, YES
  • Reject: n, N, no, No, NO, empty, anything else
  • Timeout: none (wait indefinitely)

7. Filter Syntax

Per RFC-0002:C-CRUD-VERBS, list commands support filtering.

Simple filter (shorthand):

govctl rfc list draft          # filter by status
govctl work list active        # filter by status

Explicit filter (future extension):

govctl rfc list --filter status=draft
govctl rfc list --filter phase=impl,status=normative

Implementation: Start with simple substring matching, defer explicit expressions to future ADR.

8. Stdin Handling

When --stdin flag is present:

  • Read entire stdin to EOF
  • Trim trailing newline only (preserve internal newlines)
  • Empty stdin is valid (allows heredocs with empty content)

HEREDOC pattern (recommended for multi-line):

govctl clause set RFC-0001:C-SCOPE text --stdin <<'EOF'
This clause defines the scope.
EOF

9. Dry-Run Behavior

Global --dry-run flag:

  • Shows what would be written
  • Prints diff or file preview
  • Exits with 0 (success simulation)
  • No short flag (safety flags should be explicit)

Output format:

[DRY RUN] Would write: gov/rfc/RFC-0001/rfc.json
--- before
+++ after

10. Subcommand Organization (clap)

Structure:

Use nested Subcommand enums for resource-first organization.
Resource commands contain their own verb subcommands.
Global commands remain at top level.

Backward compatibility aliases: Emit deprecation warning when old verb-first commands are used.

Consequences

Positive:

  1. Consistent UX: All commands follow the same patterns. Users learn once, apply everywhere.

  2. Agent-friendly: Predictable flags, stable error codes, machine-readable output defaults make automation reliable.

  3. Maintainable: Implementation conventions documented, not scattered across code comments.

  4. Discoverable: Distinct verbs (set, add, remove, tick) are immediately understandable. Users can explore via --help without reading documentation.

  5. Safe: Confirmation prompts prevent accidental destructive actions. Dry-run mode enables testing before committing.

  6. Debuggable: Structured error messages with codes and locations make issues easy to diagnose.

Negative:

  1. Multiple verbs: Users must learn four verbs for mutation operations (set, add, remove, tick) instead of one. However, each verb’s meaning is immediately clear.

  2. Testing complexity: Need tests for TTY detection, confirmation prompts, dry-run mode, error formatting, etc.

Trade-offs Accepted:

  • Clarity over brevity: Distinct verbs are more verbose than a unified edit command, but much easier to understand. add and remove are clearer than edit +value and edit -value.

  • Discoverability over uniformity: Four clear verbs are better than one verb with special syntax that requires documentation.

  • Safety over speed: Confirmation prompts slow down destructive operations. This is intentional to prevent accidents.