Skip to main content

Fireargs

Define each command with typed input and output models. Get composability, structured JSON I/O, and LLM-ready manifests baked in — for humans, agents, and scripts alike. Built on pydantic and click; Python 3.12+.

Installation

pip install fireargs
# or
uv add fireargs

A first command

The handler's first parameter annotation is the input model and its return annotation the output type. Fields become arguments and options; Argument(...) and Option(...) are pydantic Field()s that also carry CLI config. Import the names you use, as here, or the module (import fireargs as fa, then fa.Option(...)).

from typing import Annotated
from pydantic import BaseModel
from fireargs import Argument, Option, command


# Composable arguments and options
Name = Annotated[str, Argument(description="The user's name.")]
Times = Annotated[int, Option(default=1, ge=1, description="Repeat it n times.")]
Shout = Annotated[bool, Option(short="s", default=False, description="Use capitals.")]


# Composable groups of them
class GreetInput(BaseModel):
    name: Name
    times: Times
    shout: Shout


# Typed output, validated on every run
class Greeting(BaseModel):
    greeting: str


# A click command, typed from the handler's annotations
@command(description="Greet someone politely.")
def greet(input: GreetInput) -> Greeting:
    greeting = " ".join([f"hello {input.name}"] * input.times)
    return Greeting(greeting=greeting.upper() if input.shout else greeting)


# Output for humans; --json prints the data instead
@greet.render
def render_greeting(output: Greeting) -> str:
    return output.greeting


# Use as a normal click command, e.g. configure or apply plugins
if __name__ == "__main__":
    greet()

That gets you:

$ python greet.py world --times 2 -s        # CLI input, rendered output
HELLO WORLD HELLO WORLD
$ python greet.py world --json              # CLI input, JSON output
{
  "greeting": "hello world"
}
$ python greet.py --json '{"name":"world","times":2}'   # JSON input, JSON output
{
  "greeting": "hello world hello world"
}
$ python greet.py world --times 0
error: option '--times <value>' argument '0' is invalid. Input should be greater than or equal to 1

--help comes from click:

Usage: greet.py [OPTIONS] <name>

  Greet someone politely.

Arguments:
  name  The user's name.

Options:
  --times <value>  Repeat it n times.  [default: 1]
  -s, --shout      Use capitals.
  --json [value]   write output as JSON; pass a JSON string to also drive
                   input
  --llms           print this command's schema (name, input, output) as JSON
                   for LLM tool-use
  --help           Show this message and exit.

A first program

Group leaves with Program(router={...}):

# Composable commands: a router of leaves and nested programs
cli = Program(
    name="myapp",
    description="My CLI",
    version="1.0.0",
    router={"greet": greet, "deploy": deploy, "db": db},
)

# Use as a normal click group, e.g. configure or apply plugins
if __name__ == "__main__":
    cli()

That gives:

$ myapp --help
Usage: myapp [OPTIONS] [COMMAND] [ARGS]...

  My CLI

Options:
  -V, --version  output the version number
  --llms         print the program manifest (subcommand schemas) as JSON for
                 LLM tool-use
  --help         Show this message and exit.

Commands:
  greet   Greet someone politely.
  deploy  Deploy the current build.
  db      Database tasks.

$ myapp db migrate --json '{}'
{
  "applied": 0
}

Programs nest — router accepts other programs the same way it accepts leaves. myapp db migrate routes through both levels.

JSON mode

Every command takes --json. Bare, it switches the output to JSON; given a value, that value is the input, checked against the input schema, so scripts never assemble argv:

$ python greet.py world --times 2 --json        # CLI input, JSON output
{
  "greeting": "hello world hello world"
}
$ python greet.py --json '{"name":"world","times":3,"shout":true}'   # JSON input, JSON output
{
  "greeting": "HELLO WORLD HELLO WORLD HELLO WORLD"
}
$ python greet.py world --json | jq -r .greeting   # pipe it into other tools
hello world
$ python greet.py --json '{"times":2}'           # input errors go to stderr, exit code 1
error: invalid --json input at 'name': Field required
$ python greet.py --json '{"name":"world","times":"2"}'
error: invalid --json input at 'times': Input should be a valid integer

stdout only ever carries the data, so a script can parse it without checking for noise.

LLMs mode

Every command and program ships with --llms, which prints an MCP-tools/list-shaped manifest. For greet.py above:

{
  "tools": [
    {
      "name": "help",
      "description": "Documentation tool, not callable. Each `tools[].name` is the space-separated path beneath this binary. Invoke a tool with `<binary> <name> --json '<value>'` matching that tool's `inputSchema`; output is JSON on stdout matching `outputSchema`.",
      "inputSchema": { "type": "object", "properties": {} },
      "outputSchema": { "type": "object", "properties": {} }
    },
    {
      "name": "greet",
      "inputSchema": {
        "properties": {
          "name": {
            "description": "The user's name.",
            "title": "Name",
            "type": "string"
          },
          "times": {
            "default": 1,
            "description": "Repeat it n times.",
            "minimum": 1,
            "title": "Times",
            "type": "integer"
          },
          "shout": {
            "default": false,
            "description": "Use capitals.",
            "title": "Shout",
            "type": "boolean"
          }
        },
        "required": ["name"],
        "title": "GreetInput",
        "type": "object"
      },
      "outputSchema": {
        "properties": { "greeting": { "title": "Greeting", "type": "string" } },
        "required": ["greeting"],
        "title": "Greeting",
        "type": "object"
      },
      "description": "Greet someone politely."
    }
  ]
}

An LLM agent reads the manifest, picks a tool, and invokes it with <binary> <name> --json '<value>'. Output comes back as JSON on stdout.

Learn more

  • Commands — the @command(...) decorator and the leaf it builds.
  • Inputs — Argument() / Option() and per-field click config.
  • Outputs — the output type, serialization, and JSON Schema in --llms.
  • Programs — subcommand trees, nesting, and program-level config.
  • JSON mode — --json for programmatic and pipeline use.
  • LLMs mode — --llms and the MCP-shaped manifest.
  • Composability — share common params across commands by composing pydantic models.
  • Agent Skills — teach your coding agent to write and drive Fireargs CLIs.
  • Contributing — local setup, conventions, release flow.

License

MIT.

Metadata

Release files for fireargs 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fireargs 0.1.0
File Size Uploaded Provenance Actions
fireargs-0.1.0.tar.gz 25.6 kB Without provenance Details for fireargs-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fireargs 0.1.0
File Interpreter ABI Platform Actions
fireargs-0.1.0-py3-none-any.whl Python 3 none any Details for fireargs-0.1.0-py3-none-any.whl

Total release size: 62.0 kB

Release files / fireargs-0.1.0.tar.gz

Download URL fireargs-0.1.0.tar.gz
Size 25.6 kB
Tags Source
SHA-256 checksum
How to use checksums
9a34e2170839b4f778266b244daa145d25a618d44173f7f87b765de2d59124d5
BLAKE2b-256 checksum
How to use checksums
998566b1e4e0dc98ab394eda28b3740a8757736ab671576040fa1280cf92918b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / fireargs-0.1.0-py3-none-any.whl

Download URL fireargs-0.1.0-py3-none-any.whl
Size 36.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
52e60316eddf56ddf2ecb5a0c84b3d7fddf02b79daa6ec6c3d03f9fc8106d78a
BLAKE2b-256 checksum
How to use checksums
d9b0d49a4b00bd78bf153a0d2488a62a347a8c4950f77a5e28eca32dc04bddeb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for fireargs 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page