@flowwright/cli
v0.2.0
Published
FlowWright CLI — the `flow` command.
Readme
FlowWright CLI
@flowwright/cli provides the local-first flow command. It turns a typed
pipeline.ts into a validated execution plan, runs the dependency graph on your machine
or in declared containers, and formats the same typed event stream for developers, CI
providers, test systems, and automation.
No account, server, or hosted runner is required. If the pipeline works through flow
on a laptop, the same command can run inside any shell-capable CI system.
FlowWright · Documentation · @flowwright/core · @flowwright/runtime · GitHub
[!NOTE] FlowWright is pre-release. The CLI workflow works end to end, but commands and APIs may change before the first stable release.
Why use the CLI
- Debug before pushing — validate, explain, and run the entire pipeline locally.
- Keep pipelines in TypeScript — use functions, packages, types, editor support, code review, and unit tests instead of provider-specific YAML logic.
- Inspect before execution — FlowWright records a serializable plan before any stage runs, so dependencies, commands, conditions, and containers are visible.
- Run the DAG efficiently — independent stages can execute concurrently with a
bounded
-jvalue. - Use one event model everywhere — render readable terminal output, CI groups and annotations, JUnit XML, or NDJSON without changing the pipeline.
- Keep history local — review previous runs and stage logs under the repository's
.flowwright/directory. - Adopt incrementally — start with one command in an existing repository; add CI wrappers or the optional team control plane later.
Try it from source
Requirements: Node.js 24+ and pnpm 9.
$ git clone https://github.com/Flow-Wright/FlowWright.git
$ cd flowwright
$ corepack enable
$ pnpm install
$ pnpm build
$ node apps/cli/dist/cli.js init
$ node apps/cli/dist/cli.js runOnce installed as a package, the normal repository workflow is shorter:
$ flow init
$ flow explain
$ flow runflow init detects the package manager and available scripts, then creates a starter
pipeline.ts. It will not overwrite an existing pipeline.
A pipeline is TypeScript
// pipeline.ts
import { pipeline, stage, sh, onBranch } from "@flowwright/core";
export default pipeline({
name: "web-app",
stages: [
stage("Install", async () => {
await sh`pnpm install --frozen-lockfile`;
}),
stage("Test", {
needs: ["install"],
run: async () => void (await sh`pnpm test`),
}),
stage("Build", {
needs: ["install"],
run: async () => void (await sh`pnpm build`),
}),
stage("Deploy", {
needs: ["test", "build"],
when: onBranch("main"),
run: async () => void (await sh`pnpm deploy`),
}),
],
});Stage IDs are derived from their names, so the dependency references above use
lowercase IDs. flow validate catches missing dependencies, cycles, duplicate IDs, and
invalid execution-plan data before a command starts.
How a run works
- The CLI loads
pipeline.tsin a short-livednode --import tsxchild process. - Pipeline code records a serializable execution-plan IR; closures and live objects do not cross back into the parent process.
- The parent validates the complete plan before execution.
- The runtime topologically schedules ready stages over the dependency graph.
- Steps execute on the host or through the Docker executor when a stage declares a container.
- Every lifecycle change becomes a typed event consumed by reporters and optional local history.
This plan boundary is what keeps authoring expressive while making execution inspectable, portable, and suitable for policy checks.
Command reference
| Command | Purpose |
|---|---|
| flow init | Detect the repository and create a starter pipeline.ts |
| flow validate | Load and validate the plan without executing it |
| flow explain | Print stages, dependencies, commands, conditions, containers, and outputs |
| flow run | Validate and execute the pipeline |
| flow list | List the 50 most recent local runs |
| flow logs [id\|latest] | Read the recorded output for one run |
| flow backup <path> | Create a consistent snapshot of local run history |
| flow clean | Remove local history and logs while keeping the content cache |
| flow clean --all | Remove the entire local .flowwright/ directory |
| flow doctor | Check Node, pipeline loading, Docker, git context, and local-state access |
| flow export <provider> | Print a thin GitHub Actions, GitLab CI, or Jenkins wrapper |
| flow migrate jenkinsfile <path> | Scan a Jenkinsfile and suggest a typed pipeline plus migration report |
Run flow help or flow --help for the built-in reference.
Running pipelines
$ flow run
$ flow run -f ./ci/pipeline.ts
$ flow run -j 4
$ flow run --watch
$ flow run --no-docker
$ flow run --no-history| Option | Effect |
|---|---|
| -f, --file <path> | Use another pipeline file; defaults to pipeline.ts |
| -j, --concurrency <N> | Execute up to N ready stages concurrently; defaults to 1 |
| -w, --watch | Run once, then rerun after project files change |
| --no-docker | Execute container-declared stages directly on the host |
| --no-history | Do not persist the run or its logs |
| --no-color | Disable ANSI color even in an interactive terminal |
Watch runs intentionally disable history and coalesce changes that arrive while a run is
still active. Stop watch mode with Ctrl-C.
When a stage declares a container, the CLI uses Docker if it is available. If Docker is
missing, it reports the fallback and executes that stage on the host; use flow doctor
to detect the condition before CI starts.
Output and CI reporting
All reporters subscribe to the same typed execution events, so formatting never changes the behavior of the run.
$ flow run --reporter pretty
$ flow run --reporter github
$ flow run --reporter gitlab
$ flow run --json
$ flow run --junit .flowwright/junit.xml| Reporter | Best for |
|---|---|
| pretty | Interactive terminal output with color and stage progress |
| ci | Portable, non-colored CI logs |
| github | GitHub Actions groups, annotations, and job summary |
| gitlab | GitLab-compatible sections and collapsible output |
| json | One JSON execution event per line for automation and ingestion |
The reporter is auto-detected from common CI environment variables. An explicit
--reporter wins over detection; --json is shorthand for --reporter json. JUnit is
additive, so it can be written alongside any console reporter.
Parallel runs use grouped output to prevent concurrent stage logs from becoming interleaved and unreadable.
Use it in existing CI
FlowWright does not replace the runner. The provider checks out the repository and calls the same local command:
$ flow export github-actions
$ flow export gitlab-ci
$ flow export jenkinsEach command prints a minimal wrapper to standard output, including its intended path.
Review and redirect it into the repository when ready. The generated wrapper contains no
pipeline logic; pipeline.ts remains the source of truth.
Local history and logs
Unless --no-history is set, each run records structured state and per-stage logs under:
.flowwright/
state.db local run and stage history
runs/
<run-id>/
logs/ stage output
cache/ content-addressed cacheInspect that state without another service:
$ flow list
$ flow logs latest
$ flow logs <run-id> --stage test
$ flow backup ./backups/flowwright.db
$ flow cleanflow clean preserves the content cache because rebuilding it can be expensive. Use
flow clean --all only when the cache should also be discarded.
Migrate from Jenkins
$ flow migrate jenkinsfile ./Jenkinsfile
$ flow migrate jenkinsfile ./Jenkinsfile --out pipeline.tsThe scanner extracts declarative stages, shell commands, container images, and common
branch or tag conditions. It also reports constructs requiring manual attention—such as
shared libraries, credentials, approvals, post blocks, or plugin-specific steps—and
labels the migration easy, medium, or hard.
Migration is deliberately best-effort. The generated TypeScript is a reviewable starting point, not a promise of semantic equivalence with arbitrary Groovy.
CLI and app-suite boundary
The flow command is intentionally local-only:
- It does not start or call
flowwright-server. - It has no project, user, token, remote-run, server, or worker commands.
- It depends only on
@flowwright/core,@flowwright/runtime, and the TypeScript loader. - Pipeline execution and local history continue to work without the web application.
When a team needs shared projects, triggers, schedules, queues, RBAC, audit records,
central history, or a worker fleet, add the optional
flowwright-server, web app, and
flowwright-worker. Existing pipeline.ts definitions keep the
same execution-plan boundary.
Source layout
src/
cli.ts argument parsing, help, dispatch, and process exit handling
commands.ts run, validate, explain, history, cleanup, and doctor commands
scaffold.ts repository detection and starter pipeline generation
watch.ts recursive watch mode and rerun coordination
migrate.ts Jenkinsfile scanning and TypeScript generation
export.ts thin CI wrapper generation
reporters/ terminal, GitHub, GitLab, JUnit, and NDJSON event consumers
render.ts sequential and grouped terminal rendering
env.ts CI provider detection
git.ts branch, tag, and commit context
io.ts injectable command I/O boundaryCommand handlers return exit codes and accept injected I/O, keeping behavior testable without spawning the complete executable.
Development commands
Run these from the repository root:
$ pnpm --filter @flowwright/cli typecheck # strict TypeScript validation
$ pnpm --filter @flowwright/cli test # command, reporter, migration, and watch tests
$ pnpm --filter @flowwright/cli build # compile the flow executable
$ node apps/cli/dist/cli.js --help # exercise the built commandThe test suite covers command behavior, scaffold detection, grouped rendering, reporter selection, NDJSON, JUnit, CI exports, Jenkins migration, formatting, and watch-mode coordination.
