Skip to content

CLI Style Design Draft

This is a discussion draft for explaining what ChatStyle should standardize, what it should not standardize, and why these conventions matter for future chatxxx tools under ChatArch. It is not a final API contract; it is a design map to avoid X-Y problems.

Background And Intent

ChatArch is a GitHub organization for building reusable tools. Future projects will often follow the chatxxx CLI shape.

These CLI projects are usually initialized through chattool pypi / chatpypi init. ChatStyle should become the default dependency injected by those templates, so new tools inherit the same CLI behavior convention instead of copying interaction code into every project.

Those tools have different business domains, but they repeatedly face the same CLI interaction questions:

  • Should missing arguments trigger prompts?
  • When is interactive mode allowed?
  • How do scripts and CI disable prompts?
  • How should tokens, passwords, and API keys be displayed?
  • How should select, checkbox, and confirm behave?
  • Where do defaults come from and what should prompts display?
  • How should errors guide the next action?
  • How should projects generated by chatpypi init get the same CLI behavior by default?

ChatStyle is not meant to become a complex TUI framework, and it should not abstract every possible CLI framework feature. It should provide a small and stable set of shared interaction conventions and runtime helpers for ChatArch CLI tools. If a capability cannot be reused by multiple chatxxx tools, it should not enter ChatStyle.

One-Sentence Definition

ChatStyle is the interaction convention and lightweight runtime for ChatArch CLI tools.

Downstream projects should be able to say:

We own business logic; ChatStyle owns missing-argument prompting, interactive switches, secret display, multi-select behavior, and common CLI output conventions.

Goals

Consistent Behavior

CLI tools in the same organization should behave consistently in common situations:

  • -i forces the current command's interactive flow.
  • -I disables prompts for scripts and CI.
  • Recoverable missing arguments may prompt only when TTY is available.
  • Non-TTY environments never block on input.
  • Secrets are always masked when displayed.
  • Select and checkbox behavior is consistent.

Lower Template Complexity

Projects generated by chatpypi init should not copy prompt and resolver implementation code. Templates should depend on:

dependencies = ["click>=8.0", "chatstyle"]

Then commands should use ChatStyle public APIs.

Keep The Base Library Generic

ChatStyle should not know about ChatTool, a specific API, a specific config path, or a specific business command. It provides reusable CLI interaction building blocks.

Stay Automation-Friendly

ChatArch tools are often used by scripts, CI, GitHub Actions, or cron. Interactive convenience must not break automation.

Every prompt capability needs a non-interactive path: complete arguments, defaults, or fast failure.

Non-Goals

ChatStyle does not:

  • Build a full TUI application framework.
  • Own product command flows.
  • Read or write downstream project configuration.
  • Run installers, system commands, sudo, or remote APIs.
  • Define ChatTool-specific copy.
  • Force every project to have the same product branding.

ChatStyle standardizes CLI behavior and baseline presentation, not all business experience. The decision rule is whether a capability belongs to the shared ChatArch CLI convention, not whether it is convenient for one tool.

Conceptual Model

ChatStyle can be described in four layers:

User Intent
Command Input Contract
Interaction Runtime
Presentation Primitives
Business Logic

User Intent

A user expresses intent through:

  • Explicit arguments: --name demo --token xxx
  • Interactive answers: prompt input for missing values
  • Defaults: values from commands, config, or environment

ChatStyle only merges those inputs into complete values. It does not decide what the business command does.

Command Input Contract

The command input contract describes what fields a command needs. This is currently CommandSchema:

  • CommandField: field name, prompt copy, type, default, required flag, sensitive flag.
  • CommandConstraint: cross-field validation.
  • CommandSchema: field and constraint collection for one command.

This makes missing-argument recovery declarative instead of hand-written if/else in each command.

Interaction Runtime

The interaction runtime decides when to prompt, how to fail, and how to merge inputs:

  • interactive=True: the user explicitly requests prompts.
  • interactive=False: the user explicitly disables prompts.
  • interactive=None: automatic mode.
  • TTY available: prompting is possible.
  • TTY unavailable: no prompt; use existing defaults or fail fast.

