Documentation

7 documents, in the order a reader needs them: install the CLI, set up the cloud, choose the loop a run follows, configure the layers, run another model, connect a provider, and the generated command reference.

Guides

Fugaro CLI reference

Every command and flag fugaro ships, generated from the command tree (internal/cli.NewRootCmd) by go run ./internal/clidoc. Cobra's own help and completion commands, the root's -v, --version flag, and the -h, --help flag of every command, are part of what fugaro --help lists, so they are here too. Commands the CLI hides are not listed: they are internal, and a hidden command is not user documentation.

fugaro

Fleeting cloud workers for coding agents

Usage: fugaro

Flags:

Flag Type Default Required Description
-h, --help bool false no help for fugaro
-v, --version bool false no version for fugaro

Subcommands:

fugaro accounts

List the named Claude accounts a repository may use

Usage: fugaro accounts

Flags:

Flag Type Default Required Description
-h, --help bool false no help for accounts

Subcommands:

fugaro accounts ls

List the named Claude accounts (claude_accounts in the local config) a repository may use with fugaro run --token

List the names fugaro run --token NAME (and a repository's own agent.oauth_token) accept for a repository: "default" (the repository's own unnamed claude-oauth-token secret, always allowed) first, then every claude_accounts name in the local config whose allow_data_to lists the repository, sorted (config.AllowedClaudeAccounts, the one place that decides the question).

This is a local, read-only lookup, offline whenever a project config is already selected on this machine: it reads the local config file the CLI would otherwise select for a cloud command, and never calls Secret Manager, never reads or prints a token value, only names.

--json's "complete" is false when no project config could be selected for a reason other than an explicit --config/--project/$FUGARO_CONFIG/$FUGARO_PROJECT (several project configs with nothing to pick one, for example): "accounts" is then "default" alone, which may be short the repository's real claude_accounts, not a true answer that none are configured; "note" says why.

