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.
/plugin marketplace add kjgarza/marketplace-claude/plugin install kjgarza-senior-software-developer-plugins-senior-software-developer@kjgarza/marketplace-claudeThis skill is limited to using the following tools:
templates/conversational.py.templatetemplates/main.py.templatetemplates/pyproject.toml.templatetemplates/responses.py.templateGenerate Python command-line interfaces optimized for AI agents and agentic coding environments.
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.
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/
mkdir my-cli && cd my-cli
uv init
pyproject.toml:dependencies = [
"click>=8.1.0",
"rich>=13.0.0",
"pydantic>=2.0.0",
]
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
templates/ directoryA 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
Every error must include:
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
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
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")
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.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()
[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"]
See the coda-cli project for a complete working example:
.claude/skills/coda/scripts/coda-cli/src/coda_cli/output/conversational.py - Full output implementationsrc/coda_cli/models/responses.py - Complete response modelspyproject.toml - Project configurationepilog with usage examplesThis skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.