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
75 changes: 75 additions & 0 deletions .cursor/commands/iflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Smart dispatcher for the issue-flow lifecycle

`/iflow` inspects the state of the focus issue and **dispatches** to the next logical command in the linear lifecycle — `/issue-init`, `/issue-plan`, `/issue-start`, or `/issue-close`. It never does work those commands don't already do; it just picks the right one for you.

Off-path commands (`/issue-pause`, `/issue-cleanup`, `/issue-yolo`) are **not** auto-dispatched. Invoke them directly when you need them.

## Input

Optional free-form text after the command. `/iflow` forwards the raw trailing text to whichever command it dispatches to. Examples:

- `/iflow` — dispatch with no extra args.
- `/iflow 42` — forwards `42` (useful when state resolves to `/issue-init`).
- `/iflow bump minor` — forwards `bump minor` (meaningful when state resolves to `/issue-close`; otherwise the downstream command will ignore or ask).
- `/iflow stick to the existing logger` — forwards the hint (useful when state resolves to `/issue-plan` or `/issue-start`).

## Steps

0. **Resolve the focus issue number `N`.**
- Run `git branch --show-current`. If it matches `^(\d+)-.+`, the leading digits are the **authoritative** `N`.
- List `issue<n>_*` groups in `.issueflows/01-current-issues/`, and also check `.issueflows/02-partly-solved-issues/` and `.issueflows/03-solved-issues/` for archived groups matching `N`.
- Pick `N` using this precedence:
1. **Branch-derived `N` wins**, regardless of whether a group for `N` exists in `01-current-issues/`. State A (dispatch `/issue-init`) will simply apply when no `issue<N>_*` files are present yet. If `issue<N>_*` is archived under `02-partly-solved-issues/` or `03-solved-issues/`, warn the user that `/issue-init`'s archived-issue guard will ask for an explicit confirmation before re-opening.
2. Else if exactly one group exists in `01-current-issues/`, use that `N`.
3. Else if there are **no** groups at all in `01-current-issues/` (and no branch-derived `N`), fall through to dispatch `/issue-init` (state A); `/issue-init` itself will ask for a number.
4. Else (no branch-derived `N`, multiple groups in `01-current-issues/`), **stop** and ask the user which issue to act on. Do not guess.

1. **Detect state and choose the dispatch target.** In priority order:

| State | Condition | Dispatch to | Reason to report |
|-------|-----------|-------------|------------------|
| A | No `issue<N>_original.md` for the focus issue (or no focus issue at all) | `/issue-init` | "no `*_original.md` yet" |
| B | `issue<N>_original.md` exists, no `issue<N>_plan.md` | `/issue-plan` | "no plan file yet" |
| C | Plan exists, and either no status file or the status file does **not** contain `- [x] Done` | `/issue-start` | "plan is confirmed but status is not `- [x] Done`" |
| D | Status file contains `- [x] Done` (case-insensitive on `done`) | `/issue-close` | "status marks the issue `- [x] Done`" |

Use the first row whose condition matches. Never dispatch to more than one command in a single `/iflow` run.

2. **Announce and dispatch.** Before running the downstream command, print one line explaining the decision, for example:

```
/iflow -> /issue-plan (issue #42: no plan file yet)
```

Then run the exact same logic as the chosen command (follow `.cursor/commands/<command>.md`), forwarding the user's trailing text verbatim. The downstream command keeps all its own checkpoints (plan confirmation in `/issue-plan`, `/issue-start`'s soft stop when the plan is missing, `/issue-close`'s unrelated-changes prompt, etc.).

3. **Report.** Summary should include:
- the focus issue number and how it was resolved (branch-derived / only group / user-specified)
- which command was dispatched to and why
- the downstream command's own output
- a one-line hint when an **off-path** command is the natural next step, e.g.:
- state **D** + PR likely merged → "after the PR merges, run `/issue-cleanup`"
- mid-stream context switch needed → "to park this work, run `/issue-pause`"
- tiny fix you want in one shot → "consider `/issue-yolo` next time"

