
cli-generator
by kjgarza
SKILL.md
name: cli-generator description: Generate AI-friendly Python CLIs using Click, Pydantic, and uv. Use when user wants to create a new CLI tool that follows best practices for agentic coding environments. allowed-tools: Read, Grep, Glob, Bash, Edit, Write
AI-Friendly CLI Generator Skill
Generate Python command-line interfaces optimized for AI agents and agentic coding environments.
Core Principle: Every Output is a Prompt
In an agentic coding environment, every interaction with a CLI tool is a turn in a conversation. The tool's output—whether it succeeds or fails—should be designed as a helpful, guiding prompt for the agent's next action.
Tech Stack
- Python - Primary language
- Click - CLI framework
- Pydantic - Data validation and response models
- Rich - Terminal formatting and tables
- uv - Package management
Project Structure
my-cli/
├── pyproject.toml
├── README.md
├── src/
│ └── my_cli/
│ ├── __init__.py
│ ├── main.py # CLI entry point
│ ├── commands/ # Command modules
│ │ └── __init__.py
│ ├── models/
│ │ ├── __init__.py
│ │ └── responses.py # Pydantic response models
│ ├── output/
│ │ ├── __init__.py
│ │ └── conversational.py # AI-friendly output
│ └── core/
│ ├── __init__.py
│ ├── client.py # API client
│ └── config.py # Configuration
└── tests/
Quick Start
- Create project directory:
mkdir my-cli && cd my-cli
- Initialize with uv:
uv init
- Add dependencies to
pyproject.toml:
dependencies = [
"click>=8.1.0",
"rich>=13.0.0",
"pydantic>=2.0.0",
]
- Create the source structure:
mkdir -p src/my_cli/{commands,models,output,core}
touch src/my_cli/__init__.py
touch src/my_cli/{commands,models,output,core}/__init__.py
- Copy templates from
templates/directory
AI-Friendly Output Patterns
Pattern 1: Success Output
A successful output confirms the action AND suggests next steps with exact commands:
Bad (Traditional):
Success!
Good (AI-Friendly):
✅ Found 4 documents matching 'AI'
📋 Available Resources:
• Total documents: 4
• First document ID: 2oLo0Z72BR
• First document name: AI experience design
📊 Results:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Name ┃ ID ┃ Updated ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ AI experience design │ 2oLo0Z72BR │ 2025-11-26 │
└─────────────────────────────┴────────────┴────────────┘
💡 What's next? Try these commands:
1. 👁️ mycli show 2oLo0Z72BR - View document details
2. 📤 mycli export 2oLo0Z72BR --format json - Export as JSON
Pattern 2: Error Output (Three Parts)
Every error must include:
- What went wrong - Clear description
- How to fix - Step-by-step instructions
- What's next - Commands to try after fixing
Example:
❌ Command failed
Authentication error
🔍 What went wrong:
The Coda API returned an error: API key is invalid or expired.
🔧 How to fix:
1. Check your internet connection
2. Verify your API key is correct
3. Try regenerating your API token
💡 What's next:
• mycli auth test - Test your authentication
• mycli auth setup - Re-run interactive setup
Pattern 3: Help Text with Examples
Always include working examples in --help:
@click.command(
epilog="""
Examples:
# Search for documents
mycli search "machine learning"
# Export a table as JSON
mycli export DOC_ID TABLE_ID --format json
# List all your documents
mycli list --mine
"""
)
def search(query: str):
"""Search for documents matching a query."""
pass
Code Patterns
Response Models (models/responses.py)
"""Pydantic models for CLI command responses."""
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class Suggestion(BaseModel):
"""A suggested next command with description."""
command: str = Field(..., description="The exact command to run")
description: str = Field(..., description="What the command does")
category: Optional[str] = Field(None, description="Category: view, export, search, etc.")
class ErrorDetail(BaseModel):
"""Detailed error following 'what/how/next' pattern."""
what_went_wrong: str = Field(..., description="Clear explanation of the failure")
how_to_fix: List[str] = Field(..., description="Step-by-step fix instructions")
whats_next: List[Suggestion] = Field(..., description="Commands to try after fixing")
error_code: Optional[str] = Field(None, description="Machine-readable error code")
class CommandResult(BaseModel):
"""Result of a CLI command with conversational context."""
success: bool = Field(..., description="Whether command succeeded")
message: str = Field(..., description="Primary result message")
context: Dict[str, Any] = Field(default_factory=dict, description="Resource IDs and metadata")
data: Optional[List[Any]] = Field(None, description="Structured data results")
suggestions: List[Suggestion] = Field(default_factory=list, description="Suggested next commands")
error: Optional[ErrorDetail] = Field(None, description="Error details if failed")
Conversational Output (output/conversational.py)
"""Conversational output following 'Every Output is a Prompt' pattern."""
from typing import Any, Optional, List
from rich.console import Console
from rich.table import Table
from .responses import CommandResult, Suggestion
class ConversationalOutput:
"""Output manager that makes every interaction conversational."""
def __init__(self, console: Console, show_suggestions: bool = True):
self.console = console
self.show_suggestions = show_suggestions
def success(self, result: CommandResult) -> None:
"""Display success with context and suggestions."""
# Main success message
self.console.print(f"✅ {result.message}", style="bold green")
# Show context (resource IDs, counts, etc.)
if result.context:
self.console.print("\n📋 Available Resources:", style="bold blue")
for key, value in result.context.items():
self.console.print(f" • {key}: [cyan]{value}[/cyan]")
# Show data in table format
if result.data:
self._render_data(result.data)
# Show suggested next commands
if self.show_suggestions and result.suggestions:
self._render_suggestions(result.suggestions)
def error(self, result: CommandResult) -> None:
"""Display error with three-part pattern."""
if not result.error:
self.console.print(f"❌ {result.message}", style="bold red")
return
error = result.error
# What went wrong
self.console.print("❌ Command failed", style="bold red")
self.console.print(f" {result.message}")
self.console.print("\n🔍 What went wrong:", style="bold yellow")
self.console.print(f" {error.what_went_wrong}")
# How to fix
if error.how_to_fix:
self.console.print("\n🔧 How to fix:", style="bold green")
for i, step in enumerate(error.how_to_fix, 1):
self.console.print(f" {i}. {step}")
# What's next
if error.whats_next:
self.console.print("\n💡 What's next:", style="bold blue")
for suggestion in error.whats_next:
self.console.print(
f" • [cyan]{suggestion.command}[/cyan] - {suggestion.description}"
)
def _render_data(self, data: List[Any]) -> None:
"""Render structured data as a table."""
if not data:
return
self.console.print("\n📊 Results:", style="bold blue")
table = Table(show_header=True, header_style="bold magenta")
# Build table from first item's keys
if isinstance(data[0], dict):
for key in list(data[0].keys())[:5]: # Limit columns
table.add_column(key.replace("_", " ").title())
for item in data[:10]: # Limit rows
table.add_row(*[str(v)[:40] for v in list(item.values())[:5]])
self.console.print(table)
def _render_suggestions(self, suggestions: List[Suggestion]) -> None:
"""Render suggested next commands."""
self.console.print("\n💡 What's next? Try these commands:", style="bold yellow")
emoji_map = {
"view": "👁️", "export": "📤", "search": "🔍",
"create": "✨", "edit": "✏️", "auth": "🔐",
}
for i, s in enumerate(suggestions[:5], 1):
emoji = emoji_map.get(s.category, "")
self.console.print(f" {i}. {emoji}[cyan]{s.command}[/cyan] - {s.description}")
Main CLI Entry Point (main.py)
"""Main CLI entry point."""
import click
from rich.console import Console
from .models.responses import CommandResult, Suggestion, ErrorDetail
from .output.conversational import ConversationalOutput
console = Console()
output = ConversationalOutput(console)
@click.group()
@click.version_option()
def cli():
"""My CLI tool - AI-friendly command interface.
Examples:
mycli search "query"
mycli show RESOURCE_ID
mycli export RESOURCE_ID --format json
"""
pass
@cli.command(epilog="""
Examples:
mycli search "machine learning"
mycli search "climate" --limit 5
""")
@click.argument("query")
@click.option("--limit", default=10, help="Maximum results to return")
def search(query: str, limit: int):
"""Search for resources matching a query."""
try:
# Your search logic here
results = [] # fetch_results(query, limit)
result = CommandResult(
success=True,
message=f"Found {len(results)} results for '{query}'",
context={
"Query": query,
"Total results": len(results),
},
data=results,
suggestions=[
Suggestion(
command=f"mycli show {results[0]['id']}" if results else "mycli list",
description="View details" if results else "List all resources",
category="view"
),
Suggestion(
command=f"mycli export {results[0]['id']} --format json" if results else "mycli search 'other'",
description="Export as JSON" if results else "Try another search",
category="export" if results else "search"
),
]
)
output.success(result)
except Exception as e:
result = CommandResult(
success=False,
message="Search failed",
error=ErrorDetail(
what_went_wrong=str(e),
how_to_fix=[
"Check your query syntax",
"Verify your authentication",
],
whats_next=[
Suggestion(command="mycli auth test", description="Test authentication", category="auth"),
]
)
)
output.error(result)
if __name__ == "__main__":
cli()
pyproject.toml Template
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my-cli"
version = "0.1.0"
description = "AI-friendly CLI tool"
requires-python = ">=3.8"
dependencies = [
"click>=8.1.0",
"rich>=13.0.0",
"pydantic>=2.0.0",
"python-dotenv>=1.0.0",
]
[project.scripts]
mycli = "my_cli.main:cli"
[tool.hatch.build.targets.wheel]
packages = ["src/my_cli"]
Reference Implementation
See the coda-cli project for a complete working example:
- Location:
.claude/skills/coda/scripts/coda-cli/ - Key files:
src/coda_cli/output/conversational.py- Full output implementationsrc/coda_cli/models/responses.py- Complete response modelspyproject.toml- Project configuration
Checklist for New CLIs
- Every success output includes suggested next commands
- Every error includes: what went wrong, how to fix, what's next
- All commands have
epilogwith usage examples - Response models use Pydantic for validation
- Rich is used for formatted terminal output
- Context includes resource IDs for follow-up commands
- Table output is limited to prevent overwhelming agents
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon