cli-module
0
总安装量
1
周安装量
安装命令
npx skills add https://github.com/psincraian/myfy --skill cli-module
Agent 安装分布
amp
1
cline
1
opencode
1
cursor
1
continue
1
kimi-cli
1
Skill 文档
CliModule – Custom CLI Commands
CliModule enables custom CLI commands with full dependency injection and Typer integration.
Quick Start
from myfy.core import Application
from myfy.commands import CliModule, cli
@cli.command()
async def seed_users(user_service: UserService, count: int = 10):
'''Seed the database with test users.'''
for i in range(count):
await user_service.create(f"user{i}@example.com")
print(f"Created {count} users")
app = Application()
app.add_module(CliModule())
# Run with: myfy app seed-users --count 20
Configuration
Environment variables use the MYFY_CLI_ prefix:
| Variable | Default | Description |
|---|---|---|
MYFY_CLI_VERBOSE |
False |
Enable verbose output |
MYFY_CLI_NO_COLOR |
False |
Disable colored output |
MYFY_CLI_TIMEOUT |
300 |
Command timeout (seconds) |
Defining Commands
Basic Command
from myfy.commands import cli
@cli.command()
async def hello():
'''Say hello.'''
print("Hello, World!")
# Run: myfy app hello
With DI Injection
Services are automatically injected:
from myfy.commands import cli
from myfy.data import AsyncSession
@cli.command()
async def count_users(session: AsyncSession):
'''Count users in the database.'''
result = await session.execute(select(func.count(User.id)))
count = result.scalar()
print(f"Total users: {count}")
# Run: myfy app count-users
With Arguments
@cli.command()
async def greet(name: str):
'''Greet someone by name.'''
print(f"Hello, {name}!")
# Run: myfy app greet John
With Options
@cli.command()
async def seed(count: int = 10, verbose: bool = False):
'''Seed the database.'''
for i in range(count):
if verbose:
print(f"Creating user {i + 1}/{count}")
await create_user(i)
print(f"Created {count} users")
# Run: myfy app seed --count 50 --verbose
Command Groups
Organize related commands:
from myfy.commands import cli
# Create a group
db = cli.group("db")
@db.command()
async def seed(session: AsyncSession):
'''Seed the database.'''
await seed_data(session)
@db.command()
async def reset(session: AsyncSession, force: bool = False):
'''Reset the database.'''
if force or confirm("Are you sure?"):
await reset_data(session)
# Run: myfy app db:seed
# myfy app db:reset --force
Typer Integration
Use Typer for advanced CLI features:
import typer
from myfy.commands import cli
@cli.command()
async def import_data(
db: Database, # DI injection
file: str = typer.Argument(..., help="Path to import file"),
dry_run: bool = typer.Option(False, "--dry-run", "-n", help="Simulate import"),
format: str = typer.Option("json", "--format", "-f", help="File format"),
):
'''Import data from a file.'''
if dry_run:
print(f"Would import from {file} ({format} format)")
else:
await db.import_from_file(file, format)
# Run: myfy app import-data users.csv --format csv --dry-run
Typer Features
import typer
@cli.command()
async def deploy(
# Required argument
env: str = typer.Argument(..., help="Target environment"),
# Optional with short form
version: str = typer.Option(None, "--version", "-v"),
# Boolean flag
skip_tests: bool = typer.Option(False, "--skip-tests"),
# Choice from enum
region: Region = typer.Option(Region.US, "--region", "-r"),
):
'''Deploy the application.'''
...
# Run: myfy app deploy production -v 1.2.3 --skip-tests --region eu
Parameter Classification
Parameters are automatically classified:
| Pattern | Classification |
|---|---|
typer.Argument(...) |
CLI positional argument |
typer.Option(...) |
CLI option/flag |
| Primitive with default | CLI option |
| Primitive without default | CLI argument |
| Complex type | DI dependency |
@cli.command()
async def process(
file: str, # CLI argument (required)
count: int = 10, # CLI option (--count)
verbose: bool = False, # CLI flag (--verbose)
session: AsyncSession, # DI injection
settings: AppSettings, # DI injection
):
...
Custom Command Names
@cli.command(name="import-users") # Explicit name
async def import_users_handler(file: str):
'''Import users from file.'''
...
# Run: myfy app import-users users.csv
Function name import_users_handler becomes command name import-users by default (underscores to hyphens).
Async vs Sync
Both async and sync handlers are supported:
# Async (recommended)
@cli.command()
async def async_task(session: AsyncSession):
await session.execute(...)
# Sync (also works)
@cli.command()
def sync_task(settings: AppSettings):
print(settings.app_name)
Error Handling
@cli.command()
async def risky_operation(session: AsyncSession):
'''Perform a risky operation.'''
try:
await do_risky_thing(session)
except RiskyError as e:
print(f"Error: {e}", file=sys.stderr)
raise typer.Exit(1)
Progress and Output
import typer
@cli.command()
async def process_files(files: list[str]):
'''Process multiple files.'''
with typer.progressbar(files) as progress:
for file in progress:
await process_file(file)
typer.echo(typer.style("Done!", fg=typer.colors.GREEN))
Running Commands
# List available commands
myfy app --help
# Run a command
myfy app seed-users
# With options
myfy app seed-users --count 50
# Grouped command
myfy app db:reset --force
# With arguments
myfy app import users.csv
Best Practices
- Use docstrings – Become command help text
- Group related commands – Easier to discover
- Use Typer for complex args – Better help messages
- Handle errors gracefully – Use
typer.Exit(1)for failures - Show progress – Use
typer.progressbarfor long operations - Inject services – Don’t create DB connections manually
- Keep commands focused – One task per command