Run a single goal non-interactively

cantrip run --print "<goal>" drives one goal through the agent loop with no TUI, prints progress to stdout, and exits when the work queue drains.

When to use it

Print mode replaces the TUI with a single-shot CLI invocation:

If you want a conversation, stay in the TUI or --no-tui CLI mode — print mode runs the goal once and exits.

How to enable it

$ cantrip run --print "Add ops-tracing to this charm"
$ cantrip run -p "Audit and fix charm" /path/to/charm
$ cantrip run --print --json "Pack the charm" | tee events.ndjson

The --print flag (alias -p) takes the goal as its argument. The positional charm-path argument still works; combine it with --print to point at a charm in a non-current directory.

The JSON event stream

With --json, every cantrip.ui.events payload writes one NDJSON line to stdout. The schema is the same one the TUI and Web UIs subscribe to, exposed as a stable contract:

{"type": "task_updated", "data": {"id": "abc", "title": "...", "status": "active", ...}, "timestamp": 1730000000.0}
{"type": "chat_message", "data": {"role": "assistant", "content": "..."}, "timestamp": 1730000001.5}
{"type": "permission_decided", "data": {"tool_name": "run_command", "outcome": "deny", ...}, "timestamp": 1730000002.0}
{"type": "permission_auto_approved", "data": {"tool_name": "git_push", "reason": "yolo", ...}, "timestamp": 1730000003.0}

Top-level fields are always type (the EventType string), data (the payload object), and timestamp (UNIX seconds, fractional). The event types print mode emits include:

For the full list see the print-mode event schema in the CLI reference.

Confirmations refuse the run by default

Print mode is unattended by definition — nobody is watching for a CONFIRM prompt. If a previously-resumed session has unresolved confirmations, or one appears mid-run, the runner exits non-zero with the list:

Refusing to run unattended: pending confirmations would block the queue.
Re-run with --yolo to auto-approve `ask` permissions, or resolve them
interactively in the TUI/CLI mode first.

Pending confirmations:
  - [c1] Approve git push origin main

The fix is one of:

Exit codes

0
The work queue drained with every task in done status (or no tasks were created).
1
One or more tasks finished in failed or blocked status, the provider returned an error, the drain timed out, or pending confirmations blocked the run.
2
CLI argument error — an empty goal, or --print combined with --web.
130
Interrupted by Ctrl-C.

Composing print mode in a script

A typical CI shell invocation:

$ cantrip run \
--print "Pack and deploy the charm, run integration tests" \
--json \
--yolo \
--max-iterations 50 \
/workspace/my-charm \
| tee build.ndjson \
| jq 'select(.type == "chat_message" and .data.role == "assistant") | .data.content'

The tee keeps the full event log for later inspection; the jq filter prints only the agent's chat replies for the build log. Exit-code propagation lets the CI job decide whether the build counts as green.

Concrete automation examples

A GitHub Actions step can keep the full NDJSON transcript while still failing the job on a blocked or failed run:

- name: Cantrip charm gate
  run: |
    cantrip run \
      --print "Audit the charm, fix lint issues, then pack it" \
      --json \
      --yolo \
      "$GITHUB_WORKSPACE" \
      | tee cantrip.ndjson

For scheduled maintenance, keep the same one-shot shape but swap the goal. A nightly cron job can re-audit a repo and archive the event log without opening an interactive session:

$ cantrip run \
--print "Audit this charm and report any regressions" \
--json \
/srv/charms/my-charm \
| tee /var/log/cantrip/nightly-audit.ndjson

When you need stricter governance than blanket --yolo, pair print mode with an explicit permissions policy and use cantrip audit to inspect the decisions afterwards.

Related references