Skip to main content

Phase 05: CLI & Developer Tools

Objective: Build a comprehensive CLI tool (m) to replace Frappe's "bench", with commands for development, deployment, and management.


1. CLI Framework Setup

  • Create src/framework_m/cli/main.py

  • Use cyclopts for CLI framework (native async, better type support)

  • Setup main CLI app:

    app = cyclopts.App(name="m", help="Framework M CLI")
  • Add version command:

    m --version
  • Add help documentation for all commands

1.1 Pluggable CLI Architecture (Entry Points)

  • Define Entry Point Group: framework_m.cli_commands
  • Implement Plugin Loader in plugin_loader.py:
    • Use importlib.metadata.entry_points(group="framework_m.cli_commands")
    • Iterate over registered entry points
    • If entry point is a cyclopts.App: app.command(plugin_app, name=ep.name)
    • If entry point is a function: app.command(plugin_func, name=ep.name)
  • Goal: 3rd party apps (and framework-m-studio) can add commands to m without modifying core.
  • Customization Philosophy:
    • Standard commands (start, test) are strict relays.
    • For customization (e.g., start-mqtt, super-test): Users MUST register a custom command via entry points.
    • Result: m remains a clean, standard runner.

2. Development Server (Uvicorn Relay)

2.1 Start Command

  • Implement m start:
    • Implementation: Wrapper around uvicorn.
    • Argument Forwarding: Uses cyclopts Parameter for options.
    • Usage: m start --workers 4 --log-level debug
    • Value Add: Sets PYTHONPATH, auto-detects app, transparently passes args.

2.2 Worker Command (Taskiq Relay)

  • Implement m worker:
    • Implementation: Native Taskiq integration (better than wrapper).
    • Options: --concurrency, --verbose.
    • Usage: m worker --concurrency 8

2.3 Studio Command (Litestar Relay)

  • Implement m studio:
    • Implementation: Wrapper around uvicorn for Studio app.
    • Options: --host, --port, --reload, --log-level.
    • Usage: m studio --port 9000 --reload

3. Database Commands (Alembic Relay)

3.1 Migration Commands

  • Implement m migrate:
    • Implementation: Wrapper around MigrationManager (uses Alembic).
    • Commands: migrate, create, rollback, status, history, init.
    • Usage: m migrate create "add users" --autogenerate
    • Value Add: Auto-detects schema changes, supports custom DB URL, env vars.

4. Scaffolding (Cruft/Cookiecutter Relay)

4.1 New App (Starter Template)

  • Implement m new:app:
    • Implementation: Wrapper around cruft (cookiecutter).
    • Options: --template, --checkout, --no-input.
    • Usage: m new:app myapp --checkout main

4.2 New DocType (Jinja Relay)

  • Implement m new:doctype <name>:
    • Implementation: Template rendering (embedded templates).
    • Output: doctype.py, controller.py, test_*.py, __init__.py.
    • App Detection: --app > pyproject.toml > interactive prompt.

App Detection Strategy

  • CLI Parameter (Unattended/Automation):

    • --app <app_name>: Explicit app target.
    • If --app provided, use it directly (no prompt).
    • Automation Friendly: Scripts can pass --app to avoid prompts.
  • Auto-Detection from CWD:

    • If --app not provided, attempt to detect from current directory.
    • Check if CWD is inside an app's source tree.
    • Look for pyproject.toml in CWD or parent directories.
    • Read [project] name to determine app.
  • Interactive Prompt (Default Fallback):

    • If detection fails AND stdin is interactive (TTY):
      • List installed apps from framework_m.apps entry points.
      • Prompt user to select: "Which app? [my_app, other_app]".
    • If stdin is NOT interactive (pipe/automation):
      • Fail with clear error: "Cannot detect app. Use --app <name>.".
      • Exit code 1 (enables CI/automation to catch failures).
  • Implementation: check require_app() in new.py

  • Scaffold Output Location:

    • Files created in: {cwd}/doctypes/{doctype_name}/
    • Structure: __init__.py, doctype.py, controller.py, test_*.py
  • Templates:

    • Embedded templates in new.py (no external Jinja2 files needed).

5. Testing & Quality (Standard Tool Relays)

5.1 Test Relay

  • Implement m test:
    • Command: pytest
    • Options: --verbose, --coverage, -k.
    • Usage: m test -k "user" --verbose
    • Value Add: Configures PYTHONPATH.

5.2 Lint/Format Relay

  • Implement m lint:
    • Command: ruff check . --fix
  • Implement m format:
    • Command: ruff format .
  • Implement m typecheck:
    • Command: mypy .

6. Configuration Management

6.1 Config File

  • Create framework_config.toml structure:
    • Supports [framework], [apps] sections
    • Load/save with TOML format
    • Auto-detect in CWD or parent directories

6.2 Config Commands

  • Implement m config:show: Display current config.
  • Implement m config:set <key> <value>: Update config.

7. Utility Relays

7.1 Console (IPython Relay)

  • Implement m console:
    • Implementation: Wrapper around ipython or python -m asyncio.
    • Value Add: Pre-imports framework, async support.
    • Options: --no-ipython for plain Python.

7.2 System Info

  • Implement m info:
    • Show versions (Framework, Python).
    • --verbose for platform and service info.

7.3 Routes

  • Implement m routes:
    • Points to OpenAPI/Swagger docs.

8. Build Commands

8.1 Frontend Build

[!NOTE] Frontend build logic (m build) is defined in Phase 09: Frontend & Docs. The CLI command is registered here but implementation details are in Phase 09.

  • Implement m build (placeholder):
    • Relay to frontend build system (see Phase 09).
    • Detects package.json and runs npm run build.

8.2 Docker Build

  • Implement m build:docker:
    • Command: Wrapper around docker build.
    • Value Add: Reads image name/tag from framework_config.toml.
    • Options: --tag, --file, --no-cache, --push.

9. Pluggable Extensions

  • These commands are NOT in framework-m.
  • Provided by framework-m-studio via entry points:
    • Plugin loader implemented (load_plugins())
    • m studio (external)
    • m docs:generate (external)
    • m codegen (external)

10. CLI Entry Point

  • Configure pyproject.toml scripts: m = "framework_m.cli.main:app"
  • Test installation (m --help) - 22 commands registered.