## Constraints

- `/iflow` never skips a downstream command's own prompts. If the downstream step asks a question, surface it normally.
- `/iflow` never auto-dispatches to `/issue-pause`, `/issue-cleanup`, or `/issue-yolo`. Those are explicit choices only.
- If the focus issue cannot be resolved (multiple active issues, branch ambiguous), stop and ask. Do not pick one silently.
- Do not modify files beyond what the downstream command would normally modify. `/iflow` itself writes nothing.

## Example invocations

- `/iflow` on a fresh branch `42-fix-login` with no `issueflows` files yet
-> dispatches to `/issue-init` which infers `#42` from the branch.
- `/iflow` on branch `42-fix-login` while unrelated groups (`issue99_*`, `issue100_*`) still sit in `01-current-issues/`
-> branch-derived `N=42` wins; dispatches to `/issue-init` (state A) which captures #42 and archives the other groups per its own sweep.
- `/iflow` after `/issue-init 42` has run and `issue42_original.md` exists, but there is no `issue42_plan.md` yet
-> dispatches to `/issue-plan`.
- `/iflow` after `/issue-plan` has written and the user confirmed `issue42_plan.md`, with no `issue42_status.md` or an unchecked `- [ ] Done`
-> dispatches to `/issue-start`.
- `/iflow bump patch` after implementation is done and `issue42_status.md` contains `- [x] Done`
-> dispatches to `/issue-close bump patch`.
- `/iflow` on branch `99-partial-work` after the issue was paused (files now under `02-partly-solved-issues/`)
-> branch-derived `N=99` wins; dispatches to `/issue-init` (state A since `01-` has no `issue99_*`). `/iflow` warns up front that `/issue-init`'s archived-issue guard will trigger and ask for explicit confirmation before restoring the group to `01-current-issues/`.
60 changes: 60 additions & 0 deletions .cursor/commands/issue-cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Post-merge branch and folder cleanup

Run this after a PR has been merged (typically the one opened by `/issue-close`). It detects the merge, switches back to the default branch, and — with a **single consolidated confirm** — deletes every local branch whose commits are already in the default branch (including squash-merged branches).

`/issue-cleanup` is the only command in the workflow that touches local branches destructively (via `git branch -d`, never `-D`).

## Input

Optional free-form text after the command. Examples:

- **No extra text** — detect the current branch's PR, clean that up, plus any other local branches already merged into the default.
- A branch name — clean up that specific branch instead of the current one (e.g. `/issue-cleanup 42-fix-login`).

## Steps

1. **Detect the default branch.**
- Prefer `gh repo view --json defaultBranchRef -q .defaultBranchRef.name`.
- Else `git symbolic-ref --quiet --short refs/remotes/origin/HEAD | sed 's|^origin/||'`.
- Else fall back to `main`.

2. **Identify the branch to clean.**
- If the user passed a branch name, use it.
- Else use the current branch (`git branch --show-current`). If the current branch **is** the default, skip to step 5 (group sweep only); there is no issue branch to merge-check.

3. **Check PR / merge state for the issue branch.**
- Prefer `gh pr view <branch> --json state,mergedAt,mergeCommit,headRefName`.
- If `gh` is unavailable, approximate with `git fetch --prune` then `git cherry origin/<default> <branch>`. All commits prefixed `-` means the branch is effectively merged (including squash-merges).
- **If the branch is not merged:**
- Remind the user that the working copy is still on the issue branch (not the default). Suggest `git switch <default>` before starting unrelated work.
- Tell them to re-run `/issue-cleanup` after the PR merges.
- **Stop.** Do not delete anything.

4. **Consolidated confirm for post-merge cleanup.** Gather the full action list into a single yes/no prompt:
- `git switch <default>`
- `git pull --ff-only`
- `git fetch --prune`
- List **every** local branch whose tip is already reachable from `origin/<default>` (use `git for-each-ref --format='%(refname:short)' refs/heads/` combined with `git cherry origin/<default> <branch>` to catch squash-merges). Present the list explicitly and ask **once** before running `git branch -d <branch>` for each.
- Never use `-D` automatically. If `-d` refuses (unmerged changes), report that branch and leave it alone.

