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
41 changes: 37 additions & 4 deletions docs/skills/flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,65 @@ End-to-end pipeline orchestration — chain multiple skill workflows into a name

### new — define a flow

Create a named pipeline with an ordered list of skill invocations; generates `flow.md` and per-step implementation files under `.codevoyant/flows/{name}/`.
Create a named pipeline with an ordered list of skill invocations; generates `flow.md` and per-step implementation files under `.codevoyant/flows/{name}/`. Add `--global` to store it under `~/.codevoyant/flows/` instead, making it reusable across every project.

```bash
/flow new auth-refactor \
"/dev explore how the auth middleware works" \
"/spec new refactor auth middleware" \
"/spec go"

# reusable across projects, with a run-time parameter:
/flow new ship \
"/spec new {{input}}" \
"/spec go" \
"/git commit" \
"/pr open" \
--global
```

Pass descriptions inline to skip interactive prompts within each step (e.g. `/spec new my-feature add dark mode`).

#### Parameters — dynamic input

Put `{{placeholders}}` in any step and supply values when you run the flow:

- Bare text after the name binds to `{{input}}` — `/flow go ship "add dark mode"`.
- `--set key=value` binds a named `{{key}}` — `/flow go ship --set env=staging`.
- Anything still unset is prompted for once.

Each step's key outputs (PR numbers, plan names, file paths) are captured and threaded forward as **flow context**, so later steps can use them automatically — e.g. `/pr open` produces a PR number that a later `/pr address` step picks up without you re-typing it. Flow context is persisted, so an interrupted flow resumes with it intact.

**Chaining interactive skills.** Steps run as subagents and can't prompt you directly, so write each step to run non-interactively — pass its input inline or as a `{{param}}` (give steps that need different values distinct params, not one shared `{{input}}`), and wire a consumer step to the artifact its producer wrote (e.g. `/dev explore` → `.codevoyant/explore/{slug}/` → `/spec new` plans from it). If a step still hits a decision it can't resolve, it escalates one question back to you (`NEEDS_INPUT`) and re-runs with your answer — a fallback, not the plan.

### go — execute a flow

Run all pending steps sequentially as blocking subagents. Resumes from the first incomplete step if re-run after interruption.
Run all pending steps sequentially as blocking subagents. Resumes from the first incomplete step if re-run after interruption. Resolves the flow locally first, then globally; pass `--global` to force the global copy.

```bash
/flow go auth-refactor # execute all pending steps
/flow go ship "add OAuth login" # bind free text to {{input}}
/flow go ship --set env=staging # bind a named parameter
```

A failing step stops the pipeline; re-run `/flow go` to resume from where it stopped.

### list — list flows

Show every saved flow across both scopes (local and global) with step counts, status, and parameters.

```bash
/flow list # local + global
/flow list --global # global only
```

### status — check flow status

Print the `flow.md` checklist with current step status.
Print the `flow.md` checklist with current step status, scope, and parameters.

```bash
/flow status auth-refactor # print checklist with step status
/flow status ship --global # inspect a global flow
```

### save — create a composite skill
Expand All @@ -46,9 +78,10 @@ Turn a completed flow into a reusable skill scaffolded via `/skill new`. The gen
```bash
/flow save auth-refactor --skill auth-refactor
/flow save auth-refactor --skill auth-refactor --desc "Explore, plan, and execute auth middleware refactor"
/flow save ship --skill ship --global # source flow lives in ~/.codevoyant/flows
```

The new skill is created at `skills/{skill-name}/SKILL.md`. Run `/skill critique {skill-name}` to audit it before shipping.
The new skill is created at `skills/{skill-name}/SKILL.md`. If the source flow has `{{parameters}}`, the generated skill forwards the text you pass it to `/flow go`. Run `/skill critique {skill-name}` to audit it before shipping.

### help — list commands

Expand Down
2 changes: 2 additions & 0 deletions docs/skills/pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ The body is written in a **terse, human, junior-dev-friendly voice** — short s

