Architecture Decision Records
ADR-001: Make plan-build a dedicated standalone gist
Status: Accepted
The implementation previously lived inside the broad Zsh Setup func file while its workflows and history were spread across that repository. This coupled releases to shell configuration and made the executable depend on ambient functions.
The dedicated plan-build gist is now canonical for plan_build.zsh, SKILL.md, ARCHITECT.md, tests, and documentation. The executable contains namespaced CLI, Auggie, Superpowers, Git-worktree, architect-preflight, payload, and Claude-launch helpers. Zsh Setup retains only a cached launcher that refreshes the canonical bundle.
The Zsh Setup migration is maintained separately from the canonical executable so shell installation concerns do not leak back into this repository.
ADR-002: Keep one supported workflow surface
Status: Accepted
The supported surface is standard, prompt enhancement, brainstorm, writing-plan, architect, new architect state, and permissive Claude execution. The removed v2 entry point remains rejected as an unknown option, and stale v2-only distribution concepts are not part of the canonical skills.
This avoids multiple remote implementations and prevents documentation drift.
ADR-003: Translate convenience flags at the process boundary
Status: Accepted
--yolo remains the plan-build user interface for continuity, but _plan_build_launch_claude translates it to Claude Code's actual --dangerously-skip-permissions argument. Architect workflow approval gates remain mandatory regardless of Claude's process permission mode.
ADR-004: Test through replaceable command seams
Status: Accepted
The executable is sourceable and its _plan_build_ helpers form test seams. Command-level tests call plan_build, stub external tools and interactive choices, and use real temporary Git repositories only for exact worktree classification. Tests never use the network.
ADR-005: Activate immutable complete releases through one symlink
Status: Accepted
The thin launcher stages plan_build.zsh, SKILL.md, and ARCHITECT.md as one bundle, rejects empty files and executables that fail zsh -n, then moves the bundle into a unique immutable release directory. A serialized activation atomically switches one current symlink, and both installed skills point through it. Each invocation also receives skill paths from its selected release, so another caller can refresh without changing that invocation's executable/skill generation. The mutable HTTPS HEAD URL remains the explicit update trust boundary: HTTPS protects transport to the endpoint, but does not pin the downloaded content, establish update provenance, or make the three separate requests one remote snapshot.
Plan Build Architect Workflow
Use this workflow only when plan_build --architect launches Claude Code. Claude is the lead architect, documentation manager, task dispatcher, and verifier. OpenAI Codex is the sole code and test-code executor. CodeRabbit is the required automated reviewer.
Non-Negotiable Role Boundary
Claude may inspect every project file but may create or modify only:
SPEC.mdTASKS.mdADR.mdCHANGELOG.mdREADME.md- Markdown files under
docs/
Claude must not write application code, test code, migrations, generated source, or executable configuration. Delegate those changes to Codex through an approved task prompt.
Codex must not commit, stage, push, reset, restore, or discard changes. All approved work remains uncommitted until the complete delivery passes final verification and the user approves one final commit.
Never push automatically.
Startup Preflight
Before discovery or project-file modification:
- Confirm the current directory is a Git worktree.
- Record
git status --shortand preserve all pre-existing work. - Confirm an interactive terminal is available.
- Confirm
codex,coderabbit, andgitare available. - Inspect project instructions, manifests, existing documentation, tests, CI, and nearby implementation patterns.
- Never request or expose credential values. Use approved credential integrations when authentication is required.
If a requirement is missing, stop with exact remediation guidance.
Existing Workflow Detection
If SPEC.md, TASKS.md, ADR.md, or CHANGELOG.md exists:
- Read all existing workflow records.
- Compare their claims with Git and the current worktree.
- Identify the first incomplete or inconsistent task.
- Present the detected state and ask whether to resume.
When the launch instruction says Start mode: archive-and-start-new, archive existing workflow documents in a timestamped documentation archive before creating replacements. Never overwrite them silently.
Phase 1: Discovery and Architecture
Interview the user until product, architecture, data, integration, operational, security, and handover requirements are clear.
Rules:
- Ask exactly one question at a time.
- Include a recommended default answer with every question.
- Resolve facts from the repository instead of asking the user.
- Ask the user only for decisions, priorities, business rules, and information that cannot be discovered safely.
- Probe user roles, workflows, data structures, API or component contracts, technology constraints, integrations, edge cases, failure modes, migration, rollback, testing, deployment, monitoring, security, compliance, support, and ownership.
- Collect project name, client or owner, purpose, target users, repository scope, supported environments, deployment owner, operational owner, support role, compliance constraints, and intended handover date.
- Do not implement or dispatch Codex during discovery.
Do not enter Phase 2 until the user explicitly states either:
I am ready to generate the specPhase 1 complete
Phase 2: Specification
After the explicit Phase 1 completion phrase:
- Ask no more discovery questions.
- Create or reconcile
SPEC.mdwithout discarding valid existing content. - Include executive summary and goals, architecture and technology, data models or durable state schemas, API or component contracts, edge cases, constraints, testing strategy, out-of-scope items, and definition of done.
- Use Mermaid for applicable system context, component, deployment, and critical sequence diagrams.
- Mark non-applicable sections with a factual rationale instead of inventing behavior.
- Treat the approved specification as the requirements baseline.
- Present the specification and wait for explicit approval.
Any later requirement change is a visible amendment. Pause execution, assess affected tasks and documents, record architectural changes in ADR.md, and obtain approval before continuing.
Phase 3: Codex Task Plan
After SPEC.md approval, create or reconcile TASKS.md.
Use stable task IDs such as TASK-001. Allowed states are:
PendingIn ProgressBlockedReviewComplete
Only one task may be In Progress.
Every task must include:
- ID, title, status, and dependencies
- Objective
- Exhaustive allowed file scope
- Context and interfaces
- Self-contained prompt for Codex
- Acceptance criteria
- Unit or integration test requirements
- Verification commands
- CodeRabbit outcome
- Completion evidence
Every Codex prompt must explicitly prohibit commits and out-of-scope edits. Tasks must be atomic, sequential, and small enough for a fresh Codex context.
Present TASKS.md and wait for explicit approval before execution.
Documentation Records
Maintain these root engineering records throughout execution:
SPEC.md: approved requirements baseline and amendmentsTASKS.md: task state, prompts, reviews, and evidenceADR.md: append-only architecture decisionsCHANGELOG.md: Keep a Changelog-compatibleUnreleasedentries referencing task IDsREADME.md: product overview, prerequisites, quick start, and configuration
Every ADR uses a stable ID and records status, date, context, decision, alternatives, consequences, and affected components. Keep superseded decisions and link their replacements.
Mandatory Client Handover Package
The following files are mandatory without exception:
docs/USER_MANUAL.mddocs/RUNBOOK.mddocs/API.mddocs/DEPLOYMENT.mddocs/SECURITY.mddocs/HANDOVER.md
Create missing documents and reconcile existing ones. Do not replace valid project-specific content wholesale.
When a document or section is not applicable, retain it and state Not Applicable with a short factual rationale.
Client-facing documents describe the delivered system, not Claude, Codex, CodeRabbit, prompts, or internal execution mechanics unless disclosure is contractually required.
Implementation claims must reference relevant source files, configuration, schemas, endpoints, or verified commands. Handover documents include software version or commit SHA, generation date, document owner, and last verification date.
docs/HANDOVER.md must include deliverables, access prerequisites, verified capabilities, limitations, approved deferred risks, support boundaries, warranty assumptions, ownership transfer, and dated acceptance/sign-off placeholders.
Task Execution Loop
For each approved task, in dependency order:
- Confirm dependencies are
Complete. - Set exactly that task to
In Progress. - Record the current Git status and task baseline.
- Invoke Codex non-interactively in the current worktree with workspace-write access using the exact approved prompt from
TASKS.md. - Capture Codex's result.
- Inspect the diff for correctness, approved scope, repository conventions, unrelated changes, and secret exposure.
- Independently run the task's verification commands.
- Set the task to
Reviewand run CodeRabbit on the task diff. - Investigate every concrete finding.
- Delegate valid fixes back to Codex within the same task scope.
- Rerun focused verification and CodeRabbit after substantive fixes.
- Record commands, results, findings, and completion evidence.
- Report the outcome and wait for user approval before starting the next task.
Use non-interactive Codex execution in this form, adapting only supported sandbox flags to the installed CLI:
codex exec --sandbox workspace-write "<exact approved task prompt>"
Critical, high, and medium CodeRabbit findings block completion. Low-severity findings may be deferred only with user approval and must be recorded in CHANGELOG.md and docs/HANDOVER.md.
Scope Expansion
If Codex reports that required work exceeds the allowed file scope:
- Do not permit out-of-scope edits.
- Assess requirement, architecture, dependency, and test impact.
- Update
SPEC.md,TASKS.md, andADR.mdwhen appropriate. - Ask the user to approve the revised scope.
- Redispatch only after approval.
Failure and Retry Policy
Allow the initial Codex attempt plus at most two focused correction attempts.
If acceptance still fails:
- Stop the workflow.
- Mark the task
Blocked. - Preserve the partial diff.
- Record diagnostics and attempted fixes in task evidence.
- Ask whether to revise architecture, scope, or acceptance criteria.
- Do not start dependent tasks.
If no test framework exists, the first applicable task establishes the smallest project-appropriate validation setup. If automated testing is technically impossible, obtain user approval for explicit manual acceptance steps and record the risk in docs/HANDOVER.md.
Security Rules
- Never include credential values, tokens, private keys, personal data, or production secrets in prompts or documentation.
- Document only secret names, required environment variables, approved storage, ownership, and rotation procedures.
- Use native credential helpers and approved credential stores.
- Run available secret scanning before final completion.
- Treat suspected credentials as blocking findings.
Final Review and Single Commit
After every task is Complete:
- Finish all root engineering records and mandatory handover documents.
- Run CodeRabbit over the complete uncommitted diff.
- Delegate valid code or test fixes to Codex under the responsible task scope.
- Run the complete project lint, type-check, build, and test suite.
- Confirm the aggregate diff contains only approved changes and preserves pre-existing work.
- Validate required documents and sections, Markdown structure, links, source references, commands, examples, and Mermaid syntax where tooling is available.
- Ensure no unresolved
TODO, placeholder, or template text remains. Approved limitations belong indocs/HANDOVER.md. - Run available secret scanning.
- Present the complete diff summary, verification evidence, known limitations, and proposed commit message.
- Wait for explicit user approval.
- Stage only approved files and create one commit.
- Do not push.
Never call the delivery complete when required verification has not run. Report environmental or unrelated failures with evidence.
Changelog
Unreleased
Added
- Standalone executable
plan_build.zsh. - Canonical standard and architect Claude skills.
- Standard, prompt-enhanced, brainstorm, writing-plan, and architect workflows.
- Architect safe-resume and archive-and-start-new launch modes.
- Command-level, network-free Zsh regression tests.
- Dedicated installation, integration, architecture, task, and troubleshooting documentation.
Changed
- Plan-build-specific implementation and documentation now belong to the dedicated
plan-buildgist rather than the Zsh Setup repository. --yolois translated to Claude Code's--dangerously-skip-permissionsargument.- Active workflow documentation no longer describes removed v2 or distribution modes.
- Prompt enhancement now uses valid, immediately flushed platform
scriptinvocations, including util-linuxscript -efor child-status propagation, and cleans up temporary state and child processes on return or interruption. - The Zsh Setup cache contract now activates immutable three-file releases through one atomic
currentsymlink and identifies mutable HTTPSHEADas the update trust boundary.
Removed
- Dependence on
~/.funcand ambient shell functions. - The obsolete v2 downloader and its former workflow variants.
plan-build
plan-build is a standalone Zsh tool that launches Claude Code with a structured multi-agent implementation workflow. Claude orchestrates, Codex provides independent implementation review, and CodeRabbit reviews the resulting changes. Architect mode instead keeps Claude within a documentation-first architecture and task-dispatch role while Codex performs implementation.
Prerequisites
- Zsh
- Claude Code CLI (
claude) - OpenAI Codex CLI (
codex) - CodeRabbit CLI (
coderabbit) - Git
- The installed plan-build skills described below
--prompt also requires Auggie (auggie), an authenticated ~/.augment/session.json, Perl, and the platform script utility.
--brainstorm and --writing-plan require the enabled Claude Code plugin superpowers@claude-plugins-official. Claude Code safe mode must be disabled for those modes.
Architect mode additionally requires an interactive terminal and a normal Git worktree. Bare repositories are rejected.
Installation
Install the executable somewhere on PATH:
mkdir -p "$HOME/.local/bin"
curl -fsSL \
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/plan_build.zsh \
-o "$HOME/.local/bin/plan_build"
chmod +x "$HOME/.local/bin/plan_build"
Then install both skills:
mkdir -p \
"$HOME/.claude/skills/plan-build" \
"$HOME/.claude/skills/plan-build-architect"
curl -fsSL \
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/SKILL.md \
-o "$HOME/.claude/skills/plan-build/SKILL.md"
curl -fsSL \
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/ARCHITECT.md \
-o "$HOME/.claude/skills/plan-build-architect/SKILL.md"
For production automation, download to a temporary file in the destination directory and rename it atomically after a successful transfer.
Usage
Run the command in the project you want to change, enter the payload, then put EOF alone on a line:
plan_build
Implement pagination for the audit log.
Preserve existing API compatibility.
EOF
Available modes:
plan_build Standard short-plan workflow
plan_build --prompt Enhance with Auggie and approve
plan_build --brainstorm Superpowers design and plan
plan_build --writing-plan Superpowers implementation plan
plan_build --prompt --brainstorm Enhance, design, plan, and build
plan_build --yolo --writing-plan Permissive Claude process mode
plan_build --architect Safely resume architect state
plan_build --architect --new Archive state and start fresh
plan_build --architect --yolo Architect with permissive process mode
--brainstorm and --writing-plan are mutually exclusive. --prompt can combine with either. --architect can combine only with --new and --yolo; --new is invalid without --architect. Duplicate flags and all other flags fail. --v2 is intentionally unsupported.
The user-facing --yolo option is translated to Claude Code's --dangerously-skip-permissions argument. It never bypasses approval gates defined by either workflow.
Zsh Setup cache launcher
When curl is available, the Zsh Setup integration attempts to download this complete release bundle on every invocation:
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/plan_build.zsh
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/SKILL.md
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/ARCHITECT.md
It stages all three non-empty files, requires plan_build.zsh to pass zsh -n, moves the bundle into a unique immutable release directory, and atomically switches a single current symlink. Activation is serialized with a kernel-backed Zsh file lock. Failed refreshes leave the prior validated release active and repair its skill links before execution.
The launcher invokes the executable from the selected immutable release and binds both internal skill paths to that same release:
PLAN_BUILD_SKILL_PATH=<selected-release>/SKILL.md \
PLAN_BUILD_ARCHITECT_SKILL_PATH=<selected-release>/ARCHITECT.md \
zsh <selected-release>/plan_build.zsh "$@"
It never sources remote code into the caller shell. The mutable HTTPS HEAD download is the explicit update trust boundary: transport security authenticates the server, but the URL provides neither immutable-version pinning nor content-integrity verification. Complete-release activation and syntax validation prevent partial or syntactically invalid active releases; they do not establish provenance or guarantee that three independent downloads observed the same remote revision if publication occurs during refresh.
The installed Claude skill files are symlinks through the same current release:
~/.claude/skills/plan-build/SKILL.md
~/.claude/skills/plan-build-architect/SKILL.md
The launcher defaults to ${XDG_CACHE_HOME:-$HOME/.cache}/plan-build; Zsh Setup exposes PLAN_BUILD_CACHE_DIR and PLAN_BUILD_BASE_URL overrides. Skill paths stay fixed at ~/.claude/skills to match the canonical executable.
Testing
The suite is self-contained and never accesses the network or launches real agent CLIs:
zsh -n plan_build.zsh test_plan_build.zsh
zsh test_plan_build.zsh
git diff --check
Troubleshooting
- CLI is missing: install the named program and ensure its executable is on
PATH. - Superpowers is missing: in Claude Code run
/plugin install superpowers@claude-plugins-official. - Superpowers is disabled: run
claude plugin enable superpowers@claude-plugins-official. - Safe mode blocks planning: unset
CLAUDE_CODE_SAFE_MODE. - Auggie cannot authenticate: run
auggie loginand confirm~/.augment/session.jsonis non-empty. - Prompt enhancement does not continue:
--promptrequires an interactive terminal to approve both optional project indexing and the final enhanced prompt. - Architect preflight fails in Git: change to a checked-out worktree rather than the
.gitdirectory or a bare repository. - Architect skill is missing: install
ARCHITECT.mdat the exact skill path shown above.
Plan-Build Orchestrate Loop
Use this workflow when plan_build hands Claude Code an implementation task. The goal is to keep Claude as the orchestrator while using Codex and CodeRabbit as independent review and validation agents.
Operating Rules
- Run from the project root. Treat the current working directory as the project to modify.
- Preserve user work. Check
git statusbefore edits and do not revert unrelated changes. - Keep implementation scoped to the payload unless repository context proves a wider change is required.
- Prefer existing project conventions, scripts, test commands, and dependency managers.
- Do not call the task complete until validation has run or the reason it cannot run is documented.
- If any agent reports a plausible correctness, security, data-loss, migration, or test risk, resolve it or explicitly document why it is not applicable.
The 8-Step Workflow
1. Intake
Read the user payload fully. Identify:
- Objective and expected user-visible behavior.
- Files, modules, commands, and frameworks likely involved.
- Constraints from repository docs, package scripts, CI config, and existing patterns.
- Any ambiguity that blocks safe execution.
Only ask the user a question when no reasonable project-local assumption is safe.
2. Baseline
Inspect the repository before changing files:
git status --short
rg --files
Then read the smallest useful set of files. Prefer rg, package manifests, tests, routing files, and nearby implementations over broad file dumps.
3. Plan
Follow the planning mode supplied by plan_build:
standard: Create a short implementation plan with concrete steps and validation commands.brainstorm: Invokesuperpowers:brainstorming, honor its design and written-spec approval gates, and let it transition tosuperpowers:writing-plansafter approval.writing-plan: Invokesuperpowers:writing-plansdirectly, treating the payload as the requirements or specification.
For either Superpowers mode, save the artifacts at the paths selected by the skills. When writing-plans reaches its execution handoff, return to this workflow instead of starting implementation: Codex must review the plan first. If the user rejects or cancels a required approval, stop cleanly without modifying implementation files.
If the task touches behavior, data, auth, payments, destructive actions, or shared infrastructure, include a rollback or compatibility note.
4. Codex Plan Review
Run this step only for brainstorm and writing-plan modes. Ask Codex for an independent, read-only review of the approved spec, when present, and the implementation plan before touching implementation files. Provide the original payload and artifact paths. Ask it to focus on requirement coverage, incorrect assumptions, unsafe migrations, missing edge cases, inadequate tests, and steps that are too vague to execute.
Recommended prompt shape:
Review these planning artifacts before implementation. Check requirement coverage, technical correctness, repository fit, edge cases, migration or rollback risk, test coverage, and whether every step is executable. Report concrete findings only; do not modify files.
Task:
<payload>
Spec:
<spec path, if present>
Implementation plan:
<plan path>
Use a read-only, ephemeral Codex invocation. Resolve every valid finding in the artifacts and repeat the review if revisions are substantial. If an artifact is missing or empty, or Codex cannot complete the review, stop before implementation and report the failure.
In standard mode, skip this step and continue directly to implementation.
5. Implement
Make the change in small, reviewable edits:
- Follow existing style and abstractions.
- Add or update tests when behavior changes.
- Update docs only when user-facing usage changes.
- Avoid unrelated refactors and formatting churn.
After each meaningful edit group, re-check the diff for accidental changes.
6. Codex Code Review Pass
Ask Codex for an independent review of the local diff before finalizing. Provide the task, constraints, and current diff. Ask it to focus on bugs, edge cases, missing tests, regressions, and simpler project-native alternatives.
Recommended prompt shape:
Review this change for correctness and risk. Prioritize bugs, regressions, missing tests, and mismatches with existing project patterns. Do not rewrite the whole solution unless a specific issue requires it.
Task:
<payload>
Diff:
<git diff>
Apply fixes for valid findings, then repeat this review pass if the fixes are non-trivial.
7. CodeRabbit Review Pass
Run CodeRabbit on the branch or diff when available. Treat its output as advisory but investigate every concrete finding.
If CodeRabbit cannot run locally, record the command attempted and the failure. Continue with manual validation rather than blocking indefinitely.
8. Validate And Close
Run the planned validation commands, such as:
npm test
npm run lint
pytest
cargo test
go test ./...
Use the commands that actually exist in the project. If validation fails, fix the issue and rerun the relevant command. If a failure is unrelated or environmental, capture the evidence.
Before final response:
- Confirm
git diffcontains only intended changes. - Summarize what changed.
- Report validation run and result.
- Note any remaining risks or commands that could not run.
Plan-Build Specification
Purpose
Plan-build is a self-contained Zsh launcher for Claude Code multi-agent implementation workflows. This repository is the canonical boundary for the executable, both Claude skills, command tests, and project documentation. It does not depend on the Zsh Setup repository or on functions from ~/.func.
Public interface
plan_build [--yolo] [--prompt] [--brainstorm | --writing-plan]
plan_build --architect [--new] [--yolo]
The executable reads standard input until a line exactly equal to EOF. Empty payloads fail. Unknown and duplicate options fail with usage text. --v2 is intentionally unknown.
Standard modes
- No planning flag: use the normal short-plan eight-step workflow.
--prompt: enhance the payload through Auggie, display the result, and require interactive approval before Claude launches.--brainstorm: require enabled Claude Superpowers, then use brainstorming and writing-plans approval gates.--writing-plan: require enabled Claude Superpowers and create the implementation plan directly.--yolo: launch Claude Code with--dangerously-skip-permissions.
--brainstorm and --writing-plan are mutually exclusive. --prompt may be combined with either.
Architect mode
--architect makes Claude the documentation-first architect and dispatcher. It may combine only with --new and --yolo.
- Default: safely detect and resume existing planning state.
--new: archive existing planning documents as directed by the architect skill and start fresh.--yolo: changes Claude Code permissions only; every workflow approval gate remains mandatory.
Architect preflight happens before payload input and verifies an interactive terminal, Claude Code, Codex, CodeRabbit, Git, an exact normal Git worktree, and a readable non-empty architect skill.
Installed skills
The executable expects:
~/.claude/skills/plan-build/SKILL.md
~/.claude/skills/plan-build-architect/SKILL.md
The canonical repository sources are SKILL.md and ARCHITECT.md.
Standalone constraints
- Zsh on macOS, Ubuntu, and WSL.
- No sourcing of user shell configuration.
- All internal helpers use the
_plan_build_namespace. - External command boundaries remain replaceable for tests.
- Tests use no network and do not launch real agent CLIs.
- The program remains sourceable and runs main only when executed directly.
Zsh Setup launcher integration
A thin Zsh Setup launcher downloads one release bundle:
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/plan_build.zsh
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/SKILL.md
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/ARCHITECT.md
It must stage all three files, require each to be non-empty, and require the executable to pass zsh -n. A validated bundle moves into a unique immutable release directory. Activation is serialized by a kernel-backed Zsh file lock and occurs by atomically replacing one current symlink. The installed skill paths symlink to SKILL.md and ARCHITECT.md through current, so executable and skills always resolve through one release boundary. Cached fallback repairs missing or stale skill links before execution.
The launcher executes the selected release with PLAN_BUILD_SKILL_PATH and PLAN_BUILD_ARCHITECT_SKILL_PATH bound to that same immutable directory:
PLAN_BUILD_SKILL_PATH=<selected-release>/SKILL.md \
PLAN_BUILD_ARCHITECT_SKILL_PATH=<selected-release>/ARCHITECT.md \
zsh <selected-release>/plan_build.zsh "$@"
It does not source the downloaded executable. Refresh failure falls back to the last complete validated release; without one, launch fails. Curl is optional only when that fallback exists. Signals stop an in-flight refresh, clean temporary state, and return the conventional signal status.
The mutable HTTPS HEAD URL is the explicit update trust boundary. HTTPS authenticates the transport endpoint, but without an immutable version or pinned digest it does not provide content-integrity or provenance guarantees for updates. Because the three files are separate requests, publication during refresh can also expose different remote revisions; publishers should update the gist only from complete, tested commits.
Acceptance criteria
- Every documented mode has command-level coverage.
- Invalid combinations, duplicate flags, empty payloads, and
--v2rejection are covered. - Superpowers checks precede Auggie and payload input.
- Architect preflight precedes payload input and distinguishes normal worktrees from bare repositories.
- Prompt enhancement cannot launch Claude without explicit approval.
--yoloreaches the Claude binary only as--dangerously-skip-permissions.- Syntax checks, command tests, and
git diff --checkpass. - The Zsh Setup launcher activates and falls back only in complete three-file releases.
Plan-Build Tasks
Completed
PB-001: Establish the dedicated project boundary
- Extract the executable from Zsh Setup into
plan_build.zsh. - Remove dependencies on
~/.funcand unrelated shell helpers. - Make the executable directly runnable and safely sourceable.
PB-002: Preserve supported workflows
- Preserve standard, prompt enhancement, brainstorm, and writing-plan behavior.
- Preserve architect safe-resume and archive-and-start-new modes.
- Preserve EOF-terminated payload input and interactive enhanced-prompt approval.
- Translate
--yoloto Claude Code's real--dangerously-skip-permissionsoption. - Keep
--v2rejected through the unknown-option path.
PB-003: Canonicalize skills and documentation
- Publish the standard workflow as
SKILL.md. - Publish the architect workflow as
ARCHITECT.md. - Remove obsolete v2-only and distribution workflow claims from the active standard skill.
- Document installation, prerequisites, cache integration, compatibility, testing, and troubleshooting.
PB-004: Add standalone regression coverage
- Stub external CLIs, Claude launch, Auggie enhancement, and interactive approval.
- Cover all modes, invalid combinations, preflight ordering, safe-resume/new architect prompts, and normal versus bare Git repositories.
- Keep the suite network-free.
Completed outside this repository
ZS-001: Migrate the Zsh Setup launcher
Replace the old in-repository implementation with a thin cached launcher that downloads the canonical executable and skills from the plan-build gist. The Zsh Setup migration now uses immutable complete release directories, a serialized atomic current symlink switch, and skill links through that same release.
Required launcher contract:
zsh <selected-release>/plan_build.zsh "$@"
Required remote files:
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/plan_build.zsh
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/SKILL.md
https://opengist.resetrix.work/weehong/plan-build/raw/HEAD/ARCHITECT.md
| 1 | #!/usr/bin/env zsh |
| 2 | |
| 3 | # Standalone plan-build launcher. This file is intentionally sourceable so its |
| 4 | # command seams can be replaced by the test suite. |
| 5 | |
| 6 | _plan_build_usage() { |
| 7 | print -r -- "Usage: plan_build [--yolo] [--prompt] [--brainstorm | --writing-plan] |
| 8 | plan_build --architect [--new] [--yolo]" |
| 9 | } |
| 10 | |
| 11 | _plan_build_require_cli() { |
| 12 | local binary="$1" |
| 13 | local label="$2" |
| 14 | local resolved |
| 15 | |
| 16 | resolved="$(command -v "$binary" 2>/dev/null)" || resolved="" |
| 17 | if [[ -n "$resolved" && -x "$resolved" ]]; then |
| 18 | return 0 |
| 19 | fi |
| 20 | |
| 21 | print -u2 -r -- "Error: ${label} ('${binary}') is not installed or not in your PATH." |
| 22 | return 1 |
| 23 | } |
| 24 | |
| 25 | _plan_build_confirm_indexing() { |
| 26 | local project_root="$1" |
| 27 | local reply |
| 28 | |
| 29 | if ! (: </dev/tty) 2>/dev/null; then |
| 30 | print -u2 -r -- "Notice: No interactive terminal available; enhancing without project indexing." |
| 31 | return 1 |
| 32 | fi |
| 33 | |
| 34 | printf "Allow Auggie to index and use project context from '%s'? (y/N) " "$project_root" >/dev/tty |
| 35 | if ! IFS= read -r reply </dev/tty; then |
| 36 | print -u2 -r -- "\nNotice: Unable to read confirmation; enhancing without project indexing." |
| 37 | return 1 |
| 38 | fi |
| 39 | |
| 40 | [[ "$reply" == (y|Y|yes|YES|Yes) ]] |
| 41 | } |
| 42 | |
| 43 | _plan_build_parse_auggie_output() { |
| 44 | local log_file="$1" |
| 45 | local output_file="$2" |
| 46 | |
| 47 | perl -ne ' |
| 48 | s/\r$//; |
| 49 | s/.*\r//; |
| 50 | 1 while s/[^\x08]\x08//g; |
| 51 | s/\x08//g; |
| 52 | s/\e\][^\a]*(?:\a|\e\\)//g; |
| 53 | s/\e\[[0-?]*[ -\/]*[@-~]//g; |
| 54 | next if /Script started on/ || /Script done on/; |
| 55 | if (/^(?:✨\s*)?Enhanced prompt:\s*(.*)$/) { |
| 56 | $capturing = 1; |
| 57 | $output .= "$1\n" if length $1; |
| 58 | next; |
| 59 | } |
| 60 | next unless $capturing; |
| 61 | exit if /^🤖/ || /Tool call:/ || /Session terminated/; |
| 62 | $output .= $_; |
| 63 | END { |
| 64 | $output =~ s/^\s*\n//; |
| 65 | $output =~ s/\s+\z//; |
| 66 | print "$output\n" if length $output; |
| 67 | } |
| 68 | ' "$log_file" >| "$output_file" |
| 69 | } |
| 70 | |
| 71 | _plan_build_platform() { |
| 72 | command uname -s |
| 73 | } |
| 74 | |
| 75 | _plan_build_start_auggie_script() { |
| 76 | local platform="$1" log_file="$2" workspace="$3" cache_dir="$4" |
| 77 | local auth_file="$5" prompt_file="$6" use_project_context="$7" |
| 78 | shift 7 |
| 79 | |
| 80 | if [[ "$platform" == Darwin ]]; then |
| 81 | command script -q -t 0 "$log_file" auggie "$@" </dev/null >/dev/null 2>&1 & |
| 82 | else |
| 83 | AUGGIE_WORKSPACE="$workspace" AUGGIE_CACHE_DIR="$cache_dir" \ |
| 84 | AUGGIE_AUTH_FILE="$auth_file" AUGGIE_PROMPT_FILE="$prompt_file" \ |
| 85 | AUGGIE_INDEXING="$use_project_context" \ |
| 86 | command script -q -f -e -O "$log_file" -c ' |
| 87 | if [ "$AUGGIE_INDEXING" = 1 ]; then |
| 88 | exec auggie --print --enhance-prompt --workspace-root "$AUGGIE_WORKSPACE" --augment-cache-dir "$AUGGIE_CACHE_DIR" --augment-session-json "$AUGGIE_AUTH_FILE" --allow-indexing --wait-for-indexing --dont-save-session --instruction-file "$AUGGIE_PROMPT_FILE" |
| 89 | else |
| 90 | exec auggie --print --enhance-prompt --no-discover-workspaces --workspace-root "$AUGGIE_WORKSPACE" --augment-cache-dir "$AUGGIE_CACHE_DIR" --augment-session-json "$AUGGIE_AUTH_FILE" --dont-save-session --instruction-file "$AUGGIE_PROMPT_FILE" |
| 91 | fi |
| 92 | ' </dev/null >/dev/null 2>&1 & |
| 93 | fi |
| 94 | REPLY=$! |
| 95 | } |
| 96 | |
| 97 | _plan_build_enhance_prompt() { |
| 98 | emulate -L zsh |
| 99 | setopt localtraps |
| 100 | local prompt="$1" |
| 101 | local output_file="$2" |
| 102 | local run_dir prompt_file workspace cache_dir auth_file log_file parsed_file |
| 103 | local project_root use_project_context timeout_seconds script_pid= waited child_status=0 |
| 104 | local -a auggie_args |
| 105 | |
| 106 | trap 'return 130' INT |
| 107 | trap 'return 143' TERM |
| 108 | trap 'return 129' HUP |
| 109 | |
| 110 | if [[ -z "$prompt" ]]; then |
| 111 | print -u2 -r -- "Error: Prompt was empty." |
| 112 | return 1 |
| 113 | fi |
| 114 | if [[ ! -s "$HOME/.augment/session.json" ]]; then |
| 115 | print -u2 -r -- "Error: Auggie session file not found. Run 'auggie login' first." |
| 116 | return 1 |
| 117 | fi |
| 118 | |
| 119 | run_dir="$(mktemp -d "${TMPDIR:-/tmp}/plan-build-auggie.XXXXXX")" || return 1 |
| 120 | trap "rm -rf -- ${(q)run_dir}" EXIT |
| 121 | prompt_file="$run_dir/prompt.txt" |
| 122 | workspace="$run_dir/workspace" |
| 123 | cache_dir="$run_dir/cache" |
| 124 | auth_file="$HOME/.augment/session.json" |
| 125 | log_file="$run_dir/auggie.log" |
| 126 | parsed_file="$run_dir/enhanced.txt" |
| 127 | mkdir -p "$workspace" "$cache_dir" || { |
| 128 | return 1 |
| 129 | } |
| 130 | print -r -- "$prompt" >| "$prompt_file" || return 1 |
| 131 | |
| 132 | project_root="$(command git rev-parse --show-toplevel 2>/dev/null)" || project_root="$PWD" |
| 133 | use_project_context=0 |
| 134 | timeout_seconds=90 |
| 135 | if _plan_build_confirm_indexing "$project_root"; then |
| 136 | use_project_context=1 |
| 137 | timeout_seconds=300 |
| 138 | workspace="$project_root" |
| 139 | cache_dir="$HOME/.augment" |
| 140 | print -u2 -r -- "Indexing approved; enhancing with project context from '${project_root}'." |
| 141 | else |
| 142 | print -u2 -r -- "Enhancing without project indexing." |
| 143 | fi |
| 144 | |
| 145 | auggie_args=(--print --enhance-prompt --workspace-root "$workspace" |
| 146 | --augment-cache-dir "$cache_dir" --augment-session-json "$auth_file" |
| 147 | --dont-save-session --instruction-file "$prompt_file") |
| 148 | if (( use_project_context )); then |
| 149 | auggie_args+=(--allow-indexing --wait-for-indexing) |
| 150 | else |
| 151 | auggie_args+=(--no-discover-workspaces) |
| 152 | fi |
| 153 | |
| 154 | _plan_build_start_auggie_script "$(_plan_build_platform)" "$log_file" \ |
| 155 | "$workspace" "$cache_dir" "$auth_file" "$prompt_file" \ |
| 156 | "$use_project_context" "${auggie_args[@]}" || return 1 |
| 157 | script_pid="$REPLY" |
| 158 | trap "kill ${(q)script_pid} >/dev/null 2>&1 |
| 159 | wait ${(q)script_pid} >/dev/null 2>&1 |
| 160 | rm -rf -- ${(q)run_dir}" EXIT |
| 161 | waited=0 |
| 162 | while kill -0 "$script_pid" >/dev/null 2>&1; do |
| 163 | if command grep -aq "🤖" "$log_file" 2>/dev/null || |
| 164 | command grep -aq "Tool call:" "$log_file" 2>/dev/null; then |
| 165 | kill "$script_pid" >/dev/null 2>&1 || true |
| 166 | wait "$script_pid" >/dev/null 2>&1 || true |
| 167 | script_pid= |
| 168 | trap "rm -rf -- ${(q)run_dir}" EXIT |
| 169 | child_status=0 |
| 170 | break |
| 171 | fi |
| 172 | if (( waited >= timeout_seconds )); then |
| 173 | kill "$script_pid" >/dev/null 2>&1 || true |
| 174 | wait "$script_pid" >/dev/null 2>&1 || true |
| 175 | script_pid= |
| 176 | trap "rm -rf -- ${(q)run_dir}" EXIT |
| 177 | print -u2 -r -- "Error: Timed out waiting for Auggie to enhance the prompt." |
| 178 | return 124 |
| 179 | fi |
| 180 | sleep 1 |
| 181 | waited=$((waited + 1)) |
| 182 | done |
| 183 | if [[ -n "$script_pid" ]]; then |
| 184 | wait "$script_pid" |
| 185 | child_status=$? |
| 186 | script_pid= |
| 187 | trap "rm -rf -- ${(q)run_dir}" EXIT |
| 188 | fi |
| 189 | if (( child_status != 0 )); then |
| 190 | print -u2 -r -- "Error: Auggie process failed with status ${child_status}." |
| 191 | return "$child_status" |
| 192 | fi |
| 193 | |
| 194 | _plan_build_parse_auggie_output "$log_file" "$parsed_file" |
| 195 | if [[ ! -s "$parsed_file" ]]; then |
| 196 | print -u2 -r -- "Error: Auggie did not return an enhanced prompt." |
| 197 | return 1 |
| 198 | fi |
| 199 | command cp "$parsed_file" "$output_file" || { |
| 200 | print -u2 -r -- "Error: Unable to save the enhanced prompt." |
| 201 | return 1 |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | _plan_build_confirm_enhanced_prompt() { |
| 206 | local reply |
| 207 | |
| 208 | if ! (: </dev/tty) 2>/dev/null; then |
| 209 | print -r -- "❌ Error: Cannot review the enhanced prompt without an interactive terminal; Claude was not launched." |
| 210 | return 2 |
| 211 | fi |
| 212 | printf "Proceed with this enhanced prompt? (y/N) " >/dev/tty |
| 213 | if ! IFS= read -r reply </dev/tty; then |
| 214 | printf '\n' >/dev/tty |
| 215 | print -r -- "🛑 Review cancelled; Claude was not launched." |
| 216 | return 2 |
| 217 | fi |
| 218 | [[ "$reply" == (y|Y|yes|YES|Yes) ]] |
| 219 | } |
| 220 | |
| 221 | _plan_build_superpowers_state() { |
| 222 | awk ' |
| 223 | BEGIN { RS = "}"; state = "missing"; printed = 0 } |
| 224 | /"id"[[:space:]]*:[[:space:]]*"superpowers@claude-plugins-official"/ { |
| 225 | state = "installed" |
| 226 | if ($0 ~ /"enabled"[[:space:]]*:[[:space:]]*true/) state = "enabled" |
| 227 | else if ($0 ~ /"enabled"[[:space:]]*:[[:space:]]*false/) state = "disabled" |
| 228 | print state |
| 229 | printed = 1 |
| 230 | exit |
| 231 | } |
| 232 | END { if (!printed) print state } |
| 233 | ' |
| 234 | } |
| 235 | |
| 236 | _plan_build_superpowers_preflight() { |
| 237 | local plugin_json plugin_state |
| 238 | |
| 239 | case "${CLAUDE_CODE_SAFE_MODE:-}" in |
| 240 | 1|true|TRUE|yes|YES|on|ON) |
| 241 | print -r -- "❌ Error: Claude Code safe mode disables Superpowers." |
| 242 | print -r -- "Unset CLAUDE_CODE_SAFE_MODE before using --brainstorm or --writing-plan." |
| 243 | return 1 |
| 244 | ;; |
| 245 | esac |
| 246 | if ! plugin_json="$(command claude plugin list --json 2>/dev/null)"; then |
| 247 | print -r -- "❌ Error: Unable to inspect Claude Code plugins." |
| 248 | print -r -- "Run 'claude plugin list' to diagnose the problem." |
| 249 | return 1 |
| 250 | fi |
| 251 | plugin_state="$(print -r -- "$plugin_json" | _plan_build_superpowers_state)" |
| 252 | case "$plugin_state" in |
| 253 | enabled) return 0 ;; |
| 254 | disabled) |
| 255 | print -r -- "❌ Error: Claude Code Superpowers is installed but disabled." |
| 256 | print -r -- "Enable it with: claude plugin enable superpowers@claude-plugins-official" |
| 257 | ;; |
| 258 | missing) |
| 259 | print -r -- "❌ Error: Claude Code Superpowers is required for --brainstorm and --writing-plan." |
| 260 | print -r -- "Install it in Claude Code with: /plugin install superpowers@claude-plugins-official" |
| 261 | ;; |
| 262 | *) |
| 263 | print -r -- "❌ Error: Unable to determine Claude Code Superpowers status." |
| 264 | print -r -- "Run 'claude plugin list' to diagnose the problem." |
| 265 | ;; |
| 266 | esac |
| 267 | return 1 |
| 268 | } |
| 269 | |
| 270 | _plan_build_is_worktree() { |
| 271 | local inside_work_tree |
| 272 | inside_work_tree="$(command git rev-parse --is-inside-work-tree 2>/dev/null)" || return 1 |
| 273 | [[ "$inside_work_tree" == true ]] |
| 274 | } |
| 275 | |
| 276 | _plan_build_has_tty() { |
| 277 | (: </dev/tty) 2>/dev/null |
| 278 | } |
| 279 | |
| 280 | _plan_build_architect_preflight() { |
| 281 | local skill_path="${PLAN_BUILD_ARCHITECT_SKILL_PATH:-$HOME/.claude/skills/plan-build-architect/SKILL.md}" |
| 282 | |
| 283 | if ! _plan_build_has_tty; then |
| 284 | print -r -- "❌ Error: Architect mode requires an interactive terminal." |
| 285 | print -r -- "Run plan_build --architect from an interactive terminal." |
| 286 | return 1 |
| 287 | fi |
| 288 | _plan_build_require_cli claude "Claude Code CLI" || return 1 |
| 289 | _plan_build_require_cli codex "Codex CLI" || return 1 |
| 290 | _plan_build_require_cli coderabbit "CodeRabbit CLI" || return 1 |
| 291 | _plan_build_require_cli git "Git CLI" || return 1 |
| 292 | if ! _plan_build_is_worktree; then |
| 293 | print -r -- "❌ Error: Architect mode must run inside a Git worktree." |
| 294 | print -r -- "Change to a Git worktree, then retry plan_build --architect." |
| 295 | return 1 |
| 296 | fi |
| 297 | if [[ ! -r "$skill_path" || ! -s "$skill_path" ]]; then |
| 298 | print -r -- "❌ Error: Architect skill is missing, unreadable, or empty: $skill_path" |
| 299 | print -r -- "Install a readable, non-empty plan-build-architect skill at that path." |
| 300 | return 1 |
| 301 | fi |
| 302 | } |
| 303 | |
| 304 | _plan_build_launch_claude() { |
| 305 | local use_yolo="$1" |
| 306 | shift |
| 307 | if (( use_yolo )); then |
| 308 | command claude --dangerously-skip-permissions "$@" |
| 309 | else |
| 310 | command claude "$@" |
| 311 | fi |
| 312 | } |
| 313 | |
| 314 | _plan_build_read_payload() { |
| 315 | local line payload="" |
| 316 | while IFS= read -r line; do |
| 317 | [[ "$line" == EOF ]] && break |
| 318 | if [[ -z "$payload" ]]; then |
| 319 | payload="$line" |
| 320 | else |
| 321 | payload+=$'\n'"$line" |
| 322 | fi |
| 323 | done |
| 324 | print -r -- "$payload" |
| 325 | } |
| 326 | |
| 327 | plan_build() { |
| 328 | emulate -L zsh |
| 329 | setopt localtraps |
| 330 | local use_yolo=0 use_architect=0 use_new=0 enhance_payload=0 |
| 331 | local planning_mode="standard" |
| 332 | local payload enhanced_file= confirm_status enhance_status planning_instruction architect_start_mode |
| 333 | local standard_skill="${PLAN_BUILD_SKILL_PATH:-$HOME/.claude/skills/plan-build/SKILL.md}" |
| 334 | local architect_skill="${PLAN_BUILD_ARCHITECT_SKILL_PATH:-$HOME/.claude/skills/plan-build-architect/SKILL.md}" |
| 335 | |
| 336 | trap 'return 130' INT |
| 337 | trap 'return 143' TERM |
| 338 | trap 'return 129' HUP |
| 339 | |
| 340 | while (( $# )); do |
| 341 | case "$1" in |
| 342 | --yolo) |
| 343 | (( use_yolo )) && { |
| 344 | print -r -- "Error: Duplicate argument: --yolo" |
| 345 | _plan_build_usage |
| 346 | return 1 |
| 347 | } |
| 348 | use_yolo=1 |
| 349 | ;; |
| 350 | --architect) |
| 351 | (( use_architect )) && { |
| 352 | print -r -- "Error: Duplicate argument: --architect" |
| 353 | _plan_build_usage |
| 354 | return 1 |
| 355 | } |
| 356 | use_architect=1 |
| 357 | ;; |
| 358 | --new) |
| 359 | (( use_new )) && { |
| 360 | print -r -- "Error: Duplicate argument: --new" |
| 361 | _plan_build_usage |
| 362 | return 1 |
| 363 | } |
| 364 | use_new=1 |
| 365 | ;; |
| 366 | --prompt) |
| 367 | (( enhance_payload )) && { |
| 368 | print -r -- "Error: Duplicate argument: --prompt" |
| 369 | _plan_build_usage |
| 370 | return 1 |
| 371 | } |
| 372 | enhance_payload=1 |
| 373 | ;; |
| 374 | --brainstorm) |
| 375 | [[ "$planning_mode" == brainstorm ]] && { |
| 376 | print -r -- "Error: Duplicate argument: --brainstorm" |
| 377 | _plan_build_usage |
| 378 | return 1 |
| 379 | } |
| 380 | [[ "$planning_mode" != standard ]] && { |
| 381 | print -r -- "Error: --brainstorm and --writing-plan are mutually exclusive." |
| 382 | _plan_build_usage |
| 383 | return 1 |
| 384 | } |
| 385 | planning_mode="brainstorm" |
| 386 | ;; |
| 387 | --writing-plan) |
| 388 | [[ "$planning_mode" == writing-plan ]] && { |
| 389 | print -r -- "Error: Duplicate argument: --writing-plan" |
| 390 | _plan_build_usage |
| 391 | return 1 |
| 392 | } |
| 393 | [[ "$planning_mode" != standard ]] && { |
| 394 | print -r -- "Error: --brainstorm and --writing-plan are mutually exclusive." |
| 395 | _plan_build_usage |
| 396 | return 1 |
| 397 | } |
| 398 | planning_mode="writing-plan" |
| 399 | ;; |
| 400 | *) |
| 401 | print -r -- "Error: Unknown argument: $1" |
| 402 | _plan_build_usage |
| 403 | return 1 |
| 404 | ;; |
| 405 | esac |
| 406 | shift |
| 407 | done |
| 408 | |
| 409 | if (( use_new && ! use_architect )); then |
| 410 | print -r -- "Error: --new requires --architect." |
| 411 | _plan_build_usage |
| 412 | return 1 |
| 413 | fi |
| 414 | if (( use_architect && enhance_payload )) || |
| 415 | { (( use_architect )) && [[ "$planning_mode" != standard ]]; }; then |
| 416 | print -r -- "Error: --architect may combine only with --new and --yolo." |
| 417 | _plan_build_usage |
| 418 | return 1 |
| 419 | fi |
| 420 | |
| 421 | if (( use_architect )); then |
| 422 | _plan_build_architect_preflight || return 1 |
| 423 | else |
| 424 | _plan_build_require_cli claude "Claude Code CLI" || return 1 |
| 425 | if [[ "$planning_mode" != standard ]]; then |
| 426 | _plan_build_superpowers_preflight || return 1 |
| 427 | fi |
| 428 | _plan_build_require_cli codex "Codex CLI" || return 1 |
| 429 | _plan_build_require_cli coderabbit "CodeRabbit CLI" || return 1 |
| 430 | if (( enhance_payload )); then |
| 431 | _plan_build_require_cli auggie "Auggie CLI" || return 1 |
| 432 | _plan_build_require_cli script "script utility" || return 1 |
| 433 | _plan_build_require_cli perl "Perl" || return 1 |
| 434 | fi |
| 435 | fi |
| 436 | |
| 437 | print -r -- "📥 Reading payload... Type 'EOF' on a new line and press Enter when finished." |
| 438 | payload="$(_plan_build_read_payload)" |
| 439 | if [[ -z "$payload" ]]; then |
| 440 | print -r -- "❌ Error: Payload was empty." |
| 441 | return 1 |
| 442 | fi |
| 443 | |
| 444 | if (( enhance_payload )); then |
| 445 | enhanced_file="$(mktemp "${TMPDIR:-/tmp}/plan-build-enhanced.XXXXXX")" || return 1 |
| 446 | trap "rm -f -- ${(q)enhanced_file}" EXIT |
| 447 | print -r -- "✨ Enhancing payload with Auggie..." |
| 448 | _plan_build_enhance_prompt "$payload" "$enhanced_file" |
| 449 | enhance_status=$? |
| 450 | if (( enhance_status != 0 )); then |
| 451 | print -r -- "❌ Error: Prompt enhancement failed; Claude was not launched." |
| 452 | return "$enhance_status" |
| 453 | fi |
| 454 | payload="$(<"$enhanced_file")" |
| 455 | rm -f -- "$enhanced_file" |
| 456 | enhanced_file= |
| 457 | trap - EXIT |
| 458 | if [[ -z "$payload" ]]; then |
| 459 | print -r -- "❌ Error: Enhanced payload was empty; Claude was not launched." |
| 460 | return 1 |
| 461 | fi |
| 462 | printf '\n%s\n%s\n%s\n\n' \ |
| 463 | "━━━━━━━━━━━━━━━━ Auggie enhanced prompt ━━━━━━━━━━━━━━━━" \ |
| 464 | "$payload" \ |
| 465 | "━━━━━━━━━━━━━━━━ End enhanced prompt ━━━━━━━━━━━━━━━━━" |
| 466 | _plan_build_confirm_enhanced_prompt |
| 467 | confirm_status=$? |
| 468 | if (( confirm_status == 2 )); then |
| 469 | return 1 |
| 470 | elif (( confirm_status != 0 )); then |
| 471 | print -r -- "🛑 Enhanced prompt not approved; Claude was not launched." |
| 472 | return 0 |
| 473 | fi |
| 474 | print -r -- "✅ Enhanced prompt approved." |
| 475 | fi |
| 476 | |
| 477 | if (( use_architect )); then |
| 478 | if (( use_new )); then |
| 479 | architect_start_mode="Start mode: archive-and-start-new. Explicitly archive the prior planning state as the skill directs, then begin a new architecture plan." |
| 480 | else |
| 481 | architect_start_mode="Start mode: safe-resume-detection. Safely detect whether an existing architecture planning session should be resumed; do not archive or replace it automatically." |
| 482 | fi |
| 483 | print -r -- "🚀 Launching Claude Code in architect mode..." |
| 484 | _plan_build_launch_claude "$use_yolo" "Read \`$architect_skill\` and follow it strictly, including every gate even when permissive Claude execution is active. Stay in the documentation-first architect/orchestrator role: produce and coordinate documentation and planning only, and do not implement the requirement. |
| 485 | |
| 486 | $architect_start_mode |
| 487 | |
| 488 | Use the following payload as the initial requirement: |
| 489 | |
| 490 | $payload" |
| 491 | return $? |
| 492 | fi |
| 493 | |
| 494 | case "$planning_mode" in |
| 495 | brainstorm) |
| 496 | planning_instruction="Planning mode: brainstorm. Use the superpowers:brainstorming skill, including its approval gates and transition to superpowers:writing-plans. After the implementation plan is saved, return to the plan-build workflow for Codex plan review before implementation." |
| 497 | ;; |
| 498 | writing-plan) |
| 499 | planning_instruction="Planning mode: writing-plan. Use the superpowers:writing-plans skill with the payload as the requirements. After the implementation plan is saved, return to the plan-build workflow for Codex plan review before implementation." |
| 500 | ;; |
| 501 | *) |
| 502 | planning_instruction="Planning mode: standard. Create the workflow's normal short implementation plan." |
| 503 | ;; |
| 504 | esac |
| 505 | |
| 506 | print -r -- "🚀 Launching Claude Code with your multi-agent workflow (${planning_mode} planning)..." |
| 507 | _plan_build_launch_claude "$use_yolo" "Please read \`$standard_skill\` and strictly follow the 8-step multi-agent workflow to implement the following task. |
| 508 | |
| 509 | $planning_instruction |
| 510 | |
| 511 | $payload" |
| 512 | } |
| 513 | |
| 514 | _plan_build_main() { |
| 515 | plan_build "$@" |
| 516 | } |
| 517 | |
| 518 | if [[ "${ZSH_EVAL_CONTEXT:-}" == toplevel ]]; then |
| 519 | _plan_build_main "$@" |
| 520 | fi |
| 521 |
| 1 | #!/usr/bin/env zsh |
| 2 | |
| 3 | set -u |
| 4 | |
| 5 | typeset -r repo_dir="${0:A:h}" |
| 6 | source "$repo_dir/plan_build.zsh" |
| 7 | |
| 8 | typeset -gi failures=0 |
| 9 | functions[_plan_build_real_launch_claude]="${functions[_plan_build_launch_claude]}" |
| 10 | functions[_plan_build_real_enhance_prompt]="${functions[_plan_build_enhance_prompt]}" |
| 11 | functions[_plan_build_real_start_auggie_script]="${functions[_plan_build_start_auggie_script]}" |
| 12 | |
| 13 | fail() { |
| 14 | print -u2 -r -- "FAIL: $1" |
| 15 | failures=$((failures + 1)) |
| 16 | } |
| 17 | |
| 18 | assert_equal() { |
| 19 | local expected="$1" actual="$2" label="$3" |
| 20 | [[ "$actual" == "$expected" ]] || fail "$label (expected '$expected', got '$actual')" |
| 21 | } |
| 22 | |
| 23 | assert_contains() { |
| 24 | local output="$1" expected="$2" label="$3" |
| 25 | [[ "$output" == *"$expected"* ]] || fail "$label (missing '$expected')" |
| 26 | } |
| 27 | |
| 28 | assert_not_contains() { |
| 29 | local output="$1" unexpected="$2" label="$3" |
| 30 | [[ "$output" != *"$unexpected"* ]] || fail "$label (unexpected '$unexpected')" |
| 31 | } |
| 32 | |
| 33 | run_plan() { |
| 34 | local args="$1" input="${2:-EOF\n}" |
| 35 | output="$(printf '%b' "$input" | plan_build ${(z)args} 2>&1)" |
| 36 | rc=$? |
| 37 | } |
| 38 | |
| 39 | seam_events() { |
| 40 | print -r -- "$1" | sed -n 's/^SEAM://p' | paste -sd ' ' - |
| 41 | } |
| 42 | |
| 43 | count_matches() { |
| 44 | local directory="$1" prefix="$2" |
| 45 | local -a matches |
| 46 | matches=("$directory"/"$prefix".*(N)) |
| 47 | print -r -- "$#matches" |
| 48 | } |
| 49 | |
| 50 | typeset fixture_dir |
| 51 | fixture_dir="$(mktemp -d "${TMPDIR:-/tmp}/plan-build-tests.XXXXXX")" || exit 1 |
| 52 | trap 'rm -rf -- "$fixture_dir"' EXIT |
| 53 | mkdir -p "$fixture_dir/tmp" |
| 54 | export TMPDIR="$fixture_dir/tmp" |
| 55 | |
| 56 | command git init -q "$fixture_dir/worktree" |
| 57 | command git init -q --bare "$fixture_dir/bare.git" |
| 58 | |
| 59 | (cd "$fixture_dir/worktree" && _plan_build_is_worktree) |
| 60 | assert_equal 0 "$?" "normal Git worktree accepted" |
| 61 | (cd "$fixture_dir/bare.git" && _plan_build_is_worktree) |
| 62 | assert_equal 1 "$?" "bare Git repository rejected" |
| 63 | (cd "$fixture_dir" && _plan_build_is_worktree) |
| 64 | assert_equal 1 "$?" "non-repository rejected" |
| 65 | |
| 66 | plugin_state() { |
| 67 | print -r -- "$1" | _plan_build_superpowers_state |
| 68 | } |
| 69 | |
| 70 | assert_equal enabled "$(plugin_state '[{"id":"superpowers@claude-plugins-official","enabled":true}]')" "enabled plugin JSON" |
| 71 | assert_equal disabled "$(plugin_state $'[\n{"enabled":false,\n"id":"superpowers@claude-plugins-official"}\n]')" "reordered disabled plugin JSON" |
| 72 | assert_equal missing "$(plugin_state '[{"id":"other@market","enabled":true}]')" "missing plugin JSON" |
| 73 | assert_equal installed "$(plugin_state '[{"id":"superpowers@claude-plugins-official"}]')" "installed plugin JSON" |
| 74 | |
| 75 | typeset args expected label |
| 76 | for args expected label in \ |
| 77 | "--v2" "Unknown argument: --v2" "v2 rejection" \ |
| 78 | "--unknown" "Unknown argument: --unknown" "unknown option" \ |
| 79 | "--yolo --yolo" "Duplicate argument: --yolo" "duplicate yolo" \ |
| 80 | "--prompt --prompt" "Duplicate argument: --prompt" "duplicate prompt" \ |
| 81 | "--brainstorm --brainstorm" "Duplicate argument: --brainstorm" "duplicate brainstorm" \ |
| 82 | "--writing-plan --writing-plan" "Duplicate argument: --writing-plan" "duplicate writing plan" \ |
| 83 | "--architect --architect" "Duplicate argument: --architect" "duplicate architect" \ |
| 84 | "--architect --new --new" "Duplicate argument: --new" "duplicate new" \ |
| 85 | "--brainstorm --writing-plan" "mutually exclusive" "planning mode conflict" \ |
| 86 | "--new" "--new requires --architect" "orphan new" \ |
| 87 | "--architect --prompt" "may combine only with --new and --yolo" "architect prompt conflict" \ |
| 88 | "--architect --brainstorm" "may combine only with --new and --yolo" "architect brainstorm conflict" \ |
| 89 | "--architect --writing-plan" "may combine only with --new and --yolo" "architect writing conflict"; do |
| 90 | run_plan "$args" |
| 91 | assert_equal 1 "$rc" "$label status" |
| 92 | assert_contains "$output" "$expected" "$label message" |
| 93 | done |
| 94 | |
| 95 | export CLAUDE_CODE_SAFE_MODE=1 |
| 96 | output="$(_plan_build_superpowers_preflight 2>&1)" |
| 97 | rc=$? |
| 98 | unset CLAUDE_CODE_SAFE_MODE |
| 99 | assert_equal 1 "$rc" "safe mode failure" |
| 100 | assert_contains "$output" "safe mode disables Superpowers" "safe mode guidance" |
| 101 | |
| 102 | mkdir -p "$fixture_dir/bin" "$fixture_dir/home/.augment" "$fixture_dir/enhance-tmp" |
| 103 | print -r -- '{}' >| "$fixture_dir/home/.augment/session.json" |
| 104 | print -r -- '#!/bin/sh' >| "$fixture_dir/bin/script" |
| 105 | print -r -- 'printf "%s\n" "$@" > "$PLAN_BUILD_SCRIPT_ARGS"' >> "$fixture_dir/bin/script" |
| 106 | print -r -- 'if [ "$1" = -q ] && [ "$2" = -t ]; then log_file=$4; shift 4; "$@" > "$log_file"; exit $?; fi' >> "$fixture_dir/bin/script" |
| 107 | print -r -- 'log_file=$5; printf "Enhanced prompt: linux result\n" > "$log_file"; exit "${PLAN_BUILD_SCRIPT_STATUS:-0}"' >> "$fixture_dir/bin/script" |
| 108 | print -r -- '#!/bin/sh' >| "$fixture_dir/bin/auggie" |
| 109 | print -r -- 'printf "Enhanced prompt: mac result\n"; exit "${PLAN_BUILD_AUGGIE_STATUS:-0}"' >> "$fixture_dir/bin/auggie" |
| 110 | chmod +x "$fixture_dir/bin/script" "$fixture_dir/bin/auggie" |
| 111 | |
| 112 | _plan_build_confirm_indexing() { return 1 } |
| 113 | |
| 114 | typeset platform script_args enhance_output |
| 115 | trap 'print -u2 -r -- "test INT trap"' INT |
| 116 | typeset caller_int_trap="$(trap -p INT)" |
| 117 | for platform in Darwin Linux; do |
| 118 | _plan_build_platform() { print -r -- "$platform" } |
| 119 | : >| "$fixture_dir/script-args" |
| 120 | enhance_output="$fixture_dir/enhanced-$platform" |
| 121 | HOME="$fixture_dir/home" TMPDIR="$fixture_dir/enhance-tmp" \ |
| 122 | PATH="$fixture_dir/bin:$PATH" PLAN_BUILD_SCRIPT_ARGS="$fixture_dir/script-args" \ |
| 123 | _plan_build_real_enhance_prompt "raw prompt" "$enhance_output" |
| 124 | rc=$? |
| 125 | assert_equal 0 "$rc" "$platform script enhancement status" |
| 126 | script_args="$(<"$fixture_dir/script-args")" |
| 127 | if [[ "$platform" == Darwin ]]; then |
| 128 | assert_contains "$script_args" $'-t\n0' "macOS script immediate flushing" |
| 129 | assert_contains "$script_args" $'auggie\n--print' "macOS script direct executable dispatch" |
| 130 | assert_not_contains "$script_args" $'\ncommand\n' "macOS script excludes shell builtin" |
| 131 | assert_equal "mac result" "$(<"$enhance_output")" "macOS parsed enhancement" |
| 132 | else |
| 133 | assert_contains "$script_args" $'-q\n-f' "Linux script immediate flushing" |
| 134 | assert_contains "$script_args" $'-e\n-O' "Linux script propagates child status" |
| 135 | assert_contains "$script_args" "-c" "Linux script command mode" |
| 136 | assert_equal "linux result" "$(<"$enhance_output")" "Linux parsed enhancement" |
| 137 | fi |
| 138 | assert_equal 0 "$(count_matches "$fixture_dir/enhance-tmp" plan-build-auggie)" "$platform run directory cleanup" |
| 139 | done |
| 140 | assert_equal "$caller_int_trap" "$(trap -p INT)" "enhancement traps remain local to caller" |
| 141 | trap - INT |
| 142 | |
| 143 | platform=Darwin |
| 144 | _plan_build_platform() { print -r -- "$platform" } |
| 145 | PLAN_BUILD_AUGGIE_STATUS=7 HOME="$fixture_dir/home" TMPDIR="$fixture_dir/enhance-tmp" \ |
| 146 | PATH="$fixture_dir/bin:$PATH" PLAN_BUILD_SCRIPT_ARGS="$fixture_dir/script-args" \ |
| 147 | _plan_build_real_enhance_prompt "partial prompt" "$fixture_dir/failed-enhancement" >/dev/null 2>&1 |
| 148 | rc=$? |
| 149 | assert_equal 7 "$rc" "nonzero Auggie status propagated" |
| 150 | [[ ! -e "$fixture_dir/failed-enhancement" ]] || fail "failed Auggie output was accepted" |
| 151 | assert_equal 0 "$(count_matches "$fixture_dir/enhance-tmp" plan-build-auggie)" "failed run directory cleanup" |
| 152 | |
| 153 | platform=Linux |
| 154 | PLAN_BUILD_SCRIPT_STATUS=7 HOME="$fixture_dir/home" TMPDIR="$fixture_dir/enhance-tmp" \ |
| 155 | PATH="$fixture_dir/bin:$PATH" PLAN_BUILD_SCRIPT_ARGS="$fixture_dir/script-args" \ |
| 156 | _plan_build_real_enhance_prompt "partial prompt" "$fixture_dir/failed-linux-enhancement" >/dev/null 2>&1 |
| 157 | rc=$? |
| 158 | assert_equal 7 "$rc" "Linux script child status propagated" |
| 159 | [[ ! -e "$fixture_dir/failed-linux-enhancement" ]] || fail "failed Linux Auggie output was accepted" |
| 160 | |
| 161 | print -r -- $'noise\r\n\e[32m✨ Enhanced prompt: first\e[0m\r\nsecond\r\n🤖 tool' >| "$fixture_dir/parser.log" |
| 162 | _plan_build_parse_auggie_output "$fixture_dir/parser.log" "$fixture_dir/parser.out" |
| 163 | assert_equal $'first\nsecond' "$(<"$fixture_dir/parser.out")" "parser strips terminal output and stops at marker" |
| 164 | print -r -- "no enhanced prompt" >| "$fixture_dir/parser-empty.log" |
| 165 | _plan_build_parse_auggie_output "$fixture_dir/parser-empty.log" "$fixture_dir/parser-empty.out" |
| 166 | [[ ! -s "$fixture_dir/parser-empty.out" ]] || fail "parser accepted output without marker" |
| 167 | |
| 168 | typeset -g enhanced_text="enhanced payload" |
| 169 | typeset -gi enhanced_approval=0 |
| 170 | typeset -gi architect_preflight_result=0 |
| 171 | typeset -gi enhancement_result=0 |
| 172 | |
| 173 | _plan_build_require_cli() { |
| 174 | print -r -- "SEAM:cli:$1" |
| 175 | return 0 |
| 176 | } |
| 177 | _plan_build_superpowers_preflight() { |
| 178 | print -r -- "SEAM:superpowers" |
| 179 | return 0 |
| 180 | } |
| 181 | _plan_build_architect_preflight() { |
| 182 | print -r -- "SEAM:architect-preflight" |
| 183 | return "$architect_preflight_result" |
| 184 | } |
| 185 | _plan_build_enhance_prompt() { |
| 186 | print -r -- "SEAM:auggie:$1" |
| 187 | print -r -- "$enhanced_text" >| "$2" |
| 188 | return "$enhancement_result" |
| 189 | } |
| 190 | _plan_build_confirm_enhanced_prompt() { |
| 191 | print -r -- "SEAM:enhanced-approval" |
| 192 | return "$enhanced_approval" |
| 193 | } |
| 194 | _plan_build_launch_claude() { |
| 195 | print -r -- "SEAM:claude" |
| 196 | printf 'CLAUDE_YOLO=<%s>\nCLAUDE_PROMPT=<%s>\n' "$1" "$2" |
| 197 | } |
| 198 | |
| 199 | run_plan "" "first line\nsecond line\nEOF\nignored line\n" |
| 200 | assert_equal 0 "$rc" "standard mode status" |
| 201 | assert_contains "$output" "Planning mode: standard" "standard planning prompt" |
| 202 | assert_contains "$output" $'first line\nsecond line' "multiline payload" |
| 203 | assert_not_contains "$output" "ignored line" "EOF terminates payload" |
| 204 | |
| 205 | run_plan "--brainstorm" "build feature\nEOF\n" |
| 206 | assert_equal 0 "$rc" "brainstorm status" |
| 207 | assert_contains "$output" "Planning mode: brainstorm" "brainstorm prompt" |
| 208 | assert_equal "cli:claude superpowers cli:codex cli:coderabbit claude" "$(seam_events "$output")" "brainstorm preflight ordering" |
| 209 | |
| 210 | run_plan "--writing-plan" "build feature\nEOF\n" |
| 211 | assert_equal 0 "$rc" "writing-plan status" |
| 212 | assert_contains "$output" "Planning mode: writing-plan" "writing-plan prompt" |
| 213 | |
| 214 | enhanced_approval=0 |
| 215 | run_plan "--prompt" "raw payload\nEOF\n" |
| 216 | assert_equal 0 "$rc" "prompt mode approved status" |
| 217 | assert_contains "$output" "enhanced payload" "enhanced prompt used" |
| 218 | assert_contains "$output" "Enhanced prompt approved" "enhanced approval reported" |
| 219 | assert_equal "cli:claude cli:codex cli:coderabbit cli:auggie cli:script cli:perl auggie:raw payload enhanced-approval claude" "$(seam_events "$output")" "prompt seams ordering" |
| 220 | |
| 221 | enhancement_result=9 |
| 222 | run_plan "--prompt" "failed child\nEOF\n" |
| 223 | assert_equal 9 "$rc" "prompt child failure status propagated" |
| 224 | assert_not_contains "$output" "enhanced-approval" "failed enhancement is never parsed or approved" |
| 225 | assert_not_contains "$output" "SEAM:claude" "failed enhancement prevents launch" |
| 226 | assert_equal 0 "$(count_matches "$TMPDIR" plan-build-enhanced)" "enhanced temporary file cleanup" |
| 227 | enhancement_result=0 |
| 228 | |
| 229 | run_plan "--prompt --brainstorm" "raw combination\nEOF\n" |
| 230 | assert_equal 0 "$rc" "prompt brainstorm status" |
| 231 | assert_contains "$output" "Planning mode: brainstorm" "prompt brainstorm planning" |
| 232 | |
| 233 | enhanced_approval=1 |
| 234 | run_plan "--prompt" "declined\nEOF\n" |
| 235 | assert_equal 0 "$rc" "prompt rejection exits cleanly" |
| 236 | assert_contains "$output" "not approved" "prompt rejection message" |
| 237 | assert_equal "cli:claude cli:codex cli:coderabbit cli:auggie cli:script cli:perl auggie:declined enhanced-approval" "$(seam_events "$output")" "prompt rejection prevents launch" |
| 238 | enhanced_approval=0 |
| 239 | |
| 240 | run_plan "--architect" "design feature\nEOF\n" |
| 241 | assert_equal 0 "$rc" "architect safe-resume status" |
| 242 | assert_contains "$output" "Start mode: safe-resume-detection" "architect safe-resume mode" |
| 243 | assert_contains "$output" "documentation-first architect/orchestrator role" "architect role boundary" |
| 244 | assert_equal "architect-preflight claude" "$(seam_events "$output")" "architect preflight before launch" |
| 245 | |
| 246 | run_plan "--architect --new" "fresh design\nEOF\n" |
| 247 | assert_equal 0 "$rc" "architect new status" |
| 248 | assert_contains "$output" "Start mode: archive-and-start-new" "architect new mode" |
| 249 | |
| 250 | run_plan "--architect --yolo" "fast design\nEOF\n" |
| 251 | assert_equal 0 "$rc" "architect yolo status" |
| 252 | assert_contains "$output" "CLAUDE_YOLO=<1>" "architect yolo reaches launch seam" |
| 253 | assert_contains "$output" "including every gate" "architect yolo retains gates" |
| 254 | |
| 255 | architect_preflight_result=1 |
| 256 | run_plan "--architect" "must not be read\nEOF\n" |
| 257 | assert_equal 1 "$rc" "architect preflight failure status" |
| 258 | assert_not_contains "$output" "Reading payload" "architect preflight precedes payload" |
| 259 | assert_equal "architect-preflight" "$(seam_events "$output")" "architect failure stops processing" |
| 260 | architect_preflight_result=0 |
| 261 | |
| 262 | run_plan "" "EOF\n" |
| 263 | assert_equal 1 "$rc" "empty payload status" |
| 264 | assert_contains "$output" "Payload was empty" "empty payload message" |
| 265 | |
| 266 | print -r -- '#!/bin/sh' >| "$fixture_dir/bin/claude" |
| 267 | print -r -- 'printf "REAL_CLAUDE:"; printf " <%s>" "$@"; printf "\n"' >> "$fixture_dir/bin/claude" |
| 268 | chmod +x "$fixture_dir/bin/claude" |
| 269 | output="$(PATH="$fixture_dir/bin:$PATH" _plan_build_real_launch_claude 1 "payload")" |
| 270 | assert_contains "$output" "<--dangerously-skip-permissions>" "yolo translates to real Claude argument" |
| 271 | assert_not_contains "$output" "<--yolo>" "yolo alias not forwarded" |
| 272 | |
| 273 | if (( failures )); then |
| 274 | print -u2 -r -- "FAIL: $failures plan_build test(s)" |
| 275 | exit 1 |
| 276 | fi |
| 277 | |
| 278 | print -r -- "PASS: plan_build tests" |
| 279 |