Skip to main content

Custom Commands

Extend Fastman with your own CLI commands.

Creating a Command​

fastman make:command send-newsletter

This creates a command file that you can register with your application.

Command Structure​

# commands/send_newsletter.py
from fastman.commands.base import Command, register
from fastman.console import Output

@register
class SendNewsletterCommand(Command):
signature = "newsletter:send {--test}"
description = "Send newsletter to all subscribers"

def handle(self):
test_mode = self.flag("test")

if test_mode:
Output.info("Running in test mode...")
recipient = "test@example.com"
else:
# Get subscribers from database
recipient = "all subscribers"

Output.info(f"Sending newsletter to {recipient}...")

# Your logic here
self._send_emails(test_mode)

Output.success("Newsletter sent successfully!")

def _send_emails(self, test_mode: bool):
# Implementation
pass

Signature Syntax​

The signature attribute defines how your command is called:

# Basic command
signature = "newsletter:send"

# With required argument
signature = "newsletter:send {email}"

# With optional argument (default value)
signature = "newsletter:send {email?}"

# With options (flags)
signature = "newsletter:send {--test} {--limit=}"

Getting Arguments and Options​

def handle(self):
# Get positional argument (index 0)
email = self.argument(0)

# Get optional argument with default
limit = self.argument(1, default="10")

# Get named option
format_type = self.option("format", default="html")

# Check if flag is present
is_test = self.flag("test")

Output Methods​

Fastman provides rich output methods:

from fastman.console import Output

# Messages
Output.success("Operation completed!")
Output.error("Something went wrong")
Output.warn("This might be dangerous")
Output.info("Processing...")

# Interactive
confirmed = Output.confirm("Are you sure?")
name = Output.ask("Enter your name")
choice = Output.choice("Select option", ["A", "B", "C"])

# Progress
with Output.progress(total=100) as progress:
for i in range(100):
# do work
progress.advance()

# Tables
Output.table(
headers=["ID", "Name", "Email"],
rows=[
["1", "John", "john@example.com"],
["2", "Jane", "jane@example.com"],
],
title="Users"
)

Example: Database Backup Command​

@register
class BackupCommand(Command):
signature = "database:backup {--output=}"
description = "Create a database backup"

def handle(self):
output_path = self.option("output", default="backup.sql")

Output.info("Starting database backup...")

with Output.spinner("Creating backup..."):
self._create_backup(output_path)

Output.success(f"Backup saved to {output_path}")

def _create_backup(self, path: str):
import subprocess
subprocess.run([
"pg_dump",
"-h", "localhost",
"-U", "postgres",
"-d", "myapp",
"-f", path
], check=True)

Registering Commands​

Commands decorated with @register are automatically discovered. Make sure your command file is imported in your application.

For project-specific commands, add to your app/commands/__init__.py:

from . import send_newsletter
from . import backup