Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2478d1b
Add flow status and flow edit commands
milldr Feb 23, 2026
3e8f547
Simplify edit-status tape to just view and quit
milldr Feb 23, 2026
a235eaf
Use realistic repo URL in demo tapes
milldr Feb 23, 2026
78ab390
Add multiple demo workspaces with varied repo counts
milldr Feb 23, 2026
baf9a12
Update README with motivation, status, and edit commands
milldr Feb 23, 2026
54f493f
Add spec reference docs and simplify README
milldr Feb 23, 2026
1d14186
Clean up README features section formatting
milldr Feb 23, 2026
02ee9d6
Use emoji list for README features section
milldr Feb 23, 2026
4081d5a
Wrap quickstart in collapsible details block
milldr Feb 23, 2026
9d54130
Rename What can it do to What it does
milldr Feb 23, 2026
f20f2ef
Split quickstart into collapsible install and usage sections
milldr Feb 23, 2026
403d50f
Add full directory structure and doc links to How it works
milldr Feb 23, 2026
96a543c
Rephrase motivation OpenClaw sentence
milldr Feb 23, 2026
8afaaf0
Update feature emoji in What it does section
milldr Feb 23, 2026
1267a37
Add spec.agents config, agent-aware exec, and 4-status workflow
milldr Feb 23, 2026
c9f7afd
Update quickstart usage with descriptions and agent-aware exec
milldr Feb 23, 2026
00df1a4
Fix status checks: auto-create spec, add FLOW_REPO_SLUG, use absolute…
milldr Feb 23, 2026
8ffe046
Add unpushed commits detection to in-progress status check
milldr Feb 23, 2026
50d65bb
Document default statuses and AI-generated check commands in status spec
milldr Feb 23, 2026
82cdaaa
Show full check commands in status schema with AI-generated comments
milldr Feb 23, 2026
afcda50
Regenerate command GIFs with updated status spec and config
milldr Feb 23, 2026
fcadd85
Show real default status spec in edit-status GIF, swap local spec for…
milldr Feb 23, 2026
3b1d803
Fix render GIF worktree conflict by using web repo in edit-state tape
milldr Feb 23, 2026
efeba3c
Remove extra newline between status label and repo table
milldr Feb 23, 2026
30f0072
Regenerate command GIFs
milldr Feb 23, 2026
ec11949
Remove Files sections from PRDs 006 and 007
milldr Feb 23, 2026
fb0a3d2
Fix in-progress check to ignore repos that are behind remote
milldr Feb 23, 2026
9f56608
Add flow reset command with status, config, and state subcommands
milldr Feb 24, 2026
4fd0427
Add flow open command to launch a shell in the workspace directory
milldr Feb 24, 2026
6d051b6
Add flow reset skills subcommand to restore default agent files
milldr Feb 24, 2026
b186d6c
Override SHELL in open tape to avoid leaking personal info in prompt
milldr Feb 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,18 @@ gendocs:
vhs: build
bash demo-setup.sh
vhs docs/commands/tapes/init.tape
vhs docs/commands/tapes/state.tape
vhs docs/commands/tapes/edit-state.tape
vhs docs/commands/tapes/render.tape
vhs docs/commands/tapes/exec.tape
vhs docs/commands/tapes/list.tape
vhs docs/commands/tapes/edit-status.tape
vhs docs/commands/tapes/status.tape
vhs docs/commands/tapes/edit-config.tape
vhs docs/commands/tapes/open.tape
vhs docs/commands/tapes/delete.tape
vhs docs/commands/tapes/reset-status.tape
vhs docs/commands/tapes/reset-config.tape
vhs docs/commands/tapes/reset-state.tape
vhs docs/commands/tapes/reset-skills.tape

docs: gendocs vhs
103 changes: 55 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ Working across multiple repos means repetitive setup, scattered branches, and cl

![flow demo](demo.gif)

## Motivation

