Skip to content

Repository files navigation

baud-cli

CI PyPI Python

baud-cli provides the baud command: a small serial CLI designed for coding agents and repeatable embedded-hardware debugging. It turns the recurring one-off pyserial scripts into guarded YAML workflows with deterministic exit codes, JSON/JSONL output, raw-byte evidence, and automatically archived logs.

Install

baud-cli requires Python 3.10 or newer. The runtime dependencies are pyserial and PyYAML; uv is the recommended installer but is not a runtime requirement.

Quick install with an Agent prompt

Give this prompt to an Agent with shell access:

Read https://github.com/Nitmi/baud-cli and follow its README to install baud-cli for me.

For manual installation, use the detailed paths below.

Install the published command on Windows, macOS, or Linux:

uv tool install baud-cli
baud --help

Install from a checkout while developing:

uv tool install --editable .
baud --help

For development:

uv sync --extra dev
uv run baud --help

Install the Agent Skill from the public repository with the cross-agent skills CLI:

npx skills add Nitmi/baud-cli --skill baud

OpenClaw users can install the published ClawHub skill:

openclaw skills install @nitmi/baud

For development from this checkout, install or verify the bundled Skill directly:

uv tool install --editable .
uv tool update-shell
uv run python tools/install_skill.py
uv run python tools/install_skill.py --check --json

The canonical Skill source remains under skills/baud; the installer copies that source to ~/.agents/skills/baud. It refuses to overwrite a different installed copy unless --force is explicitly supplied.

Platform support

The serial transport uses pyserial and has no OS-specific code. CI is configured for Python 3.10 and 3.13 on Windows, macOS, and Linux, including a real pyserial loop:// round trip through the CLI.

Platform Typical port Notes
Windows COM5 USB serial drivers provide the COM port.
macOS /dev/cu.usbserial-* or /dev/cu.usbmodem* Prefer /dev/cu.* for outbound CLI sessions.
Linux /dev/ttyUSB0 or /dev/ttyACM0 The user needs permission to open the device.

On Linux, add the user to the serial-device group used by the distribution, commonly dialout or uucp, then start a new login session. USB drivers, device permissions, and DTR/RTS reset wiring remain platform and hardware concerns even though the CLI itself is portable.

Standalone Windows release

Starting with 0.1.2, tag builds produce baud-<version>-windows-x86_64.zip. It contains a single-file baud.exe and a manifest binding its SHA-256, version, target, and source commit. Python is not required at runtime. The tag workflow builds twice with fixed PYTHONHASHSEED and SOURCE_DATE_EPOCH, rejects unequal ZIP hashes, runs only baud --version, and creates GitHub artifact provenance. It does not enumerate or open serial ports.

After a tagged candidate is attached to a GitHub Release, verify it before use:

gh attestation verify baud-0.1.2-windows-x86_64.zip `
  --repo Nitmi/baud-cli `
  --source-ref refs/tags/v0.1.2 `
  --signer-workflow Nitmi/baud-cli/.github/workflows/binary-release.yml `
  --deny-self-hosted-runners

The archive can then be pinned by Embedded Agent Toolkit's authenticated component catalog. Attestation proves repository build provenance; it is not Windows Authenticode signing and does not prove a serial device or firmware identity.

Core commands

The examples use a Windows port name; replace COM14 with the connected /dev/cu.* or /dev/tty* device on macOS or Linux.

uv run baud list --json
uv run baud monitor --port COM14 --duration 30 --json
uv run baud send --port COM14 status --wait 2 --require-response --json
uv run baud probe --port COM14 --commands help status ? --endings crlf lf --json
uv run baud run examples/wash_test.yaml --json

probe follows the diagnostic pattern seen in real agent sessions: listen first, preserve raw bytes, then try harmless commands with multiple line endings. It distinguishes a responsive device from a device that transmits a startup banner but never processes PC-to-device commands.

Guarded hardware actions

A dangerous action must name successful earlier steps. The CLI refuses to load a dangerous step with no guards, and it never transmits the action if a required assertion failed:

- id: sensor_snapshot
  send: sensor_i2c_status
  wait: 3
  expect:
    contains: ["target=164"]

- id: start_wash
  send: wash_start
  dangerous: true
  requires: [sensor_snapshot]

The complete guarded example is in examples/wash_test.yaml.

Pin the physical serial device

For unattended or safety-sensitive workflows, bind the port to its enumerated USB identity. Baud checks these fields immediately before opening the port and exits with code 4 on any mismatch:

serial:
  port: COM3
  baudrate: 115200
  dtr: false
  rts: false
  expected_vid: "303A"
  expected_pid: "1001"
  expected_serial_number: "E0:72:A1:D4:1F:DC"

VID and PID strings are hexadecimal. Direct commands expose the same guard through --expected-vid, --expected-pid, and --expected-serial-number. Pyserial URLs such as loop:// continue to work when no physical identity fields are configured.

Exit codes

Code Meaning
0 Workflow or diagnostic command completed
2 Step or guard failed
3 Assertion failed
4 Port missing, busy, disconnected, or unreadable
5 Timeout
6 Encoding or protocol error
7 Workflow configuration error

Every active serial command closes the port through a context manager, even after an assertion or I/O failure. By default, human-readable .log and machine-readable .jsonl artifacts are written under logs/; use --no-log only for disposable checks.

Companion project

For Bluetooth Low Energy discovery, GATT diagnostics, notifications, guarded writes, and offline evidence workflows, see BLEA. It provides a universal Agent Plugin with a portable Skill, deterministic CLI, and local MCP server.

About

Agent-friendly serial diagnostics and guarded hardware workflows

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages