fractal.cli package#

Subpackages#

Submodules#

fractal.cli.main module#

Command-line interface for fractal.

fractal.cli.main.cli(**kwargs)[source]#

Run the fractal CLI.

Parameters:

kwargs (Any)

Return type:

None

fractal.cli.utils module#

Shared helpers for fractal CLI commands.

fractal.cli.utils.command(app, name, **kwargs)[source]#

Register a CLI command on app with error wrapping.

A command error prints Error: <message> on stderr and exits 2, beside typer’s own usage errors, so exit 1 is left to mean exactly the command’s own nonzero outcome and a script gating on one can never read a failed run as the other.

Parameters:
  • app (Typer)

  • name (str)

  • kwargs (Any)

Return type:

Callable

fractal.cli.utils.require_non_negative(**limits)[source]#

Reject negative numeric CLI options as BadParameter.

Each keyword maps a limit option’s name to its value (None is skipped); the first negative raises with that option’s flag spelling. Centralizes the >= 0 guard so every numeric --max-* cap is validated uniformly at the CLI boundary – unbounded is expressed by omitting the flag, never by a negative sentinel.

Parameters:

**limits (float | None) – Option name mapped to its value (e.g. max_depth=max_depth).

Raises:

typer.BadParameter – If any value is negative.

Return type:

None

fractal.cli.utils.require_timestamp(**instants)[source]#

Reject non-ISO-8601 timestamp CLI options as BadParameter.

Each keyword maps an instant option’s name to its value (None is skipped). The stored instants are ISO 8601 strings and the filters compare them lexicographically, so an unparseable value is not an error downstream – it silently empties the listing (anything sorting above the rows) or silently filters nothing (anything below), and a mistyped --since becomes indistinguishable from an empty mailbox. A bare date is accepted: it sorts as the prefix of that day’s first instant, which is exactly the cut it names.

Parameters:

**instants (str | None) – Option name mapped to its value (e.g. since=since).

Raises:

typer.BadParameter – If any value is not an ISO 8601 date or timestamp.

Return type:

None

class fractal.cli.utils.StreamRenderer[source]#

Bases: object

ANSI terminal renderer for parsed agent stream events.

The single CLI renderer over the seam’s normalized events – one instance per stream, passed as the render callback to Agent.stream (the loop’s pane rendering) and Node.chat so core never prints. Branches on kind only, never on the provider; session and cost facts render nothing (they are recorded, not displayed). Tracks the open text run (a delta without a trailing newline) so tool headers and closing summaries start below it, and close() ends a truncated stream on a fresh line with the em-dash $? placeholder summary. Every write flushes: piped stdout is block-buffered while the driving command’s stderr echoes are write-through, so an unflushed line would land out of order in a merged capture.

Parameters:

self (StreamRenderer)

Initialize StreamRenderer.

Parameters:

self (StreamRenderer)

close()[source]#

End the stream on a fresh line, settling the closing summary.

Called by the driving command after the stream drains – a truncated stream (killed before its result frame) otherwise leaves the cursor mid-line and shows no closing summary, so the em-dash $? placeholder marks the unaccounted turn. Idempotent, and resets for the next stream (the loop reuses one renderer across steps).

Parameters:

self (StreamRenderer)

Return type:

None

fractal.cli.utils.print_rows(rows, *, csv=False, columns=None)[source]#

Print rows as a formatted table or CSV.

Uses sys.stdout.isatty() to auto-detect format: formatted text table for terminals, CSV for pipes. The csv parameter enables CSV output.

Parameters:
  • rows (list[dict[str, Any]]) – List of row dicts.

  • csv (bool) – Output as CSV.

  • columns (list[str] | None) – Column names for the empty-result header (emitted in both formats so empty output is never ambiguous).

Return type:

None

fractal.cli.utils.print_json(rows, *, columns=None)[source]#

Print rows as a JSON array of objects.

The machine-readable counterpart to print_rows: one object per row, keys in columns order, and an empty result prints [] (never nothing), so parsers see one shape per listing.

Parameters:
  • rows (list[dict[str, Any]]) – List of row dicts.

  • columns (list[str] | None) – Key order for the row objects (default: each row’s own key order).

Return type:

None

fractal.cli.utils.init_node(path)[source]#

Resolve a Node for init (accepts repo root as-is).

When an agent runs init from inside its own worktree (_NODE set) with the default path, resolve to the caller’s repo root so the new node nests under the main repo rather than the worktree (the parent is resolved from _NODE in Node.init).

Parameters:

path (str | PathLike)

Return type:

Node

fractal.cli.utils.resolve_init_target(path)[source]#

Resolve the git-root-anchored node and project path for an init target.

fractal init/node init accept a repo root or a sub-project folder as path. The user node and every child must be anchored at the git root – Node.node_dir derives the <project>/ prefix from the .worktrees/.project cache, so anchoring at a sub-project folder would double the prefix. This resolves the target, then returns a Node at its git root plus the target’s project-relative path.

Parameters:

