Skip to content

CLI reference

The axm Command

Bash
axm
axm --help
axm --version
axm -V

No arguments or root help prints the installed command catalog without loading tool implementations. Version flags are recognized as the first argument and print the installed axm version. Unknown commands exit with code 2: the diagnostic goes to stderr and the catalog to stdout.

Available Commands

axm declares its launcher under project.scripts; it declares no tool entry points itself. The current environment supplies the catalog through axm.tools. Do not assume a fixed command count.

For example, after installing 'axm[init]':

Bash
axm init_check --help
axm init_check --path . --json-output

Use each provider's help for its domain options. Standalone provider binaries are separate interfaces, not automatically subcommands of this launcher.

Parameters

Generated tool signatures come from execute (or a registered callable). The adapter drops self, a parameter named kwargs and variadic **kwargs. Keyword-only parameters are exposed as positional-or-keyword, so the first parameter can usually be passed either positionally or by name. Scalar Annotated[..., cyclopts.Parameter(...)] metadata is retained; structured parameters are replaced by JSON-string annotations.

Non-scalar parameters

Lists, dicts, tuples, sets and other structured annotations, including Pydantic models, use one JSON token on the command line. This also applies to optional and Annotated wrappers. The wrapper decodes JSON; it does not construct a Pydantic model, tuple or set from the decoded value. The tool owns any required conversion and domain validation.

For the example tool:

Bash
axm demo_count --labels '["alpha", "beta"]'

A purely structured parameter with invalid JSON exits with code 2 before execution. For a structured union that also admits str, including supported recursive PEP 695 aliases, valid JSON is decoded and other tokens remain literal text. Thus null, 123 or a quoted JSON string is decoded rather than preserved verbatim.

Output modes

Generated tool commands use the following default rendering order:

  1. If the result failed and has a nonempty error, write it to stderr.
  2. If text is a string, write it to stdout (even an empty string).
  3. Otherwise, render a nonempty data dictionary as JSON.
  4. With no such data, print the result's string representation, except an error-only failure has already been reported on stderr.

The shared --json-output instead emits the data dictionary, including {} when empty. It does not emit a success/data/error envelope. Values unsupported by JSON serialization use their string representation. Check the process exit status in addition to parsing the JSON.

If the tool declares its own json_output parameter, the wrapper neither adds nor intercepts the shared option: the tool owns that flag's behavior.

The wrapper keeps its own failure diagnostics on stderr; it cannot prevent a provider from printing directly to stdout.

Exit statuses

Status Generated-tool behavior
0 Normal completion; also root catalog and version
1 success=False, an exception in execution, or generated-tool loading failure
2 Invalid command usage or invalid JSON for a structured parameter

Legacy axm.commands entries are ignored, including when they share a name with a tool. Migrate request–response commands to axm.tools; standalone process lifecycle commands belong under project.scripts.

Python API

These launcher helpers live in axm.cli, outside the root SDK façade. create_app() eagerly loads the catalog and is intended for introspection and tests. The installed command uses the lazy main() path.

create_app()

Create an app with every installed AXMTool registered eagerly.

Intended for tests and introspection; main() dispatches lazily.

Source code in packages/axm/src/axm/cli.py
Python
def create_app() -> cyclopts.App:
    """Create an app with every installed AXMTool registered eagerly.

    Intended for tests and introspection; main() dispatches lazily.
    """
    app = _new_app()
    for name, ep in _entry_points(_TOOLS_GROUP).items():
        try:
            app.command(build_command_for_tool(name, _load(ep)), name=name)
        except Exception:
            logger.warning("Failed to auto-register tool '%s'", name, exc_info=True)
    return app

build_command_for_tool(tool_name, tool_obj)

Build a cyclopts command callable from an AXMTool (or plain callable).

The returned function carries the tool's typed __signature__ (non-scalar params reshaped to JSON strings) and its docstring, runs execute / the callable, prints result.text, and exits non-zero on failure.

Parameters:

Name Type Description Default
tool_name str

The command name.

required
tool_obj Any

The tool instance (or plain callable).

required

Returns:

Type Description
Any

A function suitable for cyclopts.App.command.

Source code in packages/axm/src/axm/cli.py
Python
def build_command_for_tool(tool_name: str, tool_obj: Any) -> Any:
    """Build a cyclopts command callable from an AXMTool (or plain callable).

    The returned function carries the tool's typed ``__signature__`` (non-scalar
    params reshaped to JSON strings) and its docstring, runs ``execute`` /
    the callable, prints ``result.text``, and exits non-zero on failure.

    Args:
        tool_name: The command name.
        tool_obj: The tool instance (or plain callable).

    Returns:
        A function suitable for ``cyclopts.App.command``.
    """
    exec_fn = _exec_callable(tool_obj)
    params = public_params(exec_fn)
    json_params = _nonscalar_names(params)
    text_params = _text_tolerant_names(params)
    cli_params = [cli_param(p) for p in params]
    tool_param_names = [p.name for p in cli_params]
    has_local_json_output = "json_output" in tool_param_names
    shared_cli_params = (
        []
        if has_local_json_output
        else [
            inspect.Parameter(
                "json_output",
                inspect.Parameter.POSITIONAL_OR_KEYWORD,
                annotation=bool,
                default=False,
            )
        ]
    )
    cli_params.extend(shared_cli_params)
    ordered_names = [p.name for p in cli_params]

    def _command(*args: Any, **kwargs: Any) -> None:
        # cyclopts binds tokens per ``__signature__``; positional tokens (the
        # ``axm audit .`` ergonomic form) arrive in *args and are mapped back to
        # their param names here so ``execute(**kwargs)`` stays keyword-only.
        for name, value in zip(ordered_names, args, strict=False):
            kwargs[name] = value
        for key in json_params & kwargs.keys():
            value = kwargs[key]
            if isinstance(value, str):
                try:
                    kwargs[key] = json.loads(value)
                except json.JSONDecodeError as exc:
                    if key in text_params:
                        continue
                    sys.stderr.write(f"{key}: invalid JSON: {exc}\n")
                    raise SystemExit(2) from exc
        shared_json_output = (
            False if has_local_json_output else bool(kwargs.pop("json_output", False))
        )
        try:
            result = exec_fn(**kwargs)
        except Exception as exc:  # surface any tool error on stderr
            sys.stderr.write(f"{exc}\n")
            raise SystemExit(1) from exc
        _emit(result, json_output=shared_json_output)
        if getattr(result, "success", True) is False:
            raise SystemExit(1)

    _command.__name__ = tool_name
    _command.__doc__ = exec_fn.__doc__ or f"Run the {tool_name} tool."
    _command.__signature__ = inspect.Signature(cli_params)  # type: ignore[attr-defined]
    # Mirror the resolved annotations onto __annotations__ as real types (not
    # strings): cyclopts calls get_type_hints() on the command, which reads
    # __annotations__ and would otherwise try to eval our closure's stringised
    # ``**kwargs``/forward refs against this module's globals.
    _command.__annotations__ = {
        p.name: p.annotation
        for p in cli_params
        if p.annotation is not inspect.Parameter.empty
    }
    _command.__annotations__["return"] = None
    return _command

Tool Interface

See the SDK reference for AXMTool, ToolResult, metadata and the node adapter.

Validation Interface

See witnesses for their separate result contracts.