Read a PR/MR diff and generate AI-authored inline comments. Comments are terse (see the voice guide): one or two sentences — problem, then ask — with a code suggestion where it's clearer. Severities are `BLOCKING`, `CONSIDER`, `NOTE`.

Reviews evaluate the change against its **stated intent** first — does the diff actually deliver the PR/MR's purpose end-to-end (tracing the headline use case), not just whether the code is clean? A well-formed change that fails its intent is flagged `BLOCKING`.

Runs a **dedicated slop-detector pass** in parallel: a subagent whose only job is catching AI slop and unwanted agent-introduced change — unnecessary/out-of-scope edits, stochastic churn (random renames, reordering, reformatting), verbose boilerplate, dead/debug leftovers, dependency creep. A prevalent problem with agentic coding. Its findings are prefixed `Slop:`.

```bash
Expand Down
9 changes: 6 additions & 3 deletions skills/flow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,32 @@ Aliases:
"run" → go
"exec" → go
"start" → go
"ls" → list
"show" → status
"list" → status
"review" → status
"export" → save
"publish" → save

Dispatch to: references/workflows/{VERB}.md
```

The `--global` / `-g` flag (store or read a flow under `~/.codevoyant/flows` instead of the local `.codevoyant/flows`) is **not** a verb — pass it through unchanged; each workflow parses it via `references/flow-dir.md`.

## Workflow index

| Verb | File | Purpose |
| --- | --- | --- |
| new | `references/workflows/new.md` | Define a new flow (create flow.md + step files) |
| go | `references/workflows/go.md` | Execute pending steps sequentially as blocking subagents |
| list | `references/workflows/list.md` | List all flows (local and global) |
| status | `references/workflows/status.md` | Print flow.md checklist state |
| save | `references/workflows/save.md` | Turn a flow into a reusable composite skill via /skill new |
| help | `references/workflows/help.md` | Usage reference |

## Instructions

1. Extract VERB from the user's message (first non-flag positional argument after "flow").
2. Apply aliases (run/exec/start → go; show/list/review → status; export/publish → save).
2. Apply aliases (run/exec/start → go; ls → list; show/review → status; export/publish → save).
3. If VERB is empty or unrecognized, default to `help`.
4. Read and execute the corresponding workflow file from `references/workflows/{VERB}.md`.
5. Pass all remaining arguments to the workflow.
5. Pass all remaining arguments (including any `--global`/`-g` flag) to the workflow unchanged.
43 changes: 43 additions & 0 deletions skills/flow/references/flow-dir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Flow directory resolution (local vs global)

Shared logic for locating flows. Every flow workflow (`new`, `go`, `status`, `list`, `save`) resolves storage the same way.

## Scopes

- **Local** (default) — flows live under the current project: `.codevoyant/flows/{slug}/`
- **Global** — flows live under the user's home, reusable across every project: `~/.codevoyant/flows/{slug}/`

A flow is a directory `{slug}/` containing `flow.md` and `implementation/step-N.md` files, in either scope.

## The `--global` flag

`--global` (alias `-g`) selects the global scope. Parse it out of the arguments in every workflow **before** reading the flow name, and strip it from the positional args.

```bash
GLOBAL=false
case " $ARGS " in *" --global "*|*" -g "*) GLOBAL=true ;; esac
FLOW_ROOT="$( [ "$GLOBAL" = true ] && echo "$HOME/.codevoyant" || echo ".codevoyant" )"
FLOWS_DIR="$FLOW_ROOT/flows"
```

- `~` always means the real home directory — use `"$HOME"` in bash, never a literal `~` inside quotes.
- Create the global root on demand: `mkdir -p "$HOME/.codevoyant/flows"` (safe if it already exists).

## Resolving a flow by name (for `go`, `status`, `save`)

When the user names an existing flow to run/inspect, resolve which scope holds it:

1. **If `--global` was passed:** look only in `~/.codevoyant/flows/{slug}/`.
2. **Otherwise:** look in **local first** (`.codevoyant/flows/{slug}/`), then fall back to **global** (`~/.codevoyant/flows/{slug}/`).
3. If found in exactly one scope, use it. If found in **both** and `--global` was not given, prefer local but note: `ℹ Using local flow '{slug}' (a global flow with the same name also exists — pass --global to run that one).`
4. If found in neither: error `Flow '{slug}' not found (looked in local and global). Run /flow new {slug} first.`

Set `FLOW_DIR` to the resolved `{FLOWS_DIR}/{slug}/` and use it for the rest of the workflow.

## Creating a flow (for `new`)

`new` does not search — it writes to the scope selected by `--global`:

```
FLOW_DIR = {FLOWS_DIR}/{slug}/ # global if --global, else local
```
13 changes: 13 additions & 0 deletions skills/flow/references/flow-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,22 @@
## Metadata

- **Slug**: {slug}
- **Scope**: {local|global}
- **Created**: {timestamp}
- **Status**: {Active|Complete}

## Parameters

<!--
List every {{placeholder}} that appears in the steps below, one per line, so users
know what to supply at run time. `{{input}}` is the default parameter — bare text
passed to `/flow go <name> <text>` binds to it. Omit this section's list entries
(leave "_none_") if the flow has no placeholders.
-->

- `{{input}}` — {what the free-text argument means for this flow}
- `{{param-name}}` — {description}

## Steps

1. [ ] {step-1-command}
Expand Down
33 changes: 30 additions & 3 deletions skills/flow/references/step-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,39 @@
Flow: {flow-name}
Step: {N} of {total}

## Parameters

<!-- Resolved run-time values substituted before this step runs. Empty if the flow has no parameters. -->
{param-name} = {resolved-value}

## Flow context so far

<!--
Injected by `/flow go` at run time: a short, accumulating log of what earlier steps
produced (IDs, URLs, file paths, plan/branch names). Empty for step 1.
-->
{prior-step-handoffs, or "(nothing yet — this is the first step)"}

## Agent prompt

You are executing step {N} of the "{flow-name}" flow.
You are executing step {N} of the "{flow-name}" flow, running as a **subagent**. You CANNOT prompt the user — `AskUserQuestion` does not reach them from here.

Your task: {step-command}

