-
Notifications
You must be signed in to change notification settings - Fork 0
flow: global storage, list command, and dynamic parameters #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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]`. | ||
| 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). | ||
|
skapoor8 marked this conversation as resolved.
|
||
|
|
||
| 3. Launch a **blocking** (foreground) subagent: | ||
|
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"} | ||
| ``` | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.