Presentation Primitives

Presentation primitives are the low-level interaction and display blocks:

  • text/path input
  • confirm
  • select
  • checkbox
  • secret masking
  • heading/note/status
  • command suggestion
  • priority chain

These primitives should stay business-neutral.

Business Logic

Business logic belongs to downstream projects, for example:

  • Creating GitHub PRs.
  • Publishing PyPI packages.
  • Generating certificates.
  • Updating DNS records.
  • Writing config files.
  • Calling product APIs.

ChatStyle does not enter this layer.

Missing Argument Convention

Missing-argument recovery is one of ChatStyle's core values.

Recommended rules:

  1. Do not set recoverable Click options as required=True.
  2. Use CommandField(required=True) for fields required by business logic.
  3. Call resolve_command_inputs() inside the callback.
  4. Let the resolver prompt or fail based on the interactive policy.
import click

from chatstyle import CommandField, CommandSchema, add_interactive_option, resolve_command_inputs

SCHEMA = CommandSchema(
    name="publish",
    fields=(
        CommandField("package", prompt="package name", required=True),
        CommandField("repository", prompt="repository", default="pypi"),
        CommandField("token", prompt="api token", sensitive=True, required=True),
    ),
)


@click.command()
@click.option("--package", required=False)
@click.option("--repository", required=False)
@click.option("--token", required=False)
@add_interactive_option
def publish(package, repository, token, interactive):
    values = resolve_command_inputs(
        schema=SCHEMA,
        provided={"package": package, "repository": repository, "token": token},
        interactive=interactive,
        usage="Usage: publish --package TEXT [--repository TEXT] [--token TEXT] [-i|-I]",
    )
    run_publish(values)

Expected behavior:

  • Complete arguments: run directly.
  • Missing arguments with TTY: prompt for recoverable fields.
  • Missing arguments with -i: force prompts; fail if TTY is unavailable.
  • Missing arguments with -I: do not prompt; fail fast.
  • Missing arguments without TTY: do not prompt; fail fast.

Interactive Switches

ChatStyle standardizes tri-state interactive mode:

State Source Meaning
True -i / --interactive Force the current command's interactive flow
False -I / --no-interactive Disable prompts for automation
None unspecified Automatic mode; prompt only when TTY is available and recoverable fields are missing

Important boundaries:

  • -i is not a global wizard switch. It applies to the current command.
  • -I is an automation contract and must guarantee no blocking prompts.

Masking And Secrets

Secrets include:

  • tokens
  • passwords
  • API keys
  • app secrets
  • webhook secrets
  • private key fragments

Rules:

  • Hide input.
  • Mask displayed values.
  • Never write raw secrets to logs, exceptions, or summaries.
  • If empty input keeps the old value, the prompt must say so.
current: ab****yz
Token (enter to keep):

Choice And Multi-Select

ChatStyle provides a shared choice representation so downstream projects do not need to depend on questionary details.

Principles:

  • Use questionary for better UX when available.
  • Fall back to Click when optional dependencies are unavailable.
  • Labels are for humans; values are for programs.
  • Empty choices should not become business exceptions; callers decide how to handle them.
  • Checkbox supports multi-select but should not grow into a complex TUI state machine.

Output Style

The output layer should provide common presentation, not business interpretation.

Reasonable common helpers:

  • heading
  • note
  • success/warning/error/info status
  • key-value summary
  • suggested commands
  • priority chain

Helpers that need caution:

  • setup-specific helpers
  • command-specific summaries
  • automatic system command execution
  • business-specific recovery suggestions

Rethinking Setup

"Setup-stage output, suggested commands, and config-priority display" originally came from setup command scenarios, but these capabilities are not setup-specific.

A better abstraction is:

  • Flow-stage display: useful for any long-running command.
  • Suggested command display: useful for doctor, deploy, repair, and setup.
  • Config-priority display: useful for any CLI that reads config.