path (str | PathLike) – Repo root or sub-project folder (absolute or relative).

Return type:

tuple[Node, Path]

Returns:

(node, path) – a Node at the git root and the target’s path relative to it (. for the repo root).

Raises:

typer.BadParameter – If the target is a linked git worktree (outside the main repo root, so never a sub-project path), or a directory below a node worktree’s root (the sub-project would be lost).

fractal.cli.utils.resolve_node(path, *, check=True)[source]#

Resolve a Node instance from a path argument.

Parameters:
  • path (str | PathLike) – Worktree directory (absolute or relative).

  • check (bool) – Require an initialized node at the resolved path (mirrors resolve_target), so a user-facing command run outside a node fails cleanly instead of leaking a raw internal error. The pre-init private setter (config _set, which writes the very config.json this checks) passes check=False.

Return type:

Node

Returns:

Node bound to the resolved path.

Raises:

typer.BadParameter – If path is a repo root with active worktrees (should use the worktree path), or (when check) the resolved path is not an initialized node.

fractal.cli.utils.resolve_sender(path)[source]#

Resolve the acting sender: --path when given, else the calling node.

The loop exports _NODE for the node whose loop is running, so a production send attributes its sender to that node wherever the agent’s CLI call runs from (a detached step’s cwd is not a node identity); an explicit --path still wins – an operator acting as a node from outside it.

Parameters:

path (str | None) – Explicit worktree directory, or None for the default.

Return type:

Node

Returns:

Node the message’s sender attributes to.

Raises:

typer.BadParameter – If the exported _NODE does not name an initialized node (a stale or mistyped export).

fractal.cli.utils.resolve_headless(headless, node)[source]#

Resolve the launch backend: flag, else FRACTAL_HEADLESS, else the record.

An explicit --headless/--tmux wins and re-records the backend. Otherwise the exported FRACTAL_HEADLESS decides – the loop derives it for every seat from the parent’s own record, so a delegated child start follows its parent’s backend. With neither set, the node’s .headless marker names the backend it last launched with (tmux for a node that has never run headless).

Parameters:
  • headless (bool | None) – The --headless/--tmux flag, or None when not passed.

  • node (Node) – Resolved launch target, whose recorded backend is the default.

Return type:

bool

Returns:

Whether the launch runs headless.

Raises:

typer.BadParameter – If FRACTAL_HEADLESS carries anything but true or false (a broken seat env refuses cleanly, never coerces into a backend choice).

fractal.cli.utils.resolve_user_node(path, name=None)[source]#

Resolve a tree’s user (root) node from any checkout, by config not branch.

The tree-wide verbs (pause/resume/track/…) must always anchor on the user node, but resolve_node() keys on the repo’s current branch – so on a non-init checkout (the user on their own branch while nodes run) it silently mis-scopes to a lone child or dies on two. Node.resolve_user() finds the config.json marked user: true for the named tree (or the one owning the caller’s branch) and pins a Node to that branch, independent of the git checkout; this wrapper turns its no-such-tree None into the CLI refusal.

Parameters:
  • path (str | PathLike) – Any path inside the repo.

  • name (str | None) – Root branch of the tree to act on; None infers it from the caller’s branch.

Return type:

Node

Returns:

The tree’s user (root) node, branch-pinned.

Raises:

typer.BadParameter – If the repo has no such tree, or several trees leave the caller’s checkout ambiguous.

fractal.cli.utils.resolve_target(path, node=None)[source]#

Resolve the node to act on, anchored at the caller’s worktree.

path identifies the caller’s own worktree (default cwd); node, when given, selects another node by branch name within the same repo. Omitting node targets the caller’s own node.

Parameters:
  • path (str | PathLike) – Caller’s worktree directory (absolute or relative).

  • node (str | None) – Target node’s branch name, or None for the caller.

Return type:

Node

Returns:

Node bound to the resolved target worktree.

Raises:

typer.BadParameter – If node names a branch with no worktree, or the resolved target is not an initialized node.

fractal.cli.utils.resolve_ledger_target(path, node)[source]#

Resolve a cost-ledger target, answering for deleted branches.

A live target resolves normally as (node, None, None). A deleted branch fails resolution while its recorded history persists in the shared db, so it resolves through the caller as (caller, branch, latest_run) – how the fallback applies stays per command (remaining reports no budget without delegating; spent/breakdown substitute latest_run for an absent scope). The fallback accepts the same unique short names live resolution does: the registry rows that expansion consults die with the node, so the trailing segment expands against the recorded runs instead.

Parameters:
  • path (str | PathLike) – Caller’s worktree directory (absolute or relative).

  • node (str | None) – Target node’s branch name, or None for the caller.

Return type:

tuple[Node, str | None, int | None]

Returns:

(node, deleted, latest_run) – the resolved target, the deleted branch name (None for a live target), and the deleted branch’s latest recorded run id.

Raises:

typer.BadParameter – If resolution fails and the branch never recorded a run (there is no history to answer from), or a short name matches several live or recorded branches.

Module contents#

Command-line interface for fractal.