Skip to content
Development
Command

/plan-workflow

PREVIEW (experimental) - autonomous /lets:plan via a Dynamic Workflow. Shipped standalone to dogfood across projects and gather edits before folding the chain into native /lets:plan.

From plugin
lets-workflow
1622 skills15 agents22 commands
Install
$ npx -y skills add restarter/lets-workflow --agent claude-code

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/plan-workflow

Context preview

What this command does when you run it.

PREVIEW (experimental) - autonomous /lets:plan via a Dynamic Workflow. Shipped standalone to dogfood across projects and gather edits before folding the chain into native /lets:plan.

Command definition

plan-workflow.md
description: PREVIEW (experimental) - autonomous /lets:plan via a Dynamic Workflow. Shipped standalone to dogfood across projects and gather edits before folding the chain into native /lets:plan.
argument-hint: "[goal] [--fast]"

Autonomous Plan (PREVIEW workflow)

**PREVIEW / experimental.** Shipped standalone so the autonomous-planning Dynamic Workflow can be dogfooded across projects and gather edits before it's folded into native `/lets:plan --workflow` (tracked in lets-jsw00). The reusable artifact is `skills/plan-workflow/plan.workflow.js`; this command is a thin dispatcher. The interactive native `/lets:plan` is untouched. Expect rough edges - report them.

Autonomous whole-command planning: you give a GOAL + a RUBRIC up front; the workflow explores, proposes approaches, architects each, judges them against your rubric, evaluates the winner, and writes a plan - all off-context. You approve the plan at the end. **Steer-by-rubric + approve-at-end**, not gate-each-step.

> **IMPORTANT:** Deferred tools (e.g. `AskUserQuestion`) must be loaded and called as specified.

Step 0: Resolve & claim (spawn entry)

If the argument is an explicit **task id** - a single token matching the ACTIVE tracker's id shape per the **detect-task** convention (beads: `<prefix>-<alphanum>[.N]`; a numeric-id tracker: a pure-numeric id; do NOT hardcode the beads regex on a non-beads project) - rather than a free-text goal - as the autonomous pipeline launches it (`/lets:plan-workflow <id>` into a fresh worktree) - resolve-and-claim per the **detect-task** convention: treat the id as authoritative and, if the tracker's `show` reports `<id>` not `in_progress`, `Skill(skill: "lets:take-task", args: "<id>")` to claim it (the spawn-time entry claim is AUTO-MODE-exempt - see `detect-task/SKILL.md`). Then derive the GOAL from the task title + description for Step 1. A free-text goal argument skips this step (no claim).

Pipeline-state marker (spawn-claimed runs only)

When Step 0 spawn-claimed a task (the autonomous pipeline launched this), record the phase in a **per-task** marker so N parallel worktrees don't collide and the gate-notify can tell autonomous from interactive. **Per-task filename** (NOT a shared `pipeline-state` — `.lets/` is a symlink across worktrees): `.lets/cache/pipeline-state-<task-id>`, line `<task-id>|<phase>|<iso>`, phase ∈ `planning | gate-clarify | gate-approve | executing | blocked | done`. Helper:

LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel); mkdir -p "$LETS_PROJECT_ROOT/.lets/cache"
printf '%s|%s|%s\n' "{TASK_ID}" "{PHASE}" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$LETS_PROJECT_ROOT/.lets/cache/pipeline-state-{TASK_ID}"

plan-workflow writes `planning` at Step 3 (workflow launch), `gate-clarify` at GATE 1 (if clarify is needed), `gate-approve` at Step 4 (plan saved). An interactive (free-text-goal) run writes no marker → no gate notifications. **Keep in sync:** the same contract + helper live in `execute.md` (which writes `executing`/`blocked`/`done`).

Gate notifications (authoritative)

At a human gate, surface to the operator through the **active launcher** (`$LETS_LAUNCHER`) — **marker-gated** so only autonomous (spawn-claimed) runs notify; interactive runs have no marker and stay quiet. `lets notify` dispatches on `LETS_LAUNCHER` (cmux → cmux workspace, tmux → attached tmux clients, terminal → no-op) — never name a launcher in the snippet. Resolve the target by the worktree path:

LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel)
if [ -f "$LETS_PROJECT_ROOT/.lets/cache/pipeline-state-{TASK_ID}" ]; then
  lets notify --cwd "$LETS_PROJECT_ROOT" --title '{gate title}' --body '{gate body}' --json 2>/dev/null || true
fi

**Quoting:** the substituted values (`{gate title}`, `{gate body}` — the latter carries the user-authored task title) go in **single quotes**, so a title containing `$(...)`, backticks, or `$VAR` is a literal, not shell-expanded, before it reaches the injection-safe CLI. Only `$LETS_PROJECT_ROOT` stays double-quoted (it's a real shell var). If a substituted value contains a literal `'`, the orchestrator escapes it (`'\''`) when building the command. Degrades silently (`ok=true, notified=false`) when the launcher is `terminal`, unknown, its binary is absent, or nothing matches (tmux with no client attached → `no_client`); `|| true` covers the non-unix stub. The notification is **best-effort surfacing** — the gate also halts visibly in-band; never rely on it alone. Gates: **GATE 1** "Plan needs your answers" (clarify), **GATE 2** "Plan ready to approve". **Keep in sync:** `execute.md` reuses this block for its execute-blocked notify.

Step 1: Goal + Rubric

Parse the goal from the argument (or, for a spawn-claimed task, from its title + description; else ask "What are we planning?"). Strip a `--fast` token anywhere in the argument if present (sets lean/fast mode for this run); the remainder is the goal OR the task id. The strip happens at INITIAL argument parsing - BEFORE Step 0's task-id pattern match - so `/lets:plan-workflow <task-id> --fast` (the natural autonomous-pipeline invocation) still resolve-and-claims: only the flag-stripped remainder is tested against the active tracker's id shape (Step 0). Mirror how native `/lets:plan` strips `--fast` - but note the two flags mean DIFFERENT things (see `## Fast mode` below).

Gather the **RUBRIC** - the steering criteria that REPLACE the interactive picks of native `/lets:plan`. Ask the user (free text, or a short list):

  • What does "good" look like here? (priority order: simplicity / performance / consistency / minimal-blast-radius / ...)
  • Hard constraints (must-not-break, must-not-touch, compatibility).
  • Scope boundaries (explicitly OUT).
  • Success criteria.

**Do not proceed without a rubric** - it is the only steering signal the autonomous run receives. Echo the rubric back for confirmation before launching.

**GATE 1 (spawn-claimed runs - the bounded up-front clarify

Read more
Ships withlets-workflow

A development workflow plugin for Claude Code Stop babysitting your AI. Start shipping with it.

Get the whole plugin, auto-invoked
Stats
16
Stars
1
Views
3
Forks
Active
Maintenance
Go
Language
MIT
License
3d ago
Last commit
5mo ago
Created

Repo: restarter/lets-workflow