| name | nazgul:bootstrap-project |
| description | Generate a portable, Nazgul-free project bundle (docs + Claude subagents) without installing Nazgul. Runs the full pre-planning pipeline (discovery, doc-generator, reviewer-instantiation, optional designer) and emits output into standard paths (./docs/, ./docs/context/, ./.claude/agents/, ./.claude/). |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep, Agent, ToolSearch |
| metadata | {"author":"Jose Mejia","version":"2.7.1"} |
Bootstrap Project
Examples
/nazgul:bootstrap-project — Interactive: ask for objective, then run full pipeline
/nazgul:bootstrap-project "Add Stripe billing to existing app" — Use given objective
/nazgul:bootstrap-project --dry-run — Run pipeline and transform into scratch without relocating
/nazgul:bootstrap-project --yes --overwrite — Non-interactive, overwrite existing ./docs or ./.claude/agents
Arguments
$ARGUMENTS
Flags
--yes — Non-interactive; abort on ambiguous prompts.
--overwrite — Force overwrite of non-empty ./docs/ or ./.claude/agents/.
--dry-run — Run pipeline and transform into scratch; skip relocation and cleanup.
--wipe-scratch — Delete any existing ./.bootstrap-scratch/ before starting.
--resume-scratch — Keep any existing ./.bootstrap-scratch/ and proceed with the run. Pipeline agents are still invoked; each may reuse pre-existing files in scratch at its own discretion, but the skill does not explicitly skip phases based on what's already there.
Instructions
Pre-load: Run ToolSearch with query select:AskUserQuestion to load the interactive prompt tool (deferred by default). Do this BEFORE any step that uses AskUserQuestion.
Phase 1 — Pre-flight gate
Source the preflight helpers:
source "$CLAUDE_PLUGIN_ROOT/scripts/lib/bootstrap-preflight.sh"
Parse $ARGUMENTS structurally: split on whitespace, pull out known flags, and leave the rest as the free-form objective. This prevents false positives where an objective string contains a flag-like substring (e.g. "document the --dry-run workflow").
BOOTSTRAP_YES=false
BOOTSTRAP_OVERWRITE=false
BOOTSTRAP_DRY_RUN=false
BOOTSTRAP_WIPE_SCRATCH=false
BOOTSTRAP_RESUME_SCRATCH=false
BOOTSTRAP_OBJECTIVE=""
set -f
set -- $ARGUMENTS
for tok in "$@"; do
case "$tok" in
--yes) BOOTSTRAP_YES=true ;;
--overwrite) BOOTSTRAP_OVERWRITE=true ;;
--dry-run) BOOTSTRAP_DRY_RUN=true ;;
--wipe-scratch) BOOTSTRAP_WIPE_SCRATCH=true ;;
--resume-scratch) BOOTSTRAP_RESUME_SCRATCH=true ;;
--*) echo "warning: unknown flag: $tok" >&2 ;;
*) BOOTSTRAP_OBJECTIVE="${BOOTSTRAP_OBJECTIVE:+$BOOTSTRAP_OBJECTIVE }$tok" ;;
esac
done
set +f
Run checks in order. If any returns non-zero, respect the contract (hard abort or prompt):
check_no_nazgul_dir || exit $?
check_scratch_state; scratch_rc=$?
case $scratch_rc in
0) ;;
12)
if [ "$BOOTSTRAP_RESUME_SCRATCH" = "true" ]; then
true
elif [ "$BOOTSTRAP_WIPE_SCRATCH" = "true" ]; then
rm -rf ./.bootstrap-scratch
elif [ "$BOOTSTRAP_YES" = "true" ]; then
echo "error: scratch exists, --yes in effect, cannot prompt; pass --wipe-scratch or --resume-scratch" >&2
exit 12
else
echo "./.bootstrap-scratch/ exists from a prior run." >&2
exit 12
fi
;;
*) exit "$scratch_rc" ;;
esac
check_docs_agents_empty; docs_rc=$?
case $docs_rc in
0) ;;
11)
if [ "$BOOTSTRAP_OVERWRITE" = "true" ]; then
rm -rf ./docs ./.claude/agents
rm -f ./.claude/design-tokens.json ./.claude/design-system.md
elif [ "$BOOTSTRAP_YES" = "true" ]; then
exit 11
else
echo "Non-empty ./docs/ or ./.claude/agents/ detected." >&2
exit 11
fi
;;
*) exit "$docs_rc" ;;
esac
check_git_clean
if [ -n "${BOOTSTRAP_GIT_WARNING:-}" ]; then
echo "$BOOTSTRAP_GIT_WARNING" >&2
fi
Phase 2 — Objective collection
Create the scratch tree:
mkdir -p ./.bootstrap-scratch/context ./.bootstrap-scratch/docs ./.bootstrap-scratch/agents ./.bootstrap-scratch/.claude
Detect whether this is an existing codebase or an empty project:
detect_project_type
echo "Detected: $BOOTSTRAP_PROJECT_TYPE ($BOOTSTRAP_SOURCE_COUNT source files)"
Determine the objective source using a three-tier priority:
-
If $BOOTSTRAP_OBJECTIVE (parsed in Phase 1) is non-empty, use it regardless of project type. Write a minimal project-spec:
if [ -n "$BOOTSTRAP_OBJECTIVE" ]; then
cat > ./.bootstrap-scratch/context/project-spec.md <<SPEC
# Project Specification
## Source
- Method: argument
- Created at: $(date -u +%Y-%m-%dT%H:%M:%SZ)
## Vision
$BOOTSTRAP_OBJECTIVE
SPEC
fi
-
If brownfield ($BOOTSTRAP_PROJECT_TYPE = "brownfield") and no explicit objective, skip interactive questions entirely. The codebase IS the spec — Discovery will scan it and derive everything. Write a codebase-derived project-spec:
if [ -z "$BOOTSTRAP_OBJECTIVE" ] && [ "$BOOTSTRAP_PROJECT_TYPE" = "brownfield" ]; then
PROJECT_NAME=$(basename "$(pwd)")
cat > ./.bootstrap-scratch/context/project-spec.md <<SPEC
# Project Specification
## Source
- Method: brownfield-auto (derived from existing codebase)
- Created at: $(date -u +%Y-%m-%dT%H:%M:%SZ)
- Source files detected: $BOOTSTRAP_SOURCE_COUNT
## Vision
Document and analyze the existing $PROJECT_NAME codebase.
## Note
This is a brownfield project. The objective, architecture, features, and
constraints will be derived from scanning the existing source code during
the Discovery phase. No interactive input was required.
SPEC
fi
-
If greenfield ($BOOTSTRAP_PROJECT_TYPE = "greenfield") and no explicit objective:
First, enforce the --yes non-interactive contract — we cannot ask questions in that mode:
if [ -z "$BOOTSTRAP_OBJECTIVE" ] && [ "$BOOTSTRAP_PROJECT_TYPE" = "greenfield" ] && [ "$BOOTSTRAP_YES" = "true" ]; then
echo "error: greenfield project with no objective; --yes is set so we cannot prompt." >&2
echo " Re-run with an explicit objective argument, e.g.:" >&2
echo " /nazgul:bootstrap-project --yes \"your product idea here\"" >&2
exit 13
fi
Otherwise, run the condensed Tier 1 interactive flow. Ask these 5 questions one at a time, phrased naturally, and wait for each answer:
- "In a sentence or two, what are you building?"
- "Who will use this?"
- "What are the 3-5 core features?"
- "What problem does this solve?"
- "Any hard constraints? (Skip if none.)"
After collecting answers, write ./.bootstrap-scratch/context/project-spec.md with the standard sections (## Vision, ## Target Users, ## Core Features, ## Problem Statement, ## Constraints).
After Tier 1, offer Tier 2: "Got the basics. Want to go deeper on user stories and success metrics? (~5 more minutes) (y/n)"
- If yes, ask per-feature user stories and success metrics, append to the spec.
- If no, finalize with Tier 1 content only.
Verify the file exists before proceeding:
test -f ./.bootstrap-scratch/context/project-spec.md || { echo "error: project-spec.md not written" >&2; exit 30; }
Phase 3 — Pipeline execution
Export STATE_ROOT and BUNDLE_MODE for all downstream renders:
export STATE_ROOT="./.bootstrap-scratch"
export BUNDLE_MODE="true"
source "$CLAUDE_PLUGIN_ROOT/scripts/lib/bootstrap-render.sh"
Render and invoke each pipeline agent. For each agent, the renderer produces a Nazgul-free prompt pointed at the scratch tree; the LLM then executes the agent's instructions.
3a. Discovery
Render the discovery prompt and invoke it:
render_agent_prompt "$CLAUDE_PLUGIN_ROOT/agents/discovery.md" "$STATE_ROOT" > "$STATE_ROOT/.discovery-prompt.md"
Then invoke the discovery agent via the Agent tool, passing the rendered prompt as the system/initial message. The agent writes to $STATE_ROOT/context/{project-profile,project-classification,architecture-map,existing-docs}.md.
After the agent returns, verify expected outputs exist:
for f in project-profile.md project-classification.md architecture-map.md; do
test -f "$STATE_ROOT/context/$f" || {
echo "error: discovery did not produce $f; preserving scratch for debugging" >&2
exit 40
}
done
3b. Doc generation
render_agent_prompt "$CLAUDE_PLUGIN_ROOT/agents/doc-generator.md" "$STATE_ROOT" > "$STATE_ROOT/.docgen-prompt.md"
Invoke the doc-generator agent. It reads $STATE_ROOT/context/ and writes to $STATE_ROOT/docs/{PRD,TRD,ADR-*,test-plan,manifest}.md (and migration-plan.md if classification=migration).
Verify:
for f in PRD.md TRD.md test-plan.md; do
test -f "$STATE_ROOT/docs/$f" || {
echo "error: doc-generator did not produce $f; preserving scratch" >&2
exit 41
}
done
3c. Reviewer instantiation
The reviewer template at agents/templates/reviewer-base.md is rendered once per reviewer domain determined from project-profile.md. Available domain definitions live in agents/templates/reviewer-domains.json (existing structure: keys like qa-reviewer, api-reviewer, with per-domain checklist and review_steps arrays, plus description, title, context_items, category, approved_criteria, rejected_criteria). The JSON does NOT have a keywords field today, so domain selection goes via a two-step rule:
- Always include:
qa-reviewer and code-reviewer (baseline; assumed present in the JSON — if a key is missing, skip it with a warning and continue).
- Conditionally include based on keyword scan of
project-profile.md:
api-reviewer — if profile mentions api, rest, graphql, endpoint
frontend-reviewer — if profile mentions react, vue, svelte, angular, next.js, nuxt, nextjs
security-reviewer — always include when the profile mentions auth, login, password, token, jwt, oauth
performance-reviewer — if profile mentions database, caching, redis, perf
This mapping is hard-coded in select_reviewer_domains (see Step 2 below). Domains not defined in reviewer-domains.json are skipped.
For each selected domain, render the template with BUNDLE_MODE=true:
DOMAINS=$(select_reviewer_domains "$STATE_ROOT/context/project-profile.md" "$CLAUDE_PLUGIN_ROOT/agents/templates/reviewer-domains.json")
for domain in $DOMAINS; do
out="$STATE_ROOT/agents/${domain}.md"
BUNDLE_MODE=true render_template "$CLAUDE_PLUGIN_ROOT/agents/templates/reviewer-base.md" \
| substitute_domain_vars "$domain" "$CLAUDE_PLUGIN_ROOT/agents/templates/reviewer-domains.json" \
> "$out"
test -s "$out" || { echo "error: reviewer $domain rendered empty" >&2; exit 42; }
done
3d. Designer (conditional)
if grep -qiE 'react|vue|svelte|angular|swiftui|flutter' "$STATE_ROOT/context/project-profile.md"; then
render_agent_prompt "$CLAUDE_PLUGIN_ROOT/agents/designer.md" "$STATE_ROOT" > "$STATE_ROOT/.designer-prompt.md"
fi
Phase 4 — Transform, relocate, cleanup
Source the relocate helpers:
source "$CLAUDE_PLUGIN_ROOT/scripts/lib/bootstrap-relocate.sh"
Run the transform:
bash "$CLAUDE_PLUGIN_ROOT/scripts/bootstrap-transform.sh" "$STATE_ROOT"
transform_rc=$?
case $transform_rc in
0) ;;
2) echo "error: transform usage error" >&2; exit 50 ;;
3)
echo "error: transform final assertion failed — see output above." >&2
echo " scratch preserved at $STATE_ROOT for debugging." >&2
exit 51
;;
*) echo "error: transform failed (exit $transform_rc)" >&2; exit 52 ;;
esac
If --dry-run was set (parsed in Phase 1), stop here. Report the scratch location to the user:
if [ "$BOOTSTRAP_DRY_RUN" = "true" ]; then
echo "Dry-run complete. Review the bundle at $STATE_ROOT/ before re-running without --dry-run."
exit 0
fi
Relocate atomically:
relocate_bundle "$STATE_ROOT" "." || exit $?
Append to .gitignore and clean up:
append_gitignore "."
cleanup_scratch "$STATE_ROOT"
Phase 5 — Summary
count_md() { find "$1" -maxdepth 1 -type f -name '*.md' 2>/dev/null | wc -l | tr -d ' '; }
count_dir() { find "$1" -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' '; }
DOCS=$(count_md ./docs)
CTX=$(count_md ./docs/context)
AGENTS=$(count_md ./.claude/agents)
DESIGN=$(count_dir ./.claude)
cat <<SUMMARY
Bootstrap complete.
Generated:
./docs/ $DOCS documents (PRD, TRD, ADRs, test plan)
./docs/context/ $CTX context files
./.claude/agents/ $AGENTS reviewer agents
./.claude/ $DESIGN design-system files (if UI surface detected)
Next steps:
- Review ./docs/PRD.md and ./docs/TRD.md
- Commit the bundle: git add docs/ .claude/ && git commit
- Use the reviewers: invoke them from Claude Code in this repo
SUMMARY