| name | nazgul:start |
| description | Start or resume a Nazgul autonomous development loop. Use when user says "start nazgul", "run nazgul", "begin development", "resume the loop", or passes an objective for new work. Auto-detects project state — no arguments needed. |
| argument-hint | ["objective"] [--afk|--yolo|--hitl] [--max N] [--task-pr] [--conductor] |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep, Task, ToolSearch |
| metadata | {"author":"Jose Mejia","version":"2.10.0"} |
Nazgul Start
Examples
/nazgul:start — Auto-detect project state and resume or begin work
/nazgul:start "add user authentication" — Start a new objective
/nazgul:start --afk --max 20 — Run autonomously for up to 20 iterations
/nazgul:start --yolo — Full autonomous mode with no permission prompts
/nazgul:start --yolo --task-pr — YOLO mode with stacked per-task PRs
/nazgul:start --conductor — Opt into the Conductor execution engine (default: sequential); composes with any mode flag, e.g. --conductor --afk
Arguments
$ARGUMENTS
Current Project State
- Config: !
cat nazgul/config.json 2>/dev/null || echo "NOT_INITIALIZED"
- Stored objective: !
jq -r '.objective // "none"' nazgul/config.json 2>/dev/null || echo "none"
- Discovery: !
cat nazgul/context/discovery-summary.md 2>/dev/null || echo "NOT_RUN"
- Project spec: !
cat nazgul/context/project-spec.md 2>/dev/null | head -3 || echo "NONE"
- Classification: !
cat nazgul/context/project-classification.md 2>/dev/null | head -5 || echo "NOT_CLASSIFIED"
- Docs generated: !
ls nazgul/docs/*.md 2>/dev/null | wc -l | tr -d ' '
- Active tasks: !
grep -rl 'Status.*\(READY\|IN_PROGRESS\|IN_REVIEW\|IMPLEMENTED\|CHANGES_REQUESTED\)' nazgul/tasks/TASK-*.md 2>/dev/null | wc -l | tr -d ' '
- Done tasks: !
grep -rl 'Status.*DONE' nazgul/tasks/TASK-*.md 2>/dev/null | wc -l | tr -d ' '
- Total tasks: !
ls nazgul/tasks/TASK-*.md 2>/dev/null | wc -l | tr -d ' '
- Active reviewers: !
ls .claude/agents/generated/ 2>/dev/null || echo "No reviewers generated"
- Current plan: !
head -20 nazgul/plan.md 2>/dev/null || echo "No plan yet"
- Recovery Pointer: !
sed -n '/^## Recovery Pointer/,/^## /p' nazgul/plan.md 2>/dev/null | head -7 || echo "none"
- TODOs in codebase: !
grep -rn 'TODO\|FIXME\|HACK\|XXX' --include='*.ts' --include='*.js' --include='*.py' --include='*.rb' --include='*.go' --include='*.rs' --include='*.java' --include='*.md' . 2>/dev/null | head -10 || echo "none"
- Test context: !
cat nazgul/context/test-strategy.md 2>/dev/null | head -5 || echo "none"
Instructions
Format all output per references/ui-brand.md — use stage banners, status symbols, spawning indicators, and display patterns defined there.
Parse Arguments
$ARGUMENTS may contain:
- An objective string (optional — override for new work)
- Flags:
--afk, --hitl, --max N, --yolo, --task-pr, --continue, --conductor
- Or nothing at all (smart mode — this is the default)
YOLO Mode Pre-flight (--yolo)
Runs after Apply Flags / before Smart State Detection. The afk.enabled/afk.yolo/afk.task_pr config writes are already done by Apply Flags — do not repeat them here.
If --yolo was passed (mode is now afk + afk.yolo true via Apply Flags), verify the session is in a non-prompting permission mode. There is no API to read the active mode, so probe it:
- Try running a quick Bash command — if no permission prompt fires, we're good. (This holds under either
--permission-mode auto OR --dangerously-skip-permissions; both skip routine prompts.)
- If permissions ARE being prompted, STOP and tell the user:
YOLO needs a non-prompting permission mode. Restart with ONE of:
claude --permission-mode auto # recommended — autonomous, but a background
# safety classifier still blocks dangerous actions
# (curl|bash, force-push to main, prod deploys, …)
claude --dangerously-skip-permissions # blunt bypass — no safety checks; isolated VM/container only
Then re-run: /nazgul:start --yolo --max N
Prefer --permission-mode auto (requires Claude Code v2.1.83+ and Opus 4.6+/Sonnet 4.6); fall back to --dangerously-skip-permissions only in a throwaway sandbox.
- Once confirmed, proceed with full autonomous mode — no pauses, no human gates. (Under
auto, the classifier may still block a genuinely dangerous action — surface that to the user rather than retrying blindly.)
Model Selection
Read nazgul/config.json → models to determine which model to assign each pipeline agent. When delegating via the Agent tool, pass the model parameter:
| Pipeline Agent | Config Key | Default |
|---|
| Discovery | models.discovery | sonnet |
| Doc Generator | models.docs | sonnet |
| Planner | models.planning | opus |
| Implementer | models.implementation | sonnet |
| Review Gate | models.review_orchestrator | sonnet |
| Conductor | models.conductor | sonnet |
If the models section is missing from config.json, use "sonnet" as the fallback for all agents.
Fast Mode: If models.fast_mode_implementation is true, implementation and specialist agents use fast mode for ~2.5x speed improvement. This trades higher token cost for faster iteration cycles — useful for large objectives with many tasks.
Session Naming
When launching Nazgul, use session naming for identification:
- Launch with:
claude -n "nazgul-<feat_display_id>" (e.g., claude -n "nazgul-FEAT-003")
- Agent Teams sessions are auto-named by the team-orchestrator:
nazgul-impl-TASK-NNN, nazgul-review-TASK-NNN
Apply Flags (MANDATORY — runs on every path, before state detection)
Persist the CLI flags to config via the tested helper, so every mode-gated branch below reads a correct config.mode:
[ -f nazgul/config.json ] && "${CLAUDE_PLUGIN_ROOT}/scripts/apply-start-flags.sh" nazgul/config.json "$ARGUMENTS"
This sets mode (--yolo/--afk → afk, --hitl → hitl), afk.enabled, afk.yolo, afk.task_pr, max_iterations (--max N, positive integer), and execution.engine (--conductor → "conductor"; absent leaves it at its default "sequential"). --hitl wins if combined with --afk/--yolo; --conductor is orthogonal to mode and composes with any of them. Do NOT separately hand-edit these fields from flags anywhere else in this skill — this helper is the single source of truth.
Resolve Run Mode (MANDATORY — before state detection)
Pre-load: run ToolSearch with query select:AskUserQuestion (the prompt tool is deferred by default).
YOLO confirmation gate (shared): YOLO must NEVER reach execution without an interactive YES. Whenever a path resolves to YOLO via a flag or default_mode (NOT via the user actively selecting "YOLO" in the null→ask menu — that selection IS the consent), fire this gate:
AskUserQuestion header "YOLO", question "Run this objective in YOLO — no permission prompts, auto-commit?", options:
- "Yes — full autonomous, no prompts" → proceed in YOLO (mode already applied as
--yolo).
- "No — switch to HITL" → apply HITL explicitly:
[ -f nazgul/config.json ] && "${CLAUDE_PLUGIN_ROOT}/scripts/apply-start-flags.sh" nazgul/config.json "--hitl"
Determine the run mode:
- If
$ARGUMENTS contained an explicit mode flag, Apply Flags already set the mode:
--afk or --hitl → mode is applied — skip the rest of this section.
--yolo → mode is now afk + afk.yolo, BUT you must still fire the YOLO confirmation gate above. On No, it re-applies HITL via the helper. Then continue past this section.
- Otherwise (no explicit mode flag) read
nazgul/config.json → default_mode:
"hitl" → apply with no prompt: [ -f nazgul/config.json ] && "${CLAUDE_PLUGIN_ROOT}/scripts/apply-start-flags.sh" nazgul/config.json "--hitl"
"afk" → apply with no prompt: [ -f nazgul/config.json ] && "${CLAUDE_PLUGIN_ROOT}/scripts/apply-start-flags.sh" nazgul/config.json "--afk"
"yolo" → apply it ([ -f nazgul/config.json ] && "${CLAUDE_PLUGIN_ROOT}/scripts/apply-start-flags.sh" nazgul/config.json "--yolo"), then fire the YOLO confirmation gate above. On No, it re-applies HITL via the helper.
null/unset → ask: AskUserQuestion header "Mode", question "How should Nazgul run this objective?", options:
- "HITL — review each step" →
--hitl
- "AFK — autonomous, pauses on risky decisions" →
--afk
- "YOLO — fully autonomous, no permission prompts" →
--yolo
The user selecting "YOLO" here IS the consent — do NOT fire the confirmation gate again. Apply the choice via [ -f nazgul/config.json ] && "${CLAUDE_PLUGIN_ROOT}/scripts/apply-start-flags.sh" nazgul/config.json "--<choice>". Then ask "Save this as your default mode?" — on Yes, write it using jq --arg (store the bare mode — hitl, afk, or yolo — never the --hitl flag form):
jq --arg m "<hitl|afk|yolo>" '.default_mode=$m' nazgul/config.json > nazgul/config.json.tmp && mv nazgul/config.json.tmp nazgul/config.json
- Non-interactive fallback: if
AskUserQuestion is unavailable and default_mode is null, default to HITL and print a note. Never default to YOLO.
- If a path resolves to YOLO (explicit
--yolo, or default_mode: "yolo") but AskUserQuestion is unavailable to collect the confirmation, do NOT run YOLO. Force HITL: [ -f nazgul/config.json ] && "${CLAUDE_PLUGIN_ROOT}/scripts/apply-start-flags.sh" nazgul/config.json "--hitl", and print a note that interactive YOLO consent could not be collected. (YOLO never runs without an interactive YES.)
Engine Selection (MANDATORY — after Resolve Run Mode)
Read nazgul/config.json → execution.engine:
"sequential" (default, or unset) — no change. Every "Delegate to Implementer" / "Stop hook takes over" step in the states below runs exactly as written.
"conductor" (opted in via --conductor) — wherever a state below reaches its final "Delegate to Implementer" / "Stop hook takes over" step, dispatch the conductor agent instead (Agent tool, subagent_type: "nazgul:conductor", model: "$(jq -r '.models.conductor // "sonnet"' nazgul/config.json)") and stop. The conductor is self-recovering and self-contained: it reads nazgul/config.json, nazgul/plan.md, and nazgul/tasks/TASK-*.md itself and drives the whole objective wave by wave (Review Board included) with no further input from this skill. Discovery/Doc Generator/Planner steps still run first as written — the conductor only replaces the Implementer/stop-hook hand-off once a task graph exists.
Reset Loop Counters (MANDATORY)
Loop counters are per-run state, not objective state. A stale counter left over from a previous run will silently brick the loop: the stop hook hits its max-iteration or consecutive-failure gate on the very first iteration and exits 0 (allows the stop) instead of re-dispatching — so the loop "never continues" even though READY tasks exist. The same applies to the cost-governor accumulator budget.spent_usd — a stale value would trip the budget ceiling immediately.
Clearing paused here is also mandatory: the pause flag is sticky (the stop hook leaves it true on every Stop so a pause holds), so a previously-paused loop will exit 0 on its first iteration unless /nazgul:start clears it. Resuming the loop IS the explicit consent to un-pause.
Before delegating to any agent, reset the counters and clear the pause flag. The [ -f ... ] guard makes this a safe no-op in the NOT_INITIALIZED case (no nazgul/config.json yet), so the command is always safe to run regardless of ordering:
[ -f nazgul/config.json ] && \
jq '.current_iteration = 0 | .safety.consecutive_failures = 0 | .safety._prev_done_count = 0
| .paused = false
| .budget = (if (.budget | type) == "object" then .budget else {} end) | .budget.spent_usd = 0' \
nazgul/config.json > nazgul/config.json.tmp && mv nazgul/config.json.tmp nazgul/config.json
This applies to every loop-starting path (ACTIVE_LOOP, DOCS_READY, DISCOVERY_DONE, FRESH, New Objective Override). Do not skip it for those states.
Objective Identity (use existing or assign)
Every branch-setup path below that needs a feature id MUST follow this rule instead of unconditionally recomputing one. The objective identity (feat_id, feat_display_id, afk.commit_prefix, and the objectives_history entry) is assigned exactly once per objective, at objective-creation time.
- If
config.feat_id is already set (e.g. /nazgul:plan created this objective up front, or a prior start path already assigned it): reuse it. Do NOT recompute the id, do NOT overwrite feat_id/feat_display_id/afk.commit_prefix, and do NOT append to objectives_history — all of that was done at creation time. Just proceed to create the git branch/worktree using the existing feat_id/feat_display_id/afk.commit_prefix. (Recomputing here would assign the wrong id — e.g. FEAT-002 when plan made FEAT-001 — and orphan the objectives/FEAT-001-spec.md the doc-generator reads.)
- Only when
config.feat_id is null do you assign identity: compute FEAT-NNN from objectives_history.length + 1 (if board connected, prefer issue number as display_id), set feat_id + feat_display_id + afk.commit_prefix to feat(<display_id>):, and append the objective to objectives_history — exactly once.
Smart State Detection
Evaluate the preprocessor data above. Work through this state machine top-to-bottom — take the FIRST state that matches:
STATE: NOT_INITIALIZED
Detection: Config shows "NOT_INITIALIZED"
Action: Tell the user: "Nazgul not initialized. Run /nazgul:init first."
Stop here.
STATE: ACTIVE_LOOP
Detection: Active tasks > 0 (any task with status READY, IN_PROGRESS, IN_REVIEW, IMPLEMENTED, or CHANGES_REQUESTED)
Action: Auto-resume the loop.
- Tell the user: "Resuming: [stored objective]. [N] active tasks remaining."
- Read
nazgul/plan.md → Recovery Pointer
- Read the latest checkpoint in
nazgul/checkpoints/
- Read the active task manifest
- Branch Verification: Read
nazgul/config.json → branch.feature.
- If set: verify current branch matches,
git checkout <feature> if not on it
- If null (pre-v3 project): create feature branch now:
a. Capture current branch as
branch.base
b. Store pwd as branch.main_worktree_path
c. Slugify objective → feat/<display_id>-<slug>
d. Assign objective identity per Objective Identity (use existing or assign) above: reuse config.feat_id/feat_display_id/afk.commit_prefix if already set (no recompute, no objectives_history append); only when feat_id is null compute FEAT-NNN from objectives_history.length + 1, set the id fields + afk.commit_prefix, and append to objectives_history once.
e. git checkout -b feat/<display_id>-<slug>
f. Create worktree dir, store paths in config
- Mode was already applied from flags by the Apply Flags step above; do not re-derive it here. (Loop counters were already reset by the mandatory Reset Loop Counters step above.)
- Per Engine Selection above: if
execution.engine == "conductor", dispatch the conductor agent and stop. Otherwise, delegate to the appropriate agent based on active task status:
- READY/IN_PROGRESS → Implementer
- IMPLEMENTED/IN_REVIEW → Review Gate
- CHANGES_REQUESTED → Implementer (read consolidated feedback first)
- BLOCKED → Show to user, ask what to do
- The stop hook takes over from here (sequential engine only).
STATE: OBJECTIVE_COMPLETE
Detection: Total tasks > 0 AND active tasks == 0 AND done tasks == total tasks
Action: All tasks are done.
- VERIFY FROM DISK first: re-read every task manifest
(
grep -H -E '(^\- \*\*Status\*\*:|^## Status:)' nazgul/tasks/TASK-*.md). If any task is not
DONE, this state was mis-detected — report the actual statuses and route to
the appropriate state instead. Never emit NAZGUL_COMPLETE, and never write
DONE entries to plan.md, based on remembered transitions: status writes can
be blocked by guards, so claims must come from reads that happened after the
last write.
- Check if post-loop agents have already run (look for release notes, updated CHANGELOG, etc.)
- If post-loop NOT run yet:
- Tell user: "All [N] tasks complete. Running post-loop agents (documentation, release, observability)..."
- Delegate to post-loop agents (documentation → release-manager → observability)
- Verify generated docs (cross-check — MANDATORY gate). If
nazgul/config.json has .docs.verify_post_loop true (or absent — default true),
dispatch the doc-verifier agent (Agent tool, subagent_type: "nazgul:doc-verifier").
It cross-checks nazgul/docs/*.md and the current-objective entries in CHANGELOG.md
against source: every event type, config key, command/skill name, named script, and
schema version in the docs must exist in the codebase. On a clean pass it records
completion by writing the feat_id to nazgul/logs/.docs-verified. The stop hook
gates loop completion on this marker: until it matches the current feat_id,
NAZGUL_COMPLETE is withheld (a bounded backstop of 3 attempts prevents an unwritable
marker from deadlocking an unattended loop). If the verifier itself errors, it is
non-fatal — log it; the gate's attempt backstop lets the loop complete. To skip
verification set docs.verify_post_loop: false in nazgul/config.json (clean no-op).
- Auto-distill learnings (proposes only — MANDATORY gate). If
nazgul/config.json has .learning.enabled AND .learning.auto_distill_post_loop
both true, dispatch the learner agent (Agent tool, subagent_type: "nazgul:learner").
It mines this objective's review/diagnosis artifacts and writes candidate rules to
nazgul/learning/proposed-rules.md for the user to review later via /nazgul:learn.
It NEVER approves or edits the rules registry. The stop hook gates loop
completion on this: when all tasks are DONE but the learner hasn't run for this
objective, it blocks the stop with a DELEGATE instruction. The learner records
completion by writing the objective id to nazgul/learning/.distilled; until that
marker matches, the loop will not reach NAZGUL_COMPLETE (a bounded backstop
prevents an unwritable marker from looping forever). If the learner itself errors,
it is non-fatal — log it; the gate's attempt backstop lets the loop complete.
- After post-loop:
a. Read
nazgul/config.json → branch.feature and branch.base
b. If feature branch exists:
- Push the feature branch:
git push -u origin <feature-branch>
- Create PR:
gh pr create --base <base-branch> --head <feature-branch> --title "<objective> (<feat_display_id>)" --body "<task summary>"
- Clean up all worktrees (remove task worktrees and worktree parent dir)
c. Output NAZGUL_COMPLETE
- If post-loop already run:
- Tell user: "Previous objective complete: [stored objective]. Starting objective derivation for next work..."
- Fall through to FRESH state below to derive a new objective
STATE: DOCS_READY
Detection: Docs generated > 0 AND total tasks == 0
Action: Documents exist but no plan yet — regenerate documents from current context, then run the planner.
- Read stored objective from config.json
- If no objective: read the PRD overview section as the objective, store it in config.json
- Branch Setup (if
branch.feature is null):
a. Capture current branch as branch.base
b. Store pwd as branch.main_worktree_path
c. Slugify objective → feat/<display_id>-<slug>
d. Assign objective identity per Objective Identity (use existing or assign) above: reuse config.feat_id/feat_display_id/afk.commit_prefix if already set (no recompute, no objectives_history append); only when feat_id is null compute FEAT-NNN from objectives_history.length + 1, set the id fields + afk.commit_prefix, and append to objectives_history once.
e. git checkout -b feat/<display_id>-<slug>
f. Create worktree dir, store paths in config
- Tell user: "Regenerating documents from current context before planning..."
- Delegate to Doc Generator agent (regenerates all docs to reflect current objective and context)
- In HITL mode, pause for doc review.
- Tell user: "Docs ready. Running planner..."
- Delegate to Planner agent
- Review Plan (HITL mode: show plan for approval. AFK: continue.)
- Per Engine Selection above: if
execution.engine == "conductor", dispatch the conductor agent and stop. Otherwise, delegate to Implementer.
- Stop hook takes over (sequential engine only).
STATE: DISCOVERY_DONE
Detection: Discovery summary is NOT "NOT_RUN" AND docs generated == 0 AND total tasks == 0
Action: Discovery ran but no docs or plan yet.
- Check if objective exists in config.json
- If no objective: run Objective Derivation (see below)
- Branch Setup:
a. Capture current branch as
branch.base
b. Store pwd as branch.main_worktree_path
c. Slugify objective → feat/<display_id>-<slug>
d. Assign objective identity per Objective Identity (use existing or assign) above: reuse config.feat_id/feat_display_id/afk.commit_prefix if already set (no recompute, no objectives_history append); only when feat_id is null compute FEAT-NNN from objectives_history.length + 1, set the id fields + afk.commit_prefix, and append to objectives_history once.
e. git checkout -b feat/<display_id>-<slug>
f. Create worktree dir, store paths in config
- Tell user: "Discovery complete. Generating documents, then planning..."
- Delegate to Doc Generator agent. In HITL mode, pause for doc review.
- Delegate to Planner agent. In HITL mode, pause for plan review.
- Per Engine Selection above: if
execution.engine == "conductor", dispatch the conductor agent and stop. Otherwise, delegate to Implementer.
- Stop hook takes over (sequential engine only).
STATE: FRESH
Detection: None of the above matched (config exists but discovery hasn't run)
Action: Fresh project — need discovery + everything.
- Run Objective Derivation (see below) if no objective in config.json
- Branch Setup:
a. Capture current branch as
branch.base
b. Store pwd as branch.main_worktree_path
c. Slugify objective → feat/<display_id>-<slug> (lowercase, non-alnum to hyphens, max 50 chars)
d. Assign objective identity per Objective Identity (use existing or assign) above: reuse config.feat_id/feat_display_id/afk.commit_prefix if already set (no recompute, no objectives_history append); only when feat_id is null compute FEAT-NNN from objectives_history.length + 1, set the id fields + afk.commit_prefix, and append to objectives_history once.
e. git checkout -b feat/<display_id>-<slug>
f. Create worktree dir at ../<project>-nazgul-worktrees/, store path in config
- Run Discovery agent (scans codebase, classifies project, generates reviewers)
- Classify Project: In HITL mode, confirm classification with user.
- Generate Documents: Delegate to Doc Generator. In HITL mode, pause for doc review.
- Collect Context: Based on objective type, collect targeted context.
6.5. Board Sync Prompt (HITL mode only):
- Check
nazgul/context/project-profile.md for "## GitHub Integration" section
- If GitHub repo detected AND board not already enabled (
jq -r '.board.enabled' nazgul/config.json is false):
- Ask user: "GitHub repo detected ([owner]/[repo]). Track tasks on GitHub Projects?"
- Options:
a. Yes, create a new project
b. Yes, use an existing project (list them)
c. Skip for now (can run
/nazgul:board github later)
- If (a): run
gh project create --owner [owner] --title "Nazgul: [repo]", then bash scripts/board-sync-github.sh setup [number]
- If (b): let user pick, then
bash scripts/board-sync-github.sh setup [number]
- If (c): continue without board sync
- In AFK mode: skip board prompt (user must run
/nazgul:board explicitly)
- Delegate to Planner: Planner reads context + docs, decomposes into tasks.
- Review Plan (HITL): Show plan for approval. AFK: continue.
- Per Engine Selection above: if
execution.engine == "conductor", dispatch the conductor agent and stop. Otherwise, delegate to Implementer: start working on the first READY task.
- Stop hook takes over (sequential engine only).
Objective Derivation
When no objective exists in config.json and none was provided as an argument, Nazgul derives one from project signals. The approach depends on whether this is a greenfield project.
Check: Is this a Greenfield Project?
If classification is GREENFIELD (from nazgul/context/project-classification.md) OR the codebase has fewer than 10 source files:
→ Go to Greenfield Stack Scaffolding (below)
Otherwise → continue with signal scanning:
Step 1: Scan for signals (use the preprocessor data above + additional reads)
Gather signals in priority order:
- Project profile — read
nazgul/context/project-profile.md for stated goals, purpose
- TODO/FIXME/HACK comments — from the preprocessor TODOs data
- Failing tests — run the project's test command (from config.json
project.test_command) and capture failures
- README roadmap — read the project's README.md for "roadmap", "planned features", "next steps" sections
- Recent git activity —
git log --oneline -10 for patterns like "WIP:", "started:", incomplete work
- Open GitHub issues —
gh issue list --limit 5 --state open (if gh is available)
Step 2: Present or select
HITL mode — Present discovered signals as an interactive menu:
Nazgul scanned your project and found potential work:
1. [signal description] (source: TODOs in src/payments/)
2. [signal description] (source: 2 failing tests in auth.test.ts)
3. [signal description] (source: GitHub issue #12)
4. Something else — tell me what you want to build
Which objective should I pursue?
Wait for user selection. If user picks "something else", use their input.
AFK mode — Auto-select the highest-priority signal:
- Priority: failing tests > TODOs with urgency keywords (FIXME, HACK) > open issues > WIP commits > general TODOs
- If zero signals found: error — "No objective could be derived from project context. Run
/nazgul:start 'your objective' to specify one."
Step 3: Store
Write the derived/selected objective to config.json:
{
"objective": "[the derived objective]",
"objective_set_at": "[ISO 8601 timestamp]"
}
Do NOT append to objectives_history here — the single per-objective append is owned by the Objective Identity (use existing or assign) rule and happens at branch setup (only when config.feat_id is null). Appending here too would double-append.
Greenfield Stack Scaffolding
For greenfield projects, Nazgul first checks for a project spec (see Step 0 in references/greenfield-scaffolding.md), then runs an interactive stack selection, tool pre-flight check, and configuration workflow. Consult references/greenfield-scaffolding.md for the full process including project spec detection, stack selection menus, AFK defaults, tool configuration steps, infrastructure scaffolding, and config storage.
For the tool detection commands table (check commands and install commands per platform), see references/tool-preflight.md.
New Objective Override (argument provided)
When the user explicitly passes an objective string in $ARGUMENTS:
- Check for existing active work:
- Archive old work (if applicable):
- Create
nazgul/archive/[YYYY-MM-DD-HHMMSS]/ directory
- Move: plan.md, tasks/, reviews/, docs/, checkpoints/ into archive
- Keep: config.json (will be updated), context/ (still valid for same project)
- Update the PREVIOUS objective's
objectives_history entry with completed_at and plan_archived_to
- Clear the old objective identity so the new objective gets a fresh one: set
branch.feature, feat_id, and feat_display_id to null in config.json. (If you skip this, the idempotent Objective Identity rule would REUSE the previous objective's id for the new objective.)
- Store new objective in config.json: set
objective, objective_set_at. Do NOT append to objectives_history here — the single per-objective append is owned by the Objective Identity (use existing or assign) rule and fires at branch setup now that feat_id was cleared in step 2.
- Proceed with FRESH state pipeline (discovery if stale → docs → plan → implement)
--continue Flag (backward compatibility)
If --continue is present, behave exactly as ACTIVE_LOOP state (loop counters are reset by the mandatory Reset Loop Counters step).
If no active tasks found: "Nothing to continue. Run /nazgul:start to auto-detect what to do."
Wave-Based Execution
Before dispatching the implementer, check for wave-based parallel execution:
- Read
nazgul/plan.md for a ## Wave Groups section
- Read
nazgul/config.json for parallelism.wave_execution and parallelism.enabled
If wave groups exist AND wave_execution is true AND parallelism is enabled:
a. Identify the current wave — the lowest wave number with READY tasks
b. Collect all READY tasks in that wave
c. If only 1 task: dispatch implementer normally (no parallelism overhead)
d. If 2+ tasks: dispatch parallel Agent Teams:
- Create a worktree per task:
git worktree add <worktree_dir>/TASK-NNN -b feat/<display_id>/TASK-NNN <feature-branch>
- One implementer per task, each working in its own worktree with fresh context
- Pass worktree path to each implementer teammate
- Team name:
nazgul-impl-wave-[N]
- Max parallel agents:
parallelism.max_parallel_teammates from config
- Each agent commits in its own worktree
e. Wait for all agents in the wave to complete
f. After wave completion:
- Merge each task branch into feature branch:
git merge --no-ff feat/<display_id>/TASK-NNN
- If merge conflict:
git merge --abort, mark conflicting task BLOCKED with conflict details
- Clean up completed task worktrees and delete task branches
- Promote next wave's tasks from PLANNED to READY (via dependency check)
g. Continue to next wave
If wave groups don't exist OR wave_execution is false:
- Fall back to sequential execution (current behavior — pick next READY task, dispatch implementer)
Safety rules:
- If any task in a wave fails, complete remaining tasks in that wave but don't start next wave
- If dependency analysis is missing, default to sequential
- Never run more agents than
max_parallel_teammates
AFK Mode Notes
afk.enabled is true (set by the Apply Flags step — do not set it here)
- Auto-commit on every state transition with dynamic prefix from config (e.g.,
feat(FEAT-003): or feat(#42):)
- Security rejections → BLOCKED (requires human review later)
- No pauses for human review
YOLO Mode Notes
- Everything in AFK mode, PLUS:
- Zero permission prompts — all tool calls execute immediately
- All reviewers still run — the full review gate executes for every task
- Default: Feature-level PR — tasks merge into feature branch (same as hitl/afk), single PR created at objective completion
- Optional:
--task-pr — enables stacked per-task PRs targeting feature branch (legacy behavior)
- Worktree isolation — each task gets its own worktree at
<worktree_dir>/TASK-NNN with branch feat/<display_id>/TASK-NNN
- Tests, lint, security guards fully active
- Requires launching Claude Code with
--dangerously-skip-permissions