5. **Optional folder sweep** (safe; no destructive git).
- Group files in `.issueflows/01-current-issues/` by issue number (`issue<N>_*`).
- For each group whose status file contains `- [x] Done` (case-insensitive on `done`), move the group to `.issueflows/03-solved-issues/`.
- Groups without a checked `Done` stay put — do not route them to `.issueflows/02-partly-solved-issues/` from `/issue-cleanup`. That is `/issue-pause`'s job and requires a human decision.

## Output

Report:
- default branch resolved
- PR/merge status of the target branch
- post-merge commands run (if any)
- list of local branches deleted (with `-d` output per branch)
- list of branches skipped because `-d` refused, with a one-line reason
- folder sweep summary (`issue<N>` → `03-solved-issues/`, or "nothing to sweep")

## Constraints

- Never use `git branch -D` or `git push --force`.
- Never delete the default branch.
- If anything is ambiguous (detached HEAD, multiple remotes, missing tracking info), report and stop rather than guessing.
- Do not open or update PRs. Do not bump versions. Those belong to `/issue-close`.
20 changes: 6 additions & 14 deletions .cursor/commands/issue-close.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Close out the current issue

Run this when implementation is done and you are ready to land the work.
Run this when implementation is done and you are ready to land the work (commit, push, PR). Post-merge branch hygiene now lives in a separate command, `/issue-cleanup`, which you run **after** the PR is merged.

## Input

Expand All @@ -10,7 +10,7 @@ Optional text after the command (same line). Examples:
- **`bump`** or **`patch`** — bump the **patch** semver (e.g. `1.2.0` → `1.2.1`).
- **`bump minor`** or **`minor`** — bump **minor**.
- **`bump major`** or **`major`** — bump **major**.
- **Free text** — if it clearly asks to release or bump the version, infer `patch`, `minor`, or `major` from wording (e.g. bugfix release → patch); if unclear, ask once.
- **Free text** — if it clearly asks to release or bump the version, infer `patch`, `minor`, or `major` from wording (e.g. "bugfix release" → patch); if unclear, ask once.

Other optional notes still apply: branch name, PR title, draft PR, skip issue doc update, commit all changes, etc.

Expand Down Expand Up @@ -43,19 +43,11 @@ Other optional notes still apply: branch name, PR title, draft PR, skip issue do
- Open a PR against the default branch (e.g. `main`).
- Describe the change, how to test it, and link the GitHub issue (e.g. `Closes #123` or `Refs #123` in the PR body).

7. **Post-merge branch cleanup**
- Detect the default branch (prefer `gh repo view --json defaultBranchRef -q .defaultBranchRef.name`; fall back to `git symbolic-ref --quiet --short refs/remotes/origin/HEAD | sed 's|^origin/||'`, else `main`).
- Detect merge status of this PR with `gh pr view <branch> --json state,mergedAt,mergeCommit,headRefName`. If `gh` is unavailable, approximate with `git fetch --prune` followed by `git rev-list <branch>..origin/<default>` and `git cherry origin/<default> <branch>` (all commits marked `-` means squash-merged).
- **If the PR is merged:**
- Ask once whether to run the standard post-merge cleanup. On yes, run: `git switch <default> && git pull --ff-only && git fetch --prune`. These are non-destructive because the issue branch is already merged.
- List local branches whose tip is already reachable from `origin/<default>` (including squash-merged ones detected via `git cherry`). Present them as a single group and ask **once** (one consolidated yes/no listing every branch) before running `git branch -d <branch>` for each. Never use `-D` automatically; if `-d` refuses, report the branch and stop touching it.
- **If the PR is not yet merged:**
- Remind the user that the working copy is still on the issue branch, not the default branch. Suggest `git switch <default>` before starting any unrelated work so new changes don't accidentally land on the issue branch. Tell them to re-run `/issue-close` after the PR merges so the post-merge cleanup actually runs.

