Skip to main content
New: Deck Doctor. Upload your deck, get CPO-level feedback. 7-day free trial.
TemplateFREEā±ļø 15 minutes

CLI Tool Specification Template

A structured template for specifying CLI tool features, commands, flags, and output formats. Covers command hierarchy, argument validation, help text,...

By Tim Adair• Last updated 2026-03-05
CLI Tool Specification Template preview

CLI Tool Specification Template

Free CLI Tool Specification Template — open and start using immediately

or use email

Instant access. No spam.

Need a custom version?

Forge AI generates PM documents customized to your product, team, and goals. Get a draft in seconds, then refine with AI chat.

Generate with Forge AI

What This Template Is For

A CLI tool specification defines every command, flag, argument, and output format your command-line tool will support. It serves as the contract between the engineering team building the tool and the developers who will use it daily.

Many CLI tools ship without a written spec and accumulate inconsistent flag names, conflicting output formats, and undocumented behaviors. A specification written before implementation prevents these problems. It forces you to think through the full command tree, validate naming conventions, and design error messages before writing code.

This template works for internal developer tools, open-source CLIs, and platform SDKs. If you are deciding whether a CLI is the right interface at all, the RICE framework can help you score it against alternatives like a web dashboard or API-only approach. For the broader context of building developer-facing products, the Technical PM Handbook covers how to gather requirements from engineering users. You can also reference the feature spec template for individual commands that need deeper product detail, or check developer experience for principles that apply to CLI design.


When to Use This Template

Use this template when you are building a new CLI tool or adding a major command group to an existing one. It is most useful when multiple engineers will contribute commands and you need a consistent design language across the tool.

Skip this template for one-off scripts or internal automation that only one person will run. Those can be documented with inline comments and a README.


How to Use This Template

  1. Start by defining the tool name, purpose, and installation method. This section should be clear enough that someone unfamiliar with the project understands what the tool does in 30 seconds.
  2. Map out the full command hierarchy before specifying individual commands. Group related commands under subcommands (e.g., tool auth login, tool auth logout).
  3. For each command, document required arguments, optional flags, default values, and output format. Be explicit about what happens when required arguments are missing.
  4. Write example invocations for every command. Developers learn CLIs by copying examples, not by reading flag descriptions.
  5. Define a consistent error format and exit code convention. Every command should return 0 on success and non-zero on failure, with structured error output that scripts can parse.

The Template

Tool Overview

FieldDetails
Tool Name[name]
Purpose[One sentence describing what the tool does]
Target Users[Who will use this tool]
Installation[How to install: npm, brew, binary, etc.]
Runtime Requirements[Node.js version, OS support, dependencies]
Config File[Path and format: ~/.toolrc, .tool.yaml, etc.]
Auth Method[API key, OAuth, token file, none]

Command Hierarchy

tool
ā”œā”€ā”€ init                    [Initialize a new project]
ā”œā”€ā”€ config
│   ā”œā”€ā”€ set                 [Set a configuration value]
│   ā”œā”€ā”€ get                 [Get a configuration value]
│   └── list                [List all configuration values]
ā”œā”€ā”€ [command-group-1]
│   ā”œā”€ā”€ [subcommand-1]      [Description]
│   ā”œā”€ā”€ [subcommand-2]      [Description]
│   └── [subcommand-3]      [Description]
ā”œā”€ā”€ [command-group-2]
│   ā”œā”€ā”€ [subcommand-1]      [Description]
│   └── [subcommand-2]      [Description]
└── version                 [Print version information]

Global Flags

FlagShortTypeDefaultDescription
--help-hbooleanfalseShow help for any command
--verbose-vbooleanfalseEnable verbose output
--quiet-qbooleanfalseSuppress non-error output
--format-fstringtextOutput format: text, json, yaml
--config-cstring~/.toolrcPath to config file
--no-colorbooleanfalseDisable colored output

Command: [command-name]

Description. [What this command does]

Usage:

tool [command-name] <required-arg> [optional-arg] [flags]

Arguments:

ArgumentRequiredTypeDescription
Yesstring[What this argument represents]
[optional-arg]Nostring[What this argument represents, default value]

Flags:

FlagShortTypeDefaultDescription
--flag-1-astring[Description]
--flag-2-bbooleanfalse[Description]
--dry-runbooleanfalsePreview changes without executing

Output (text):

[Expected output format]

Output (json):

{
  "status": "success",
  "data": {}
}

Examples:

# Basic usage
tool [command-name] my-arg

# With flags
tool [command-name] my-arg --flag-1 value --dry-run

# Piped output
tool [command-name] my-arg --format json | jq '.data'

[Repeat for each command]


Error Handling

Exit codes:

CodeMeaning
0Success
1General error
2Invalid arguments or flags
3Authentication failure
4Network error
5Resource not found

Error output format:

Error: [Human-readable message]
Code: [ERROR_CODE]
Hint: [Suggested fix]

Run 'tool [command] --help' for usage information.

JSON error format:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message",
    "hint": "Suggested fix"
  }
}

Configuration File Format

# ~/.toolrc
version: 1
defaults:
  format: text
  verbose: false