Flow is designed to be called by other AI agents — tools like [OpenClaw](https://github.com/openclaw) that integrate with Slack, Linear, or other services to understand context and then programmatically create state files and initialize workspaces. Prompt OpenClaw with a Slack thread or Linear ticket and let your agent create the workspace using Flow 🌊

Agents should call deterministic tools rather than relying on freeform interpretation with skills. This leads to more consistent results and reproducible environments.

## Quickstart

### 1. Install
<details>
<summary><strong>Install</strong></summary>

```bash
brew install milldr/tap/flow
Expand All @@ -30,89 +37,89 @@ cd flow
make install
```

### 2. Create a workspace
</details>

<details>
<summary><strong>Usage</strong></summary>

### 1. Create a workspace

Flow creates a workspace with an empty state file.

```bash
flow init
```

```
✓ Created workspace calm-delta

Next: flow state calm-delta
```
### 2. Add repos

### 3. Add repos
Open the state file in `$EDITOR` and define which repos and branches belong together.

```bash
flow state calm-delta # Open state.yaml in $EDITOR
flow edit state calm-delta
```

### 4. Render it
### 3. Render it

Flow fetches each repo into a shared bare clone cache, then creates lightweight worktrees in the workspace directory. Rendering is idempotent — re-running fetches updates and skips worktrees that already exist.

```bash
flow render calm-delta
```

```
✓ Workspace ready
### 4. Start working

flow exec calm-delta -- <command>
flow exec calm-delta -- cursor .
flow exec calm-delta -- claude
```
Launch your default agent directly into the workspace. Flow reads the `spec.agents` list from your global config and runs the one marked `default: true`.

Flow fetches each repo into a bare clone cache (`~/.flow/repos/`), then creates lightweight worktrees in the workspace directory. Running `render` again is idempotent — it fetches updates and skips worktrees that already exist.

## State file

Each workspace is defined by a `state.yaml` file. Run `flow state <workspace>` to open it in your editor.

```yaml
apiVersion: flow/v1
kind: State
metadata:
name: vpc-ipv6
description: IPv6 support across VPC services
created: "2026-02-18T12:00:00Z"
spec:
repos:
- url: git@github.com:acme/vpc-service.git
branch: feature/ipv6
path: vpc-service
- url: git@github.com:acme/subnet-manager.git
branch: feature/ipv6
path: subnet-manager
```bash
flow exec calm-delta
```

| Field | Description |
|-------|-------------|
| `metadata.name` | Optional human-friendly name |
| `metadata.description` | Optional description |
| `spec.repos[].url` | Git remote URL |
| `spec.repos[].branch` | Branch to check out |
| `spec.repos[].path` | Directory name in the workspace (defaults to repo name) |
See the [spec reference](docs/specs/) for YAML file schemas and the [command reference](docs/commands/) for all commands.

</details>

See the full [command reference](docs/commands/) for usage, flags, examples, and GIF demos.
## What it does

🌳 **Workspaces as code** — Declare git worktrees in a YAML state file for instant, reproducible workspace setup across repos and branches.

🚦 **Status tracking** — Define custom check commands that dynamically resolve the status of each repo in a workspace.

🤖 **AI agent integration** — Generate shared context files and agent instructions across repos so your AI tools have the right skills and knowledge from the start.

## How it works

Flow stores everything under `~/.flow` (override with `$FLOW_HOME`):

```
~/.flow/
├── config.yaml # Global config
├── status.yaml # Global status spec
├── agents/
│ └── claude/
│ ├── CLAUDE.md # Shared agent instructions
│ └── skills/
│ ├── flow-cli/SKILL.md # Flow CLI skill
│ └── workspace-structure/SKILL.md
├── workspaces/
│ └── calm-delta/ # Workspace ID
│ ├── state.yaml # Workspace manifest (name: vpc-ipv6)
│ ├── status.yaml # Optional workspace-specific status spec
│ ├── CLAUDE.md # Generated workspace context
│ ├── .claude/
│ │ ├── CLAUDE.md → agents/claude/CLAUDE.md
│ │ └── skills → agents/claude/skills/
│ ├── vpc-service/ # Worktree
│ └── subnet-manager/ # Worktree
└── cache/
├── acme-vpc-service.git/ # Bare clone
└── acme-subnet-manager.git/ # Bare clone
└── repos/
└── github.com/acme/
├── vpc-service.git/ # Bare clone
└── subnet-manager.git/ # Bare clone
```

Bare clones are shared across workspaces. Worktrees are cheap — they share the object store with the bare clone, so multiple workspaces pointing at the same repo don't duplicate data.

See the [spec reference](docs/specs/) for YAML file schemas and the [command reference](docs/commands/) for usage, flags, and GIF demos.

## Requirements

- Go 1.25+
Expand Down
134 changes: 120 additions & 14 deletions demo-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,124 @@
# Called from Makefile: `make demo` runs this, then `vhs demo.tape`.
set -e

export FLOW_HOME="/tmp/flow-demo/.flow"
FLOW="$(pwd)/flow"
rm -rf /tmp/flow-demo /tmp/demo
mkdir -p /tmp/demo

# Create a local git repo with a feature branch
dir="/tmp/demo/app"
mkdir -p "$dir"
git -C "$dir" init -q
echo "# app" > "$dir/README.md"
echo "package main" > "$dir/main.go"
git -C "$dir" add .
git -C "$dir" commit -q -m "initial commit"
git -C "$dir" checkout -q -b feature/ipv6
echo "// IPv6 support" >> "$dir/main.go"
git -C "$dir" add .
git -C "$dir" commit -q -m "add ipv6 support"
mkdir -p /tmp/demo "$FLOW_HOME/workspaces" "$FLOW_HOME/repos"

# --- Create local git repos with feature branches ---

create_repo() {
local name="$1" branch="$2" file="$3" msg="$4"
local dir="/tmp/demo/$name"
mkdir -p "$dir"
git -C "$dir" init -q
echo "# $name" > "$dir/README.md"
echo "package main" > "$dir/main.go"
git -C "$dir" add .
git -C "$dir" commit -q -m "initial commit"
git -C "$dir" checkout -q -b "$branch"
echo "// $msg" >> "$dir/$file"
git -C "$dir" add .
git -C "$dir" commit -q -m "$msg"
}

create_repo "app" "feature/ipv6" "main.go" "add ipv6 support"
create_repo "api" "feat/auth" "main.go" "add auth endpoints"
create_repo "docs" "update/guides" "README.md" "update setup guide"
create_repo "web" "feat/dashboard" "main.go" "add dashboard page"

# --- Pre-populate bare clone cache for realistic URLs ---

for name in app api docs web; do
bare="$FLOW_HOME/repos/github.com/acme/${name}.git"
mkdir -p "$(dirname "$bare")"
git clone --bare "/tmp/demo/$name" "$bare" -q
# Add fetch refspec so worktrees can resolve origin/* refs
git -C "$bare" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git -C "$bare" fetch -q origin
done

# --- Create and render pre-existing workspaces ---

create_workspace() {
local id="$1" yaml="$2"
mkdir -p "$FLOW_HOME/workspaces/$id"
echo "$yaml" > "$FLOW_HOME/workspaces/$id/state.yaml"
}

create_workspace "bold-creek" "$(cat <<'YAML'
apiVersion: flow/v1
kind: State
metadata:
name: api-refactor
description: Refactor authentication API
created: "2026-02-21T14:30:00Z"
spec:
repos:
- url: github.com/acme/api
branch: feat/auth
path: api
- url: github.com/acme/docs
branch: update/guides
path: docs
YAML
)"

create_workspace "swift-pine" "$(cat <<'YAML'
apiVersion: flow/v1
kind: State
metadata:
name: infra-update
description: Update infrastructure across services
created: "2026-02-19T09:00:00Z"
spec:
repos:
- url: github.com/acme/app
branch: feature/ipv6
path: app
YAML
)"

# Render both workspaces so exec and status work
$FLOW render bold-creek
$FLOW render swift-pine

# --- Add local commits so status checks detect diffs ---

add_local_change() {
local ws_dir="$FLOW_HOME/workspaces/$1/$2"
git -C "$ws_dir" fetch -q origin 2>/dev/null || true
echo "# local change" >> "$ws_dir/README.md"
git -C "$ws_dir" add .
git -C "$ws_dir" commit -q -m "wip: local changes"
}

add_local_change "bold-creek" "api"
add_local_change "bold-creek" "docs"
add_local_change "swift-pine" "app"

# --- Create status specs ---
# The default spec (written by EnsureDirs on first flow command) uses gh + jq.
# For the demo, we keep the default for the edit-status tape (so it shows the
# real commands), then the status tape swaps in a local-only spec before running.

# Write a local-only spec that the status tape will swap in (no gh needed).
cat > "$FLOW_HOME/status-local.yaml" <<YAML
apiVersion: flow/v1
kind: Status
spec:
statuses:
- name: closed
description: All PRs merged or closed
check: 'false'
- name: in-review
description: Non-draft PR open
check: 'false'
- name: in-progress
description: Local diffs or draft PR
check: git -C "\$FLOW_REPO_PATH" diff --name-only "origin/\$FLOW_REPO_BRANCH" 2>/dev/null | grep -q .
- name: open
description: Workspace created, no changes yet
default: true
YAML
Binary file modified demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 31 additions & 13 deletions demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,49 @@ Type `export EDITOR="vim -u NONE"`
Enter
Sleep 200ms

# Swap in local-only status spec (no gh needed for demo)
Type `cp "$FLOW_HOME/status-local.yaml" "$FLOW_HOME/status.yaml"`
Enter
Sleep 200ms

Type "clear"
Enter
Sleep 500ms

Show

# === flow init ===
# === flow status (pre-existing workspaces) ===

Sleep 500ms

Type "flow status"
Sleep 500ms
Enter
Sleep 4s

# === flow exec ===

Type "flow exec api-refactor -- ls"
Sleep 500ms
Enter
Sleep 3s

# === Clear and start creating a new workspace ===

Type "clear"
Enter
Sleep 500ms

# === flow init ===

Type "flow init demo"
Sleep 500ms
Enter
Sleep 3s

# === flow state (add a repo in vim) ===
# === flow edit state (add a repo in vim) ===

Type "flow state demo"
Type "flow edit state demo"
Sleep 500ms
Enter
Sleep 1500ms
Expand All @@ -57,11 +82,11 @@ Sleep 500ms

# Now in insert mode after " repos: " — type the repo entry
Enter
Type@30ms " - url: /tmp/demo/app"
Type@30ms " - url: github.com/acme/web"
Enter
Type@30ms " branch: feature/ipv6"
Type@30ms " branch: feat/dashboard"
Enter
Type@30ms " path: app"
Type@30ms " path: web"
Sleep 3s

# Exit insert mode, save and quit
Expand All @@ -78,13 +103,6 @@ Sleep 500ms
Enter
Sleep 5s

# === flow exec ===

Type "flow exec demo -- ls"
Sleep 500ms
Enter
Sleep 3s

# === flow list ===

Type "flow list"
Expand Down
Loading