Skip to content

Cli

cli

AXM-Init CLI entry point — Project scaffolding tool.

Usage::

axm-init scaffold my-project
axm-init reserve my-package --dry-run
axm-init version

check(path='.', *, json_output=False, agent=False, verbose=False, category=None)

Check a project against the AXM gold standard.

Source code in packages/axm-init/src/axm_init/cli.py
@app.command()
def check(
    path: Annotated[
        str,
        cyclopts.Parameter(help="Path to project to check"),
    ] = ".",
    *,
    json_output: Annotated[
        bool,
        cyclopts.Parameter(name=["--json"], help="Output as JSON"),
    ] = False,
    agent: Annotated[
        bool,
        cyclopts.Parameter(name=["--agent"], help="Compact agent-friendly output"),
    ] = False,
    verbose: Annotated[
        bool,
        cyclopts.Parameter(
            name=["--verbose", "-v"], help="Show all checks including passed"
        ),
    ] = False,
    category: Annotated[
        str | None,
        cyclopts.Parameter(name=["--category", "-c"], help="Filter to one category"),
    ] = None,
) -> None:
    """Check a project against the AXM gold standard."""
    from axm_init.core.checker import (
        CheckEngine,
        format_agent,
        format_json,
        format_report,
    )

    project_path = Path(path).resolve()
    if not project_path.is_dir():
        print(f"❌ Not a directory: {project_path}", file=sys.stderr)  # noqa: T201
        raise SystemExit(1)

    try:
        engine = CheckEngine(project_path, category=category)
        result = engine.run()
    except ValueError as e:
        print(f"❌ {e}", file=sys.stderr)  # noqa: T201
        raise SystemExit(1) from e

    if agent:
        print(json.dumps(format_agent(result), indent=2))  # noqa: T201
    elif json_output:
        print(json.dumps(format_json(result), indent=2))  # noqa: T201
    else:
        print(format_report(result, verbose=verbose))  # noqa: T201

    if result.score < 100:
        raise SystemExit(1)

main()

Main entry point.

Source code in packages/axm-init/src/axm_init/cli.py
def main() -> None:
    """Main entry point."""
    app()

reserve(name, *, author='', email='', dry_run=False, json_output=False)

Reserve a package name on PyPI.

Source code in packages/axm-init/src/axm_init/cli.py
@app.command()
def reserve(
    name: Annotated[
        str,
        cyclopts.Parameter(help="Package name to reserve"),
    ],
    *,
    author: Annotated[
        str,
        cyclopts.Parameter(name=["--author", "-a"], help="Author name"),
    ] = "",
    email: Annotated[
        str,
        cyclopts.Parameter(name=["--email", "-e"], help="Author email"),
    ] = "",
    dry_run: Annotated[
        bool,
        cyclopts.Parameter(name=["--dry-run"], help="Skip actual publish"),
    ] = False,
    json_output: Annotated[
        bool,
        cyclopts.Parameter(name=["--json"], help="Output as JSON"),
    ] = False,
) -> None:
    """Reserve a package name on PyPI."""
    from axm_init.adapters.credentials import CredentialManager
    from axm_init.core.reserver import reserve_pypi

    author = author or _git_config_get("user.name")
    email = email or _git_config_get("user.email")
    _require_identity(author, email, json_output)

    creds = CredentialManager()

    if not dry_run:
        try:
            token = creds.resolve_pypi_token(interactive=not json_output)
        except SystemExit as e:
            if json_output:
                print('{"error": "No PyPI token found"}')  # noqa: T201
            logger.debug("PyPI token resolution failed", exc_info=True)
            raise SystemExit(1) from e
    else:
        token = creds.get_pypi_token() or ""

    result = reserve_pypi(
        name=name,
        author=author,
        email=email,
        token=token or "",
        dry_run=dry_run,
    )

    if json_output:
        print(  # noqa: T201
            json.dumps(
                {
                    "success": result.success,
                    "package_name": result.package_name,
                    "version": result.version,
                    "message": result.message,
                },
                indent=2,
            )
        )
    else:
        if result.success:
            print(f"✅ {result.message}")  # noqa: T201
            print(f"   View at: https://pypi.org/project/{name}/")  # noqa: T201
        else:
            print(f"❌ {result.message}", file=sys.stderr)  # noqa: T201
            raise SystemExit(1)

