Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ snapshot:
demo: build
bash demo-setup.sh
vhs demo.tape
vhs demo-status.tape

gendocs:
go run ./cmd/gendocs
Expand Down
48 changes: 23 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

CLI for managing multi-repo development workspaces using git worktrees.

Working across multiple repos means repetitive setup, scattered branches, and cleanup debt. Flow uses a YAML state file to define which repos and branches belong together, then materializes the workspace with bare clone caching and git worktrees.

![flow demo](demo.gif)
Working across multiple repos means repetitive setup, scattered branches, and cleanup debt. ${\color{cyan}\texttt{flow}}$ uses a YAML state file to define which repos and branches belong together, then materializes the workspace with bare clone caching and git worktrees.

## Features

Expand All @@ -16,13 +14,25 @@ Working across multiple repos means repetitive setup, scattered branches, and cl

🤖 **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.

## Why flow?
## Why ${\color{cyan}\texttt{flow}}$?

AI agents work best when they have deterministic tools instead of freeform instructions. Asking an agent to "set up a multi-repo workspace" produces inconsistent results — but giving it a CLI that manages YAML state files, bare clone caches, and git worktrees produces the same result every time.

Flow is that deterministic layer. It gives agents (and humans) a small set of reliable commands for workspace lifecycle: create, define repos in YAML, render worktrees, check status. Agents call these tools through embedded skills rather than interpreting setup instructions on their own.
${\color{cyan}\texttt{flow}}$ is that deterministic layer. It gives agents (and humans) a small set of reliable commands for workspace lifecycle: create, define repos in YAML, render worktrees, check status. Agents call these tools through embedded skills rather than interpreting setup instructions on their own.

Beyond workspace creation, ${\color{cyan}\texttt{flow}}$ centralizes agent skills across all your workspaces and lets you check the status of many workstreams in parallel. It is not opinionated about which agent or editor you use — configure Claude, Cursor, or anything else in a single config file. Everything is customizable: status checks, agent skills, workspace structure.

### Describe the problem, ${\color{cyan}\texttt{flow}}$ does the rest

`flow init` creates a workspace pre-configured with agent instructions and skills, then launches you directly into your preferred agent — Claude, Cursor, or anything else you configure. The agent reads the embedded flow skill to understand how workspaces work, plus any custom skills you've added (like resolving repos by friendly names). Describe what you're working on and the agent handles the rest: editing the state file, rendering worktrees, and getting everything ready to go.

![flow init](demo.gif)

### Track all your workstreams in one place

Beyond workspace creation, flow centralizes agent skills across all your workspaces and lets you check the status of many workstreams in parallel. It is not opinionated about which agent or editor you use — configure Claude, Cursor, or anything else in a single config file. Everything is customizable: status checks, agent skills, workspace structure.
`flow status` gives you a live view across all active workspaces. Statuses are fully customizable — they're just one-line shell commands in a config file, not hardcoded logic. Have your AI generate complex status checks (query GitHub PRs, check CI, inspect git state) and hide them behind simple labels. Nothing in ${\color{cyan}\texttt{flow}}$ is opinionated; anything that could be is configurable.

![flow status](demo-status.gif)

## Getting started

Expand Down Expand Up @@ -51,30 +61,19 @@ make install

### Create a workspace and start working

Flow creates a workspace and launches your configured agent. Describe what you're working on and the agent handles the rest.
${\color{cyan}\texttt{flow}}$ creates a workspace and launches your configured agent. Describe what you're working on and the agent handles the rest.

```bash
flow init my-project
flow init
```

The agent reads its embedded skills to edit `state.yaml`, run `flow render`, and begin working in the repos.

### Manual workflow

Use `--no-exec` to skip the agent launch and set things up yourself:

```bash
flow init my-project --no-exec
flow edit state my-project # add repos and branches
flow render my-project # clone repos, create worktrees
flow exec my-project # launch agent manually
```

See the [spec reference](docs/specs/) for YAML file schemas and the [command reference](docs/commands/) for all commands.

## How it works

Flow stores everything under `~/.flow` (override with `$FLOW_HOME`):
${\color{cyan}\texttt{flow}}$ stores everything under `~/.flow` (override with `$FLOW_HOME`):

