flow
Action-centered CLI for managing a development machine: dotfiles, packages, setup profiles, dev containers, remote targets, project git status, and shell completion.
Quick Start
From the repository:
uv sync --locked --extra dev --extra build
uv run flow --help
uv run flow --version
Initialize a dotfiles repo, link a profile, and preview setup:
flow dotfiles init --repo git@github.com:you/dotfiles.git
flow dotfiles link --profile linux-work --dry-run
flow setup show linux-work
flow setup run linux-work --dry-run
Install the CLI as a local uv tool:
uv tool install --force .
flow --help
Build a standalone binary:
make build
./dist/flow --help
make install # installs dist/flow to ~/.local/bin/flow
build/, dist/, and flow.spec are generated by packaging/binary builds and
are ignored. .venv/ is the uv-managed local environment. venv/ is legacy
local clutter and should not be used.
Command Surface
# Dotfiles
flow dotfiles init [--repo URL]
flow dotfiles link [--profile NAME] [--dry-run] [--skip PKG...]
flow dotfiles unlink [PACKAGES...] [--dry-run]
flow dotfiles status [PACKAGES...]
flow dotfiles edit PACKAGE [--no-commit]
# Dotfiles and module repos
flow dotfiles repos list
flow dotfiles repos status [--repo NAME]
flow dotfiles repos pull [--repo NAME] [--dry-run]
flow dotfiles repos push [--repo NAME] [--dry-run]
# Packages
flow packages install [NAMES...] [--profile NAME] [--dry-run]
flow packages remove NAMES... [--dry-run]
flow packages list [--all]
# Setup/bootstrap
flow setup list
flow setup show PROFILE
flow setup run [PROFILE|--profile NAME] [--dry-run] [--var KEY=VALUE]
# Remote targets
flow remote list
flow remote enter TARGET [--user USER] [--namespace NAME] [--platform NAME] [--session NAME] [--no-tmux] [--dry-run]
flow enter TARGET
# Dev containers
flow dev create NAME --image IMAGE [--project PATH] [--dry-run]
flow dev attach NAME
flow dev exec NAME [CMD...]
flow dev enter NAME
flow dev stop NAME [--kill]
flow dev remove NAME [--force]
flow dev respawn NAME
flow dev list
# Projects
flow projects check [--fetch]
flow projects fetch
flow projects summary
flow sync
# Shell completion
flow completion zsh
flow completion install-zsh [--dir DIR] [--rc FILE] [--no-rc]
# Global flags
flow --version
flow --quiet
flow --no-color
Aliases:
dotfiles->dotdotfiles repos->dotfiles repopackages->package,pkgprojects->project;flow sync->flow projects check --fetchsetup->bootstrap,provisionremote enter->enterdev attach->dev connectdev remove->dev rm
Configuration
Flow reads XDG paths:
- config:
~/.config/flow - data:
~/.local/share/flow - state:
~/.local/state/flow
The main config lives at ~/.config/flow/config.yaml. A dotfiles repo may also
provide an overlay at _shared/flow/.config/flow/; after dotfiles init, that
overlay is merged into config and manifest loading.
repository:
url: git@github.com:you/dotfiles.git
branch: main
pull-before-edit: true
paths:
projects: ~/projects
defaults:
container-runtime: auto # auto | docker | podman | podman-rootful
container-registry: registry.example.com
container-tag: latest
tmux-session: default
targets:
personal@orb: personal.orb
work@ec2:
host: work.internal
identity: ~/.ssh/id_work
Packages and setup profiles are YAML manifest data loaded from the same config directories:
packages:
- name: fd
type: pkg
sources:
apt: fd-find
brew: fd
- name: neovim
type: binary
source: github:neovim/neovim
version: "0.10.4"
platform-map:
linux-x64: nvim-linux-x86_64.tar.gz
install:
bin: [bin/nvim]
profiles:
linux-work:
os: linux
shell: zsh
packages:
- fd
- binary/neovim
runcmd:
- mkdir -p ~/projects
See example/README.md for a complete runnable dotfiles/setup fixture.
Dotfiles Layout
_shared/
zsh/
.zshrc
nvim/
.config/nvim/
_module.yaml
system/
_root/
etc/hostname
linux-work/
i3/
.config/i3/config
_shared/ applies to every profile. Profile directories add or override package
sets. _root/ marks absolute paths and plans sudo-backed link actions.
External module repos are declared with _module.yaml:
source: github:org/nvim-config
ref:
branch: main
Modules are regular git clones managed by flow, not git submodules. The
dotfiles repo and all module repos are operated through flow dotfiles repos.
Architecture
Flow uses a single execution boundary:
cli -> app -> domain -> actions -> adapters
src/flow/cli.py: Typer command definitions and argument parsing only.src/flow/app/: use-cases that load config/state and build action plans.src/flow/domain/: pure dataclasses, parsers, resolvers, and planners.src/flow/actions/:ActionPlan, domain-to-primitive expansion, execution, dry-run rendering, JSONL audit logs, and rollback.src/flow/adapters/: filesystem, process, git, package-manager, download, archive, container, and tmux adapters.
Use-cases do not directly mutate files or run shell commands for non-interactive
work. They produce actions and pass them to ActionExecutor. Interactive
handoffs such as attaching to tmux remain explicit boundaries.
Action audit records are append-only JSONL files under the relevant state
directory, normally ~/.local/state/flow/actions.jsonl.
Development
make deps # uv sync --locked --extra build --extra dev
make test # unit tests, excluding e2e
make test-e2e # requires Docker or healthy Podman
make check # tests plus CLI smoke checks
make package # wheel and sdist in dist/
make build # PyInstaller binary in dist/flow
make clean # remove build/test artifacts
make distclean # also remove .venv/ and venv/
Direct commands are equivalent:
uv run pytest tests/ -q --ignore=tests/e2e
FLOW_RUN_E2E=1 uv run pytest tests/e2e/ -v
uv build
uv run pyinstaller --noconfirm --clean --onefile --name flow --paths src src/flow/__main__.py