scaffold(path='.', *, name=None, org, author, email, license='Apache-2.0', license_holder=None, description='', workspace=False, member=None, check_pypi=False, json_output=False)

Scaffold a new Python project with best practices.

Source code in packages/axm-init/src/axm_init/cli.py
@app.command()
def scaffold(
    path: Annotated[
        str,
        cyclopts.Parameter(help="Path to initialize project"),
    ] = ".",
    *,
    name: Annotated[
        str | None,
        cyclopts.Parameter(name=["--name", "-n"], help="Project name"),
    ] = None,
    org: Annotated[
        str,
        cyclopts.Parameter(name=["--org", "-o"], help="GitHub org or username"),
    ],
    author: Annotated[
        str,
        cyclopts.Parameter(name=["--author", "-a"], help="Author name"),
    ],
    email: Annotated[
        str,
        cyclopts.Parameter(name=["--email", "-e"], help="Author email"),
    ],
    license: Annotated[
        str,
        cyclopts.Parameter(name=["--license", "-l"], help="License type"),
    ] = "Apache-2.0",
    license_holder: Annotated[
        str | None,
        cyclopts.Parameter(
            name=["--license-holder"],
            help="License holder (defaults to --org)",
        ),
    ] = None,
    description: Annotated[
        str,
        cyclopts.Parameter(name=["--description", "-d"], help="Description"),
    ] = "",
    workspace: Annotated[
        bool,
        cyclopts.Parameter(
            name=["--workspace", "-w"],
            help="Scaffold a UV workspace instead of a standalone package",
        ),
    ] = False,
    member: Annotated[
        str | None,
        cyclopts.Parameter(
            name=["--member", "-m"],
            help="Scaffold a member sub-package with this name",
        ),
    ] = None,
    check_pypi: Annotated[
        bool,
        cyclopts.Parameter(name=["--check-pypi"], help="Check PyPI availability"),
    ] = False,
    json_output: Annotated[
        bool,
        cyclopts.Parameter(name=["--json"], help="Output as JSON"),
    ] = False,
) -> None:
    """Scaffold a new Python project with best practices."""
    from axm_init.adapters.copier import CopierAdapter, CopierConfig
    from axm_init.core.templates import TemplateType, get_template_path

    if workspace and member is not None:
        msg = "--workspace and --member are mutually exclusive"
        if json_output:
            print(json.dumps({"error": msg}))  # noqa: T201
        else:
            print(f"❌ {msg}", file=sys.stderr)  # noqa: T201
        raise SystemExit(1)

    target_path = Path(path).resolve()
    project_name = name or target_path.name

    if check_pypi:
        _check_pypi_availability(project_name, json_output=json_output)

    if member is not None:
        _scaffold_member(
            target_path,
            member,
            org=org,
            author=author,
            email=email,
            license_type=license,
            description=description,
            json_output=json_output,
        )
        return

    template_type = TemplateType.WORKSPACE if workspace else TemplateType.STANDALONE

    data = _build_scaffold_data(
        workspace=workspace,
        project_name=project_name,
        description=description,
        org=org,
        license_type=license,
        license_holder=license_holder or org,
        author=author,
        email=email,
    )

    copier_adapter = CopierAdapter()
    copier_config = CopierConfig(
        template_path=get_template_path(template_type),
        destination=target_path,
        data=data,
        trust_template=True,  # Internal AXM template is trusted
    )
    result = copier_adapter.copy(copier_config)

    _print_scaffold_result(result, project_name, target_path, json_output=json_output)

version()

Show axm-init version.

Source code in packages/axm-init/src/axm_init/cli.py
@app.command()
def version() -> None:
    """Show axm-init version."""
    from axm_init import __version__

    print(f"axm-init {__version__}")  # noqa: T201