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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"name": "flow",
"description": "Stack-agnostic AI dev workflow \u2014 twelve slash commands, a focused reviewer agent, and hooks that layer with your project's .flow/ config",
"source": "./plugins/flow",
"version": "0.6.0",
"version": "0.7.0",
"category": "workflow",
"keywords": [
"workflow",
Expand Down
2 changes: 1 addition & 1 deletion plugins/flow/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flow",
"version": "0.6.0",
"version": "0.7.0",
"description": "Reusable AI dev workflow for Claude Code: orchestrated agents, session lifecycle, deep reviews, and project-aware slash commands.",
"author": {
"name": "kaiomp"
Expand Down
6 changes: 3 additions & 3 deletions plugins/flow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Then, inside any project:
/flow:init
```

This creates `.flow/config.md` (project-level config) and `CLAUDE.md` (project memory) from templates, plus an `issues/` directory for the local PM backend.
This creates `.flow/config.md` (project-level config) and `CLAUDE.md` (project memory) from templates, plus an `issues/` directory for the local PM backend. Your dev/lint/test/build commands are filled in for you: detected from whichever manifest declares them, and where none does — a script-style repo with a venv and a `tests/` directory, say — inferred from the repo and verified by running them. You are not handed a config file to complete by hand.

## Commands

Expand All @@ -30,7 +30,7 @@ This creates `.flow/config.md` (project-level config) and `CLAUDE.md` (project m
| `/flow:pause` | Snapshot current state to `.flow/session-progress.md`; `land` also ships — CI checks, issue closing, ff-merge. |
| `/flow:audit` | Dry-run review: lint, typecheck, security pass without shipping. |
| `/flow:cleanup` | Tidy stray branches, stale session files, and `.flow/session/`. |
| `/flow:health` | Inspect the project's flow setup and report missing pieces. |
| `/flow:health` | Inspect the project's flow setup and report missing pieces (and fill in any blank `*_cmd`). |
| `/flow:deep-review` | Spawn the reviewer agent with extra rigor on the working diff. |
| `/flow:flawz` | Pressure-test a plan/spec/design for real flaws before you act on it. |
| `/flow:autopilot` | Autonomous multi-sprint loop: agent team, deep-review, push, repeat. |
Expand Down Expand Up @@ -74,7 +74,7 @@ Custom agents communicate through files in `.flow/session/` (e.g. `investigation
| `question_wip` | Max questions open at once in `.flow/questions.md` (default 3) |
| `schema_glob` / `docs_glob` / `route_pattern` | Optional drift-check heuristics tuning |

Hooks and helper scripts read these values. Nothing is hardcoded — `flow` adapts to your stack. See `references/config-template.md` for the full annotated template.
Hooks and helper scripts read these values. Nothing is hardcoded — `flow` adapts to your stack. See `references/config-template.md` for the full annotated template, and `references/stack-command-inference.md` for how the `*_cmd` values get worked out when no manifest declares them.

### `config.md` is private by default

Expand Down
6 changes: 4 additions & 2 deletions plugins/flow/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ asks before dropping session state), then `/flow:init` again.

## 2. Check `.flow/config.md`

Init fills in what it detected, so most projects only need a glance. A blank value means
"skip that step" — fill it in by hand if detection missed something:
Init fills these in for you — from your manifest where it declares them, and otherwise by
inferring the command from the repo and verifying it runs — so most projects only need a
glance. A blank value means "skip that step": nothing to run for it. Re-run `/flow:init` if a
blank should have a command:

```
workflow_mode: solo
Expand Down
18 changes: 15 additions & 3 deletions plugins/flow/commands/health.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
---
description: Verify your workflow setup — config, hooks, required commands, PM backend connectivity.
allowed-tools: Bash, Read, Glob, Grep
allowed-tools: Bash, Read, Edit, Glob, Grep
---
# /flow:health

Sanity check. Run this after `/flow:init` and any time `/flow:*` commands feel broken.

Diagnostic, not a repair tool: report what is wrong and where the fix is, and change nothing —
with one exception, a blank `*_cmd` in config.md, which you fill in yourself (Check 1).

## Checks

Run each check and collect the result. Print a table at the end.
Expand All @@ -16,6 +19,15 @@ Run each check and collect the result. Print a table at the end.
- Does it parse? (frontmatter or key:value lines as defined in the template)
- Are required fields present: `workflow_mode`, `pm_backend`, `dev_cmd`, `lint_cmd`, `build_cmd`, `test_cmd`?

A `*_cmd` key that is present but **blank** is your work, not the user's. Do not report it as
"fill this in yourself" — that is exactly the chore `/flow:init` Step 3 exists to remove.
Infer it from the repo, verify it runs, and write it in, following
`${CLAUDE_PLUGIN_ROOT}/references/stack-command-inference.md`. Report it as `FIXED` with the
command you wrote. Only a key you inferred and could *not* verify — or one with genuinely
conflicting evidence — is worth raising with the user, and then with a recommendation, not an
open question. A key that is blank because the project has no such step is `OK`, not a
finding; say so in the note.

### 2. CLAUDE.md freshness

- Does `$CLAUDE_PROJECT_DIR/CLAUDE.md` exist?
Expand Down Expand Up @@ -58,12 +70,12 @@ Print a table:

```
CHECK STATUS NOTE
config.md OK
config.md FIXED test_cmd was blank -> .venv/bin/pytest (23 passed)
CLAUDE.md freshness STALE last touched 124 days ago
hooks OK 2 wired
commands on PATH FAIL `<missing>` not found
pm backend OK
git OK
```

End with a one-line summary: `N/M checks passed`. If anything failed, point at the fix (`/flow:init`, install missing tool, etc.).
End with a one-line summary: `N/M checks passed`. If anything failed, point at the fix (`/flow:init`, install missing tool, etc.) — and where the fix is something you can do, such as a blank `*_cmd`, do it rather than prescribe it.
49 changes: 39 additions & 10 deletions plugins/flow/commands/init.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
---
description: Bootstrap a new project with .flow/config.md and CLAUDE.md tailored to your stack.
allowed-tools: Bash, Read, AskUserQuestion
allowed-tools: Bash, Read, Edit, Glob, Grep, AskUserQuestion
---
# /flow:init

Bootstrap a project so the rest of `/flow:*` works. This drives `scripts/init.js`, which
writes `.flow/config.md` and `CLAUDE.md` and auto-detects your stack commands from files
already on disk (`package.json` scripts, `go.mod`, `Cargo.toml`, `pyproject.toml`, …). You
only need to supply what the script cannot infer.
already on disk (`package.json` scripts, `go.mod`, `Cargo.toml`, `pyproject.toml`,
`requirements*.txt`, a `Makefile`, …). Anything a manifest does not declare, **you** work out
from the repo in Step 3 — the user is only ever asked the questions in Step 1.

## Step 1: Ask the user

Expand All @@ -19,7 +20,8 @@ Use `AskUserQuestion`, batched where possible:
what `git remote get-url origin` parses to.
4. If `linear`: ask for `pm_linear_team` (the team key, e.g. `ENG`).

Do not ask about dev/lint/typecheck/build/test/format commands — the script detects those.
Do not ask about dev/lint/typecheck/build/test/format commands — the script detects what the
manifests declare and Step 3 works out the rest from the repo.

For the project name, don't ask by default — the script infers it from `package.json`'s
`name` field, falling back to the directory basename. Only ask via `AskUserQuestion` if
Expand All @@ -45,7 +47,32 @@ Because nothing is overwritten, editing a template in a plugin update does *not*
project that was already initialised. To pick up template changes, run `/flow:uninstall`
first, then `/flow:init` again.

## Step 3: Backend bootstrapping
## Step 3: Fill in what detection missed

The script ends by naming every key it could not fill:

```
[flow init] Done. Stack commands still blank: dev_cmd, build_cmd, format_cmd
```

That list is **yours to finish, not the user's**. A repo with a venv, a `tests/test_*.py` and
an `app.py` has a test command and a dev command; they are simply not declared in a manifest.
Telling the user to "edit `.flow/config.md` to fill in your project commands" hands back a
chore you can complete in one pass.

For each blank key, follow `${CLAUDE_PLUGIN_ROOT}/references/stack-command-inference.md`:
find the evidence (entrypoints, test files, linter configs, `.github/workflows/`), prefer the
project's own environment (`.venv/bin/pytest`, not a bare `pytest`), **run the command to
verify it works**, then write it into the matching `- <key>:` line with `Edit`. Leave a key
blank only when the project genuinely has nothing to run for it, and say which and why.

Nothing here is overwritten by the script, so this step works the same on a re-run against an
already-initialised project whose commands were left blank.

Ask the user only when the evidence genuinely conflicts — two test runners and nothing in the
repo picking between them. Never ask for a command you could have found and verified.

## Step 4: Backend bootstrapping

- `pm_backend=local`: create `$CLAUDE_PROJECT_DIR/issues/` with a `.gitkeep` and a
`README.md` explaining the format (one markdown file per issue, frontmatter with
Expand All @@ -58,15 +85,17 @@ first, then `/flow:init` again.
collaborators should get the same stack setup. Only `.flow/local.md` is machine-private
(`private_globs` covers it), so machine-specific values belong there, not in `config.md`.

## Step 4: Report
## Step 5: Report

Relay the script's output verbatim — which files were `created` / `left untouched`, and
which stack commands were detected.
which stack commands were detected. Then add what Step 3 produced: each command you inferred,
how you verified it, and any key you deliberately left blank.

## Step 5: Verify the setup
## Step 6: Verify the setup

Run `/flow:health` straight away rather than suggesting it. Init is exactly the point where
a wrong answer is cheapest to fix, and health is read-only.
a wrong answer is cheapest to fix, and health only diagnoses — the one thing it repairs is a
blank `*_cmd`, which Step 3 should already have left nothing to do.

Report only what health flags. If everything passes, one line is enough — do not reprint the
whole table on top of the Step 4 report.
whole table on top of the Step 5 report.
2 changes: 1 addition & 1 deletion plugins/flow/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flow",
"version": "0.6.0",
"version": "0.7.0",
"private": true,
"scripts": {
"test": "node --test scripts/*.test.js hooks/*.test.js"
Expand Down
6 changes: 4 additions & 2 deletions plugins/flow/references/config-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ Examples (replace with your own — these are illustrative, not defaults):
- e.g. lint_cmd: `<your-linter>`
- e.g. test_cmd: `<your-test-runner>`
<!-- template-only:end -->
> `/flow:init` fills these in from your manifest. A blank value means "skip that step" —
> add one by hand if the detection missed it.
> `/flow:init` fills these in: from your manifest where it declares them, otherwise by
> working the command out from the repo and running it once to check. A blank value means
> "skip that step" — there is genuinely nothing to run for it. Re-run `/flow:init` if a blank
> should have a command; filling them in is its job, not yours.

---

Expand Down
80 changes: 80 additions & 0 deletions plugins/flow/references/stack-command-inference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Inferring stack commands

`scripts/init.js` fills `.flow/config.md`'s `*_cmd` keys from manifests it can read —
`package.json` scripts, `go.mod`, `Cargo.toml`, `pyproject.toml`, `requirements*.txt`, a
`Makefile`. That covers most repos. It does not cover a repo whose commands are real but
undeclared: a script-style Python project with a venv, a `tests/test_*.py`, and an `app.py`
has a test command and a dev command, just not written down anywhere a parser can find.

For those, init prints the keys it could not fill:

```
[flow init] Done. Stack commands still blank: dev_cmd, build_cmd, format_cmd
```

That line is addressed to the **agent**, not the user. Everything init leaves blank is
discoverable by looking at the repo for a minute, and asking the user to go and edit a
config file by hand is handing back homework that the agent can finish in one pass.

## The procedure

For each blank key: find the evidence, form the command, run it, write it down.

### 1. Find the evidence

| Key | Where the answer usually is |
| --- | --- |
| `dev_cmd` | An entrypoint (`app.py`, `main.py`, `manage.py`, `server.js`, `cmd/*/main.go`); a `Procfile`; a `docker-compose.yml` service command; a `Dockerfile` `CMD`; the README's "run it" section |
| `test_cmd` | `test_*.py` / `*_test.py` / `tests/`, `*.test.js`, `*_test.go`; a test runner in `requirements*.txt`; the test step of a CI workflow under `.github/workflows/` |
| `lint_cmd` | A linter config (`.flake8`, `.eslintrc*`, `ruff.toml`, `.golangci.yml`); a linter in the dependency list; the CI lint step |
| `typecheck_cmd` | `mypy.ini`, `tsconfig.json`, a type checker in the dependency list |
| `build_cmd` | A build config (`setup.py`, `Dockerfile`, `vite.config.*`), or nothing — plenty of projects have no build |
| `format_cmd` | A formatter in the dependency list or its config (`.prettierrc`, `[tool.black]`) |

CI workflows are the strongest evidence of all: they are commands the project already runs
in a clean checkout, and they are usually correct.

### 2. Prefer the project's own environment

A repo with a virtualenv wants `.venv/bin/pytest`, not a bare `pytest` resolved against
whatever happens to be on `PATH`. Same for `node_modules/.bin/`. Write the command the way
this project runs it, and keep the path relative to the project root.

### 3. Verify before writing

A command that was never run is a guess. Run each one and check it does what the key claims:

- `test_cmd`, `lint_cmd`, `typecheck_cmd`, `format_cmd` — run it. Real findings (failing
tests, lint errors) mean the command works; that is a pass. Only "command not found",
"no such file", "unknown option", or "no tests ran" mean the command is wrong.
- `build_cmd` — run it if it is cheap. If it is slow or writes artifacts, verify the tool
resolves (`<tool> --version`) and say in the report that the full build was not run.
- `dev_cmd` — never leave a server running. Check the entrypoint exists and the interpreter
resolves (`.venv/bin/python --version`, `node --version`), or start it and stop it once it
is listening. Record the port in `dev_port` while you are there.

Fix and re-run what fails. Do not write a command that did not survive this step.

### 4. Write it into `.flow/config.md`

Edit the `- <key>: <command>` lines in place. Do not rewrite the rest of the file — the
values around them are the user's.

Leave a key blank only when the project genuinely has nothing to run for it (no build step,
no formatter). A blank means "skip that step", so it must be a decision, not a shrug. Say
which keys you left blank and why.

### 5. Report

One block, in the init/health report:

```
Inferred and verified:
test_cmd: .venv/bin/pytest (23 passed)
dev_cmd: .venv/bin/python app.py (starts, listens on 5000)
Left blank: build_cmd (no build step), format_cmd (no formatter in requirements.txt)
```

Ask the user only when the evidence genuinely conflicts — two test runners, two entrypoints,
and nothing in the repo picks between them. "I could not be bothered to look" is not a
conflict.
Loading
Loading