profiles:
  default:
    [config-key]: [value]
  staging:
    [config-key]: [value]

Filled Example: Deployment CLI (shipit)

Tool Overview

FieldDetails
Tool Nameshipit
PurposeDeploy applications to staging and production environments
Target UsersBackend and frontend engineers on the platform team
Installationnpm install -g @acme/shipit
Runtime RequirementsNode.js 18+, macOS or Linux
Config File.shipit.yaml in project root
Auth MethodOAuth token stored in ~/.shipit/credentials

Command Hierarchy

shipit
ā”œā”€ā”€ init                    Initialize shipit in current project
ā”œā”€ā”€ auth
│   ā”œā”€ā”€ login               Authenticate with the platform
│   ā”œā”€ā”€ logout              Clear stored credentials
│   └── status              Show current auth status
ā”œā”€ā”€ deploy
│   ā”œā”€ā”€ create              Create a new deployment
│   ā”œā”€ā”€ status              Check deployment status
│   ā”œā”€ā”€ logs                Stream deployment logs
│   └── rollback            Roll back to previous version
ā”œā”€ā”€ env
│   ā”œā”€ā”€ list                List environments
│   ā”œā”€ā”€ set                 Set an environment variable
│   └── get                 Get an environment variable
└── version                 Print shipit version

Command: deploy create

Description. Create a new deployment from the current branch to the specified environment.

Usage:

shipit deploy create <environment> [flags]

Arguments:

ArgumentRequiredTypeDescription
YesstringTarget environment: staging, production

Flags:

FlagShortTypeDefaultDescription
--branch-bstringcurrent branchGit branch to deploy
--tag-tstringSpecific git tag to deploy
--skip-testsbooleanfalseSkip pre-deploy test suite
--dry-runbooleanfalsePreview deployment plan without executing
--notify-nstringSlack channel for deployment notifications

Examples:

# Deploy current branch to staging
shipit deploy create staging

# Deploy specific tag to production
shipit deploy create production --tag v2.4.1

# Preview deployment without executing
shipit deploy create production --dry-run

# Deploy and notify the team
shipit deploy create staging --notify #platform-deploys

Output (text):

Deploying acme-api@v2.4.1 to production...

  Build:     ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ 100%
  Tests:     ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ 100%
  Deploy:    ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘ā–‘  40%

Deployment dep_8a3b2c1d in progress.
Run 'shipit deploy status dep_8a3b2c1d' to check progress.

Output (json):

{
  "id": "dep_8a3b2c1d",
  "environment": "production",
  "branch": "main",
  "tag": "v2.4.1",
  "status": "in_progress",
  "started_at": "2026-03-05T14:30:00Z",
  "url": "https://deploy.acme.com/dep_8a3b2c1d"
}

Key Takeaways

  • Define the full command hierarchy before specifying individual commands to ensure consistent naming
  • Include example invocations for every command. Developers learn CLIs by copying examples
  • Use consistent exit codes so scripts can reliably detect and handle failures
  • Support both human-readable and machine-parseable output formats (text and JSON)
  • Document error messages with actionable hints that tell the user how to fix the problem
  • Write the spec before implementation to prevent naming drift and inconsistent flag behavior

About This Template

Created by: Tim Adair

Last Updated: 3/5/2026

Version: 1.0.0

License: Free for personal and commercial use

Frequently Asked Questions

How many subcommand levels should a CLI tool have?+
Limit nesting to two levels maximum (e.g., `tool group command`). Three or more levels make the tool harder to discover and type. If you find yourself needing a third level, consider flattening the hierarchy or splitting into separate tools. Most successful CLIs like `git`, `docker`, and `kubectl` stay at two levels.
Should flags use single-dash or double-dash prefixes?+
Use double-dash for full flag names (`--verbose`) and single-dash for single-character shortcuts (`-v`). This is the POSIX convention and what developers expect. Allow combining short flags (`-vq` for `--verbose --quiet`). Never use single-dash for multi-character flags.
When should a CLI output JSON versus plain text?+
Default to human-readable text for interactive use. Support `--format json` for scripts and automation. Some teams add `--format yaml` as well. The key rule is that JSON output should be stable. If you change the JSON schema, treat it as a breaking change and version your output format.
How do I handle authentication in a CLI?+
Store credentials in a dotfile in the user's home directory (e.g., `~/.toolrc/credentials`). Support environment variables as overrides for CI/CD environments (`TOOL_API_KEY`). Never require the user to pass credentials as command-line arguments since those appear in shell history. The [Technical PM Handbook](/technical-pm-guide) covers security considerations for developer-facing tools.
Should I include a --dry-run flag for every command?+
Include `--dry-run` on every command that modifies state (deployments, deletions, configuration changes). It is not needed for read-only commands like `list` or `status`. The dry-run output should show exactly what would happen, not just "no changes made." ---

Explore More Templates

Browse our full library of PM templates, or generate a custom version with AI.

Free PDF

Like This Template?

Subscribe to get new templates, frameworks, and PM strategies delivered to your inbox.

or use email

Join 10,000+ product leaders. Instant PDF download.

Want full SaaS idea playbooks with market research?

Explore Ideas Pro →