Documentation

The CLI, in depth. Then the verbs.

The landing page tells you what Flux is. This page tells you how it resolves where you are, what it puts in front of the agent, and what each verb does with it.

Why a CLI

A skill runs inside a session. It does not choose the directory the session was born in, it cannot see which binaries are on the PATH, and finding that out costs tool calls that eat the context the work needed.

The CLI runs before the session and has the environment. It answers, for free, three questions the skill would answer expensively:

  1. Where am I? Which repo, which context manifest applies, where the checkouts live.
  2. What exists on this machine? Which review agents are installed, whether gh is available, whether the vault exists.
  3. What is missing? If a hard requirement of the verb is absent, the CLI aborts before a whole session is spent discovering that mid-work.

The answers go into the first prompt as a block of text. The skill reads the block instead of investigating.

Design note. The family has a harness-neutrality rule: skills may not name a product or assume which agent they run in. The CLI is the explicit exception, because it runs before the session and the environment is precisely what it has to offer.

Install

The CLI lives in cli/ inside the Flux repository and compiles to a single binary with Bun.

cd cli
bun run setup

That runs bun build --compile, re-signs the binary, and installs it to ~/.local/bin/flux. Make sure ~/.local/bin is on your PATH. Bun is needed to compile; at runtime the binary depends on neither Bun nor Node.

Why the re-signing step exists: on Macs under MDM with EndpointSecurity (JumpCloud, SentinelOne and the like), Bun's default ad-hoc signature is rejected. setup.sh runs codesign --force --sign - and then warms the binary up, because the first exec right after signing races an asynchronous scan by the security daemon and can die with exit 137 with nothing wrong with the binary. The script absorbs that race there, not the first time you actually need the command.

flux                  # interactive mode
flux resolve --json   # print the resolved context

Mental model

Three layers, and the distinction matters because error messages speak in these terms:

layerwhat it isprovided by
CLIthe flux binary: resolves context and launchescli/
Skillsthe verbs (flux:review, flux:build, …), in Markdownplugins/flux/skills/
Agentsthe reviewers and specialists the skills dispatchyour machine and your repos

The CLI implements no verb. It builds the prompt that invokes the verb's skill and gets out of the way. Type the same prompt by hand inside a session and you get the same result; the CLI only spares you the discovery.

Agents live at three levels the CLI calls lenses:

L1The holistic reviewer. One, looking at the whole change.
L2The specialist suite on your machine for that repo, installed by you.
L3Agents versioned inside the repo, maintained by its team.

The CLI discovers L2 and L3 and reports which it found. When a repo has neither, flux equip exists to create the L2.

Usage

Verbs

flux <verb> [target] [--repo <slug>] [flags]
verbwhat it does
reviewformal review of a PR or doc (specialists + holistic reviewer), persisted to the vault
peekquick read-only glance at a PR, diff or doc; does not persist, does not post
refinePRD and plan in one round, from an idea, thread or bug
issuecreates a code-grounded issue from any source
buildimplements a ticket and delivers a draft PR
iteratecloses the loop on a PR: threads, CI, push
landorchestrates a multi-PR delivery to merge
replyfollows a Slack case, grounded in code
mapsurveys the family's installation on this machine
equipequips a repo with an execution engine and specialists