```
~/.flow/
Expand All @@ -84,8 +83,7 @@ Flow stores everything under `~/.flow` (override with `$FLOW_HOME`):
│ └── claude/
│ ├── CLAUDE.md # Shared agent instructions
│ └── skills/
│ ├── flow-cli/SKILL.md # Built-in: workspace management
│ ├── workspace-structure/SKILL.md # Built-in: directory layout
│ ├── flow/SKILL.md # Built-in: workspace management
│ └── find-repo/SKILL.md # Your own custom skill
├── workspaces/
│ └── calm-delta/ # Workspace ID
Expand All @@ -94,7 +92,7 @@ Flow stores everything under `~/.flow` (override with `$FLOW_HOME`):
│ ├── CLAUDE.md # Generated workspace context
│ ├── .claude/
│ │ ├── CLAUDE.md → agents/claude/CLAUDE.md
│ │ └── skills → agents/claude/skills/
│ │ └── skills/ # Consolidated from shared + repo skills
│ ├── vpc-service/ # Worktree
│ └── subnet-manager/ # Worktree
└── repos/
Expand All @@ -105,13 +103,13 @@ Flow stores everything under `~/.flow` (override with `$FLOW_HOME`):

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.

Flow ships two built-in skills (`flow-cli` and `workspace-structure`) and preserves any custom skills you add to the same directory. Run `flow reset skills` to update the built-in skills without touching your own.
${\color{cyan}\texttt{flow}}$ ships a built-in `flow` skill and consolidates skills from all repos into each workspace's `.claude/skills/` directory on render. Add your own skills to the shared directory or to individual repos. Run `flow reset skills` to update the built-in skill without touching your own.

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

## Customization

Flow stores everything under `~/.flow` (override with `$FLOW_HOME`). Edit these files to customize your setup:
${\color{cyan}\texttt{flow}}$ stores everything under `~/.flow` (override with `$FLOW_HOME`). Edit these files to customize your setup:

| Command | What it configures |
|---------|-------------------|
Expand Down
105 changes: 50 additions & 55 deletions demo-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,41 @@ FLOW="$(pwd)/flow"
rm -rf /tmp/flow-demo /tmp/demo
mkdir -p /tmp/demo "$FLOW_HOME/workspaces" "$FLOW_HOME/repos"

# --- Create a fake claude binary that simulates an interactive agent session ---
# --- Configure flow agent ---
# A launcher script writes .claude/settings.json to pre-approve tools before
# launching claude. This avoids permission dialogs during the demo.

mkdir -p /tmp/flow-demo/bin
cat > /tmp/flow-demo/bin/claude <<'SCRIPT'
cat > /tmp/flow-demo/launch-claude.sh <<'LAUNCHER'
#!/bin/bash
DIM='\033[2m'
CYAN='\033[36m'
GREEN='\033[32m'
BOLD='\033[1m'
RESET='\033[0m'

echo ""
echo -e " ${DIM}[mocked agent session]${RESET}"
echo ""
echo -e " ${DIM}Flow includes skills that teach your agent to manage workspaces.${RESET}"
echo -e " ${DIM}Add your own skills for repo discovery, PR lookup, or any custom workflow.${RESET}"
echo -e " ${DIM}Paste a Slack thread, dictate a bug report — the agent handles the rest.${RESET}"
echo ""
echo -e " ${BOLD}Enter your prompt:${RESET}"
echo ""
printf " > "
read -r task

echo ""
sleep 0.5
echo -e " ${CYAN}● Reading skill: flow-cli${RESET}"
sleep 0.6
echo -e " ${CYAN}● Reading skill: find-repo${RESET}"
echo -e " ${DIM}→ github.com/acme/web${RESET}"
sleep 0.6
echo -e " ${CYAN}● Editing state.yaml${RESET}"
echo -e " ${DIM}name: fix-dashboard-charts${RESET}"
echo -e " ${DIM}repo: github.com/acme/web @ fix/dashboard-charts${RESET}"
sleep 0.6
echo -e " ${CYAN}● Running flow render...${RESET}"
sleep 0.8
echo ""
echo -e " ${GREEN}✓ Workspace ready${RESET}"
echo ""
echo -e " ${CYAN}● Analyzing web/src/components/Charts.tsx...${RESET}"
sleep 0.8
echo ""
printf " > "
read -r cmd
echo ""
SCRIPT
chmod +x /tmp/flow-demo/bin/claude
echo '{"permissions":{"allow":["Bash","Edit","Write","Read","Glob","Grep","Skill"]}}' > .claude/settings.json
exec claude --model haiku
LAUNCHER
chmod +x /tmp/flow-demo/launch-claude.sh

cat > "$FLOW_HOME/config.yaml" <<YAML
apiVersion: flow/v1
kind: Config
spec:
agents:
- name: claude
exec: /tmp/flow-demo/launch-claude.sh
default: true
YAML

# --- Pre-trust the demo directory so Claude skips the workspace trust dialog ---

CLAUDE_JSON="$HOME/.claude.json"
if [ -f "$CLAUDE_JSON" ]; then
python3 -c "
import json, sys
with open('$CLAUDE_JSON') as f:
config = json.load(f)
config.setdefault('projects', {})
config['projects']['/tmp/flow-demo'] = {'hasTrustDialogAccepted': True}
with open('$CLAUDE_JSON', 'w') as f:
json.dump(config, f, indent=2)
"
fi

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

Expand All @@ -67,29 +55,36 @@ create_repo() {
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"
git -C "$dir" checkout -q -b "$branch" 2>/dev/null || true
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"
create_repo "billing" "feat/billing-v2" "main.go" "billing v2 migration"
create_repo "gateway" "feat/rate-limits" "main.go" "add rate limiting"
create_repo "config" "feat/env-vars" "main.go" "add env var support"
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"
create_repo "billing" "feat/billing-v2" "main.go" "billing v2 migration"
create_repo "gateway" "feat/rate-limits" "main.go" "add rate limiting"
create_repo "config" "feat/env-vars" "main.go" "add env var support"
create_repo "apps" "main" "main.go" "initial app setup"
create_repo "infrastructure" "main" "main.go" "initial infra setup"

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

for name in app api docs web billing gateway config; do
for name in app api docs web billing gateway config apps infrastructure; 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

# Also create under SSH URL path (in case Claude uses git@ format)
ssh_bare="$FLOW_HOME/repos/git@github.com:acme/${name}.git"
mkdir -p "$(dirname "$ssh_bare")"
cp -a "$bare" "$ssh_bare"
done

# --- Create and render pre-existing workspaces ---
Expand Down Expand Up @@ -184,7 +179,7 @@ YAML
)"

# Render all workspaces
# Use --reset=false to skip interactive prompt when branches already exist
# Use --reset=false to use existing branches as-is instead of creating fresh from base
$FLOW render bold-creek --reset=false
$FLOW render swift-pine --reset=false
$FLOW render calm-ridge --reset=false
Expand Down
Binary file added demo-status.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions demo-status.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Regenerate: make demo
Output demo-status.gif

Set Shell "bash"
Set FontFamily "Menlo"
Set FontSize 16
Set Width 1100
Set Height 650
Set Padding 20
Set Theme "Catppuccin Mocha"
Set TypingSpeed 30ms
Set PlaybackSpeed 1
Set LetterSpacing 0
Set LineHeight 1.2

# --- Hidden env setup ---
Hide

Type `export FLOW_HOME=/tmp/flow-demo/.flow`
Enter
Sleep 200ms

Type `export PATH="$(pwd):$PATH"`
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@0ms `clear`
Enter
Sleep 500ms

Show

# === See all workstreams at a glance ===

Sleep 500ms

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

Type "flow status api-refactor"
Sleep 500ms
Enter
Sleep 4s

Sleep 4s
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.
54 changes: 14 additions & 40 deletions demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Output demo.gif
Set Shell "bash"
Set FontFamily "Menlo"
Set FontSize 16
Set Width 900
Set Height 550
Set Width 1100
Set Height 650
Set Padding 20
Set Theme "Catppuccin Mocha"
Set TypingSpeed 30ms
Expand All @@ -20,63 +20,37 @@ Type `export FLOW_HOME=/tmp/flow-demo/.flow`
Enter
Sleep 200ms

Type `export PATH="/tmp/flow-demo/bin:$(pwd):$PATH"`
Type `export PATH="$(pwd):$PATH"`
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

# Clear screen and print first section comment (hidden from recording)
Type@0ms `clear && printf '\033[2;3m# create a workspace — agent launches automatically\033[0m\n'`
Type@0ms `clear`
Enter
Sleep 500ms

Show

# === Create a workspace — agent launches automatically ===

Sleep 500ms

Type "flow init dashboard"
Sleep 500ms
Enter
Sleep 3s
# === flow init — launches Claude automatically ===

# The fake claude is now running and showing its prompt.
# Type the task description into the agent.
Type "dashboard charts broke after the auth migration, check the web frontend"
Sleep 500ms
Enter
Sleep 5s

# Agent has finished setting up. Exit the session.
Type "/exit"
Type "flow init"
Sleep 500ms
Enter
Sleep 2s

# === See all workstreams at a glance ===
# Wait for Claude to launch and show the workspace trust dialog
Sleep 3s

# Clear screen and print second section comment (hidden from recording)
# Accept the workspace trust dialog
Hide
Type@0ms `clear && printf '\033[2;3m# see all workstreams at a glance\033[0m\n'`
Enter
Sleep 500ms
Show
Sleep 3s

Sleep 500ms

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

Type "flow status api-refactor"
# Type the task into Claude
Type "Our auth migration broke prod — API errors in github.com/acme/apps, wrong docs in github.com/acme/docs, and infra config drift in github.com/acme/infrastructure. Set up a workspace for this, then ask me what to look at first."
Sleep 500ms
Enter
Sleep 4s

Sleep 4s
# Wait for Claude to read skills, edit state.yaml, run flow render
Sleep 40s
Loading
Loading