Usage: fugaro accounts ls

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for ls
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no repository as owner/name (default: this checkout's origin)

Subcommands:

This command has no subcommands.

fugaro allow-delete

Record the Terraform addresses a run may delete or replace (the project's owner)

Record which Terraform addresses the project's owner approves for deletion or replacement, in a managed project's fugaro/allow-delete.json at the root of the runs bucket. An approval lives in that object and nowhere else: this command is Fugaro's only writer of it, with the operator's own credentials.

An address is the Terraform address a plan reports, as in google_cloud_run_v2_job.web or module.web.google_storage_bucket.data, and nothing else is recorded — no note and no reason: the object's own version history is the record of who approved what and when.

Run it in your own terminal, as an operator, with your own credentials. Pass the addresses to approve, and they replace the list: an invocation that leaves an address out removes that approval, so the command names the entries it would drop and asks for the project's name before writing such a list. With no addresses the command prints the list and writes nothing; it never empties the list, because an empty list is what an absent one already means.

What the object is for: a run's own credentials cannot write it, and the deployer account a managed apply impersonates will not be able to (D75, Task 1.5), so a managed apply can take its allow-list from the owner's object instead of from the command line of whatever session is applying: fugaro managed apply reads this object and takes no address flag of its own, so the session that applies cannot widen the list. (the converge still honours its own --allow-delete on its own command line, where the addresses go to tf.Guard directly.) What this does not do: a coding agent's session holds the operator's credentials, so it can write this object directly (gcloud storage cp) or unset its agent marker and run this command itself. The refusal below is a speed bump against an agent that uses the program, not a boundary against one that goes around it (SECURITY.md records the wider Owner/Editor bypass).

On a project that is not managed, this writes an object nothing reads: the check below is that the bucket is the project's own runs bucket, which every installation has, not that the project is Fugaro-managed.

The list is read fail-closed: an absent object, an empty one, one naming another project, one whose fields are not exactly the three this command writes, or one this package cannot read is no addresses — and no addresses refuses every delete and replacement rather than permitting them. The write is fail-closed too: if the current list cannot be read, nothing is written, and the write is conditioned on the object being unchanged since that read, so a concurrent write is never silently overwritten (run the command again).

Usage: fugaro allow-delete [ADDRESS...]

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for allow-delete
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro artifacts

List and download what an artifact run delivered

Read and fetch the files a mode: artifact run put in the runs bucket.

Both subcommands read the run's result.json, never a bucket listing: ls prints the record's own table, and get is the object — so an artifact the record names and the bucket no longer holds (they expire with the run, runs_days) is a recorded row that ls shows and a get that says exactly that.

The record was written by an untrusted, model-driven run, so get treats every name in it as hostile: it downloads into a fresh directory it creates (./<run>-artifacts/, with --out naming that directory's parent), refuses a name that is absolute, walks out of that directory or begins with a dot, refuses to overwrite an existing file without --force, and verifies the recorded size and sha256 as the bytes arrive, leaving nothing behind when they do not match. What it writes is never opened, executed or unpacked.

Usage: fugaro artifacts

Flags:

Flag Type Default Required Description
-h, --help bool false no help for artifacts

Subcommands:

fugaro artifacts get

Download a run's artifacts, verifying them against its record

Download one artifact NAME, or every artifact the record lists when NAME is omitted, verifying each one's size and sha256 against the run's result.json as the bytes arrive. A mismatch is an error naming both hashes, and nothing is written — not even the file the download streamed into.

The destination is a fresh directory this command creates, ./<run-id>-artifacts/ unless --out names another parent for it. A name in the record that is absolute, walks out of that directory, begins with a dot or holds a control character is refused — a refusal, never a flattened name. An existing file needs --force to be replaced.

Nothing downloaded is ever opened, executed or unpacked: the recorded content type, the size and the sha256 are printed, and that is all. The sha256 proves the bytes are the bytes the run uploaded; it is not tamper evidence.

Usage: fugaro artifacts get RUN [NAME]

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--force bool false no replace a file that is already at a destination path
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for get
--out string "" no the directory to create the run's artifact directory in (default: the working directory)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no the repository RUN belongs to (owner/name; needed only for a bare run ID)

Subcommands:

This command has no subcommands.

fugaro artifacts ls

List what a run's record says it delivered

List the artifacts RUN (<repo-slug>/<run-id>, or a bare run ID) recorded in its result.json: name, recorded size, short sha256, recorded content type and the probe's verdict. The record is shown as it is — a name get cannot retrieve is listed with the reason, never filtered out, and a missing object is not ls's to hide: ls is the record, get is the object.

--json prints the record's artifact list verbatim.

Usage: fugaro artifacts ls RUN

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for ls
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no the repository RUN belongs to (owner/name; needed only for a bare run ID)

Subcommands:

This command has no subcommands.

fugaro backends

List the compute backends an installation can run its jobs on

Usage: fugaro backends

Flags:

Flag Type Default Required Description
-h, --help bool false no help for backends

Subcommands:

fugaro backends ls

List the compute backends this fugaro knows and which one the project uses

List the compute backends a project config's backend: key may name, and which one the selected project uses:

cloud-run Cloud Run jobs (the default, and the one every installation runs on today) cloud-batch Cloud Batch jobs, one VM per run (the VM backend)

A config that names no backend runs on cloud-run. A config that names cloud-batch is refused by this build ("not enabled yet"): the backend and its opener are built and tested, and nothing selects them until Fugaro has passed the live checklist for it.

This is a local, read-only lookup: it reads the project config and the backend names internal/backend defines, and it calls no cloud API. With --json it prints one object instead of two lines, carrying the list, the selected project and the name its config selects.

Usage: fugaro backends ls

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for ls
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro budget

Show and change the project's model-spend caps and kill switches

Look at and change the project's budget: the caps, today's counters, the kill switches and the runs in flight, kept in the project's Firebase Realtime Database (design §8). show needs the Viewer role on the Firebase project; set, kill and resume need the budget-admin role (the project's owners and editors, and terraform.budget_admins). preview needs the Viewer role too, and reads the project's recipes from the runs bucket as recipes ls does, which a plain project Viewer cannot (see docs/gcp-setup.md).

Usage: fugaro budget

Flags:

Flag Type Default Required Description
-h, --help bool false no help for budget

Subcommands:

fugaro budget kill

Halt every run of the project or of one repository (budget admins)

Set the project's kill switch (--all) or one repository's (--repo [owner/name]), recording who, when and why. --repo with no value means the repository this checkout belongs to, as it does for set. show takes owner/name (its empty --repo means no repository filter, not a checkout), and preview already means this checkout's repository without the flag. Runs in flight halt within seconds (a run in its last stage finishes pushing its work first) and new runs refuse. --all asks you to type the project's name (or pass --yes); one repository needs no confirmation.

Usage: fugaro budget kill (--all | --repo [owner/name]) [--reason TEXT]

Flags:

Flag Type Default Required Description
--all bool false no the whole project
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for kill
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--reason string "" no why (recorded, shown by budget show and watch)
--region string "" no GCP region (overrides the project config's)
--repo string "" no one repository, given as owner/name (no value: this checkout's origin)
--yes bool false no confirm without typing the project's name

Subcommands:

This command has no subcommands.

fugaro budget preview

Estimate a batch's worst-case model spend against today's caps

Estimate what N runs of a recipe may cost at most, and how that compares with today's remaining daily caps. It is a ceiling, not a forecast: a run is bounded by the smaller of budget.per_run_usd and the recipe's most stages times the repository's agent.max_budget_usd.

That figure is the model ceiling and nothing else: a run also bills for the machine it asked for, and no budget key bounds compute at all -- the run's own timeout is the cap. So for every workflow the checkout defines the preview also prints a compute ceiling -- the workflow's own resources over its own timeouts.total, at this installation's compute prices -- and says out loud that it is NOT CAPPED, because for a run whose expensive half is a render on a big machine the model ceiling is not the worst case. The basis is timeouts.total and not a render step's own timeout: a render that overruns still burns the run's clock to the deadline. Without a price table for the installation's region the line names what is missing and prints no dollar figure, rather than $0.00.

When the repository's recorded runs of the last 30 days are at least 5, the preview also prints, clearly labelled typical:, what one run of the recipe usually costs: the mean recorded cost of each stage kind (on the model the checkout pins for it, with the recipe's role mapping applied as a run applies it), at the recipe's typical stage count -- one round of each loop the recipe and its round knobs leave in, the two fix stages sharing one mean -- over the runs of the window, whatever they ended as: a stage counts once it recorded a cost. A kind the window has no cost for on that model leaves the line out and names the model, rather than pricing the pinned model with what another one cost. Runs the figure cannot use are said to be left out, never counted as runs that cost nothing: a run on a subscription (agent.auth oauth), whose model_usd is notional rather than charged, a result.json that is missing or unreadable, and a record with no per-stage cost at all (one from before the runner kept it, or stages that all cost nothing).

The recipe resolves as it does for a run: the repository's .fugaro/recipes, the project's published recipes, then the catalog. The knobs come from the checkout's fugaro.yaml; without a checkout of the repository here, agent.max_budget_usd is unknown and only budget.per_run_usd bounds the figure, so with neither the preview says it is unbounded.

It reads the database's caps and today's counters (the Viewer role), and the repository's runs (the runs bucket, for the typical line). A recipe that is not in the checkout also reads the project's recipes from the runs bucket, as recipes ls does, which a plain project Viewer cannot read (the converge removes that access): not reading it costs the project layer, and the ceiling with it. The typical line's own read of the bucket is the one that costs the line and nothing else: a bucket it cannot read leaves the ceiling standing. It writes nothing.

Usage: fugaro budget preview --recipe NAME --runs N [--repo R]

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for preview
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--recipe string "" yes the recipe to preview (default, lint-fix, pick, premium, review-only, solo, standard, test-and-fix)
--region string "" no GCP region (overrides the project config's)
--repo string "" no the repository (owner/name); default: the checkout's origin
--runs int 0 yes how many runs to estimate

Subcommands:

This command has no subcommands.

fugaro budget prices

Show the effective model price table, offline

Show the model prices the budget accounts with: the table built into this fugaro, its source and the day it was last checked (a warning when that is over 90 days ago), with the selected project config's model_prices applied and marked local. A model a provider route claims is priced from the built-in provider price catalog instead: its CATALOG column says which tier and which end of that tier's range is charged, and the catalog has its own source, date and 30-day staleness warning. It never reaches the network.

Usage: fugaro budget prices

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for prices
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro budget resume

Clear a kill switch (budget admins)

Clear the project's kill switch (--all) or one repository's (--repo [owner/name]), recording who and when. --repo with no value means the repository this checkout belongs to, as it does for set. Asks you to type the project's name (or pass --yes): runs start again.

Usage: fugaro budget resume (--all | --repo [owner/name]) [--reason TEXT]

Flags:

Flag Type Default Required Description
--all bool false no the whole project
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for resume
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--reason string "" no why (recorded, shown by budget show and watch)
--region string "" no GCP region (overrides the project config's)
--repo string "" no one repository, given as owner/name (no value: this checkout's origin)
--yes bool false no confirm without typing the project's name

Subcommands:

This command has no subcommands.

fugaro budget set

Set a cap, the lease limit or the mode (budget admins)

Set caps in the project's budget database, with an ETag-guarded write that shows the old and the new value.

--global the project's daily and per-run caps; also takes --max-reserve and --mode --defaults the caps of every repository that has none of its own --repo [owner/name] one repository's caps; --repo with no value means the repository this checkout belongs to, which needs no owner/name at all

--max-reserve (the largest single lease) and --mode (observe, or enforce: caps bite) are project-wide, so they go with --global. Amounts are US dollars from 0 to 100000, and a per-run cap may not exceed the daily cap of the same scope. --clear removes the scope's caps.

--repo owner/name always works, from anywhere; the bare --repo is refused outside a checkout whose origin names a repository, and never stands for the project-wide caps. kill and resume take --repo the same way. preview needs no bare form: left out, --repo already means this checkout's repository. show is the one whose empty --repo is not the checkout (it means no repository filter), so it takes owner/name.

Raising a cap (setting one that was unset counts), or lowering the mode to observe, loosens what runs may spend, so it asks you to type the project's name (or pass --yes). Lowering needs no confirmation.

Usage: fugaro budget set (--global | --defaults | --repo [owner/name]) [--daily USD] [--per-run USD] [--max-reserve USD] [--mode observe|enforce] | --clear

Flags:

Flag Type Default Required Description
--clear bool false no remove the scope's caps
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--daily float64 0 no the daily cap, in US dollars
--defaults bool false no the default caps of repositories without their own
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
--global bool false no the project's own caps
-h, --help bool false no help for set
--max-reserve float64 0 no the largest single lease, in US dollars (with --global)
--mode string "" no observe or enforce (with --global)
--per-run float64 0 no the per-run cap, in US dollars
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no one repository's caps, given as owner/name (no value: this checkout's origin)
--yes bool false no confirm a raise without typing the project's name

Subcommands:

This command has no subcommands.

fugaro budget show

Show caps, today's counters, kill switches, headroom and the runs in flight

Show the project's budget: the mode, the caps, today's (UTC) counted, spent and notional dollars, the headroom under the caps, the kill switches and the runs in flight. By default the global figures and the project config's repositories; --repo one repository; --all every repository the database knows. Notional is a subscription's list-price figure, never billed and never capped. Counted is spent plus what runs have reserved and not released.

Usage: fugaro budget show

Flags:

Flag Type Default Required Description
--all bool false no every repository in the database
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for show
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no only this repository (owner/name)

Subcommands:

This command has no subcommands.

fugaro cancel

Cancel a run, letting the runner open a draft PR first

Cancel RUN (<repo-slug>/<run-id>, or a bare run ID).

cancel writes the run's cancel marker; the runner sees it, stops the current stage and finalizes a draft PR. If the execution is still in a stage after --grace, cancel stops it through Cloud Run. It never stops a run that is opening its PR (finalize) or writing back its cache: it waits up to 10 more minutes for finalize, and counts writeback as finalized. --grace is never shorter than the workflow's timeouts.finalize_reserve (5m when neither the run nor this checkout tells) plus 40s for the runner to notice the cancel. --now stops the execution at once (so it takes no --grace), without waiting for the draft PR, but still not mid-finalize.

Usage: fugaro cancel RUN

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
--grace duration 3m0s no how long to wait for the runner to finalize before cancelling the execution (at least the run's finalize reserve plus 40s)
-h, --help bool false no help for cancel
--json bool false no print the result as JSON
--now bool false no cancel the execution at once, without waiting for a draft PR
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro completion

Generate the autocompletion script for the specified shell

Generate the autocompletion script for fugaro for the specified shell. See each sub-command's help for details on how to use the generated script.

Usage: fugaro completion

Flags:

Flag Type Default Required Description
-h, --help bool false no help for completion

Subcommands:

fugaro completion bash

Generate the autocompletion script for bash

Generate the autocompletion script for the bash shell.

This script depends on the 'bash-completion' package. If it is not installed already, you can install it via your OS's package manager.

To load completions in your current shell session:

source <(fugaro completion bash)

To load completions for every new session, execute once:

Linux:

fugaro completion bash > /etc/bash_completion.d/fugaro

macOS:

fugaro completion bash > $(brew --prefix)/etc/bash_completion.d/fugaro

You will need to start a new shell for this setup to take effect.

Usage: fugaro completion bash

Flags:

Flag Type Default Required Description
-h, --help bool false no help for bash
--no-descriptions bool false no disable completion descriptions

Subcommands:

This command has no subcommands.

fugaro completion fish

Generate the autocompletion script for fish

Generate the autocompletion script for the fish shell.

To load completions in your current shell session:

fugaro completion fish | source

To load completions for every new session, execute once:

fugaro completion fish > ~/.config/fish/completions/fugaro.fish

You will need to start a new shell for this setup to take effect.

Usage: fugaro completion fish

Flags:

Flag Type Default Required Description
-h, --help bool false no help for fish
--no-descriptions bool false no disable completion descriptions

Subcommands:

This command has no subcommands.

fugaro completion powershell

Generate the autocompletion script for powershell

Generate the autocompletion script for powershell.

To load completions in your current shell session:

fugaro completion powershell | Out-String | Invoke-Expression

To load completions for every new session, add the output of the above command to your powershell profile.

Usage: fugaro completion powershell

Flags:

Flag Type Default Required Description
-h, --help bool false no help for powershell
--no-descriptions bool false no disable completion descriptions

Subcommands:

This command has no subcommands.

fugaro completion zsh

Generate the autocompletion script for zsh

Generate the autocompletion script for the zsh shell.

If shell completion is not already enabled in your environment you will need to enable it. You can execute the following once:

echo "autoload -U compinit; compinit" >> ~/.zshrc

To load completions in your current shell session:

source <(fugaro completion zsh)

To load completions for every new session, execute once:

Linux:

fugaro completion zsh > "${fpath[1]}/_fugaro"

macOS:

fugaro completion zsh > $(brew --prefix)/share/zsh/site-functions/_fugaro

You will need to start a new shell for this setup to take effect.

Usage: fugaro completion zsh

Flags:

Flag Type Default Required Description
-h, --help bool false no help for zsh
--no-descriptions bool false no disable completion descriptions

Subcommands:

This command has no subcommands.

fugaro config

Work with fugaro.yaml

Usage: fugaro config

Flags:

Flag Type Default Required Description
-h, --help bool false no help for config

Subcommands:

fugaro config example

Print an annotated fugaro.yaml template

Usage: fugaro config example

Flags:

Flag Type Default Required Description
-h, --help bool false no help for example

Subcommands:

This command has no subcommands.

fugaro config init

Write the minimal fugaro.yaml of a repository whose project publishes a project layer

Write the minimal fugaro.yaml (version, project, gcp_project) into this checkout, for a project that publishes a project layer. Non-interactive: without --yes it prints the file and exits 1 (a dry run), writing nothing. It never overwrites a fugaro.yaml, and writes nothing that does not resolve against the project layer. Unlike fugaro config publish, it never refuses in a coding-agent session: it writes a repository file, which still goes through a reviewed pull request, not a published, trusted object.

Usage: fugaro config init

Flags:

Flag Type Default Required Description
--base-branch string "" no the repository's base branch, when it is not main
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for init
--profile string "" no the project layer's profile to use (default: its default_profile)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--yes bool false no write the file (without it, print it and exit 1 without writing)

Subcommands:

This command has no subcommands.

fugaro config layer

Print the project layer published for this checkout's project

Usage: fugaro config layer

Flags:

Flag Type Default Required Description
-h, --help bool false no help for layer
--json bool false no print machine-readable output

Subcommands:

This command has no subcommands.

fugaro config list

List the Fugaro installations this machine has

List the project configs under $XDG_CONFIG_HOME/fugaro/projects (else ~/.config/fugaro/projects), with the GCP project and region of each and what is known about its published shared config.

This is a local read: it opens no bucket and needs no project selected, so it works outside a checkout and with no config at all (an empty list).

Usage: fugaro config list

Flags:

Flag Type Default Required Description
-h, --help bool false no help for list
--json bool false no print machine-readable output

Subcommands:

This command has no subcommands.

fugaro config publish

Publish the project layer: defaults and profiles for every repository of the project (fugaro/project-layer.yaml in the runs bucket)

Usage: fugaro config publish FILE

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--executable-changes bool false no allow a change to a profile's commands, image.apt or image.setup (they run as shell in every repository that takes the profile)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for publish
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro config show

Print the checkout's fugaro.yaml resolved over the project layer, with where each value comes from

Usage: fugaro config show

Flags:

Flag Type Default Required Description
-h, --help bool false no help for show
--json bool false no print machine-readable output
--offline bool false no never read the runs bucket: resolve against the cached project layer, if any
--project-layer string "" no resolve against this project layer file instead of the published one
--workflow string "" no show only this workflow's keys besides the top-level ones

Subcommands:

This command has no subcommands.

fugaro diagnose

Explain how a run went: status, tests, review findings, the agent's last word, logs

Gather what is known about RUN (<repo-slug>/<run-id>, or a bare run ID): its status and reason, the last test run's failed and flaky tests, the last review's findings, the agent's final message, the newest log lines, its cost and its pull request. Everything printed is redacted of the credentials in this environment, on top of the runner's own redaction.

Usage: fugaro diagnose RUN

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for diagnose
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro doctor

Check that Fugaro is set up and ready, without changing anything

doctor only ever reads: the environment, Terraform, the local project config and its installation's billing and IAM policy (with a budget backend, who can sign sign-in tokens for it: warnings, never changes), the Fugaro plugin's wiring in .claude/settings.json, a checkout's fugaro.yaml and its stored secrets by name. With --check-github-app, in your own terminal, it also asks GitHub whether each GitHub App the config records is installed on its repository with the permissions Fugaro's runs ask for: that is the one thing that reads a secret's value (the App's private key, with your own credentials, held in memory only to sign, never printed), and it is refused in a coding agent's session. Otherwise it never prints a secret's value and never creates, enables or changes anything.

Inside a checkout whose project publishes a project layer (fugaro config publish), doctor also names which layer applies, flags a run that launched without it or on an older generation, a stale per-repository copy (the one Cloud Build and the daily image check read), and a workflow whose image was last built from settings it no longer resolves to: every one of these degrades with a note instead of failing when the bucket is unreachable.

--plugin checks only the plugin's wiring: offline, no credentials, safe for CI. --strict makes a stale, unpinned, foreign or unwired plugin fail the command; the informational states ("not installed", "cannot compare") never do, with or without --strict. A repository's CI runs fugaro doctor --plugin --strict.

With no local Fugaro installation configured, doctor says so and names fugaro setup as the fix, rather than guessing at a project.

Exit 1 when a check fails, with the one-line fix to run.

Usage: fugaro doctor [--json] [--plugin] [--strict]

Flags:

Flag Type Default Required Description
--check-github-app bool false no also check each recorded GitHub App is installed with the permissions runs ask for: reads the App's private key from Secret Manager with your own credentials, in memory only, for one signed token (refused in a coding agent's session; GODEBUG=http2debug would print the token, so unset it)
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--dir string . no a directory in the checkout, for the plugin wiring
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for doctor
--json bool false no print machine-readable output
--plugin bool false no check only the plugin's wiring (offline, no credentials)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--strict bool false no fail on a stale, unpinned, foreign or unwired plugin (CI mode)

Subcommands:

This command has no subcommands.

fugaro help

Help about any command

Help provides help for any command in the application. Simply type fugaro help [path to command] for full details.

Usage: fugaro help [command]

Flags:

Flag Type Default Required Description
-h, --help bool false no help for help

Subcommands:

This command has no subcommands.

fugaro image

Build and inspect a workflow's derived container image

Usage: fugaro image

Flags:

Flag Type Default Required Description
-h, --help bool false no help for image

Subcommands:

fugaro image build

Build the derived image with Cloud Build; --local builds it with local Docker and smoke-tests it

Build the workflow's derived image.

Without --local, Cloud Build builds it from the repository's base branch as the repository's build account (cloning with its bitbucket-token, or a read-only token its GitHub App mints) and pushes it to the repository's own registry, which fugaro update --repo creates. It pushes a candidate, smoke-tests it without network, and only then points latest at it and records what it was built from, unless a newer build is already recorded. Run it from the repository's checkout: the checkout's fugaro.yaml and origin say what to build.

A Cloud Build is billable, so it asks for the project's name typed at a real terminal: --json, a pipe and a coding agent's session never confirm it (it prints the one line to run in your own terminal window and exits 1). --local builds with your own Docker and costs nothing in the cloud.

Usage: fugaro image build

Flags:

Flag Type Default Required Description
--base string "" no base image (default: for a Cloud Build build the local config's base_images entry for the workflow's base, else the published fugaro-base matching this fugaro version)
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for build
--json bool false no print machine-readable output (--local only: a Cloud Build is never submitted with --json, nobody can type its confirmation)
--local bool false no build with the local Docker daemon from the checkout you are in
--no-smoke bool false no skip the smoke test of the built image (on Cloud Build, latest is then promoted unsmoked)
--no-wait bool false no submit the Cloud Build build and return without waiting for it
--platform string linux/amd64 no image platform; Cloud Run runs linux/amd64
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no owner/name of the repository to build with Cloud Build (default: the checkout's origin, which it must match)
--tag string "" no tag for the built image (default fugaro-<dir>-<workflow>:local)
--workflow string "" no workflow to build; optional when fugaro.yaml defines one

Subcommands:

This command has no subcommands.

fugaro image check

Decide whether each workflow's image needs a rebuild (the daily check; locally it only prints the decision)

Evaluate the rebuild triggers of the repository's workflows against the head of its base branch: a blobless clone, the build record in the runs bucket, and the base image's digest.

Run from a checkout, it uses your own git access and ADC and only prints each workflow's decision: it never submits a build (--dry-run is accepted, and is what it always does). Start a rebuild with fugaro image build.

With --job, as the repository's daily check job runs it (it refuses to submit unless CLOUD_RUN_JOB or CLOUD_RUN_EXECUTION is set, as Cloud Run sets them, and in a coding agent's session; --dry-run is always allowed), it reads the job's spec from FUGARO_CHECK_SPEC, submits a Cloud Build rebuild when a trigger fires (not with --dry-run), writes builds/<slug>/<workflow>/check.json, and logs one JSON line per workflow. It exits 2 when the check itself failed.

Usage: fugaro image check

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--dry-run bool false no never submit a build (locally the check never does anyway)
--force bool false no decide rebuild whatever the triggers say (with --job: a build is submitted)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for check
--job bool false no run as the repository's daily check job (reads FUGARO_CHECK_SPEC; submits rebuilds)
--json bool false no print machine-readable output (locally; the job always logs JSON)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--workflow string "" no check only this workflow

Subcommands:

This command has no subcommands.

fugaro image refresh

Move a repository onto this release's base image: pin it, point the daily image check job at it, rebuild what is stale

Run it in the checkout of an onboarded repository, in your own terminal window (or with --yes, unattended: see below).

It prints the whole plan first, then, each step with its own confirmation: (2) resolves this release's base image of each selected workflow's kind at its -pinned tag, verifies its cosign signature, reads the digest back through the installation's remote repository and pins base_images.<kind> to it, as fugaro update --base does (nothing is copied); (3) points the repository's daily image check job at it (its image and FUGARO_CHECK_SPEC's base images) through the Cloud Run Admin API, with no Terraform; (4) rebuilds each selected workflow whose build record says it was built from another base, or that has no build record or one that cannot be read (billable: the project's name typed for each, or --yes); (5) names fugaro update --anchor when fugaro.yaml lacks gcp_project.

A base_images entry that is neither this release's pin nor a release image an earlier fugaro copied (a development or hand-pushed image) stops it before anything changes, with or without --yes: remove the entry from the local config, then rerun. It never writes fugaro.yaml, never onboards a repository and never runs Terraform. A coding agent's session is refused, --yes included (there is still no --json or --plan-only to get around that). A run that stops says which steps finished and what to rerun, --yes included when it was given; a rerun skips what is done. Exit codes: 0 done, 1 a refusal or a confirmation not given, 2 a cloud failure.

Unattended use: pass --yes to run it from CI or a script, with no terminal. It confirms every step, the base pin, the check-job update and EACH billable build, with no cap on how many builds run: read the printed plan's build count first. --yes does not cover a coding agent's session (refused, same as without it) or a custom, dev or hand-pushed base image (still refused with the one-line fix); there is still no --json or --plan-only.

Usage: fugaro image refresh

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--expect-digest stringArray [] no cross-check the digest of a base image's release signature, KIND=sha256:<hex>, as for the converge (repeatable)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for refresh
--image-source string "" no the registry and owner the release base images come from, as for the converge (default ghcr.io/dimipaun)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no owner/name of the repository; it must be this checkout's origin (refresh reads fugaro.yaml from the checkout)
--workflow stringArray [] no a workflow to refresh (repeatable; default: every workflow in fugaro.yaml)
--yes bool false no confirm every step without asking (the base copy, the check-job update and each billable build, with no cap on how many run), and without a terminal: for CI or a script. The plan is still printed first. Never confirmed in a coding agent's session (CLAUDECODE and the like): nothing is applied there, --yes included. Without --yes nothing changes: a real terminal and the typed confirmations are still required, as always

Subcommands:

This command has no subcommands.

fugaro image render

Print the Dockerfile that builds the workflow's derived image

Print the Dockerfile that builds the workflow's derived image.

Unlike other fugaro commands, this one has no --json form: its output is the Dockerfile itself, not a report about one.

Usage: fugaro image render

Flags:

Flag Type Default Required Description
-h, --help bool false no help for render
--workflow string "" no workflow to render; optional when fugaro.yaml defines one

Subcommands:

This command has no subcommands.

fugaro image status

Show each workflow's image: when it was built and from what, and its last daily check

For each workflow of the local config's repositories (or --repo's), print what the runs bucket records: the image's build time and age, source commit and base digest (builds/<slug>/<workflow>/image.json), and the last daily check's time, decision and reasons, and the last rebuild's status (check.json).

Usage: fugaro image status

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for status
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no only this repository (owner/name, as the local config's repos has it)

Subcommands:

This command has no subcommands.

fugaro logs

Print a run's logs from Cloud Logging

Print the logs of RUN (<repo-slug>/<run-id>, or a bare run ID), oldest first.

With -f, keep printing until the execution ends and its logs settle. Every line is redacted of the credentials in this environment, on top of the runner's own redaction.

Usage: fugaro logs RUN

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
-f, --follow bool false no keep printing new entries until the execution ends
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for logs
--json bool false no print one JSON object per entry
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--url bool false no print the execution's Cloud console link instead of the logs

Subcommands:

This command has no subcommands.

fugaro managed

Act on a managed project (a project created by fugaro with the coding agent's refusal removed)

Act on a managed project: one Fugaro created with the coding agent's confirmation refusal removed, where what may be applied is decided by a deterministic gate over the saved plan rather than by a prompt.

This is not fugaro project (the project's own configuration) and not fugaro setup (which creates and re-installs an installation): a managed project's agent-authored root is applied by fugaro managed apply.

Usage: fugaro managed

Flags:

Flag Type Default Required Description
-h, --help bool false no help for managed

Subcommands:

fugaro managed apply

Apply the agent-authored Terraform root of a managed project, as the deployer account

Apply the agent-authored Terraform root ROOT of a managed project.

ROOT is a directory in this repository's checkout (the checkout whose fugaro.yaml names the project). Its files are copied into this command's own workdir — never run in place, so terraform's plan file, lock file and provider directory are not written beside the configuration under review — the state backend is set to the project's own managed state bucket, the root is planned into a saved plan file, the plan is gated against the project's managed allowlist and against the addresses the project's OWNER approved, and the saved plan file is applied as the project's deployer service account.

An address this command may delete or replace is never one of this command's arguments: an approval lives in the owner's object at gs://<runs bucket>/fugaro/allow-delete.json, written with the operator's own credentials by fugaro allow-delete. This command registers no flag for one, so there is no way to pass one.

The refusal this command's apply does not carry is the coding agent's: in a managed project a coding agent's session applies cloud changes with no human prompt, and what replaces the prompt is the gate (a refused plan is not applied), the marker (an unreadable one is not an apply), the owner's list (an address the owner did not approve is refused) and the deployer account (which holds no project-level IAM permission and cannot write the runs bucket). None of the four is a boundary against a session that goes around this program.

The prompt comes back whenever the whole-project inventory does not clear the project: before anything is planned, this command reads every resource of the GCP project (Cloud Asset, with the operator's own credential) and compares it with what the installation's Terraform states account for. A resource nothing accounts for, or an inventory that cannot be read, keeps this run's confirmation: a coding agent's session is refused and a person is asked, with the reason printed first. Nothing is halted and nothing is deleted.

Usage: fugaro managed apply ROOT

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for apply
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro managed flag

Flag an existing GCP project as managed (the project's owner)

Flag an existing GCP project PROJECT_ID as managed, recording the managed marker in that project's own runs bucket.

This is how a project Fugaro did not create becomes managed: the marker is what tells a later run that this project's applies may skip the typed confirmation, and it is normally written by the run that created the project (fugaro setup --create-project --managed). Flagging is the after-the-fact way, answered by the owner on 2026-10-16 (design D58).

Run it in your own terminal, as an operator, with your own credentials, and type the GCP project ID it asks for. A coding agent's session is refused, and nothing stands in for the typed ID — not --yes, not --non-interactive, not a pipe.

PROJECT_ID must be the exact GCP project ID of the installation selected by --project, --config or the checkout: one invocation names one project, and there is no bulk form, no wildcard and no prefix.

It records two things in gs://<runs bucket>/fugaro/managed.json: that this project is managed, and the monthly budget amount and currency the owner states with --budget and --budget-currency. The amount is required, with no default (D64), because the marker's amount is what the apply-time budget read opens the gate on: a marked project that records no amount is refused by every managed apply rather than applied without a budget control. The amount is the budget ALERT amount the installation's Terraform creates and nothing else — this command does not create or change a budget, and it makes no billing call.

It writes BOTH marks a managed project carries, in one step: that object and the label fugaro_managed=true on the GCP project itself (Task 3.0). The read requires both — the label is what a project can be narrowed by, and a project without it is not managed whatever its runs bucket holds — so writing the object alone would leave this project exactly as unmanaged as it was. The label is written on the project, by the account running this command, which is why the account needs resourcemanager.projects.update on it; the project's other labels are kept. A project that already carries that key with another value is refused rather than overwritten — the label someone else put there is a statement, not an empty slot — and the object written first is not taken back, so that refusal says which half is there and which is missing.

What flagging gives up, stated before it writes: the marker is what turns the typed confirmation off (D55), so from this project's next apply on, the deterministic gate, the whole-project inventory, the owner-written delete list (fugaro allow-delete) and the recorded budget are what stand in the confirmation's place — and a coding agent's session can apply a plan the gate admitted with no human prompt.

What it does not do: it is not a boundary against a session that goes around this program. A coding agent's session holds the operator's credentials, can write this object directly with gcloud storage cp, and can unset its own marker and run this command; SECURITY.md records the wider Owner/Editor bypass. The refusal below is a speed bump against an agent that uses the program, not a wall against one that goes around it.

A project that already records an amount is refused: the amount is the owner's decision at the time it was taken, and this command adds a budget rather than editing one. The one object it does replace is a version-1 creation fact — what fugaro setup --create-project without --managed leaves, and the state the managed apply refuses for having no amount recorded. What it replaced is said in the output, and the write is conditioned on the object it read, so nothing is overwritten blind.

Usage: fugaro managed flag PROJECT_ID

Flags:

Flag Type Default Required Description
--budget int64 0 no the monthly budget alert amount to record, in whole units of --budget-currency (required)
--budget-currency string "" no the currency --budget is in, three uppercase letters such as USD (required)
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for flag
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro plan

Read a plan's graph artifact

Usage: fugaro plan

Flags:

Flag Type Default Required Description
-h, --help bool false no help for plan

Subcommands:

fugaro plan graph

Print a plan's graph: its tasks, their dependencies and their footprints

Print the graph artifact written beside a plan.

The artifact is <plan>.graph.json — for docs/plans/foo.md it is docs/plans/foo.graph.json. With no --plan, the one graph artifact under docs/plans/ is read; more than one is refused rather than guessed at.

A graph this build does not understand is refused rather than rendered: a future schema version, a malformed document, an edge to a node that is not there, or a cycle. This is a local read and it writes nothing.

Usage: fugaro plan graph

Flags:

Flag Type Default Required Description
-h, --help bool false no help for graph
--json bool false no print machine-readable output
--plan string "" no the plan document the graph sits beside (default: the one graph artifact under docs/plans)

Subcommands:

This command has no subcommands.

fugaro project

The project's policy, its allow-list and the repositories enrolled

Usage: fugaro project

Flags:

Flag Type Default Required Description
-h, --help bool false no help for project

Subcommands:

fugaro project policy

The project's published policy (config/policy/shared)

Usage: fugaro project policy

Flags:

Flag Type Default Required Description
-h, --help bool false no help for policy

Subcommands:

fugaro project policy publish

Publish the project's policy from the operator's config (budget admins)

Build the project's policy from this machine's project config — the budget block, the providers (their secrets by NAME, never a value), the model_prices overrides and the allow-list — and write it to config/policy/shared with an ETag-guarded write. The diff follows the write: it is printed once the node holds the policy, and a write that failed prints no diff — the node is as it was, and the command says so. From then on the node is the ceiling for every key it names, and a key it leaves out keeps the value the job's environment was deployed with.

Publishing refuses without the rules marker (config/limits/policyRules): a policy published before the rules are deployed is one the runs it bounds cannot read, so run fugaro update --firebase-project with a Fugaro that deploys them first. It also refuses a node written by a newer Fugaro.

allow_repos is seeded from this machine's repos: map and from the shared config's repository list, and only when the node is absent or --seed-repos is passed: a later publish never re-adds a name an operator removed. It is never seeded from the bucket's runs/<slug>/ prefixes, which every run of a profile can write.

--delete removes the policy (the children first, the shared node last, so a launch mid-delete still sees the policy and keeps its tighter ceiling) and puts the project back on the ceiling its jobs' environments carry. It goes through the same rules-marker gate as a publish, refuses without --yes while runs are in flight, and asks you to type the project's name otherwise. The config/policy/repos children are removed too, shared node or not: a delete that failed on the shared node leaves them behind, and this is what clears them.

Each repository an account a profile names admits also gets a child under config/policy/repos/$slug, holding the NAMES of its admitted accounts and nothing else: the accounts published are the ones some profile names, qualified by that profile's provider, so an account this config defines but no profile names admits nothing a run could read. The children are narrowed before the shared node and widened after it, so a publish that stops between the two writes leaves the installation tighter than the config asked, never looser. When the node this publish writes carries a member an older Fugaro refuses whole — a credentials block, or routing.claude.via: cascade — the diff warns: older launchers (and, for the cascade, older runner images) stop until they are upgraded.

routing.claude is written where the operator writes it: this config. A publish refuses to drop an active route the config does not account for, and refuses to turn the credentials cascade off — a node carrying routing.claude.via: cascade with a config that names no Claude switch is refused with its own sentence, because the cascade is not a route: it decides which credential carries a Claude call, never where the call goes, and it is what hands a run the project's other credentials. A config that writes a routing block naming no Claude switch in the same state is refused too. --remove-routing is how the operator says the block goes away on purpose.

Usage: fugaro project policy publish [--seed-repos] [--delete] [--yes] [--remove-routing]

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--delete bool false no remove the published policy (project-centric mode off again)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for publish
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--remove-routing bool false no drop the published policy's routing block on purpose (an active route stops being taken)
--seed-repos bool false no seed allow_repos from the operator's repositories again
--yes bool false no confirm the deletion without typing the project's name

Subcommands:

This command has no subcommands.

fugaro project policy show

Show the published policy

Usage: fugaro project policy show [--json]

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for show
--json bool false no machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro project repos

The project's allow-list: which repositories may run

Usage: fugaro project repos

Flags:

Flag Type Default Required Description
-h, --help bool false no help for repos

Subcommands:

fugaro project repos add

Enroll a repository: add it to the policy's allow-list

Add a canonical repository (github/acme/app, bitbucket/acme/*) to the published policy's allow_repos, through the same ETag-guarded write the publisher uses, and print the change. A bare owner/name takes its provider from this machine's project config, and * is the list's own entry for every repository: it is written as it is. Only allow_repos changes.

Usage: fugaro project repos add <provider/owner/name | owner/name | *>

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for add
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro project repos ls

List the allow-list and the repositories that have run here

Usage: fugaro project repos ls

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for ls
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro project repos rm

Un-enroll a repository: remove it from the allow-list

Remove an entry from the published policy's allow_repos. Removing an allowed repository stops its runs — a run of a repository the list does not admit is refused before it mints a git token — unless another entry on the list still admits it, which is the case a pattern makes (github/acme/app stays admitted while github/acme/* is there); the confirmation says which of the two it is. Removing * refuses every repository the rest of the list does not admit. So it asks you to type the project's name, and while a run is in flight --yes alone is not enough.

Usage: fugaro project repos rm <provider/owner/name | owner/name | *>

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for rm
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--yes bool false no confirm the removal without typing the project's name (not while a run is in flight)

Subcommands:

This command has no subcommands.

fugaro project show

Show the effective project: caps, policy, allow-list, repositories running now

Print the effective project: the caps the installation's nodes carry (global, defaults and per repository), the published policy, the allow-list with where each entry came from, and the repositories the run registry holds. The registry holds the runs in flight, so that list is what is running now — an empty one is nothing running, not nothing that has ever run. The last pair is what an operator needs: repositories that are running and are not on the allow-list (their next run is refused until 'fugaro project repos add'), and allow-list entries nothing is running now.

Usage: fugaro project show [--json]

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for show
--json bool false no machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro project sync

Offer the GitHub App's installed repositories that the allow-list does not admit

Ask GitHub which repositories the project's GitHub App is installed on (the App API, with the App's own credentials) and offer the ones the published allow_repos does not admit, as one list confirmed once. The accepted list is written through the same ETag-guarded write project repos add uses; entries are only ever added, so a sync never un-enrolls a repository — including one the App is not installed on at all (another provider's, or one whose installation was removed). A repository the list already admits, a pattern included, is not offered: its runs are admitted already.

Reading the App's private key is --github-app-key: without it, nothing is read. Under it the key is read from the project's github-app-key secret with your own credentials and held in memory only to sign the App's 9-minute JWT (never printed, never passed as an argument, never in an error), and the flag is refused in a coding agent's session. The App ID is a number, not a secret: --github-app-id, or the one this machine's config already holds.

Usage: fugaro project sync [--github-app-id <id>] [--github-app-key] [--yes]

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
--github-app-id string "" no the GitHub App's ID (a number, not a secret); default: the one this machine's config holds
--github-app-key bool false no read the project's GitHub App private key from Secret Manager to ask GitHub (your own credentials; refused in a coding agent's session)
-h, --help bool false no help for sync
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--yes bool false no confirm the batch without typing the project's name

Subcommands:

This command has no subcommands.

fugaro recipes

List, show, validate and publish recipes, the task loop a run follows after implement

A recipe is found by name in .fugaro/recipes/NAME.yaml in the repository, then in the project's fugaro/recipes/NAME.yaml in the runs bucket, then in the catalog (default, lint-fix, pick, premium, review-only, solo, standard, test-and-fix). See docs/recipes.md.

Usage: fugaro recipes

Flags:

Flag Type Default Required Description
-h, --help bool false no help for recipes

Subcommands:

fugaro recipes ls

List the recipes of this checkout, the project and the catalog, and which one wins for each name

Usage: fugaro recipes ls

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for ls
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--verbose bool false no also print each recipe's use_when and steps

Subcommands:

This command has no subcommands.

fugaro recipes publish

Publish a recipe to the project, for every repository of it (fugaro/recipes/NAME.yaml in the runs bucket)

Usage: fugaro recipes publish FILE

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for publish
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro recipes show

Show the recipe a run would get for NAME, and the layer it comes from

Usage: fugaro recipes show NAME

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for show
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro recipes validate

Check a recipe file (no cloud access)

Usage: fugaro recipes validate FILE

Flags:

Flag Type Default Required Description
-h, --help bool false no help for validate
--json bool false no print machine-readable output

Subcommands:

This command has no subcommands.

fugaro report

Report what the project spent, by day, week, month, year, repository, model or person

Report model spend from the project's spend history.

Finished days are read from the spendDaily documents in the project's Firebase Firestore (you need roles/datastore.viewer on the Firebase project, which launchers, operators and budget admins have). Today, yesterday and any day the daily job has not archived yet are computed live from the budget database and marked (partial). --since and --until are UTC dates (YYYY-MM-DD); --since also takes Nd, the last N days up to --until (default 30d); --until defaults to today.

--by day|week|month|year groups in time; weeks are ISO weeks in UTC, starting on Monday (2026-W40). --by repo|model|person groups across the range. Dollars are exact (integer micro-dollars). Model $, NOTIONAL~ (a subscription's list-price figure, never billed) and COMPUTE $ are separate columns and never summed; compute reads n/a when no run's compute was estimated. A person is the stored requester address; unknown is spend, and runs, with no requester recorded. By model, a subscription model's dollars are notional (priced from its tokens, so they can differ slightly from the run's recorded notional total).

--csv writes RFC 4180 with spreadsheet-safe cells (a cell starting with = + - @ is prefixed with '). --json prints {project, source, degraded, by, since, until, weeks, rows[{key, start, end, partial, model_micros, model_usd, notional_micros, notional_usd, compute_micros, compute_usd, compute_estimated, run_hours, runs, calls, tokens}], totals, warnings}; *_usd are exact decimal strings and null means n/a or not recorded for that breakdown.

A project without Firestore history says so and shows run-record totals instead (from the runs bucket, not budget history).

Usage: fugaro report

Flags:

Flag Type Default Required Description
--by string day no group by day, week, month, year, repo, model or person
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--csv bool false no print CSV (RFC 4180)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for report
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no only this repository (owner/name)
--since string 30d no first day: YYYY-MM-DD (UTC) or Nd, the last N days
--until string "" no last day, YYYY-MM-DD (UTC; default today)

Subcommands:

This command has no subcommands.

fugaro restore

Rebuild a managed project from zero, into a new GCP project ID, and print what is not restored

Rebuild a managed project from zero into a NEW GCP project ID.

A managed project is one fugaro created, with a marker and an owner-written delete list. When it is lost — deleted, or closed — its ID stays reserved for about thirty days, so this is a restore into a new project:

The OLD project's own creation fact is read whenever the old project can still be read: a project that is not managed is refused, whatever flags are given, and a managed one gives the monthly budget amount unless --budget and --budget-currency are given. When the old project cannot be read (deleted, closed, not yours to read) the restore proceeds only on --budget and --budget-currency. The billing account is always yours to give: the creation fact is not a credential and records none.

NEW_PROJECT_ID must not exist: an existing project is refused, never adopted. If the creation fails or does not end in a project this run created and marked, the local project config file is put back as it was and the restore can be run again; what the creation did in the cloud (the project, its billing link, its state bucket, the installation's resources, any secrets) is not undone, and the message says so.

Every step is money or permanent, so the project's name is typed at a real terminal, and there is no --yes, no --non-interactive and no address argument. The command is refused in a coding agent's session.

It ends by printing what was NOT restored, as a list: the runs that were in flight and their counters, the spend history and the kill-switch state, the Firestore and RTDB application data, the Firestore database's location (which is permanent and can never be changed), every secret value, the plan files that were never stored, and the owner-written delete list — fugaro/allow-delete.json is not carried across and nothing here re-creates it, so until the owner writes it again with fugaro allow-delete every delete and replace in the new project is refused (D74). The project layer and the project's recipes are not held by the repository and are not re-published: the command names them and the one command that publishes each. The deployer account is the one thing that is re-created rather than lost, and the output says so.

Usage: fugaro restore NEW_PROJECT_ID ROOT

Flags:

Flag Type Default Required Description
--billing-account string "" no the billing account the new project is linked to (required: the creation fact records the amount and no account)
--budget int64 0 no the new project's monthly budget amount, in whole units of --budget-currency (default: the amount the OLD project's creation fact records)
--budget-currency string "" no the budget's currency, such as USD (travels with --budget)
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--display-name string "" no with the creation: the new project's display name (default: its ID)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for restore
--parent string "" no with the creation: organizations/<number> or folders/<number> to create the project under (default: no parent; an organization is never guessed)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)

Subcommands:

This command has no subcommands.

fugaro run

Launch a task in the cloud; it always ends with a PR

Launch a task in the cloud. The task is TEXT or --task-file (- for stdin).

A repeated --run-id reports the existing launch instead of starting another, so a caller that is unsure whether its launch went through can simply repeat it. --retry RUN launches a run whose task was stored but never started.

--pr N continues Fugaro's pull request N: the run acts on the PR's review comments by the authors fugaro.yaml's followup.trusted lists, on the PR's own branch, with the base branch and workflow of the runs before it. TEXT, or --task-file, is optional then, and adds instructions.

--recipe NAME --pr N, NAME naming a mode: review recipe such as review-only or pick, reviews any open pull request of the repository instead (not only Fugaro's own) and never pushes or opens one; repeat --pr (2 to 4 times) for pick, which compares them. TEXT, or --task-file, is optional then too, and adds instructions to the review.

--attempts N (2 to 4) launches N independent runs of the same task as a best-of-N batch, under one batch label (bestof-<first run id> unless --batch is given); once they have all opened a pull request, compare them with fugaro run --recipe pick. It takes no --pr, --run-id or --retry.

--title TEXT names the run: one line, at most 100 characters. It is what fugaro watch and runs ls list the run by, and the title (with the body's first heading) of the pull request the run opens when the agent writes no pr.md of its own. Without it the run is named by the task's first line, so briefs that start with the same preamble all read alike. A follow-up (--pr) keeps its run's title unless --title is given, and --retry takes none (the stored task keeps its own).

Usage: fugaro run [TEXT]

Flags:

Flag Type Default Required Description
--attach stringArray [] no attach a file to the run: repeatable, uploaded to the run's own inputs/ and downloaded by a mode: artifact run into the checkout's inputs/ (needs a mode: artifact recipe; refused on a run ID that already has a task)
--attempts int 0 no launch N (2 to 4) independent runs of the same task, workflow, ref and recipe as a best-of-N batch (bestof-<first run id> unless --batch is given); refused with --pr, --run-id, --retry or a review-mode recipe
--batch string "" no batch label, to group runs in fugaro runs ls
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for run
--json bool false no print machine-readable output
--no-budget-check bool false no skip the launch pre-check of the budget (kill switches, missing caps, no headroom); the run is held to the same limits when it starts
--pr intSlice [] no continue Fugaro PR N: act on its trusted review comments (TEXT adds instructions); with a mode: review --recipe, review PR N instead (repeatable, 1 to 4, any open PR)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--recipe string "" no the recipe (task loop) to run: .fugaro/recipes/NAME.yaml, the project's, or the catalog's (default, lint-fix, pick, premium, review-only, solo, standard, test-and-fix); default: agent.recipe in fugaro.yaml, else default; a follow-up (--pr) keeps its previous run's
--ref string "" no base branch or ref (default: the repo's base_branch)
--region string "" no GCP region (overrides the project config's)
--repo string "" no repository as owner/name (default: this checkout's origin)
--retry string "" no launch the stored task of RUN (<repo-slug>/<run-id> or a run ID) that never started
--run-id string "" no run ID to use; repeating one reports its launch instead of starting another
--task-file string "" no read the task from this file, or - for stdin
--title string "" no the run's title, one line: what fugaro watch and runs ls list the run by, and the pull request's title unless the agent wrote its own pr.md (default: the task's first line); a follow-up (--pr) keeps its run's title unless this is given
--token string "" no named Claude account (claude_accounts in the local config) this run uses, for a workflow whose agent.auth is oauth; default: agent.oauth_token in fugaro.yaml, else the repository's default account; a follow-up (--pr) keeps its previous run's account unless this is given explicitly, which overrides it (checked the same way as a first run's)
--total-timeout string "" no override the workflow's timeouts.total for this run, such as 45m
--workflow string "" no workflow to run (default: the repo's only workflow)

Subcommands:

This command has no subcommands.

fugaro runs

List and inspect runs

Usage: fugaro runs

Flags:

Flag Type Default Required Description
-h, --help bool false no help for runs

Subcommands:

fugaro runs ls

List runs with their status, stage, cost and PR

List runs, newest first, joining the runs bucket with what the backend knows.

By default ls shows the last 7 days of the local config's repositories (every repository in the bucket when the config lists none). A run shows as succeeded only when its result.json says so; an execution that ended without finalizing is an infra_error.

--pr N lists the runs on pull request N of one repository (the first run and its follow-ups), over the last 90d unless --since says otherwise; the totals line is then the PR's total cost.

Usage: fugaro runs ls

Flags:

Flag Type Default Required Description
--all bool false no every repository in the runs bucket
--batch string "" no only runs of this batch
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for ls
--json bool false no print machine-readable output
--mine bool false no only runs you requested
--pr int 0 no only runs on this pull request (needs one repository; --since defaults to 90d)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no only this repository (owner/name)
--since string 7d no only runs started within this long (7d, 36h, 90m; 0 for all)
--watch bool false no redraw until every run has settled

Subcommands:

This command has no subcommands.

fugaro runs stats

Aggregate run records by the person who started each run

Count the runs one installation's records hold, and what they cost, grouped by the person who started each one: how many jobs an identity began, how they ended, what they spent, and a total row.

The numbers come from the run records, not from the budget database: each run's launch.json carries the launcher it recorded at launch, and its result.json the outcome and the settled cost. Nothing else is read, no counter is consulted, and nothing is written.

Attribution, as the records themselves state it:

The money is model dollars, counted the way the budget screens count them. COUNTED $ is the model spend that counts toward the caps (a run on an API key or Vertex), and NOTIONAL $ the model spend of a run on a subscription (agent.auth oauth), which counts toward the plan's limits and is no one's bill: they are separate columns and are never summed. Compute is not attributed to a person: no run's compute is, and the budget database's own person view leaves it out too. AVG $/RUN is COUNTED $ over COUNTED RUNS, the runs that settled a real-dollar cost; a notional run, a run with no readable record and a record from before the cost breakdown existed are none of them averaged in as a run that cost nothing.

Who sees whom. An operator of the installation -- the identity the local config's user: (or git config user.email) names, listed in its terraform.operators, the list fugaro doctor's runs-bucket-iam check reads -- sees every user. Anyone else sees their own row alone, and --all-users asks for the whole table. An installation that names no operators (one from before the 0.7.0 hardening, say) places nobody, and the view then stays private: the default is never the wider one. Whose numbers a table shows is printed above it.

A private view is the caller's own row. A caller whose identity cannot be told at all -- the local config names nobody, and git config user.email answers nothing -- is refused, never quietly widened: the error says so, and names --all-users. An identity with no key of its own because nothing printable is left of it (a pasted zero-width character, say) is shown no row instead: the table is empty, and its view line says the CLI cannot tell who you are. A row keyed 'unknown' is every run of the window that recorded no launcher -- never one person's -- so it is not shown as the caller's own either, and a note says so.

The launcher text is what a launcher wrote and is read for nothing else: it is sanitised and size-capped before it is printed, and it authorises and admits nothing. Two identities that differ only in what the sanitising drops -- an invisible character a paste left in one of them, say -- or only past the cap are one key and one row: the row is the key, not the person. A private view is not read off that key. It holds the runs whose launch recorded your identity itself -- the identity fugaro run writes to launch.json at launch, not the requested_by that runs ls --mine compares -- so someone whose identity merely sanitises to the same text is never counted in it and your own row can be smaller than that key's row in the full table.

A window can hold every run of every repository in the bucket, so the read is bounded: at most --max-runs records are read per call, newest first, and a window holding more says so in the output rather than publishing figures over a part of itself silently. The bound is on the read, not on a person: the newest --max-runs runs are taken before any view is applied, so a private view can be over fewer of the caller's own runs than --max-runs suggests. A run with neither a launch nor a record -- never launched, cancelled before launch, or a stale claim -- never started and is counted in no figure. The count of those is the window's, over everyone's runs, and is no more the caller's own than the truncation is.

--json prints {project, by, view, you, operator, since, until, max_runs, truncated, rows[], totals[], unlaunched, notes}; rows and totals carry {user, currency, runs, succeeded, failed, halted, infra_error, other, no_record, counted_usd, notional_usd, counted_runs, avg_counted_usd}, and the counts add up: runs == succeeded + failed + halted + infra_error + other + no_record.

Usage: fugaro runs stats

Flags:

Flag Type Default Required Description
--all bool false no every repository in the runs bucket
--all-users bool false no show every user's row, not only your own (the table names whose numbers it shows)
--by string user no what to break the figures down by (user)
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for stats
--json bool false no print machine-readable output
--max-runs int 5000 no read at most this many runs, newest first (a bigger window is truncated and says so)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no only this repository (owner/name)
--since string 7d no only runs started within this long (7d, 36h, 90m; 0 for all)

Subcommands:

This command has no subcommands.

fugaro secrets

Store and list a repository's secrets in Secret Manager

Usage: fugaro secrets

Flags:

Flag Type Default Required Description
-h, --help bool false no help for secrets

Subcommands:

fugaro secrets ls

List the project's (or, with --repo, a repository's) secrets: names, scope and versions, never values

List secrets: their logical names, IDs, version counts and where each value comes from, never any value (this command has no access call at all).

The default listing is the installation's project-wide containers -- the scope secrets set stores to by default for a name that has one -- one row per name, with the repositories the local config allows to use each one (bitbucket-token is never stored there by default: a repository access token serves one repository, so it has only repository copies). --project-wide is accepted as a synonym, so existing scripts keep working.

--repo (or a bare --repo, for this checkout's origin) lists one repository instead: its own containers, each with SCOPE repo (the repository's own container), project (a project-wide container this repository may use and has no copy of), or "project, repo override stored" (both exist; the repository's own version wins). Nothing about a repository is listed without --repo: the project's containers are not one repository's.

In a --repo listing, MISSING marks a name a job of that repository needs and no enabled version resolves for: the answer to "which required secret is gone" after a restore. "Required" is what the jobs of the repository's workflows mount and cannot run without, worked out from the checkout's fugaro.yaml the way a deploy works it out: the git credential of its provider, the model credentials its agent.auth names (claude-oauth-token and each allowed claude_accounts token for oauth; each allowed model provider's key for api-key; none for vertex), and each workflows.<w>.secrets name. anthropic-api-key is never marked: the job mounts it only when it has a version and runs without it, and fugaro validate says when a pinned model needs it. A required name with nothing behind it gets a row of its own — a missing secret has no container to be listed from, so its SECRET, LATEST and CREATED read "-" (in --json its id is empty and it has no created). The same marks a container that exists with no enabled version behind it, which resolves to nothing as surely as no container at all. Run this from a checkout of the repository: only its fugaro.yaml says what is required, so elsewhere, or with a fugaro.yaml that does not parse, nothing is marked and a note on stderr says so.

The default (project-wide) listing has no MISSING column: a name absent there may be stored per repository and perfectly present.

Usage: fugaro secrets ls

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for ls
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--project-wide bool false no the default for a name that has a project-wide meaning, accepted as a synonym so existing scripts keep working (cannot be combined with --repo)
--region string "" no GCP region (overrides the project config's)
--repo string "" no work on one repository's own copy, given as owner/name (no value: this checkout's origin); without it, a name that has a project-wide meaning is stored and listed project-wide

Subcommands:

This command has no subcommands.

fugaro secrets set

Store a secret's value, read from stdin or a hidden prompt, as a new version

Store the value of the repository's secret NAME as a new version in Secret Manager, creating the secret if it doesn't exist.

NAME is one of anthropic-api-key, bitbucket-token, claude-oauth-token, github-app-key, the secret of a model provider in the local config's providers block (for a repository that provider's allow_data_to lists), or a secret some workflow declares under secrets: in the repository's fugaro.yaml (run it from a checkout of the repository for those).

The value is read only from stdin: pipe or redirect it in (fugaro secrets set bitbucket-token < token-file), or, when stdin is a terminal, paste it at a hidden prompt. It is never taken from an argument, a flag or the environment, and never printed. One trailing newline (\n or \r\n) is dropped, and the command says so. Values must be 4 bytes to 64 KiB, on one line except for github-app-key (a PEM, which must be redirected from a file), and a one-line value may not start or end with whitespace (a copy-and-paste slip, so it is refused rather than stored). Ctrl-C at the prompt stores nothing and restores the terminal.

Scope: with neither flag, a NAME that has a project-wide meaning is stored once for the whole Fugaro project, in the project's own Secret Manager -- the API key, the GitHub App key, a model provider's own key (any provider, allow_data_to or not), a reserved secret your local config's project_secrets block allow-lists, or a named Claude account's token whose account allow-lists at least one repository. --project-wide is accepted as a synonym of that default, so existing scripts keep working. Inside a shared job there is no IAM boundary between repositories: the profile is the boundary, and the value reaches every enrolled repository. A workflow's own secret has no project-wide meaning and is stored in the repository's own container as before.

bitbucket-token is the exception, and has no project-wide default: a Bitbucket credential is a repository access token, issued for one repository and usable for no other, so it is always stored in this repository's own container and every repository is asked for its own value (docs/git-providers.md). Storing it for the project is refused unless project_secrets.bitbucket-token allow-lists the repositories it is meant for.

With --repo (owner/name, or with no value for this checkout's origin) the value is stored as the repository's own copy instead. That copy overrides the project copy from then on: disabling its last enabled version is the only way back.

Usage: fugaro secrets set NAME

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for set
--json bool false no print machine-readable output
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--project-wide bool false no the default for a name that has a project-wide meaning, accepted as a synonym so existing scripts keep working (cannot be combined with --repo)
--region string "" no GCP region (overrides the project config's)
--repo string "" no work on one repository's own copy, given as owner/name (no value: this checkout's origin); without it, a name that has a project-wide meaning is stored and listed project-wide

Subcommands:

This command has no subcommands.

fugaro setup

Stand up or adopt the installation's, or a repository's, cloud resources through Terraform

setup plans the installation's shared resources (the runs bucket, registries, custom roles, the scheduler account, log isolation, an optional budget) with Terraform, adopting what the bootstrap already made, and applies the plan it showed once you confirm by typing the project's name (or pass --yes). It then writes the project config from the installation's outputs. With no project config yet, --name, --gcp-project and --region say what to create: projects/<name>.yaml.

It enables the Cloud Resource Manager API when it is disabled (setup reads the project's number through it before Terraform can enable it), creates the Terraform state bucket first when it doesn't exist, and offers to remove project Viewers' read access to the runs bucket, each after its own confirmation. --plan-only never takes --yes: what the plan needs made first (the state bucket, the API) takes the project's name typed at a terminal, or the run stops and says so. A plan that would delete or replace anything is refused unless --allow-delete names the address.

A new project config grants the launcher and operator roles to you, the authenticated user (user:<your email> from the Application Default Credentials), so that fugaro run works; --launcher or --operator replaces that, an existing or adopted config is never changed by it, and with a service account's credentials it stops and names the two flags.

On a first run it asks its questions once, in this order, each with a suggestion Enter takes, and writes the local config, so later runs ask nothing: the GCP project's ID (--gcp-project; a new project named after this one, fugaro-<name>, which Enter takes — and, offered second when it differs, the project GOOGLE_CLOUD_PROJECT or the credentials' quota project names, the one you are already working in; never gcloud's default project), the region (--region; us-east5), whether to create the GCP project or adopt one that already exists (--create-project; "create", what Enter takes, is a new fugaro-<name> project, and "adopt" leaves one that is already there exactly as it is), and the project's name (--name; from the repository's owner, else this directory's). Every question has its flag, so only the confirmations below need a terminal at all: without one, or with --non-interactive, --yes or --json, the run names every missing flag in one error — every one but the project question, whose answer it does not need. A run nobody can ask does not create a project (it adopts, the answer that changes nothing in the cloud), and --create-project is how a scripted run says otherwise. Where the installation already exists and this machine has no local config for it, setup keeps it rather than offering a second one: it writes the config from the installation and applies nothing, says which roles to ask an owner for, and says in one line that the installation is already set up and that it converges as fugaro update does. Its name is the installation's own (from the runs bucket's marker), not the directory's, and there is nothing left to ask. The budget section (rtdb_url, firebase_project, firebase_api_key, token_signer: none a secret) is filled from the Firebase root's outputs, read only. A --name given for an installation that already exists must be the installation's own name. With several project configs, --name of a project that has none is a first run of that name, and a checkout whose origin repository exactly one project lists selects that project.

Before the first billable image build of a GitHub repository, setup asks GitHub (as the App, with the App's key read from Secret Manager into memory only) whether the App is installed on the repository with the permissions runs ask for (Contents: Write, Pull requests: Write, Issues: Read, Metadata: Read): if not, the build is left for you with each missing permission and its fix and nothing is billed. A repository the local config does not list is asked about first, at the start, before any plan.

setup runs as a converge of stages (preflight, installation, with --firebase-project the Firebase backend and the images, the secrets, the plugin wiring and the repository, and on a first run the project — the one stage that may create the GCP project, and it does that only when the first question's answer was to create: an answer of "adopt" reads nothing and creates nothing), each with its own confirmation, in order, and stops at the first that fails or needs you; a rerun resumes, and one with nothing to do says "No changes" and exits 0. Exit codes: 0 done (or only planned), 1 a refusal, or a step left for you (see left_for_you in --json), 2 a cloud failure; --json prints the stages, left_for_you and the failed stage on every outcome.

setup --anchor, run in the checkout of a repository the project lists, writes only the gcp_project: line of its fugaro.yaml (the line teammates find the shared config from), with no Terraform, no discovery, no publish and no image copy; plain fugaro setup writes the same line as its last stage, after the whole converge. Before it writes anything it checks, and reports together (one line per workflow, with its fix in order), that every workflow's build record in the runs bucket exists and was submitted by a fugaro release at or after the one that added the field, and that the base image the image was built FROM (the record's base_ref, whose fugaro binary the image holds) is such a release too: a missing record, a record that does not say its base, an older or development CLI, or an older, development or hand-pushed base fails. It also checks that each base image the local config sets for those workflows is this release's pin through the remote repository, or a release image an earlier fugaro copied, at or after that release, since the next build starts from it (a development or hand-pushed one is never replaced by fugaro setup --base or fugaro image refresh: remove its base_images entry first). The fix it names is: that removal when needed, fugaro image refresh --repo <owner/name> --workflow <name> in the checkout (it verifies the release's base image and pins it by digest, points the daily image check job at it and rebuilds the image), then fugaro setup --anchor. The image checks trust the build records (written by the builds, unsigned: the same trust as the shared config), and a hand-pushed image under a release-looking tag passes them. Then the diff; --yes writes, a terminal asks, and otherwise it prints the line and writes nothing. Exit codes: 0 written or already present and safe, 1 a check failed or nothing was written, 2 a build record could not be read from the cloud (missing credentials included). A coding agent's session may run it, --yes included: it edits only a local file.

setup --firebase-project <firebase-project-id> builds the budget backend in a Firebase project you created and linked to billing, and it is the advanced case: the run's Firebase project is the installation's own --gcp-project by default (one id for both, D7), so this flag is for a project of its own — and it is also what adds the backend to the run, so a setup without it builds none. It creates nothing and links nothing (a project is created or billing linked only with --create-project and --link-billing, and it refuses one that is missing, has no billing, or whose database holds data and no Fugaro mark). It runs three applies, each with its own plan and confirmation: the installation (the history account), the Firebase root (the database, a restricted sign-in key, the token signer, and the budget admins: the GCP project's owners and editors who are users or groups, plus --budget-admin), and the installation again (the history job, once its image exists). Between the last two it deploys the database's rules, mark, project name, mode (--budget-mode; an absent one is seeded observe) and largest lease, after a confirmation of their own, then writes the local config. The same step creates the Firestore database for the spend history (fugaro report) if the project has none: its location, us-east5, is permanent (chosen once, never changed or deleted by Fugaro), so setup asks you to type the location to confirm it, at a real terminal (--yes, --non-interactive, --json and a pipe never confirm it: the step is then left for you), and it refuses to adopt a database that holds data or is in another location; it also deploys deny-everything Firestore rules and the Fugaro mark. --plan-only stops after the installation's plan. Each repository then needs fugaro setup --repo to pick up the jobs' environment.

setup --repo [PATH] onboards the repository of the checkout at PATH (default: the current directory), whose fugaro.yaml says what it needs, into the installation fugaro setup applied. It adopts what the bootstrap made for it (checking each resource's marks and live grants first), creates the rest, and deploys each workflow's job once its secrets are stored and its image is built. It offers each workflow's first image build (billable: the project's name typed at a real terminal, never --yes), then deploys the built image and unpauses the daily check. It prints the fugaro secrets set commands still needed, and adds the repository to the local config. A repository the local config does not list takes the same opt-in as the converge's stages (below): its owner/name typed at your own terminal, or --onboard-repo.

One confirmation per run, in a project Fugaro owns. A project is Fugaro's own when this run created it (--create-project) or its runs bucket carries Fugaro's mark (fugaro/project.json naming this project and this GCP project, in a bucket named fugaro-runs-<gcp project> that belongs to that project; read-only check: a custom runs bucket never qualifies). There, at a terminal, the ordinary steps (the Terraform applies of the installation, the Firebase root, the history job and the repository, the state bucket, the Cloud Resource Manager API, the Viewer-read removal, image copies and base-image pins that only add tags, the Firestore marks and rules, the local config) share one review screen and ONE typed project name, asked when the first of them has something to change (so a rerun with nothing to do asks nothing). Each step still prints its own exact plan. It never covers creating a project, linking billing, the Firestore database's permanent location, a Cloud Build, replacing a registry tag (--replace-image), an unlisted repository or a secret: each keeps its own typed confirmation. The state bucket and the API enable are fixed, announced, free steps; every Terraform plan is shown as it runs and is covered only if it is creates and in-place updates of the installation's own resource kinds, granting only the modules' roles to the listed launchers, operators and budget admins (the review prints them, with where each came from) or to Fugaro's own service accounts: its attributes are the modules' (images in this project's registry, jobs of this project, logs to this project, private buckets), and a Firebase project other than the installation's is covered only if verified as Fugaro's; any other plan (a destroy or replace, an IAM binding or policy, an unlisted member, a widened role) stops that step, which asks its own typed name. A wrong name applies nothing. The confirmation is per run and never stored. Any other project (adopted, unmarked, or a mark that fails to read) keeps a typed name at each step; --yes covers the ordinary steps as ever and takes no review; --non-interactive, --json, a pipe and a coding agent never take it; --plan-only shows the review and applies nothing.

What --yes covers: the ordinary steps, which are the installation's and the Firebase root's Terraform applies, the images' copy, the local config's writes, the plugin wiring and a known repository's steps. It never covers creating a project or linking billing, a secret, a repository the project does not list, the Firestore database's permanent location, a billable first image build, or replacing a tag in your registry (--replace-image KIND names the intent; the project's name is typed at a real terminal): with --yes, --non-interactive, --json or a pipe each of those is left for you, with the one line to run. In a coding agent's environment (CLAUDECODE, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_SSE_PORT, CLAUDE_CODE_REMOTE, CURSOR_AGENT or AI_AGENT is set) setup applies nothing, --yes included, and says to run it in your own terminal window; --plan-only and the read-only checks still work, and so does setup --anchor (with --yes), which edits only the checkout's fugaro.yaml. That is a mitigation, not a barrier: an agent can unset its own environment. A run that adopted an existing installation changes nothing in the cloud in the same run (the local config and, in a checkout, the plugin wiring in .claude/settings.json are the only files it writes).

In a checkout of a repository of this project, the converge's secrets stage asks, at hidden prompts, for the secrets its jobs mount (the git credential, the Claude credential agent.auth names, none for vertex, the allowed model providers' keys, the workflows' own), and skips what is already stored. It prompts only in your own terminal: never with --yes, --non-interactive or --json (--json never prompts; the stage then exits 1 with the one-line fugaro secrets set commands), never when stdin, stdout or stderr is not a terminal (a pipe is never read), and never when a coding agent's variable is set (CLAUDECODE and the like; CLAUDE_CODE_SSE_PORT is also set by the Claude Code IDE extension in VS Code and JetBrains terminals, so a person's own IDE terminal can be refused: its refusal tells you what to do about it). A pasted private key may have at most 120 lines. It creates each secret's container, before the repository stage, with the labels setup --repo's Terraform adopts (fugaro, fugaro_repo, fugaro_secret).

--create-project is the flag that answers the wizard's first question, and the only way to answer it without a terminal (a run with no terminal takes the other answer, adoption); the question itself decides nothing more than which of the two the project stage acts on. --create-project --gcp-project <id> creates that GCP project and adds Firebase to it, as you, after you type its ID at a terminal; --link-billing <account> then links that billing account (it may incur charges), after you type the account's ID. Each is its own typed confirmation, never given by --yes, --non-interactive or --json, and the ID is never the gcloud default project. Without --link-billing, setup prints the one gcloud command that links billing and stops. An existing project you can read is adopted; one that is held by someone else, or pending deletion, is refused; its existing billing link is never changed.

The plugin-wiring stage merges the Fugaro marketplace and plugin into the checkout's .claude/settings.json, pinned to this release's tag, after showing the diff and asking (--yes confirms it); see update-skills. The repository stage runs setup --repo for the checkout, after the secrets, when its origin's default branch (origin/HEAD, main or master, as last fetched) has a fugaro.yaml naming this project (else it is skipped and says why); it asks once for the GitHub App's ID of a GitHub repository (or take --github-app-id) and keeps its own confirmations, the first image build's included.

Both stages act only on a repository the project's local config already lists, or one you opt in: a repository not listed yet needs its owner/name typed at your own terminal (naming the checkout's path), or --onboard-repo owner/name, which must equal the origin (setup --repo takes both too); --yes, --non-interactive and a coding agent's environment never cover it (the flag is refused there), so a cloned third-party repository cannot onboard itself. The secrets stage is behind the same gate.

Usage: fugaro setup [--repo [PATH]]

Flags:

Flag Type Default Required Description
--alert-email string "" no where failed image checks and rebuilds are reported (default: the local config's)
--allow-delete stringArray [] no a resource address the plan may delete or replace (repeatable)
--allow-fork bool false no let the plugin-wiring stage move the ref of a fugaro marketplace in .claude/settings.json that names a repository other than dimipaun/fugaro (a fork you host); never done otherwise
--allow-job-delete bool false no with --repo: lower the jobs' deletion protection, for offboarding
--anchor bool false no in a checkout of an onboarded repository, only write the gcp_project line into its fugaro.yaml, after checking that every workflow's job image was submitted by fugaro 0.4.0 or later and built FROM a release base image of 0.4.0 or later, and that no base image the local config sets is a development or older one (no Terraform, no publish, no image copy; the diff, then --yes writes or a terminal asks)
--base stringSlice [] no copy the release's base image (the only kind: base) into the project's registry now, besides what the checkout's fugaro.yaml needs (comma-separated or repeated)
--base-image stringArray [] no a base image the image checks run and builds start from, recorded in the local config under its base kind (repeatable; KIND=IMAGE, or just IMAGE when its repository is named fugaro-<kind>; the kinds you don't name keep theirs)
--billing-account string "" no the billing account the budget is on
--budget int64 0 no a monthly budget on the project, in whole units of --budget-currency
--budget-admin stringArray [] no with --firebase-project: an IAM member who may change caps and kill switches besides the GCP project's owners and editors (repeatable; default: the local config's terraform.budget_admins)
--budget-currency string "" no the budget's currency, such as USD
--budget-mode string "" no with --firebase-project: off, observe or enforce: the jobs' budget mode in the project config, and for observe and enforce the project-wide mode in the database (an absent one is seeded observe)
--check-github-app bool false no let the GitHub App pre-check read the App's private key from Secret Manager with your own credentials (held in memory only, for one signed token; refused in a coding agent's session; GODEBUG=http2debug would print the token, so unset it); without it setup checks only with the key you typed in this run
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--config-only bool false no only write the local config, from the installation's outputs (else the flags)
--create-project bool false no create the GCP project --gcp-project names and add Firebase to it, as you, after you type its ID at a terminal (never with --yes, --non-interactive or --json). An ID that exists and you can read is adopted; one held by someone else is refused. Billing is not linked unless --link-billing is also given; without it setup prints the one gcloud command that links it
--display-name string "" no with --create-project: the project's display name (default: its ID)
--expect-digest stringArray [] no pin the digest of a release image, KIND=sha256:<hex> (KIND is base or history; repeatable): the digest of the source tag's manifest (the index), obtained out of band such as from the release notes. A base kind is cross-checked against it: its digest is verified against the release's cosign signature either way, and the two must agree. For history it is the trust anchor: without it setup trusts what the release tag in ghcr.io resolves to now, which is whoever can write that tag
--firebase-project string "" no build the budget backend in this Firebase project: the run's Firebase project is the installation's own --gcp-project by default (one id for both, D7), so this is the advanced flag, for a Firebase project of its own (one you created and linked to billing, or created with --create-project). It is also the flag that asks for the backend — a run without it builds none — and that backend is three confirmed applies: the database's rules and mark, the Firestore history database (permanent us-east5 location, typed confirmation) and the config
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
--github-app-id string "" no the GitHub App's ID, for a GitHub repository (not a secret; recorded in the local config; setup asks once at a terminal, and --non-interactive needs it)
-h, --help bool false no help for setup
--image-source string "" no the registry and owner the release images come from: a base kind is resolved and signature-verified there and pinned through the installation's remote repository, a history image is copied from it (default ghcr.io/dimipaun; a fork names its own, such as ghcr.io/acme)
--json bool false no print the result as JSON on stdout (progress goes to stderr)
--launcher stringArray [] no an IAM member who launches and watches runs (repeatable; default: the local config's, and for a new config you, the authenticated user)
--link-billing string "" no with --create-project: link this billing account (an ID such as 0123AB-4567CD-89EF01) to the project, after you type the account's ID at a terminal. This may incur charges on that account; never with --yes. It never changes a project's existing billing link
--managed bool false no with --create-project: create a Fugaro-managed project. The monthly budget amount is required — --budget, --budget-currency and --billing-account together, with no default and no suggestion taken for you — and the amount and the currency are recorded in the project's creation fact (gs://fugaro-runs-<gcp-project>/fugaro/managed.json), where a later run and a restore read what was chosen. It removes no confirmation: the project's creation still types its ID, and the installation's ordinary steps share the run's one review. Flagging a project that already exists as managed is fugaro managed flag's, run by the owner at a terminal, and never setup's
--name string "" no the project's name: for a project config setup creates, and what names an installation that has no name yet (with a config, it must be its name; an installation's name never changes; with several project configs it names the one to create or use; adopting an installation defaults it to the installation's)
--no-build bool false no with --repo: don't offer the first image builds
--no-log-isolation bool false no leave Fugaro job logs in _Default instead of their own log bucket
--non-interactive bool false no never prompt, and never read stdin: a step that needs you is listed under left_for_you (exit 1), and applying needs --yes, else the run only plans (a fresh state bucket needs its typed confirmation even then, so that plan exits 1); --yes never covers the typed-only steps (see --yes). With --config-only it only stops them prompting: their confirmations then need --yes
--onboard-repo string "" no owner/name of the checkout's origin repository (compared case-insensitively, without .git): the one non-interactive opt-in to wire and onboard a repository the project does not list yet, on github.com or bitbucket.org with an origin git config does not rewrite (--yes never covers it, and a coding agent's environment refuses it; any other origin takes the typed confirmation)
--operator stringArray [] no an IAM member who onboards repositories (repeatable; default: the local config's, and for a new config you, the authenticated user)
--parent string "" no with --create-project: organizations/<number> or folders/<number> to create the project under (default: no parent; an organization is never guessed)
--plan-only bool false no stop after showing the plan (never takes --yes: it creates nothing on a flag's word)
--print-vars bool false no print the Terraform variables and exit, with no cloud calls and no Terraform (ungated: no discovery, and with --repo no readiness gates)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--provision-batch bool false no after the installation's ordinary steps, provision the Batch compute backend into a state of its own (design docs/design/compute-backend.md §8.1 task 6): the Compute and Batch APIs, the run accounts' IAM, the launcher's, reaper's and preload accounts and roles, a Cloud NAT and router, and a preloaded boot image rebuilt on a schedule. It is money (a billable build, a NAT's hours) and permanent, so --yes never covers it and it is typed at a real terminal; nothing runs it by default, and no run can use the backend until the enable task. It excludes --repo and the flags that only read or only stop (--forget, --print-vars, --config-only, --publish-config, --anchor)
--publish-config bool false no publish the shared config to the runs bucket and stop (setup does it after it writes the local config)
--region string "" no GCP region (overrides the project config's)
--registry-cleanup string "" no Artifact Registry cleanup: dry-run (the default), on or off
--replace-image stringArray [] no KIND (history; repeatable): let the converge replace that image's tag in your registry when it names another image, a release tag or history:latest (never done otherwise, so a hand-pushed history:latest needs it once). A base kind is refused: it is pinned by digest through the remote repository, so nothing of it is ever copied over. It names the intent only: replacing is confirmed by typing the project's name at a real terminal, never by --yes, --non-interactive or --json
--repo bool false no onboard the repository of the checkout at PATH (default: the current directory) instead of the installation
--retire bool false no with --repo: run the retirement this config change needs (a workflow or a named account removed, a secret no longer mounted): remove the protected addresses from Terraform's state (destroying nothing), apply, then delete the orphaned Cloud Run jobs through the API. The project's name is typed twice at a real terminal, and --yes, --non-interactive, --json, a pipe and a coding agent cover neither. Excludes --plan-only, --print-vars and --forget, which read or forget rather than apply
--runs-bucket string "" no the runs bucket (default: the project config's, else fugaro-runs-<gcp-project>)
--scheduler-region string "" no Cloud Scheduler region of the daily image checks (default: the region, or the nearest one Scheduler offers)
--state-bucket string "" no the Terraform state bucket (default: the project config's, else fugaro-tfstate-<gcp-project>)
--yes bool false no confirm the ordinary steps without asking (only after reading what they do). Never covers creating a project, billing, a secret, an unlisted repository, the Firestore location, a billable first image build or replacing an image tag (typed at a real terminal). Under a coding agent's environment variable (CLAUDECODE and the like) setup applies nothing, --yes included (except setup --anchor, which only edits the checkout's fugaro.yaml and honours --yes there); that is a mitigation, not a barrier: an agent that unsets its own variables is not stopped, and the real controls are the typed confirmations and the skills' lint

Subcommands:

This command has no subcommands.

fugaro stats

Aggregate one installation's run records into figures a page can publish

Usage: fugaro stats

Flags:

Flag Type Default Required Description
-h, --help bool false no help for stats

Subcommands:

fugaro stats runs

Publish per-recipe duration and cost percentiles over a window, and nothing else

Aggregate one named installation's own run records into the figures the caps-and-costs guide publishes: the minimum, the median and the 90th percentile of a run's wall-clock duration and of its total cost, grouped by recipe.

The records are read read-only, over --since's window, from the installation --project names -- and result.json alone is read: the task a run was given is never read, so no task text can reach the output. Nothing a reader cannot look up is printed: the groups are recipe names and the window, every figure is a number or a count, and no field carries a repository, run ID, branch, pull request, task, person or commit.

--project is required and has no default, the sandbox included: an aggregation is about one named installation, and this command never guesses one. There is no --all. The command is refused in a coding agent's session: the records are an installation's own data, and reading them is the owner's step.

A recipe with fewer than --min-runs runs in the window is not published at all -- a range from four runs reads as a promise precisely because it is a range -- and a run whose recipe this binary does not ship, or whose record carries no finished duration or no cost breakdown, or whose result.json cannot be read at all, is counted and not grouped: one bad object never makes the figures unproducible.

--json prints {project, since, until, min_runs, recipes[{recipe, runs, compute_not_estimated, duration_s{min, median, p90}, cost_usd{min, median, p90}}], totals{runs, recipes, runs_below_min_runs, runs_unknown_recipe, runs_not_estimated, runs_without_a_figure}, not_a_promise}.

The closing line is the disclaimer the figures travel with: a cap is a ceiling rather than an estimate, a range is what one window of one installation's runs did rather than a promise, and every figure prints the number of runs behind it.

Usage: fugaro stats runs

Flags:

Flag Type Default Required Description
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for runs
--json bool false no print machine-readable output
--min-runs int 20 no the least runs a recipe may carry a range from (a thinner group is not published)
--project string "" yes the installation whose run records are read (required: this command never guesses one, and the sandbox is named like any other)
--region string "" no GCP region (overrides the project config's)
--since string 90d no the window: Nd days back from today, or a Go duration (90d)

Subcommands:

This command has no subcommands.

fugaro update

Re-converge the installation, or a checkout's repository, after a CLI upgrade

update re-converges an installation that already exists against this release: the same Terraform roots, the same stages, the same confirmations and the same exit codes as fugaro setup, for a project that is set up already (or, with --repo, for the repository of the checkout at PATH). Run it after upgrading the CLI: it applies what the release changed, and one with nothing to do says "No changes" and exits 0.

It creates nothing. The flags that do — --create-project, --display-name, --parent, --managed, --link-billing and --provision-batch — are fugaro setup's, and update refuses each by name rather than ignoring it, so a re-converge cannot create a GCP project, link a billing account or provision the Batch backend by accident. A first installation is fugaro setup.

--forget is update's own: it is the rollback, and it turns log isolation and registry cleanup off with a guarded apply before it removes every address from Terraform's state — in a checkout, the repository's addresses with them — destroying nothing else.

Everything else is setup's help word for word, because it is the same converge: the review screen and the one typed project name in a project Fugaro owns; the steps that keep their own typed confirmation (a project's creation and the billing link are setup's, and here they cannot arise at all, but the Firestore database's permanent location, a billable first image build, replacing a registry tag, an unlisted repository and the secrets are not); the secrets stage's hidden prompts and the one line of fugaro secrets set commands it leaves instead; the plugin wiring; --anchor, which writes the checkout's gcp_project line and nothing else; --plan-only, --print-vars, --config-only and --publish-config; and --json, which prints the same result object. Exit codes: 0 done (or only planned), 1 a refusal or a step left for you, 2 a cloud failure.

Usage: fugaro update [--repo [PATH]]

Flags:

Flag Type Default Required Description
--alert-email string "" no where failed image checks and rebuilds are reported (default: the local config's)
--allow-delete stringArray [] no a resource address the plan may delete or replace (repeatable)
--allow-fork bool false no let the plugin-wiring stage move the ref of a fugaro marketplace in .claude/settings.json that names a repository other than dimipaun/fugaro (a fork you host); never done otherwise
--allow-job-delete bool false no with --repo: lower the jobs' deletion protection, for offboarding
--anchor bool false no in a checkout of an onboarded repository, only write the gcp_project line into its fugaro.yaml, after checking that every workflow's job image was submitted by fugaro 0.4.0 or later and built FROM a release base image of 0.4.0 or later, and that no base image the local config sets is a development or older one (no Terraform, no publish, no image copy; the diff, then --yes writes or a terminal asks)
--base stringSlice [] no copy the release's base image (the only kind: base) into the project's registry now, besides what the checkout's fugaro.yaml needs (comma-separated or repeated)
--base-image stringArray [] no a base image the image checks run and builds start from, recorded in the local config under its base kind (repeatable; KIND=IMAGE, or just IMAGE when its repository is named fugaro-<kind>; the kinds you don't name keep theirs)
--billing-account string "" no the billing account the budget is on
--budget int64 0 no a monthly budget on the project, in whole units of --budget-currency
--budget-admin stringArray [] no with --firebase-project: an IAM member who may change caps and kill switches besides the GCP project's owners and editors (repeatable; default: the local config's terraform.budget_admins)
--budget-currency string "" no the budget's currency, such as USD
--budget-mode string "" no with --firebase-project: off, observe or enforce: the jobs' budget mode in the project config, and for observe and enforce the project-wide mode in the database (an absent one is seeded observe)
--check-github-app bool false no let the GitHub App pre-check read the App's private key from Secret Manager with your own credentials (held in memory only, for one signed token; refused in a coding agent's session; GODEBUG=http2debug would print the token, so unset it); without it update checks only with the key you typed in this run
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--config-only bool false no only write the local config, from the installation's outputs (else the flags)
--expect-digest stringArray [] no pin the digest of a release image, KIND=sha256:<hex> (KIND is base or history; repeatable): the digest of the source tag's manifest (the index), obtained out of band such as from the release notes. A base kind is cross-checked against it: its digest is verified against the release's cosign signature either way, and the two must agree. For history it is the trust anchor: without it update trusts what the release tag in ghcr.io resolves to now, which is whoever can write that tag
--firebase-project string "" no build the budget backend in this Firebase project: the run's Firebase project is the installation's own --gcp-project by default (one id for both, D7), so this is the advanced flag, for a Firebase project of its own (one you created and linked to billing, or created with --create-project). It is also the flag that asks for the backend — a run without it builds none — and that backend is three confirmed applies: the database's rules and mark, the Firestore history database (permanent us-east5 location, typed confirmation) and the config
--forget bool false no roll back: turn log isolation and registry cleanup off, then remove every address from Terraform's state
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
--github-app-id string "" no the GitHub App's ID, for a GitHub repository (not a secret; recorded in the local config; update asks once at a terminal, and --non-interactive needs it)
-h, --help bool false no help for update
--image-source string "" no the registry and owner the release images come from: a base kind is resolved and signature-verified there and pinned through the installation's remote repository, a history image is copied from it (default ghcr.io/dimipaun; a fork names its own, such as ghcr.io/acme)
--json bool false no print the result as JSON on stdout (progress goes to stderr)
--launcher stringArray [] no an IAM member who launches and watches runs (repeatable; default: the local config's, and for a new config you, the authenticated user)
--name string "" no the project's name: for a project config update creates, and what names an installation that has no name yet (with a config, it must be its name; an installation's name never changes; with several project configs it names the one to create or use; adopting an installation defaults it to the installation's)
--no-build bool false no with --repo: don't offer the first image builds
--no-log-isolation bool false no leave Fugaro job logs in _Default instead of their own log bucket
--non-interactive bool false no never prompt, and never read stdin: a step that needs you is listed under left_for_you (exit 1), and applying needs --yes, else the run only plans (a fresh state bucket needs its typed confirmation even then, so that plan exits 1); --yes never covers the typed-only steps (see --yes). With --forget and --config-only it only stops them prompting: their confirmations then need --yes
--onboard-repo string "" no owner/name of the checkout's origin repository (compared case-insensitively, without .git): the one non-interactive opt-in to wire and onboard a repository the project does not list yet, on github.com or bitbucket.org with an origin git config does not rewrite (--yes never covers it, and a coding agent's environment refuses it; any other origin takes the typed confirmation)
--operator stringArray [] no an IAM member who onboards repositories (repeatable; default: the local config's, and for a new config you, the authenticated user)
--plan-only bool false no stop after showing the plan (never takes --yes: it creates nothing on a flag's word)
--print-vars bool false no print the Terraform variables and exit, with no cloud calls and no Terraform (ungated: no discovery, and with --repo no readiness gates)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--publish-config bool false no publish the shared config to the runs bucket and stop (update does it after it writes the local config)
--region string "" no GCP region (overrides the project config's)
--registry-cleanup string "" no Artifact Registry cleanup: dry-run (the default), on or off
--replace-image stringArray [] no KIND (history; repeatable): let the converge replace that image's tag in your registry when it names another image, a release tag or history:latest (never done otherwise, so a hand-pushed history:latest needs it once). A base kind is refused: it is pinned by digest through the remote repository, so nothing of it is ever copied over. It names the intent only: replacing is confirmed by typing the project's name at a real terminal, never by --yes, --non-interactive or --json
--repo bool false no onboard the repository of the checkout at PATH (default: the current directory) instead of the installation
--retire bool false no with --repo: run the retirement this config change needs (a workflow or a named account removed, a secret no longer mounted): remove the protected addresses from Terraform's state (destroying nothing), apply, then delete the orphaned Cloud Run jobs through the API. The project's name is typed twice at a real terminal, and --yes, --non-interactive, --json, a pipe and a coding agent cover neither. Excludes --plan-only, --print-vars and --forget, which read or forget rather than apply
--runs-bucket string "" no the runs bucket (default: the project config's, else fugaro-runs-<gcp-project>)
--scheduler-region string "" no Cloud Scheduler region of the daily image checks (default: the region, or the nearest one Scheduler offers)
--state-bucket string "" no the Terraform state bucket (default: the project config's, else fugaro-tfstate-<gcp-project>)
--yes bool false no confirm the ordinary steps without asking (only after reading what they do). Never covers creating a project, billing, a secret, an unlisted repository, the Firestore location, a billable first image build or replacing an image tag (typed at a real terminal). Under a coding agent's environment variable (CLAUDECODE and the like) update applies nothing, --yes included (except update --anchor, which only edits the checkout's fugaro.yaml and honours --yes there); that is a mitigation, not a barrier: an agent that unsets its own variables is not stopped, and the real controls are the typed confirmations and the skills' lint

Subcommands:

This command has no subcommands.

fugaro update-skills

Pin the Fugaro plugin in the repository's .claude/settings.json to this release

update-skills merges the Fugaro plugin's marketplace and its enablement into the checkout's .claude/settings.json, pinned to this binary's release tag (a dev build writes no pin). Every other key is kept; a fork's marketplace repository is kept and only its ref moves; a file that is not valid JSON is never rewritten; a changed file is re-indented to two spaces. A marketplace that names another repository than dimipaun/fugaro is reported with a warning and left alone unless --allow-fork says it is yours (then only its ref moves); a plugin the repository sets to false stays disabled. It shows the diff and writes the file; it never commits: review it with git diff. It needs no credentials, no network and no Terraform. Outside a checkout it prints the settings to add.

--check writes nothing: it prints how the pin compares with this binary (and with the plugin installed on this machine when Claude Code's record is readable) and exits 1 on any warning state (outdated, newer, unpinned, foreign, not wired, installed differs); "not installed" and "cannot compare" are informational.

Usage: fugaro update-skills [--check] [--dir D] [--json]

Flags:

Flag Type Default Required Description
--allow-fork bool false no move the ref of a marketplace repository other than dimipaun/fugaro (a fork you host) to this release's tag
--check bool false no print the state and exit 1 unless it is ok (informational states pass); write nothing
--dir string . no a directory in the checkout
-h, --help bool false no help for update-skills
--json bool false no print machine-readable output

Subcommands:

This command has no subcommands.

fugaro upgrade

Bring checkouts up to this fugaro: pin the plugin, install or update it in Claude Code, refresh the repository's images

upgrade brings each checkout (PATH, default the current directory; several are processed one after the other, each on its own) up to this fugaro, in three steps, stopping that checkout at the first step that fails:

pin what fugaro update-skills does: the Fugaro plugin in the checkout's .claude/settings.json pinned to this release's tag, shown as a diff and never committed; a fork's marketplace moves only with --allow-fork; a file that is not valid JSON is never rewritten; a pin newer than this fugaro is never moved down. plugin the plugin installed (user scope) or updated (each install that applies to the checkout, at its own scope) in Claude Code through the claude CLI on PATH: a fixed set of claude plugin commands, never with -y. Without claude it prints the slash commands to type in Claude Code. Running Claude Code sessions apply it after a restart. cloud what fugaro image refresh does for the checkout's repository: this release's base image, the daily image check job, the builds. It asks at each step, or with --yes confirms every step itself, each billable build included, with no cap. Skipped where there is nothing to refresh from here: no fugaro.yaml, no local project config (a teammate), a repository not onboarded. After a real refresh it also compares the deployed job's whole copy of the local project config (providers, model_prices, budget settings, the region's compute price override and the GitHub App ID; multi-model.md) with what the local config would write now: a version upgrade never refreshes that copy, so a mismatch prints a note naming the fields and the one command that does, fugaro update --repo . (never run here: its Terraform confirmations stay yours). --check makes no cloud call, so it cannot tell and says so instead. It checks each workflow's default-account job; named-account jobs are updated by the same fugaro update --repo .

It never upgrades fugaro itself: brew upgrade dimipaun/tap/fugaro && fugaro upgrade is the whole sequence.

--local runs pin and plugin only. Under --local and in a coding agent's session (CLAUDECODE and the like) the cloud step is skipped before it reads anything, never attempted, with the command to run in your own terminal, so the exit code is 0 when the local steps succeeded. --check writes nothing, runs no claude command and makes no cloud call: it reads the settings file, Claude Code's record of installed plugins and the local project config, prints what each step would do and exits 1 when anything is stale (build records are not read). Without --yes, --local or --check it needs a real terminal. There is no --json.

Exit codes: 0 every checkout finished or had nothing to do; 1 a refusal, a failed local step, a cloud step that cannot start from the checkout's files (a bad fugaro.yaml or origin, an unreadable config; with --check too) or (with --check) something stale; otherwise a failed cloud step exits with the refresh's own code, 2 for a cloud call that failed. The summary names each checkout's steps and, for one that stopped, the line to rerun.

Usage: fugaro upgrade [PATH...]

Flags:

Flag Type Default Required Description
--allow-fork bool false no move the ref of a marketplace repository other than dimipaun/fugaro (a fork you host) to this release's tag, and add it to Claude Code
--check bool false no write nothing, run no claude command, make no cloud call: print what each step would do and exit 1 when anything is stale
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--expect-digest stringArray [] no cross-check the digest of a base image's release signature, KIND=sha256:<hex> (repeatable; one PATH only)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for upgrade
--image-source string "" no the registry and owner the release base images come from: a base kind is resolved and signature-verified there and pinned through the installation's remote repository (default ghcr.io/dimipaun)
--local bool false no run the pin and plugin steps only (the only mode that does everything it can inside a coding agent's session)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no owner/name of the repository; it must be the checkout's origin (one PATH only)
--workflow stringArray [] no a workflow to refresh (repeatable; default: every workflow in fugaro.yaml; one PATH only)
--yes bool false no confirm every step of the cloud step without asking (the base copy, the check-job update and each billable build, with no cap), and without a terminal; never in a coding agent's session

Subcommands:

This command has no subcommands.

fugaro validate

Check a fugaro.yaml against the schema and the repository

Usage: fugaro validate [path]

Flags:

Flag Type Default Required Description
-h, --help bool false no help for validate
--json bool false no print machine-readable output
--offline bool false no never read the runs bucket: resolve against the cached project layer, if any
--project-layer string "" no resolve against this project layer file instead of the published one (also checks the file; for CI without bucket access, and before fugaro config publish)

Subcommands:

This command has no subcommands.

fugaro verify

Run and record this repository's build, test or lint command (inside a Fugaro run)

Usage: fugaro verify build|test|lint

Flags:

Flag Type Default Required Description
-h, --help bool false no help for verify
--rerun-failed bool false no rerun only the tests that failed in the previous test run, to detect flaky tests

Subcommands:

This command has no subcommands.

fugaro version

Print the fugaro version

Usage: fugaro version

Flags:

Flag Type Default Required Description
-h, --help bool false no help for version

Subcommands:

This command has no subcommands.

fugaro watch

Watch the project's spend, repositories and running agents live

Watch the project's budget backend live: the project's total against its caps, one block per repository (spend against its cap, burn rate, kill state) and one row per running agent (stage, round, age, spend, silent, lost and near-deadline flags). It reads the project's Firebase Realtime Database as you (the Viewer role), like fugaro budget show.

At a terminal watch opens the interactive screen. With --plain, --json or --once, or when stdin or stdout is not a terminal, it prints text frames (at most one per --interval, 10s) or, with --json, one JSON document per change (at most one a second); --once prints one snapshot and exits. Those modes have no kill keys: fugaro budget kill and resume are the script path.

A project with no budget backend shows its run records only (as fugaro runs ls does, for the last 24 hours) and says so; fugaro runs ls --watch stays for the question "did my runs finish".

A finished run stays shown for --keep (default 6h) or among the most recent --keep-count (default 15), whichever is stricter; a failed one stays until it is acknowledged (x in the interactive screen) or a day passes. --all shows every finished run (the a key toggles this without relaunching). --json is always unfiltered: it carries every finished run and, among them, the ones ready for review.

The "Ready for your review" list drops a pull request that is already merged or closed. The state is asked of your own gh (when it is installed and logged in), by pull request number and repository, never through a shell and never with the run's title or any other run text; the answer is cached, and anything that goes wrong — no gh, no login, a rate limit, a failure, a timeout — leaves the pull request listed as it is today, with one note saying so. --no-pr-state turns the lookup off entirely (nothing is executed), and --json is unaffected: its ready[] is the runs bucket's own record.

Usage: fugaro watch

Flags:

Flag Type Default Required Description
--all bool false no show every finished run, not just the active and recent ones
--ascii bool false no use ASCII instead of ⚠ ▓░ ▸
--config string "" no the project config file to use (default: the project's $XDG_CONFIG_HOME/fugaro/projects/<name>.yaml)
--gcp-project string "" no the GCP project ID; it must be the selected project config's gcp_project
-h, --help bool false no help for watch
--interval duration 0s no the least time between plain frames (default 10s) and the polling period (default 5s)
--json bool false no print one JSON document per change instead of a screen
--keep duration 6h0m0s no how long a finished run stays shown (with --keep-count, whichever is stricter)
--keep-count int 15 no how many finished runs stay shown (with --keep, whichever is stricter)
--no-color bool false no no colour (NO_COLOR and TERM=dumb do the same)
--no-pr-state bool false no do not ask gh about the ready PRs: list every one of them, run nothing
--once bool false no print one snapshot and exit (with --json or plain text)
--plain bool false no print text frames instead of the interactive screen
--poll bool false no read by polling instead of streaming (for proxies that buffer)
--project string "" no the Fugaro project to act on (default: the checkout's fugaro.yaml project:, else $FUGARO_PROJECT, else the only project config)
--region string "" no GCP region (overrides the project config's)
--repo string "" no only this repository (owner/name)

Subcommands:

This command has no subcommands.