{Any relevant context from prior steps — e.g., "Step N-1 produced output at .codevoyant/explore/..."}
**Inputs — use what the flow already gave you.** Treat the **Parameters** and **Flow context so far** above as ground truth: a PR number, plan name, branch, or file path there was produced by an earlier step or supplied by the user. Prefer them over asking or guessing. If the Flow context names an **artifact** a prior step produced (an exploration directory, a plan, a doc), **consume it** — do not start that work over. (E.g. if a prior `/dev explore` wrote `.codevoyant/explore/{slug}/`, point this step's skill at that exploration instead of re-researching.)

**Run the skill non-interactively.** Follow all rules of the skill, but pass inputs inline so it never stops to ask. Do NOT call `AskUserQuestion` — it will not reach the user.

**If you truly cannot proceed** — a decision is required that Parameters and Flow context don't cover and that has no safe, clearly-stated default — do NOT guess and do NOT block. Stop and end your report with exactly one line:

```
NEEDS_INPUT: {the single question the user must answer}
```

The flow (running on the main thread) will ask the user and re-run you with the answer added to Parameters. Use this sparingly — only for a decision that would change the outcome.

When you finish successfully, end your report with a single handoff line listing any values later steps may need — IDs, URLs, paths, names, **and any artifact paths you created** — so the flow can thread them forward:

Execute the skill command above. Follow all rules and conventions of that skill exactly as if the user had invoked it directly. Report clearly when done.
```
HANDOFF: {key=value; key=value; ...} (or "HANDOFF: none" if nothing to pass on)
```
70 changes: 51 additions & 19 deletions skills/flow/references/workflows/go.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,92 @@
# Workflow: flow go

Execute all pending (unchecked) steps of a named flow sequentially. Each step runs as a blocking foreground subagent. After each step completes, mark it `[x]` in `flow.md` before proceeding.
Execute all pending (unchecked) steps of a named flow sequentially. Each step runs as a blocking foreground subagent. Parameters (`{{placeholders}}`) are resolved from user input before running, and each step's key outputs are threaded forward into later steps as **flow context**. After each step completes, mark it `[x]` in `flow.md` before proceeding.

Steps run **non-interactively** — a subagent cannot prompt the user. When a step genuinely needs an answer it can't derive, it returns a `NEEDS_INPUT:` line and this orchestrator (on the main thread) asks the user and re-runs the step. This is how interactive skills chained in a flow still reach the human.

## Step 0: Parse arguments

```
--global / -g → read the flow from ~/.codevoyant/flows (see references/flow-dir.md)
--set key=value → bind a named parameter (repeatable, e.g. --set feature="add OAuth" --set env=staging)
FLOW_NAME = first non-flag positional arg (required)
FLOW_DIR = .codevoyant/flows/{FLOW_NAME}/
INPUT = all remaining bare (non-flag, non-name) positional text, joined with spaces → the {{input}} parameter
```

If `FLOW_NAME` is missing, error: "Usage: /flow go <name>. A flow name is required."
If `FLOW_NAME` is missing, error: "Usage: /flow go <name> [input text] [--set k=v] [--global]. A flow name is required."

Build the parameter map `PARAMS`:
- `input` = `INPUT` (the joined free text), if any
- each `--set key=value` → `PARAMS[key] = value`

## Step 0.5: Resolve the flow location

Resolve `FLOW_DIR` per `references/flow-dir.md` (local-first, then global; `--global` forces global only). If not found in any scope, error: "Flow '{FLOW_NAME}' not found (looked in local and global). Run /flow new {FLOW_NAME} first."

If `FLOW_DIR/flow.md` does not exist, error: "Flow '{FLOW_NAME}' not found. Run /flow new {FLOW_NAME} first."
## Step 1: Read flow.md and resolve parameters

## Step 1: Read flow.md
Read `FLOW_DIR/flow.md`. Parse the Steps checklist and the Parameters section.

Read `FLOW_DIR/flow.md` and parse the Steps checklist section.
**Resolve every `{{placeholder}}` used anywhere in the steps:**
1. Scan all step commands for `{{name}}` tokens; collect the unique set `NEEDED`.
2. For each token in `NEEDED`:
- If `PARAMS[name]` is already set (from `--set`, or `input` from free text) → use it.
- Else → **prompt the user once** for the value, using AskUserQuestion (free-text via Other): question `Value for {{name}}?`, header `Parameter`, and (for `{{input}}`) show the flow's Parameters description as help. Store the answer in `PARAMS[name]`.
Comment thread
skapoor8 marked this conversation as resolved.
3. Do not proceed with any unresolved token. If the user dismisses a prompt, abort: "Cancelled — {{name}} is required to run this flow."

Collect all lines matching the pattern `N. [ ] ...` (unchecked) in order. These are the **pending steps**.
Collect the pending steps: all lines matching `N. [ ] ...` (unchecked), in order. If none are pending, report "Flow '{FLOW_NAME}' is already complete." and exit.

If all steps are already `[x]` (no pending steps found): report "Flow '{FLOW_NAME}' is already complete." and exit.
Initialize the **flow context** accumulator `CONTEXT`. **On resume:** if `FLOW_DIR/context.md` exists (an earlier, interrupted run), load it into `CONTEXT` so the remaining steps keep the handoffs from steps already marked `[x]`. Otherwise start empty. (Without this, a resumed flow loses e.g. the PR number a completed `pr open` step produced.)

## Step 2: Execute steps sequentially

For each pending step in order:

1. Report: `▶ Step {N}: {step-command}`
1. **Substitute parameters** in the step command: replace every `{{name}}` with `PARAMS[name]`. Call the result `RESOLVED_COMMAND`. Report: `▶ Step {N}: {RESOLVED_COMMAND}`

2. Read `FLOW_DIR/implementation/step-N.md` to get the agent prompt.
2. Read `FLOW_DIR/implementation/step-N.md` to get the agent prompt. Prepare the prompt for this run by filling its injection points:
- Replace `{step-command}` occurrences with `RESOLVED_COMMAND` (substitute `{{placeholders}}` here too).
- Fill the **Parameters** section with the resolved `key = value` pairs (or "none").
- Fill the **Flow context so far** section with `CONTEXT` (or "(nothing yet — this is the first step)" when empty).
Comment thread
skapoor8 marked this conversation as resolved.

3. Launch a **blocking** (foreground) subagent:
Comment thread
skapoor8 marked this conversation as resolved.
```
Agent(
subagent_type: general-purpose,
run_in_background: false,
description: "Flow step {N}: {step-command}",
prompt: {full contents of step-N.md}
description: "Flow step {N}: {RESOLVED_COMMAND}",
prompt: {filled contents of step-N.md}
)
```
Wait for the subagent to return before continuing.
Wait for the subagent to return. It runs the skill **non-interactively** and, if it can't resolve a required decision from Parameters/Flow context, returns a `NEEDS_INPUT:` line rather than guessing or blocking.

4. After the subagent returns: update `FLOW_DIR/flow.md` — change the step's `[ ]` to `[x]`:
4. **Handle NEEDS_INPUT (interactive escalation).** If the subagent's report ends with `NEEDS_INPUT: {question}`:
a. You are on the main thread — ask the user that `{question}` with **AskUserQuestion** (free-text via Other).
b. Add the answer to `PARAMS` (so later steps can reuse it) and note it in `CONTEXT`, then **re-run this step** from sub-step 2 with the answer now available.
c. Cap at 3 escalations per step; if the step still returns `NEEDS_INPUT` after that, treat it as a step failure (below).
This is what lets an interactive skill's mid-run question (e.g. `/dev explore` asking "generate proposals?", or `/spec new` needing to pick an exploration) reach the user even though the step ran in a subagent.

5. **Capture the handoff.** Read the subagent's returned report. Extract the `HANDOFF:` line if present; otherwise summarize the key outputs (IDs, URLs, paths, names) in one line yourself. Append to `CONTEXT`:
```
N. [x] {step-command}
[step {N} · {RESOLVED_COMMAND}] → {handoff or one-line summary}
```
Also update `{Status}` in the Metadata section to `Active` (leave as Active until all steps complete).
Keep `CONTEXT` terse — it is injected into every later step, so it must stay a short bulleted log, not full transcripts. **Persist it:** write the accumulated `CONTEXT` to `FLOW_DIR/context.md` so an interrupted flow can resume with it (see Step 1).

6. Update `FLOW_DIR/flow.md` — change this step's `[ ]` to `[x]` (keep the original `{{placeholder}}` text in flow.md; only the run used resolved values). Leave `Status` as `Active` until all steps complete.

5. Report: `✓ Step {N} complete.`
7. Report: `✓ Step {N} complete.`

6. Proceed to next pending step.
8. Proceed to next pending step.

**On step failure:** if a subagent reports it could not complete (blocking error), stop the loop, leave the step unchecked, and report which step failed and why. `CONTEXT` is already persisted to `FLOW_DIR/context.md`, so re-running `/flow go` resumes from this step with prior context intact. Do not run later steps — they likely depend on this one's context.

## Step 3: Final report

After all steps are complete, update `{Status}` in `FLOW_DIR/flow.md` to `Complete`.
After all steps are complete, update `Status` in `FLOW_DIR/flow.md` to `Complete` and remove `FLOW_DIR/context.md` (a fresh run starts clean).

Report:
```
✅ Flow "{FLOW_NAME}" complete. All {N} steps finished.

Parameters used: {key=value list, or "none"}
```
Loading
Loading