With no arguments, flux opens an interactive mode that asks for the verb (a menu with each one's description), the target, the repo, and whether to run on another machine. It assembles the equivalent argv and follows the same path as always. It only opens when stdin is a TTY; inside a script, flux with no arguments prints the usage and exits.

flux                              # interactive
flux peek                         # glance at the current working tree
flux peek 8249                    # glance at PR #8249 of the cwd repo
flux review 8249 --repo backoffice
flux build LAB-142 --repo flux

The target is passed to the verb uninterpreted, with one exception: a Linear ticket requires --repo. A LAB-142 does not say which repo the work happens in, and the CLI refuses rather than guess.

Mechanical subcommands

Three subcommands open no session: they print JSON and exit. The skills consume them, and you use them to debug.

flux resolve --json prints the resolved context and nothing else. It is the command for "why does flux think I am in this profile?".

{
  "profile": "personal",
  "manifest_path": "/Users/you/www/personal/.claude/flux-context.json",
  "anchor": "/Users/you/www/personal/flux",
  "flux_root": "/Users/you/.claude/plugins/cache/flux/flux/1.29.1/plugins/flux",
  "flux_root_source": "env:FLUX_HOME",
  "exec_command": "workflow",
  "exec_fallback": "flux-engine-flux",
  "lenses": { "l2_paths": ["/Users/you/agents/personal/flux"], "l3_paths": [] },
  "warnings": []
}

flux preflight <verb> [target] --json runs the full check for a specific verb: hard and soft requirements, holistic reviewer, capability level. Each verb declares what it needs. A missing hard requirement aborts; a missing soft one degrades, and the CLI states what is lost.

levelwhen
FULL-tentativomanifest present and specialists found
REDUCEDlocal checkout present, without the two above
THINno local checkout
UNAVAILABLEa hard requirement is missing

FULL is tentative because the CLI sees the disk, not the session: it knows the agent file exists, not whether the harness registered it as invokable. The skill confirms that from inside.

flux gather pr <n|URL> --json collects a PR through gh: metadata, diff, and with --threads the review threads. Only verbs that answer threads ask for them.

Flags

flageffect
--repo <slug>target repo. Required when the target is a Linear ticket
--harness <claude|cursor|codex>which agent harness to invoke. Value required; an invalid one fails before any resolution
--dryprint the command that would run, and exit. Nothing runs
--safedrop the permission bypass from the invocation
--newopen in a new terminal tab instead of the current one
--remote [alias]run on another machine over SSH. Without an alias, asks which
--yes / -yskip the banner preview
--jsonJSON output. Used by the mechanical subcommands

--dry is the inspection tool. It prints the exact command line that would be launched, prompt included. It is how you check what the CLI discovered without spending a session.

Which harness

The CLI builds the invocation for three harnesses. Which one is resolved by a cascade, stopping at the first rung that answers:

#rungharness_source
1FLUX_CLAUDE_CMDoverride: the harness reads desconhecido, because the CLI cannot know what your wrapper is
2--harnessflag
3FLUX_HARNESSenv
4preferred_harness in the manifestmanifesto
5claude, with a warning on stderrdefault

--harness together with FLUX_CLAUDE_CMD is an error: the flag is explicit intent, and asking for both is an ambiguity the CLI will not resolve for you. FLUX_HARNESS and preferred_harness are environment defaults, so the override beats them silently. Rung 5 is not the old silent default: the banner carries harness_source: default, so the skill can tell "chose claude" from "nobody chose".

harnessinvocation
claudeclaude --dangerously-skip-permissions -- "<prompt>"
cursorcursor agent --print --force -- "<prompt>"
codexcodex exec --dangerously-bypass-approvals-and-sandbox -- "<prompt>"

The -- before the prompt is mandatory, not style: the body starts with --- PREFLIGHT RESOLVIDO, and without the separator cursor and codex read that as an option and refuse. --safe drops the permission flag on all three. --new is only supported on claude for now; the other two warn and run in the current tab.

The banner preview. Before launching, the CLI shows the block it is about to send and opens a three-option menu: send as is, attach an extra comment, cancel. The comment is appended to the prompt; it is where you add "focus on the date parsing" without rewriting the command. --yes skips it, and it does not appear when stdin is not a TTY.

The context manifest

flux-context.json declares the context of a workspace: where the repos live, which vault to use, which Linear organization, which agents exist. It is looked up at .claude/flux-context.json or .cursor/flux-context.json, walking up the directory tree from the anchor.

{
  "name": "acme",
  "workspace_root": "~/www/acme",
  "repos": ["api-gateway", "web-monorepo", "payments"],
  "vault_root": "~/.notes",
  "vault_context": "acme",
  "linear_org": "acme-eng",
  "no_emdash": true,
  "exec_command": "workflow",
  "exec_fallback": { "payments": "acme-engine-payments", "default": "acme-implement" },
  "specialists_root": "~/agents/{repo}/repo-owner.md",
  "holistic_reviewer": "acme:reviewer"
}
fieldpurpose
nameprofile name, shown in the banner
workspace_rootwhere checkouts live. Without it, the manifest's directory
reposknown slugs, used to validate and suggest
vault_root · vault_contextwhere verbs persist boards and reports
linear_orgturns LAB-142 into a clickable URL
no_emdashforbids em-dashes in text bound for GitHub
exec_commandname of the repos' native execution command
exec_fallbackexecution engine per repo, when the repo has no native one
specialists_rootpath template for the L2 suite, with {repo}
holistic_reviewerthis context's L1 reviewer
preferred_harnessclaude, cursor or codex. Fourth rung of the harness cascade; only the CLI reads it

Without a manifest the CLI still works. The profile becomes generico: the anchor is the cwd, the repos are the subdirectories with a .git, and nothing is persisted to a vault. What is lost is stated in the banner, not discovered halfway.

exec_fallback takes two shapes: a scalar when one command serves every repo, or a map when repos have different engines. Resolution is repo → default → none. A map that names neither this repo nor a default sends build to autonomous mode rather than borrowing another repo's engine: running code through the wrong pipeline, silently, is worse than running it through none.

How context is resolved

The anchor is the target, not the cwd

cd ~
flux build LAB-142 --repo flux

That has to find the profile of the workspace where flux lives, not your home's. So: parse the target first, without opening anything; the anchor is the checkout of --repo when given, else the cwd; the manifest is found walking up from the anchor.

When the slug does not resolve nearby, the CLI scans the known manifests for one that claims it (through the repos field, or by having <workspace_root>/<slug>/.git). One claims it: that whole profile is adopted, with a warning. More than one: it asks. None: it anchors on the cwd and warns.

slug "flux" resolved by scanning manifests (not by a nearby manifest) — check the context is right

FLUX_ROOT: where the skills live

The CLI needs the contract files (shared/*.md) to check hard requirements. In order: $FLUX_HOME if set and existing; the harness plugin variables (CLAUDE_PLUGIN_ROOT, CURSOR_PLUGIN_ROOT, CODEX_PLUGIN_ROOT); and a heuristic that scans ~/.claude* and ~/.cursor for plugins/cache/flux/flux/<version>/ and picks the highest version.

The heuristic warns, because it can pick the wrong cache when you have more than one account or config dir. If you do, set FLUX_HOME explicitly. That rung exists for exactly that.

The PREFLIGHT block

The CLI's product. It goes at the top of the prompt, and the skill reads it instead of investigating:

--- PREFLIGHT RESOLVIDO (flux-cli v1.29.1) ---
perfil: acme
manifesto: /Users/you/www/acme/.claude/flux-context.json
ancora: /Users/you/www/acme/payments
flux_root: /Users/you/.claude/plugins/cache/flux/flux/1.29.1/plugins/flux
flux_root_source: env:FLUX_HOME
exec_command: workflow
exec_fallback: acme-engine-payments
lentes:
  l2_paths: /Users/you/agents/payments
  l3_paths: ausente
harness: claude
harness_source: manifesto
flux_cmd: /flux: (session_revalidation_required)
--- FIM PREFLIGHT RESOLVIDO ---

The block is advisory, deliberately. It is a starting point, not the final truth. session_revalidation_required lists what the skill must re-check from inside the session, because it depends on state the CLI cannot see:

fieldwhy the CLI does not decide it
flux_cmdthe invokable form (/flux:review or /flux-review) depends on the harness
adddir_cmdsame
holistic_verificationthe CLI sees the agent file on disk, not its registration as invokable
capability_levelbecause it depends on the previous one

A CLI that asserted those four would be lying with the appearance of precision. It asserts what it measured and marks the rest.

Running remotely

flux review 8249 --repo backoffice --remote worzix
flux review 8249 --repo backoffice --remote          # asks which

Without an alias, the CLI reads the Host entries of ~/.ssh/config, tests which are reachable now, and shows only those in a menu. A machine can be opted out with a comment right above its Host block:

# flux:ignore
Host production
  HostName ...

It is for machines that sit in ~/.ssh/config for deploy convenience and where an agent harness must never run.

Two known traps. --remote builds ssh -t <host> zsh -lic 'flux ...' and does not cd: the session is born in the remote $HOME. So the manifest may not be found walking up from there, and FLUX_ROOT is resolved by the heuristic on the remote, which can hit another config dir's cache. If you use more than one account or context on the remote, wrap the call in a shell function that does an explicit cd and passes FLUX_HOME and FLUX_CLAUDE_CMD.

Environment variables

variableeffect
FLUX_HOMEpath to plugins/flux/. Beats the heuristic. Set it if you have more than one config dir
FLUX_CLAUDE_CMDreplaces the whole invocation. For pointing at a wrapper of your own. First rung of the harness cascade
FLUX_HARNESSclaude, cursor or codex. Third rung, below --harness
CLAUDE_PLUGIN_ROOT · CURSOR_PLUGIN_ROOT · CODEX_PLUGIN_ROOTplugin root, when the harness exports it
SHELLshell used to execute. Default /bin/zsh

FLUX_CLAUDE_CMD and shell functions. The CLI executes through [$SHELL, "-i", "-c", ...], an interactive shell. Your .zshrc is loaded, so shell functions work as the target. It is the mechanism to route the session through a wrapper of your own: pick an account, export variables, whatever it is. The CLI does not validate an override's target, because it cannot: whoever dictated the invocation knows what it is.

Troubleshooting

The resolved profile is wrong

Run flux resolve --json and look at anchor and manifest_path. If the anchor is in the wrong place, you either forgot --repo or the manifest is not where you think. A "scanning manifests" warning means the slug was found by wide search and is worth checking.

flux_root_source: heuristica-cli

The CLI guessed where the skills live. It works, but can hit the wrong cache with more than one config dir. Set FLUX_HOME.

The binary dies with exit 137 right after install

A race with the macOS security daemon (MDM, EndpointSecurity). setup.sh already absorbs it; if it happens outside, wait a few seconds and run again.

<binary> not found on PATH

The resolved harness is not on the PATH. The check uses /usr/bin/which, which only sees binaries; a shell function does not appear there even though it works at execution, which uses an interactive shell. If your claude is a function, point at it with FLUX_CLAUDE_CMD: the guard is skipped when the invocation comes from an override, because the CLI cannot validate a target you dictated. For cursor, the guard checks the cursor binary, not the agent subcommand; probing it live would cost seconds per call, so that stays a known limitation.

The verbs: the cycle

Everything above is what the CLI does before a verb runs. This is what the verbs do. The family is a cycle, and each verb hands off to the next:

Two verbs sit outside the cycle and prepare the ground (map, equip); two are optional detours (refine, probe). Every verb opens its output with a profile banner declaring which lenses it found and what degraded, so a reduced run can never pass for a full one.

Every entry has the same shape. What it takes, what it produces, where it writes, what it never does, and where it hands off. The full contract of each verb is its SKILL.md; this is the navigable layer over it.

flux:issue SKILL.md ↗

Turns any source (a Slack thread, free text, a PR) into a high-quality issue, grounded in real code by the repo's specialists, with link discipline and correct writing. Writes a reviewable draft to the vault and only creates in Linear after you approve.

Takes
a thread, a text, a PR
Produces
issue body with code-grounded evidence (file:line, verdicts: confirms / partial / refutes / no evidence)
Writes
vault board; Linear only after the human gate
Gates
creation in Linear
Hands off to
flux:build <ticket>

flux:build SKILL.md ↗

The execution link. Takes a ticket and a repo, resolves which execution engine that repo has, and dispatches. It does not implement the task: it resolves repo and engine, loads context, and delegates. Before dispatching it measures scope from what is already on screen, and offers a cut when the task will not fit one run.

Takes
a ticket or a description, plus a repo
Produces
code and a draft PR, through the repo's engine
Engine
native (.claude/commands/workflow.md) → exec_fallback from the manifest → autonomous mode
Gates
scope, when the task reads as too large for one run
Hands off to
flux:review or flux:peek on the PR

flux:review · flux:peek review ↗ peek ↗

review is the formal one: holistic reviewer plus the repo's specialists, reconciled, with the canonical text badges (request-change, breaking-change, question, suggestion, praise, note), persisted to the vault and posted to GitHub. peek is the quick glance: holistic reviewer only, printed in chat, nothing persisted, nothing posted.

Takes
a PR, a branch, a diff, a doc, a path
Produces
findings with file:line citations; review also produces a decision
Writes
review: vault and GitHub. peek: nothing
Hands off to
flux:iterate on the PR

flux:iterate SKILL.md ↗

The active sibling of review. Closes the loop on one PR: reads the open threads, verifies every claim against the real code, applies what holds, replies, reacts, resolves, commits, pushes, and reconciles the PR title and description with what the round decided. By default it stays alive watching CI and new bot rounds until the PR settles. --dry drafts the replies read-only and saves them to the vault.

Before any of that comes the base-integration gate: a PR that no longer merges with its base is a PR nobody writes on, and its green CI is a false signal. Mechanical conflicts are resolved; semantic ones (same function, same test, a migration, a contract) always wait for a human, even under --auto.

Takes
one PR
Produces
threads verified, fixes applied, replies posted, CI watched, a board in the vault
Writes
a dedicated worktree for the PR branch, never the main tree; GitHub, on your own PR. Someone else's PR is no-push by default: interaction only, unless you ask and confirm in text
Never
approves, merges, force-pushes without lease, resolves a thread marked needs-discussion, or renames a title's ticket prefix
Hands off to
flux:land when the PR is one of several, or the human merge

flux:land SKILL.md ↗

The layer above review and iterate. Sees the set of PRs of one feature spread across repos and drives all of them to merge-ready, in the right order and without regression: discovers them, orders them by dependency, validates regression with the specialists, keeps each one merge-ready by delegating to flux:iterate, and emits a go / no-go. It assumes the PRs already exist; creating them is flux:build's job.

Takes
an issue or feature that maps to several PRs
Produces
merge order, blocking graph, regression verdict per PR, go/no-go, a delivery board with one child iterate board per PR
Writes
through each PR's iterate, in that PR's worktree; the delivery board in the vault
Never
merges. The go/no-go is a recommendation; the merge is yours. Never runs an iterate inline: one PR is one subagent, and the main context only orchestrates
Hands off to
the human merge, then flux:reply to communicate

flux:reply SKILL.md ↗

Follows a case of work on Slack, grounded in the codebase. The unit is the case, not the thread: a discussion rarely stays where it started, it leaves the helpdesk, becomes a DM, and comes back as a thread in a team channel. The board follows it across every surface instead of leaving each move as an orphan note. Fact-gathering goes to one prospector per repo, in parallel; writing goes to an answerer.

Takes
a Slack permalink (thread, DM, channel)
Produces
a reply drafted from verified facts, and a live board with the open items, who holds each one, and where
Writes
the board in the vault; a draft in Slack, only after you choose it
Never
sends a message. Sending is yours, in Slack, after reviewing the draft. Reacting only through the menu you chose
Hands off to
flux:issue when the case produces work, or flux:land when it tracks a delivery

Setup verbs setup

Two verbs sit outside the cycle. They are what you do to a machine or a repo so that the cycle can run with every lens and without falling into autonomous mode.

flux:map SKILL.md ↗

The sanity verb. Answers the question no other verb has the scope to answer: what does this machine have, and what is broken? Surveys the agent roots, the context manifests, the known repos, the three lenses of each one, and name: collisions, and writes the index the other verbs consume. Run it again and it shows what changed since last time. Suggested before anything, required by nothing.

Takes
nothing (the machine), or --repo to narrow
Produces
inventory, delta since the last run, and an integrity report with one-click remediation
Writes
the agents index, after a gate. Repairs are dispatched to flux:equip, accepted one by one, never applied by map itself
Never
writes inside any checkout, or invokes a repo agent to "see if it works"
Flags
--dry report only · --apply skips the index gate, and only that · --no-fix report with the remediation commands printed, nothing offered

flux:equip SKILL.md ↗

Equips a repo with the two things the other verbs consume but do not produce: the execution engine (L0) that flux:build dispatches to, and the local specialist suite (L2) that review, iterate and land reconcile. Diagnoses first, then writes only what is missing. Everything goes through the write-destination contract, never inside the target checkout, and writing to the manifest is an action with its own gate.

Takes
a repo, optionally --engine-only, --agents-only, --from-kit, --expose-l3
Produces
an engine command and/or a specialist suite, authored by reading the repo's real stack, scripts and conventions
Writes
to the approved destination on your machine; exec_fallback and write_destinations in the manifest only after their own gate
Never
writes inside the target repo, overwrites what exists, or runs any task
Hands off to
flux:build if there was work waiting; nothing if it was maintenance

Optional verbs optional

Two detours before flux:issue. Neither is required: whoever does not need to refine or investigate goes straight to the issue.

flux:refine SKILL.md ↗

Refinement in one round, a fast SDD. Takes the same input as flux:issue (an idea, a thread, a bug) and produces, before the issue body, the three artifacts a feature needs to be sliced well: a PRD, a TRD and a plan of vertical slices, all grounded in the real code. It measures scope before working: what does not fit one round is refused, and you get the proposed cut instead of a shallow refinement that would circulate as a spec.

Takes
an idea, a thread, a bug; optionally --repo, repeatable for two
Produces
PRD, TRD, ordered slices, on the same board flux:issue consumes; or a reasoned refusal with the cut
Writes
vault only
Never
creates an issue, writes code, or refines a red scope "just a little"
Hands off to
flux:issue, which reads the board and writes the issue bodies

flux:probe SKILL.md ↗

Investigates production telemetry (Sentry and Datadog) and returns a quantified dossier: aggregates the targets in parallel, tests the current explanation against the physics of the numbers, confronts the client side with the server side, and crosses it with the real code through the specialists. Writes to the same board flux:issue consumes.

Takes
Sentry issue URLs, Datadog queries; --window, --sample, --service to shape the collection
Produces
distributions, percentiles, correlations, the plausibility tests that separate the real cause from the one the title suggests
Writes
vault only
Never
changes state at the source (no resolving, assigning, commenting), posts to Slack, or concludes from a single event
Hands off to
flux:issue

probe is a verb of the family but not of the CLI: flux probe is not a subcommand. Invoke it from inside a session as /flux:probe.