agentsclimarketplace

Adding cli command

Skill ComeOnOliver/skillshub/skills/https-deeplearning-ai/sc-agent-skills-files/adding-cli-command

Provides Typer templates, handles registration, and ensures consistency. ALWAYS use this skill when adding or modifying CLI commands. Use when user requests to add/create/implement/build/write a new command (e.g., "add edit command", "create search feature") OR update/modify/change/edit an existing command.From its SKILL.md

Install
npx -y skills add ComeOnOliver/skillshub --skill adding-cli-command

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

3.6 KB, 837 tokens by cl100k_base, as published. Nobody here has run it

Adding CLI Commands

Templates and workflow for adding or updating Typer CLI commands.

<cli_app> refers to the name of your CLI application (e.g., task, myapp, todo).

Workflow

  1. Identify command type (single command, command group, or destructive)
  2. Create file in src/<cli_app>/commands/<command>.py
  3. Use appropriate template below
  4. Register in src/<cli_app>/commands/__init__.py

Template A: Single Command

For commands taking arguments directly (<cli_app> add "item").

import typer
from typing import Annotated

from <cli_app>.storage import add_task
from <cli_app>.display import display
from <cli_app>.constants import EXIT_INVALID_INPUT

app = typer.Typer()


@app.command()
def add(
    title: Annotated[str, typer.Argument(help="task title")],
    priority: Annotated[str, typer.Option("--priority", "-p", help="priority level")] = "low",
):
    """Add a new task."""
    if not title.strip():
        display.error("Title cannot be empty")
        raise typer.Exit(EXIT_INVALID_INPUT)

    task = add_task(title=title, priority=priority)
    display.success(f"Added '{task.title}'")

Template B: Command Group

For commands with subcommands (<cli_app> db migrate, <cli_app> db status).

import typer

from <cli_app>.storage import storage
from <cli_app>.display import display

app = typer.Typer(help="Database operations.")


@app.command()
def migrate():
    """Run database migrations."""
    storage.migrate()
    display.success("Migrations complete")


@app.command()
def status():
    """Show database status."""
    info = storage.get_status()
    display.info(f"Version: {info.version}")

Template C: Destructive Command

For deletion with confirmation.

import typer
from typing import Annotated

from <cli_app>.storage import get_task, delete_task
from <cli_app>.display import display
from <cli_app>.constants import EXIT_ERROR

app = typer.Typer()


@app.command()
def clear(
    task_id: Annotated[int, typer.Argument(help="task ID to delete")],
    force: Annotated[bool, typer.Option("--force", "-f", help="skip confirmation")] = False,
):
    """Delete a task permanently."""
    task = get_task(task_id)
    if not task:
        display.error(f"Task {task_id} not found")
        raise typer.Exit(EXIT_ERROR)

    if not force:
        confirm = typer.confirm(f"Delete '{task.title}'?", default=False)
        if not confirm:
            display.info("Cancelled")
            raise typer.Abort()

    delete_task(task_id)
    display.success(f"Deleted '{task.title}'")

Registration

Register in commands/__init__.py:

import typer

from .add import app as add_app
from .clear import app as clear_app

app = typer.Typer(help="<cli_app> CLI.", no_args_is_help=True)

# Single commands - add WITHOUT name
app.add_typer(add_app)
app.add_typer(clear_app)

# Command groups - add WITH name
# app.add_typer(db_app, name="db")

Conventions

RuleExample
ArgumentsAnnotated[str, typer.Argument(help="...")]
OptionsAnnotated[str, typer.Option("--name", "-n", help="...")]
DocstringsImperative mood, < 60 chars
OutputAlways via display module
Exit codes0=success, 1=error, 2=invalid
DestructiveMust have --force flag, default=False confirmation

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.