The dev CLI
python dev.py <command>
Colon-separated commands work, and so does the plain form: migrate:status and
migrate status are the same command.
Migrations
| Command | What it does |
|---|---|
| migrate | Apply pending migrations |
| migrate --step N | Apply only the first N pending |
| migrate --pretend | Print what would run, touching nothing |
| migrate --seed | Migrate, then run the DatabaseSeeder |
| migrate:status | Which migrations ran, and in which batch |
| migrate:rollback | Revert the last batch |
| migrate:rollback --step N | Revert the last N batches |
| migrate:reset | Revert everything (Banned in persistence-first & automated agent workflows) |
| migrate:refresh | Reset, then re-run (Banned in persistence-first & automated agent workflows) |
| migrate:fresh | Drop every table, then re-run (Banned in persistence-first & automated agent workflows) |
| migrate:install | Create the migrations table only |
Safety Notice: Craft Engine enforces Absolute Data Persistence. Destructive commands (
migrate:fresh,migrate:reset,migrate:refresh,db wipe) are strictly prohibited in production, test, and automated agent environments. See Database Safety.
Database
| Command | What it does |
|---|---|
| db seed | Run DatabaseSeeder |
| db seed --class UserSeeder | Run one seeder |
| db show | Connection, driver, host, database |
| db tables | List tables |
| db ping | Verify the connection; non-zero exit on failure |
| db wipe --force | Drop every table (Banned in persistence-first & automated agent workflows) |
db wipe refuses to run without --force.
Generators
| Command | Creates |
|---|---|
| make model Product | app/Models/Product.py |
| make model Product -m | Model plus a create migration |
| make controller Product | app/Http/Controllers/ProductController.py |
| make controller Product -r | Controller with index/show/store/update/destroy |
| make migration create_products_table | Timestamped migration |
| make migration add_color_to_products_table | An alter migration |
| make middleware EnsureAdmin | app/Http/Middleware/ |
| make request StoreProduct | FormRequest |
| make resource Product | API resource |
| make job SendEmail | Queued job |
| make event OrderPlaced | Event |
| make listener NotifyTeam | Listener |
| make policy Product | Policy |
| make seeder Product | Seeder |
| make service Billing | Plain service class |
| make auth [--views] [-f] | Full authentication stack (Controller, FormRequests, Forge views, routes) |
Names are normalised: service_order, service-order and ServiceOrder all
produce ServiceOrder. Suffixes are added once — make controller Product and
make controller ProductController both give ProductController.
Generators refuse to overwrite. Pass --force when you mean it.
Migration names drive the stub: create_*_table produces a create migration and
add_*_to_*_table produces an alter migration, with the table inferred.
Routes
python dev.py route list
python dev.py route list --method POST
python dev.py route list --path /api
Queue
python dev.py queue work
python dev.py queue work --queue emails
python dev.py queue work --once
See Queues and events.
Cache
python dev.py cache clear
Firewall (WAF) & Security Audit
| Command | What it does |
|---|---|
| firewall list | List IP whitelist/blacklist rules and reputation scores |
| firewall allow <ip> | Add an IP address to the trusted whitelist |
| firewall block <ip> [-r reason] | Add an IP address to the permanent blacklist |
| security audit [--limit N] | Display recent authentication attempts and honeypot events |
python dev.py firewall list
python dev.py firewall allow 192.168.1.100
python dev.py firewall block 203.0.113.55 -r "Port scanner detected"
python dev.py security audit --limit 50
AI Coding Agents & Discovery
Craft Engine is optimized for autonomous AI coding agents (Cursor, Claude Code, Windsurf, AGY):
| Command | What it does |
|---|---|
| agent:scaffold [-f] | Bootstrap AI context files (.cursorrules, llms.txt, llms-full.txt, .agents/mcp.json) and install the whole agent catalog into .claude/ |
| agent:rules | Alias for agent:scaffold |
| agent:list [--kind K] | List catalog entries: agent, skill, command, reference |
| agent:install NAME... [-f] | Install named agents, skills or commands (shared references always come along) |
| agent:install --all [-f] | Install the whole catalog |
python dev.py agent:scaffold
python dev.py agent:list --kind agent
python dev.py agent:install code-reviewer test-driven-development ship
Installs refuse to overwrite an existing entry without --force, and check every
conflict before writing anything. See AI Agents.
Application
| Command | What it does |
|---|---|
| serve | Development server (--host, --port, --no-reload) |
| tinker | Interactive shell with the app booted |
| about | Environment, debug, Python, database, cache, queue |
| key:generate | Generate APP_KEY and write it to .env |
tinker gives you app, db and the facades:
>>> from app.Models.User import User
>>> User.query().count()
3
Exit codes
Commands exit non-zero on failure, so they compose in scripts and CI:
python dev.py db ping && python dev.py migrate
Adding a command
dev is built with Typer. Add commands in
engine/cli/app.py:
@cli.command("stats")
def stats():
"""Show application statistics."""
app = get_app()
total = app.make("db").statement("SELECT COUNT(*) AS n FROM users").fetchone()
echo(f"Users: {total['n']}")
Use get_app() to boot the application lazily — importing it at module level
would slow down every command, including --help.