So the implementation should avoid giving setup special treatment. The current direction is to move these helpers into generic output/flow APIs:

render_stage("Check environment")
render_suggested_commands(["sudo systemctl restart demo"])
render_priority_chain(["CLI option", "ENV", "config file", "default"])

chatstyle.setup is not part of the first public core; setup-like needs are handled by generic flow/output helpers.

ChatStyle And chattool pypi

ChatStyle's main delivery path is through chattool pypi / chatpypi init when creating ChatArch CLI tools. Templates should generate minimal code:

  • Add the chatstyle dependency.
  • Use Click as the command framework.
  • Use CommandSchema for inputs.
  • Use add_interactive_option() for -i/-I.
  • Use resolve_command_inputs() for missing-argument recovery.
  • Use mask_secret() and select/checkbox helpers for common interactions.

Templates should not copy ChatStyle internals.

API Layering Proposal

Stable Public API

Good candidates for chatstyle.__init__:

  • CommandField
  • CommandSchema
  • CommandConstraint
  • resolve_command_inputs
  • add_interactive_option
  • ask_text
  • ask_path
  • ask_confirm
  • ask_select
  • ask_checkbox
  • mask_secret

Candidate Public API

Names and boundaries need discussion:

  • render_heading
  • render_note
  • render_status
  • render_suggested_commands
  • render_priority_chain
  • render_flow_start
  • render_stage
  • render_success
  • render_warning
  • render_failure
  • render_commands
  • create_choice
  • get_separator

Avoid Or Expose Carefully

  • Underscore helpers.
  • Helpers tied to one business scenario.
  • APIs that require optional dependency objects as parameters.
  • APIs that execute system commands or write files.

Module Boundary Draft

Suggested long-term modules:

chatstyle.input       input contract, resolution, Click integration
chatstyle.tui         prompt primitives, choice representation, adapters
chatstyle.render      common output, flow stages, suggested commands, priority chains
chatstyle.security    secret display and input
chatstyle.core        TTY / interactive policy, error helpers, shared constants
chatstyle.patterns    cross-module recipes

flow is the core flow-display module; setup-like scenarios are composed from flow / output helpers.

Design Principles

Small Core

Prefer fewer APIs over turning single-project convenience helpers into foundational contracts.

Graceful Degradation

Rich, questionary, and prompt_toolkit are UX improvements, not runtime requirements.

Automation First

Every interactive feature must be bypassable through explicit arguments or -I.

Declarative First

Missing-argument recovery and defaults should be described by schemas instead of copied if/else blocks in callbacks.

Business Outside

ChatStyle does not know what publish, certificate, DNS, PR, or setup means.

Open Questions

  1. Should chatstyle.setup be reintroduced later as a scenario wrapper?
  2. What stable output APIs should exist: heading/note only, or status/summary/table as well?
  3. Should CommandSchema support richer choice value/label objects instead of Sequence[str]?
  4. Is prompt_if_missing the right name, or should it become prompt_if_defaulted / confirm_default?
  5. Do errors need structured error codes for automation?
  6. How complex should the chatpypi init example be: text/path/secret only, or also select/checkbox?
  7. Do we need project presets like standard_cli_schema(), or should everything stay explicit?
  8. Should docs define ChatStyle as "specification first, runtime second"?

Current Recommendation

Short term:

  • Keep CommandSchema as the core.
  • Strengthen prompt, mask, interactive, and Click integration tests.
  • Keep setup-scenario behavior behind generic flow/output APIs.
  • Explain concepts and conventions before APIs in docs.
  • Let chatpypi init depend on ChatStyle instead of copying implementation.

Long term:

  • ChatStyle should become the single source of ChatArch CLI behavior conventions.
  • Downstream projects should receive interaction updates through dependency upgrades.
  • Every new API should answer: does this belong to the shared ChatArch CLI convention, and is it useful for at least two chatxxx tools?