8. **After review**
7. **After review**
- Address feedback, push updates, and merge when approved and CI is green.
- Re-run `/issue-close` after the merge to pick up the post-merge cleanup in step 7.
- Remind the user that the working copy is still on the issue branch, not the default. Suggest `git switch <default>` before starting unrelated work so new changes don't accidentally land on the issue branch.
- Once the PR is merged, run **`/issue-cleanup`** to switch back to the default branch, `git pull --ff-only`, `git fetch --prune`, and delete local branches whose commits are already in the default branch (single consolidated confirm). `/issue-close` no longer does post-merge cleanup itself.

## Output

Summarize what was committed, pushed, and the PR URL (or next step if blocked).
Summarize what was committed, pushed, and the PR URL (or next step if blocked). Remind the user to run `/issue-cleanup` after the PR merges.
57 changes: 57 additions & 0 deletions .cursor/commands/issue-pause.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Pause work on the current issue

Use this when you need to switch context without closing the issue. The command records where things stand, archives the issue group into `.issueflows/02-partly-solved-issues/`, and (optionally) parks your working tree safely.

## Input

Optional free-form text after the command. Examples:

- **No extra text** — update status from what is visible in the diff and current TODOs.
- Short note — used as the "Remaining work" text verbatim (e.g. `/issue-pause waiting on design input from @user`).

## Steps

0. **Locate the focus issue.** In `.issueflows/01-current-issues/`, identify the `issue<N>_*` group (original, plan, status). If multiple groups exist and the focus is ambiguous, ask which one to pause. If there is none, **stop** and tell the user there is nothing to pause.

1. **Update the status file.** Create or update `issue<N>_status.md` in `.issueflows/01-current-issues/` with:

```markdown
# Status for issue #<N>: <title>

- [ ] Done

## Done so far
- Short bullets of what has landed or been tried.

## Remaining work
- Explicit next steps so a future you (or someone else) can resume.

## Paused on
<date, branch name, and any blockers — e.g. waiting on external input, blocked on another PR>.
```

Keep any earlier content the user wrote; only add / update the sections above. The `- [ ] Done` checkbox **must** remain unchecked — a pause is not a close.

2. **Move the issue group.** Move every `issue<N>_*` file from `.issueflows/01-current-issues/` to `.issueflows/02-partly-solved-issues/`. Report the moves.

3. **Working-tree guard.** Run `git status --porcelain`. Report what is dirty. Then offer, as **one** consolidated prompt, up to three actions and let the user pick any combination:
- **WIP commit** — stage all tracked changes and commit with a message like `WIP: pause issue #<N> — <short note>`. Never silently include untracked files; list them and ask.
- **Switch to default branch** — detect the default branch (prefer `gh repo view --json defaultBranchRef -q .defaultBranchRef.name`, else `git symbolic-ref --quiet --short refs/remotes/origin/HEAD`, else `main`) and run `git switch <default>` (only after the WIP commit if the tree is dirty, to avoid `git switch` refusing).
- **Stay put** — leave branch and working tree untouched.

4. **Branch hygiene note (non-destructive).** Tell the user the issue branch (typically `<N>-<slug>`) is still on the remote / local list. `/issue-pause` never deletes branches — resume with `/issue-init <N>` (or just check out the branch) when ready to continue. The matching folder is now `.issueflows/02-partly-solved-issues/`.

## Output

Report:
- status file path updated
- issue group moves (source → destination)
- working-tree actions taken (WIP commit SHA, branch switched to, or "left as-is")
- next step hint (e.g. "to resume: `/issue-init <N>` will pull the group back into `.issueflows/01-current-issues/` after confirming the archived-issue guard")

## Constraints

- The focus issue's `- [ ] Done` checkbox must stay unchecked. `/issue-pause` is **not** `/issue-close`.
- Do not delete branches. Do not `git reset` or `git stash drop`.
- Do not open a PR. Do not bump the version. Those are `/issue-close`.
- Do not run tests. Pausing should be quick.
Loading
Loading