Architecture Decision Records
ADR-001: Documentation-First Architect Mode
Status: Accepted
Date: 2026-07-28
Context
The existing plan_build workflow makes Claude Code the implementation orchestrator but does not enforce durable specifications, atomic Codex task prompts, client handover documentation, or a strict separation between documentation and code execution.
Decision
Add an opt-in built-in --architect mode. Claude Code owns discovery, architecture, documentation, dispatch, and verification. Codex is the sole code and test-code executor. CodeRabbit reviews every task and the final aggregate diff. Tasks execute sequentially, and all approved work is committed once after final verification and explicit user approval.
Alternatives Considered
- Replace the default workflow: rejected because direct implementation remains useful.
- Keep Claude as an implementation fallback: rejected because it weakens the requested role boundary.
- Execute tasks in parallel: rejected because sequential execution provides clearer scope, review, and failure attribution.
Consequences
- Architect mode requires an interactive terminal and additional workflow state documents.
- Long-running work can resume from repository records rather than chat history.
- Delivery takes more approval steps but produces a complete auditable handover package.
Affected Components
functest_plan_build.zsharchitect-workflow.md- Installer scripts addressed by
TASK-003
ADR-002: Remove the Standalone v2 Integration
Status: Accepted
Date: 2026-07-28
Context
The built-in --v2 path downloads a standalone script from an unavailable endpoint. Retaining the path exposes a broken user interface and creates a second workflow source that can drift.
Decision
Remove the v2 downloader, parser branch, passthrough behavior, usage text, and documentation. --v2 now follows the normal unknown-argument path.
Alternatives Considered
- Repair or recreate the standalone gist: rejected because v2 is no longer required.
- Preserve the broken option as undocumented behavior: rejected because it would remain a misleading public contract.
Consequences
--grilland--distributeare no longer reachable throughplan_build.- The built-in command has one maintained implementation path.
Affected Components
functest_plan_build.zshREADME.md
Changelog
All notable changes to this project are documented in this file.
Unreleased
Added
TASK-002: Added built-inplan_build --architectwith safe-resume, new-workflow, yolo, preflight, and command-level regression coverage.- Added the canonical documentation-first architect workflow source.
Changed
- Documented architect mode prerequisites and supported flag combinations.
Removed
TASK-001: Removed the obsolete standalone v2 downloader, forwarding behavior, and public usage contract.
Fixed
TASK-002: Reject bare Git repositories during architect preflight while accepting normal worktrees.
Bash Script Installer for Zsh
The "Bash Script Installer" simplifies the setup of Zsh.
Zsh
Ubuntu
bash -c "$(curl -fsSL https://tinyurl.com/y6fhb594)"
WSL
bash -c "$(curl -fsSL https://tinyurl.com/ycyct6wp)"
MacOS
bash -c "$(curl -fsSL https://tinyurl.com/yptybtpa)"
Configuration
bash -c "$(curl -fsSL https://tinyurl.com/y8vf753j)"
Shell framework and prompt
The installers use Oh My Zsh for framework features and plugins, with the OMZ theme disabled:
ZSH_THEME=""
source "$ZSH/oh-my-zsh.sh"
eval "$(starship init zsh)"
Starship owns the prompt and loads after Oh My Zsh. The managed Starship config is installed at ~/.config/starship.toml. A Nerd Font is required for the Powerline symbols.
Plan-build workflow
plan_build launches Claude Code as the implementation orchestrator, with Codex and CodeRabbit as independent reviewers. The configuration installer also installs its workflow at ~/.claude/skills/plan-build/SKILL.md.
Architect mode keeps Claude focused on architecture and documentation while Codex implements one approved task at a time and CodeRabbit reviews each task. It uses ~/.claude/skills/plan-build-architect/SKILL.md.
plan_build # Standard plan and build
plan_build --prompt # Enhance the payload with Auggie first
plan_build --writing-plan # Superpowers implementation plan, Codex review, then build
plan_build --prompt --brainstorm # Auggie, Superpowers design and plan, Codex review, then build
plan_build --yolo --writing-plan # Pass --yolo through to Claude Code
plan_build --architect # Documentation-first architecture workflow
plan_build --architect --new # Archive existing planning state and start a new workflow
plan_build --architect --yolo # Forward --yolo without bypassing workflow approval gates
--brainstorm and --writing-plan are mutually exclusive. They require the enabled superpowers@claude-plugins-official Claude Code plugin; plan_build reports the install or enable command and exits before Auggie when the requirement is not met.
--architect may combine only with --new and --yolo. It requires an interactive terminal, a Git worktree, Claude Code, Codex, CodeRabbit, Git, and the installed architect skill. The obsolete --v2 integration has been removed.
On a machine without this full setup (config.sh normally installs the skill), install just the plan-build skill with:
mkdir -p ~/.claude/skills/plan-build ~/.claude/skills/plan-build-architect
curl -fsSL https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD/orchestrate-loop.md -o ~/.claude/skills/plan-build/SKILL.md
curl -fsSL https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD/architect-workflow.md -o ~/.claude/skills/plan-build-architect/SKILL.md
Plan Build Architect Mode Specification
1. Executive Summary & Goals
Problem Statement
The existing plan_build command asks Claude Code to plan and implement a task through an eight-step multi-agent workflow. It does not provide a documentation-first delivery mode in which Claude acts strictly as architect and documentation manager while OpenAI Codex performs all implementation work.
This creates three handover risks:
- Requirements and architecture decisions can remain implicit in agent conversations.
- Implementation tasks may not have durable, atomic scopes and acceptance criteria.
- The final repository may lack the user, operational, API, deployment, security, and acceptance documentation required for a client handover.
Solution
Add an optional built-in plan_build --architect mode. In this mode, Claude Code owns discovery, specifications, task decomposition, review orchestration, progress records, and final handover documentation. Codex is the sole implementation executor. CodeRabbit reviews every completed task and the final aggregate diff.
Architect mode follows three gated phases:
- Discovery and Architecture: Claude interviews the user one question at a time and recommends a default answer for each decision.
- Documentation Synthesis: After an explicit phase-completion phrase, Claude writes the approved requirements into
SPEC.mdwithout asking additional questions. - Codex Execution Plan: After specification approval, Claude writes atomic sequential task briefs and self-contained Codex prompts into
TASKS.md.
Codex tasks execute only after TASKS.md approval. Exactly one task may be active at a time. All implementation and documentation changes remain uncommitted until the complete workflow, reviews, tests, and client handover package pass final verification. The user must approve the single final commit. The workflow never pushes automatically.
Goals
- Preserve the existing default
plan_buildbehavior. - Add
--architectas an explicit opt-in mode. - Remove the obsolete and broken
--v2integration from the built-in command. - Keep Claude from writing implementation or test code in architect mode.
- Make Codex tasks atomic, sequential, auditable, and constrained by file scope.
- Require explicit approval at every phase and execution boundary.
- Require CodeRabbit review after every task and over the final combined diff.
- Produce complete internal engineering records and client-facing handover documentation.
- Resume interrupted architect workflows safely.
- Preserve existing project documentation and user work.
- Produce one user-approved commit only after all tasks are complete.
Non-Goals
- Replacing the existing standard, brainstorm, writing-plan, prompt-enhancement, or yolo workflows.
- Allowing unattended architect execution.
- Allowing Claude to implement application or test code.
- Automatically pushing the final commit.
- Inventing project behavior, infrastructure, controls, or APIs that do not exist.
2. User Stories
- As a project owner, I want Claude to interview me one question at a time, so that architectural decisions remain understandable and deliberate.
- As a project owner, I want each question to include a recommended default, so that discovery can proceed efficiently.
- As a project owner, I want explicit phase gates, so that no specification or implementation begins before I approve it.
- As a project owner, I want a cohesive
SPEC.md, so that requirements and contracts have one approved baseline. - As a project owner, I want atomic Codex prompts in
TASKS.md, so that implementation work has clear boundaries. - As a project owner, I want only one Codex task active at a time, so that failures and diffs remain attributable.
- As a project owner, I want Claude to inspect but not implement code, so that architecture and execution responsibilities remain separated.
- As a project owner, I want CodeRabbit to review every task, so that defects are found before dependent work begins.
- As a project owner, I want one final holistic review, so that cross-task regressions are detected.
- As a project owner, I want all work committed once at the end, so that the complete delivery appears as one coherent change.
- As a project owner, I want interrupted sessions to resume from durable records, so that long projects do not lose state.
- As a client, I want user and operational documentation, so that I can adopt and run the delivered system.
- As a client, I want deployment and rollback instructions, so that releases can be managed safely.
- As a client, I want security and ownership documentation, so that operational responsibilities are explicit.
- As a client, I want a formal acceptance checklist, so that handover completion is objectively verifiable.
- As an engineer, I want approved specification changes recorded, so that implementation does not silently drift from requirements.
- As an engineer, I want file-scope enforcement, so that Codex cannot introduce unrelated changes.
- As an engineer, I want focused tests per task and full verification at completion, so that failures are localized without sacrificing system confidence.
- As an operator, I want documentation claims tied to source files and verified commands, so that instructions can be trusted.
- As a security reviewer, I want secrets excluded from prompts and documents, so that the workflow does not leak credentials.
3. System Architecture & Tech Stack
Existing Context
The feature extends a Zsh function-based command distributed through the Zsh Setup OpenGist. The current command launches Claude Code with an installed workflow document. Existing behavior is covered by a Zsh test script that stubs external CLIs and asserts command-level output and argument contracts.
Runtime Components
| Component | Responsibility in architect mode |
|---|---|
plan_build Zsh function |
Parse flags, reject incompatible combinations, run preflight, collect the initial payload, and launch Claude with the architect workflow. |
| Claude Code | Lead architect, documentation manager, task dispatcher, scope verifier, review triager, and phase-gate controller. |
| OpenAI Codex CLI | Sole application and test-code executor, invoked non-interactively for one approved task at a time. |
| CodeRabbit CLI | Automated reviewer after every task and over the final aggregate diff. |
| Git | Baseline detection, diff isolation, traceability, and one final user-approved commit. |
| Markdown files | Durable workflow state, approved requirements, decisions, execution records, and client handover package. |
Canonical Workflow Source
Architect mode must have one canonical workflow document in the Zsh Setup gist. The configuration installer and all supported platform installers distribute that same document to:
~/.claude/skills/plan-build-architect/SKILL.md
The active local installation and published OpenGist source must remain identical after rollout.
Execution Topology
flowchart TD
U[Project Owner] -->|Initial payload| PB[plan_build --architect]
PB -->|Preflight and architect prompt| C[Claude Code]
C -->|One question at a time| U
U -->|Explicit phase approvals| C
C -->|Writes and maintains| D[Engineering and handover Markdown]
C -->|One approved task prompt| X[Codex CLI]
X -->|Scoped code and tests| W[Shared Git worktree]
C -->|Inspect diff and verify| W
C -->|Review request| R[CodeRabbit CLI]
R -->|Findings| C
C -->|Valid fixes delegated| X
C -->|Task and final approval requests| U
U -->|Final commit approval| C
C -->|One commit, no push| G[Git history]
Responsibility Boundary
Claude may inspect every project file but may create or modify only documentation and orchestration records during architect execution:
SPEC.mdTASKS.mdADR.mdCHANGELOG.mdREADME.md- Files under
docs/that form the client handover package
Codex owns all application code, configuration code, migrations, generated source, and test-code modifications. If a required change falls outside an approved task scope, Codex stops and reports the required expansion rather than modifying the file.
Interactive Constraint
Architect mode requires a usable interactive terminal. It must fail before modifying project files when a TTY is unavailable. This protects all mandatory approval gates.
4. Data Models & Schemas
Architect mode introduces no application database schema. Its durable state is represented by structured Markdown documents.
Project Metadata
Discovery must collect:
- Project name
- Client or owning organization
- Product purpose
- Target users
- Repository scope
- Supported environments
- Deployment owner
- Operational owner
- Support contact role
- Compliance constraints
- Intended handover date
Sensitive identities may use user-approved placeholders.
Task Record
Every TASKS.md task must contain:
| Field | Requirement |
|---|---|
| ID | Stable sequential identifier such as TASK-001. |
| Title | Concise implementation outcome. |
| Status | One of Pending, In Progress, Blocked, Review, or Complete. |
| Dependencies | IDs that must be complete first, or None. |
| Objective | Exact feature, behavior, or function Codex must implement. |
| Allowed File Scope | Exhaustive files or bounded path patterns Codex may create or modify. |
| Context / Interfaces | Approved schemas, contracts, imports, conventions, and relevant existing behavior. |
| Prompt for Codex | Self-contained non-interactive execution prompt. |
| Acceptance Criteria | Observable behavior and required test coverage. |
| Verification Commands | Focused lint, type-check, unit, integration, or build commands. |
| CodeRabbit Outcome | Findings, triage result, and rerun status. |
| Completion Evidence | Commands run, results, and relevant diff evidence. |
Only one task may have In Progress status.
Task State Transitions
stateDiagram-v2
[*] --> Pending
Pending --> InProgress: approved and dependencies complete
InProgress --> Review: Codex reports completion
InProgress --> Blocked: execution or scope blocker
Review --> InProgress: valid findings require fixes
Review --> Complete: acceptance and review pass
Blocked --> Pending: user approves revised scope or architecture
Complete --> [*]
Architecture Decision Record
ADR.md is append-only. Every decision uses a stable ID such as ADR-001 and records:
- Status
- Date
- Context
- Decision
- Alternatives considered
- Consequences
- Affected files or components
- Superseding ADR, when applicable
Superseded decisions remain visible.
Changelog
CHANGELOG.md follows Keep a Changelog conventions. Work remains under Unreleased until the final delivery and is grouped under:
- Added
- Changed
- Fixed
- Security
- Deprecated
- Removed
Entries reference task IDs. Detailed execution evidence remains in TASKS.md.
Mandatory Handover Package
The following files are mandatory without exception:
| File | Audience and purpose |
|---|---|
README.md |
Product overview, prerequisites, quick start, and configuration. |
SPEC.md |
Approved requirements, architecture, schemas, contracts, constraints, and acceptance baseline. |
TASKS.md |
Internal task execution and evidence record. |
ADR.md |
Internal architecture decision history. |
CHANGELOG.md |
Delivery and release history. |
docs/USER_MANUAL.md |
End-user workflows and expected outcomes. |
docs/RUNBOOK.md |
Operations, monitoring, backup, recovery, and troubleshooting. |
docs/API.md |
Authentication, endpoints or commands, examples, and errors. |
docs/DEPLOYMENT.md |
Environments, infrastructure, release, rollback, and validation. |
docs/SECURITY.md |
Access control, secret handling, data protection, rotation, and known risks. |
docs/HANDOVER.md |
Deliverables, ownership, support boundaries, limitations, and formal acceptance. |
Non-applicable material must be marked Not Applicable with a factual rationale. Files may not be omitted.
5. CLI & Component Contracts
Supported Invocation
plan_build --architect
Optional architect flags:
--new: Start a new architect workflow after archiving existing workflow documents.--yolo: Forward Claude's permissive execution mode but never bypass architect approval gates.
Incompatible Flags
Architect mode must reject combinations with:
--brainstorm--writing-plan--prompt
The --v2 option, its downloader function, and its usage documentation must be removed entirely. After removal, passing --v2 follows the normal unknown-argument error path. The built-in workflow does not expose the former v2-only --grill or --distribute concepts.
Preflight Contract
Before discovery or file modification, architect mode verifies:
- Interactive TTY
- Git repository context
claudecodexcoderabbitgit- Installed architect workflow document
A missing requirement causes a non-zero exit with exact remediation guidance. Project-specific tools are checked when their approved tasks require them.
Phase-Gate Contract
Phase 1 to Phase 2
Claude may leave discovery only after the user states either:
I am ready to generate the specPhase 1 complete
Claude asks one question at a time and includes one recommended default with every question.
Phase 2 to Phase 3
Claude writes SPEC.md without asking more questions. It waits for explicit specification approval before creating TASKS.md.
Phase 3 to Execution
Claude writes TASKS.md and waits for explicit task-plan approval before invoking Codex.
Between Tasks
Claude dispatches one task, verifies scope and acceptance criteria, completes CodeRabbit triage, reports evidence, and waits for user approval before starting the next task.
Final Commit
Claude presents the final verification results, aggregate diff summary, and proposed commit message. It stages and commits only after explicit user approval. It never pushes.
Codex Invocation Contract
Claude invokes Codex non-interactively in the current project worktree with workspace-write access. The exact prompt stored in the approved task record is passed to Codex. Every prompt includes:
- Objective
- Allowed File Scope
- Context / Interfaces
- Acceptance Criteria
- Unit or integration test requirements
- Verification commands
- Prohibition on commits
- Instruction to stop before touching out-of-scope files
Codex execution output is captured for verification. Claude independently inspects the diff and reruns required commands.
CodeRabbit Contract
CodeRabbit runs after each Codex task and once over the final combined diff.
- Claude investigates every concrete finding.
- Valid findings are delegated to Codex within the same task.
- Critical, high, and medium findings block progress until resolved.
- Low-severity findings may be deferred only with user approval.
- Approved deferrals are recorded in
CHANGELOG.mdanddocs/HANDOVER.md. - Tests and CodeRabbit rerun after substantive fixes.
Retry Contract
After the initial Codex attempt, Claude may dispatch at most two focused correction attempts. If acceptance criteria still fail:
- Stop execution.
- Mark the task
Blocked. - Preserve the partial diff.
- Record diagnostics and attempted corrections.
- Ask the user whether to revise architecture, scope, or acceptance criteria.
- Do not start dependent tasks.
Resume Contract
When workflow documents already exist, Claude:
- Reads
SPEC.md,TASKS.md,ADR.md, andCHANGELOG.md. - Compares their recorded state with Git and the current worktree.
- Identifies the first incomplete task.
- Presents the detected state and asks whether to resume.
--new must archive existing workflow documents before creating replacements. It must never overwrite them silently.
6. Documentation Contracts
Existing Documentation
Claude updates existing documents in place and preserves valid project-specific content. It creates only missing files. Wholesale replacement is prohibited unless reconciliation proves that no valid content would be lost.
Client-Facing Content
Client documents describe the delivered system and do not expose Claude, Codex, CodeRabbit, prompts, or internal execution mechanics unless contractual or compliance requirements require AI disclosure.
Traceability
Implementation-specific claims reference relevant source files, configurations, schemas, endpoints, or verified commands. Handover documents include:
- Software version or final commit SHA
- Generation date
- Document owner
- Last verification date
Diagrams
Use maintainable Mermaid diagrams for system context, component interactions, deployment topology, and critical operational sequences when applicable. If a diagram is not applicable, state why.
Handover Acceptance
docs/HANDOVER.md includes:
- Deliverables
- Environment and access prerequisites
- Verified capabilities
- Known limitations
- Approved deferred risks
- Support boundaries
- Warranty assumptions
- Ownership transfer
- Dated client acceptance checklist
- Sign-off placeholders
7. Edge Cases & Constraints
Existing Dirty Worktree
Claude records the initial Git status and preserves pre-existing work. It may not revert, overwrite, stage, or include unrelated changes. If approved and pre-existing changes cannot be distinguished safely, execution stops for user direction.
Scope Expansion
Codex stops when required work exceeds the task's allowed scope. Claude assesses the impact, updates SPEC.md, TASKS.md, and ADR.md as appropriate, and requests approval before redispatch.
Requirement Changes
Approved SPEC.md is the baseline. Later requirements are explicit amendments. Execution pauses while Claude assesses affected tasks and documentation. Architectural amendments require an ADR. No changed task is dispatched without approval.
Missing Test Framework
The first applicable task establishes the smallest project-appropriate test and validation setup. If automated testing is technically impossible, Claude obtains approval for explicit manual acceptance steps and records the risk in docs/HANDOVER.md.
Secrets and Sensitive Data
Prompts and documents never contain credential values, tokens, private keys, personal data, or production secrets. They may document secret names, environment variables, storage systems, ownership, and rotation procedures. Available secret scanning runs before final commit, and suspected credentials block completion.
Non-Applicable Documentation
Mandatory documents remain present and mark irrelevant sections as Not Applicable with rationale. The workflow never fabricates content to fill a template.
Single Commit
No task-level commits are created. All approved implementation, tests, engineering records, and client documents remain in the worktree until every task is complete and final verification passes. The final commit includes only approved files.
Interrupted Sessions
Durable task states and evidence, not chat history, determine resume position. Claude must validate recorded state against actual files and Git before continuing.
8. Testing & Acceptance Strategy
Primary Test Seam
The highest existing seam is command-level testing of the sourced Zsh function with external CLI stubs. Extend this seam rather than testing private parser details independently.
Required Workflow Tests
--architectproduces the expected Claude invocation and architect workflow instruction.--architect --newis accepted.--architect --yoloforwards yolo behavior while preserving the architect instruction.--architectrejects every incompatible flag combination.- Duplicate architect flags fail with usage guidance.
- Missing TTY fails before workflow modification.
- Missing required CLI or skill fails with remediation guidance.
- Existing non-architect modes retain their current observable behavior.
--v2is rejected as an unknown argument and no v2 downloader remains.- Existing non-v2 modes retain their current observable behavior after v2 removal.
Per-Task Verification
Every Codex task includes focused unit or integration tests and relevant lint, type-check, build, and test commands. Claude reruns them independently before CodeRabbit review is considered complete.
Final Verification
Before requesting commit approval, Claude verifies:
- Every task is
Complete. - No task has unresolved blocking review findings.
- The complete project lint, type-check, build, and test suite passes.
- The aggregate diff contains only approved changes.
- Required documentation files and sections exist.
- Markdown structure and internal links are valid.
- Referenced source paths and commands exist and are accurate.
- Endpoint and command examples match implementation.
- Mermaid syntax is validated where tooling is available.
- No unresolved
TODO, placeholder, or template text remains. - Approved known limitations are recorded in
docs/HANDOVER.md. - Available secret scanning reports no suspected credentials.
- Handover documents match the final implementation.
9. Implementation Decisions
- Architect mode is opt-in and does not replace existing
plan_buildmodes. - Architect mode is implemented only for the built-in workflow.
- The obsolete
_plan_build_v2downloader,--v2parser branch, passthrough logic, usage text, README examples, and standalone-install references are removed. - Claude is the architect and documentation manager, not an implementation agent.
- Codex is the sole implementation and test-code executor.
- CodeRabbit is mandatory for task and final reviews.
- Execution is sequential with one active task.
- User approval is mandatory at phase, task, deferral, and commit boundaries.
- All changes are committed once after complete verification.
- The final commit is never pushed automatically.
- Existing documentation is reconciled rather than overwritten.
- Complete client handover documentation is mandatory without exception.
10. Out of Scope
- Repairing, replacing, or migrating the unavailable standalone v2 script or remote endpoint; only the built-in integration and references are removed.
- Parallel task execution.
- Frontend/backend distribution to different executors.
- Autonomous approval decisions.
- Multiple task commits.
- Automatic pushes, releases, or deployments.
- Replacement of project issue trackers or external documentation systems.
- Generating implementation code during specification work.
11. Definition of Done
Architect mode is complete when:
- The built-in command exposes the approved CLI contract.
- No built-in v2 downloader, flag parser, usage text, or documentation reference remains.
- The canonical architect skill is distributed by every supported installer.
- The current machine can refresh the active function and skill from the published source.
- Command-level tests cover architect behavior, v2 rejection, and preservation of existing non-v2 modes.
- The workflow enforces role boundaries, phase gates, task states, retries, reviews, and the single-commit policy.
- The workflow mandates the complete internal and client documentation package.
- Syntax and workflow tests pass.
- Published OpenGist content matches the verified local source.
Plan Build Architect Mode Execution Plan
Execution Policy
This plan implements the approved SPEC.md. Claude Code is the architect, documentation manager, dispatcher, and verifier. OpenAI Codex is the sole code and test-code executor. CodeRabbit reviews every task and the final aggregate diff.
The following rules apply to every task:
- Execute tasks sequentially in ID order.
- Only one task may be
In Progressat a time. - Do not start a task until its dependencies are
Completeand the user approves dispatch. - Codex may modify only the task's Allowed File Scope.
- Codex must stop and report when required work exceeds that scope.
- Codex must not commit, stage, push, reset, restore, or discard changes.
- Claude independently checks the diff and reruns verification commands.
- CodeRabbit reviews each task. Valid critical, high, and medium findings must be fixed before completion.
- Codex receives at most two focused correction attempts after its initial execution.
- All changes remain uncommitted until every task, final review, full verification, and handover document passes.
- Claude updates
SPEC.md,ADR.md,CHANGELOG.md,README.md, and the mandatorydocs/handover package. Codex does not edit those documents unless a later approved scope amendment explicitly says otherwise.
Claude Documentation Workstream
This workstream runs alongside Codex execution and is not delegated:
- Preserve and reconcile existing documentation rather than replacing it blindly.
- Maintain
SPEC.mdas the approved baseline and record approved amendments. - Create and maintain append-only
ADR.mddecisions. - Maintain Keep a Changelog-compatible
CHANGELOG.mdentries referencing task IDs. - Remove obsolete v2 usage and standalone-install references from
README.mdafterTASK-001passes. - Document
plan_build --architect,--new, compatibility rules, prerequisites, and examples afterTASK-002passes. - Document installer distribution and local refresh behavior after
TASK-003passes. - Create or update
docs/USER_MANUAL.md,docs/RUNBOOK.md,docs/API.md,docs/DEPLOYMENT.md,docs/SECURITY.md, anddocs/HANDOVER.mdbefore final review. - Mark non-applicable sections explicitly with a factual rationale rather than omitting files or inventing behavior.
- Add final commit SHA placeholders only until the final commit exists; replace or explain the self-reference limitation during finalization.
TASK-001: Remove the obsolete v2 integration
Status: Complete
Dependencies: None
Objective
Remove the broken standalone v2 downloader and all --v2 parsing and forwarding behavior from the built-in plan_build function. Preserve every existing non-v2 mode. Make --v2 use the normal unknown-argument failure path.
Allowed File Scope
functest_plan_build.zsh
No other file may be created, modified, renamed, or deleted by Codex.
Context / Interfaces
funccurrently defines_plan_build_v2(), which downloads a now-unavailable standalone script.plan_build()currently performs a preliminary argument pass to strip--v2and forward all remaining arguments.- The normal parser already rejects unknown arguments with a non-zero status and usage output.
- Existing supported behavior includes
--yolo,--prompt,--brainstorm, and--writing-plan. test_plan_build.zshis the highest command-level test seam. It sourcesfunc, stubs external commands, invokesplan_build, and asserts status and output.- Do not redesign the existing parser beyond the smallest change required to remove v2.
Acceptance Criteria
_plan_build_v2()no longer exists.- The v2 URL, temporary-file logic, Bash forwarding, and v2 status handling no longer exist.
plan_build()no longer performs a v2 pre-parse or maintains v2 passthrough arguments.- Usage output contains no
--v2,--grill, or--distributetext. plan_build --v2returns non-zero through the standard unknown-argument path and identifies--v2as unknown.- Existing standard, yolo, prompt, brainstorm, and writing-plan behavior remains unchanged.
- Tests cover v2 rejection and continue covering existing modes.
- Zsh syntax validation and the complete existing plan-build test suite pass.
Verification Commands
zsh -n func
zsh -n test_plan_build.zsh
zsh test_plan_build.zsh
git diff --check -- func test_plan_build.zsh
git diff -- func test_plan_build.zsh
Prompt for Codex
Objective:
Remove the obsolete standalone v2 integration from the built-in plan_build Zsh function. Delete the _plan_build_v2 downloader and remove all --v2 pre-parsing, passthrough, forwarding, and usage text. After the change, --v2 must be handled by the existing normal unknown-argument branch. Preserve all non-v2 behavior.
Allowed File Scope:
- func
- test_plan_build.zsh
Do not create, modify, rename, or delete any other file. Do not stage, commit, push, reset, restore, or discard changes.
Context / Interfaces:
- func defines _plan_build_v2() immediately before plan_build(). It downloads a missing remote plan_build.sh and executes it with Bash.
- plan_build() currently strips --v2 in a preliminary argument loop, then either forwards to _plan_build_v2 or enters the normal parser.
- The normal parser already prints "Error: Unknown argument: <argument>", prints usage, and returns 1.
- Supported non-v2 flags are --yolo, --prompt, --brainstorm, and --writing-plan.
- test_plan_build.zsh is a command-level Zsh test that sources func, stubs external CLI checks and Claude, and asserts output/status.
- Make the smallest correct parser change. Do not refactor unrelated shell functions or reformat unrelated content.
Acceptance Criteria:
1. _plan_build_v2 and all of its URL/download/temporary-file/forwarding code are removed.
2. plan_build has no use_v2 or passthrough state and no preliminary v2 argument pass.
3. Usage text has no v2, grill, or distribute reference.
4. plan_build --v2 returns status 1 and includes "Unknown argument: --v2".
5. Standard, --yolo, --prompt, --brainstorm, and --writing-plan behavior remains unchanged.
6. Add a command-level regression assertion for --v2 rejection.
7. All existing tests pass.
Required Verification:
- zsh -n func
- zsh -n test_plan_build.zsh
- zsh test_plan_build.zsh
- git diff --check -- func test_plan_build.zsh
Report the files changed, tests run, and results. If any required change falls outside the allowed scope, stop and report the needed scope expansion without touching that file.
CodeRabbit Outcome
Passed with zero findings.
Completion Evidence
- Codex removed the v2 downloader and parser forwarding only within the approved files.
plan_build --v2now returns the normal unknown-argument error.zsh -n funcpassed.zsh -n test_plan_build.zshpassed.zsh test_plan_build.zshpassed.git diff --check -- func test_plan_build.zshpassed.
TASK-002: Add the built-in architect command contract
Status: Complete
Dependencies: TASK-001
Objective
Add --architect to the built-in plan_build function. Support --new and --yolo, enforce incompatible and duplicate flag rules, run fail-fast architect preflight, collect the initial payload through the existing input contract, and launch Claude with the canonical installed architect skill. Preserve existing non-architect modes.
Allowed File Scope
functest_plan_build.zsh
No other file may be created, modified, renamed, or deleted by Codex.
Context / Interfaces
- Claude will create the canonical repository workflow document
architect-workflow.mdbefore this task is dispatched. - Installers will later distribute it to
~/.claude/skills/plan-build-architect/SKILL.mdinTASK-003. - Architect mode is interactive and requires
claude,codex,coderabbit,git, a Git worktree, a usable TTY, and a readable non-empty installed skill. - Preflight must run before payload collection or any project-file modification.
--newis valid only with--architect.--architectmay combine only with--newand--yolo.- Architect mode must reject
--prompt,--brainstorm, and--writing-plancombinations. --yolois forwarded to Claude but the architect workflow itself retains mandatory human gates.- The initial payload continues using the existing
EOF-terminated stdin behavior. - Existing command-level tests stub helpers and Claude. Architect tests must not launch real external CLIs or require a real TTY.
Acceptance Criteria
- Usage documents built-in architect syntax and no v2 syntax.
- Duplicate
--architectand duplicate--newfail with usage guidance. --newwithout--architectfails.- Architect combinations with prompt, brainstorm, or writing-plan fail before external tools launch.
- A dedicated architect preflight checks all required tools, Git context, TTY, and installed skill with actionable errors.
- Architect preflight occurs before payload reading.
- Empty payload remains an error.
- The Claude invocation explicitly directs Claude to read
~/.claude/skills/plan-build-architect/SKILL.md, obey the architect role boundary, and treat the payload as the initial high-level requirement. --newis represented unambiguously in the Claude instruction.--yolois forwarded to Claude.- Existing non-architect modes retain current observable behavior.
- Command-level tests cover success, compatibility failures, duplicates, new/resume instruction, yolo forwarding, and preflight failure without invoking real tools.
Verification Commands
zsh -n func
zsh -n test_plan_build.zsh
zsh test_plan_build.zsh
git diff --check -- func test_plan_build.zsh
git diff -- func test_plan_build.zsh
Prompt for Codex
Objective:
Implement the built-in plan_build --architect command contract in Zsh. Architect mode launches Claude as a documentation-first architect/orchestrator using ~/.claude/skills/plan-build-architect/SKILL.md. It must support --new and --yolo, reject incompatible or duplicate flags, run a fail-fast preflight, preserve the existing EOF-terminated payload input, and leave every existing non-architect mode unchanged.
Allowed File Scope:
- func
- test_plan_build.zsh
Do not create, modify, rename, or delete any other file. Do not stage, commit, push, reset, restore, or discard changes.
Context / Interfaces:
- TASK-001 has already removed --v2.
- Existing plan_build supports --yolo, --prompt, --brainstorm, and --writing-plan.
- --architect may combine only with --new and --yolo.
- --new without --architect is invalid.
- --architect combined with --prompt, --brainstorm, or --writing-plan is invalid.
- Duplicate --architect and duplicate --new must fail with usage output.
- Architect preflight must verify, before payload reading: an interactive TTY; a Git worktree; claude, codex, coderabbit, and git availability; and a readable non-empty ~/.claude/skills/plan-build-architect/SKILL.md.
- Error output must identify the missing requirement and provide practical remediation.
- The current input contract reads lines until EOF appears on its own line and rejects an empty payload.
- The Claude prompt must explicitly tell Claude to read ~/.claude/skills/plan-build-architect/SKILL.md, follow it strictly, remain within its documentation/orchestration boundary, and use the payload as the initial high-level requirement.
- When --new is present, the prompt must explicitly request the skill's archive-and-start-new behavior. Otherwise it must permit safe resume detection.
- --yolo forwards the existing Claude yolo argument but does not weaken workflow approval gates.
- test_plan_build.zsh uses function stubs. Add or stub a dedicated architect preflight seam so tests never invoke actual CLIs or depend on the test runner's TTY.
- Prefer the existing command-level test seam. Do not add another test framework.
- Make minimal localized changes and do not refactor unrelated functions.
Acceptance Criteria:
1. Usage includes plan_build --architect [--new] [--yolo].
2. Duplicate architect/new flags and invalid combinations return status 1 with clear errors and usage.
3. Architect preflight executes before payload collection and checks every stated dependency.
4. Empty payload returns status 1.
5. A successful architect invocation directs Claude to the canonical installed skill and includes the initial payload.
6. --new changes the instruction to start a new archived workflow; default behavior permits resume detection.
7. --yolo is forwarded to Claude.
8. Existing standard, prompt, brainstorm, writing-plan, and yolo tests remain green.
9. Tests cover architect success and every parser/preflight edge without launching real external processes.
Required Verification:
- zsh -n func
- zsh -n test_plan_build.zsh
- zsh test_plan_build.zsh
- git diff --check -- func test_plan_build.zsh
Report the files changed, tests run, and results. If any required change falls outside the allowed scope, stop and report the needed scope expansion without touching that file.
CodeRabbit Outcome
CodeRabbit reported one major finding: checking only the exit status of git rev-parse --is-inside-work-tree could accept a bare repository. Codex added an exact true check and regression fixtures for normal and bare repositories. The follow-up CodeRabbit review passed with zero findings.
Completion Evidence
- Codex added the built-in architect parser, preflight, launch prompt, and command-level tests only within the approved files.
- Architect tests cover success, new workflow, safe resume, yolo forwarding, invalid combinations, duplicates, empty payload, preflight ordering, normal worktrees, and bare repository rejection.
zsh -n funcpassed.zsh -n test_plan_build.zshpassed.zsh test_plan_build.zshpassed.git diff --check -- func test_plan_build.zshpassed.- Final CodeRabbit review passed with zero findings.
TASK-003: Distribute and verify the canonical architect skill
Status: Pending
Dependencies: TASK-002
Objective
Extend the configuration and platform installers to atomically install the canonical architect-workflow.md source as ~/.claude/skills/plan-build-architect/SKILL.md. Add repository-level tests that verify every supported installer uses the same source and destination while preserving the existing plan-build skill installation.
Allowed File Scope
config.shzsh_macos.shzsh_ubuntu.shzsh_wsl.shtest_plan_build_installers.zsh(new)
Codex must not modify architect-workflow.md; Claude owns its approved content. No other file may be created, modified, renamed, or deleted by Codex.
Context / Interfaces
config.shcurrently downloadsorchestrate-loop.mdto~/.claude/skills/plan-build/SKILL.mdthrough a temporary file and atomic move.- Each platform installer has equivalent existing plan-build skill installation logic.
- The new canonical repository source is
architect-workflow.md. - The new installed destination is
~/.claude/skills/plan-build-architect/SKILL.md. - Existing plan-build skill distribution must continue unchanged.
- All four installers must use their existing gist raw base rather than introducing a new remote endpoint.
- Failure to download or install the architect skill must be visible and must not leave a partial destination file.
- The new test should validate repository contracts without network access, elevated privileges, home-directory changes, or execution of full platform installers.
Acceptance Criteria
config.shinstalls both the existing plan-build skill and the architect skill.- macOS, Ubuntu, and WSL installers install both skills.
- Every installer downloads
architect-workflow.mdfrom its existing gist raw base. - Every installer targets
~/.claude/skills/plan-build-architect/SKILL.md. - Parent directories are created safely.
- Downloads use temporary files and atomic moves.
- Failed downloads remove temporary files and emit clear errors or warnings consistent with each installer's existing behavior.
- Existing plan-build skill installation remains intact.
- A new Zsh test validates all four installer contracts statically and runs without external services.
- Bash and Zsh syntax checks pass.
Verification Commands
bash -n config.sh zsh_macos.sh zsh_ubuntu.sh zsh_wsl.sh
zsh -n test_plan_build_installers.zsh
zsh test_plan_build_installers.zsh
zsh test_plan_build.zsh
git diff --check -- config.sh zsh_macos.sh zsh_ubuntu.sh zsh_wsl.sh test_plan_build_installers.zsh
git diff -- config.sh zsh_macos.sh zsh_ubuntu.sh zsh_wsl.sh test_plan_build_installers.zsh
Prompt for Codex
Objective:
Distribute the canonical architect workflow through every supported Zsh Setup installer. Install repository file architect-workflow.md from the existing gist raw base as ~/.claude/skills/plan-build-architect/SKILL.md, using the same safe temporary-file and atomic-move pattern as the existing plan-build skill. Add a network-free repository test for the installer contracts.
Allowed File Scope:
- config.sh
- zsh_macos.sh
- zsh_ubuntu.sh
- zsh_wsl.sh
- test_plan_build_installers.zsh (new)
Do not modify architect-workflow.md. Do not create, modify, rename, or delete any other file. Do not stage, commit, push, reset, restore, or discard changes.
Context / Interfaces:
- config.sh and all three platform installers already install orchestrate-loop.md at ~/.claude/skills/plan-build/SKILL.md.
- Preserve that existing installation exactly.
- Add architect-workflow.md as a second canonical source.
- Install it at ~/.claude/skills/plan-build-architect/SKILL.md.
- Use each script's existing GIST_RAW_BASE. Do not add or depend on another gist or endpoint.
- Follow each script's current logging and failure conventions.
- Create destination directories safely.
- Download to a temporary sibling file and move only after successful curl completion.
- Remove temporary files on failure so partial skills cannot become active.
- test_plan_build_installers.zsh must inspect repository files and assert the source filename, installed destination, existing-skill preservation, and safe download pattern for config.sh plus macOS, Ubuntu, and WSL scripts.
- The test must not access the network, invoke sudo, execute the installers, or write to the real home directory.
- Keep changes localized to existing skill-install blocks.
Acceptance Criteria:
1. All four installers install the existing plan-build skill and new architect skill.
2. All use architect-workflow.md and ~/.claude/skills/plan-build-architect/SKILL.md.
3. All use temporary download files and atomic moves, with cleanup on failure.
4. Existing installation behavior remains intact.
5. The new static contract test passes without external services.
6. Existing plan_build tests remain green.
Required Verification:
- bash -n config.sh zsh_macos.sh zsh_ubuntu.sh zsh_wsl.sh
- zsh -n test_plan_build_installers.zsh
- zsh test_plan_build_installers.zsh
- zsh test_plan_build.zsh
- git diff --check -- config.sh zsh_macos.sh zsh_ubuntu.sh zsh_wsl.sh test_plan_build_installers.zsh
Report the files changed, tests run, and results. If any required change falls outside the allowed scope, stop and report the needed scope expansion without touching that file.
CodeRabbit Outcome
Pending.
Completion Evidence
Pending.
Final Review and Handover Gate
This gate begins only after TASK-001, TASK-002, and TASK-003 are Complete.
Claude must:
- Complete all root engineering records and mandatory
docs/handover files. - Run CodeRabbit over the complete uncommitted diff.
- Delegate valid code or test fixes to Codex under the responsible task's original scope and retry limit.
- Run all focused commands plus the complete repository verification suite.
- Validate required documents, links, source references, examples, Mermaid syntax where tooling exists, and absence of unresolved placeholders.
- Run available secret scanning and block suspected credentials.
- Confirm Git contains only approved changes and preserves pre-existing user work.
- Present the final diff summary, verification evidence, known limitations, and proposed single commit message.
- Wait for explicit user approval before staging and creating one commit.
- Never push automatically.
Final Commit
Status: Pending user approval after final gate.
Proposed message: feat: add plan_build architect workflow
| 1 | # ============================================================================= |
| 2 | # OS-SPECIFIC ALIASES |
| 3 | # ============================================================================= |
| 4 | if [[ "$OSTYPE" == "linux-gnu"* ]] && command -v apt-get >/dev/null 2>&1; then |
| 5 | # Linux Only System update & cleanup |
| 6 | alias uu='sudo apt-get update && \ |
| 7 | sudo apt-get upgrade -y && \ |
| 8 | sudo apt-get full-upgrade -y && \ |
| 9 | sudo apt-get autoremove -y && \ |
| 10 | sudo apt-get autoclean -y && \ |
| 11 | sudo apt-get clean' |
| 12 | elif [[ "$OSTYPE" == "darwin"* ]]; then |
| 13 | # macOS Only |
| 14 | alias flushdns='sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder' |
| 15 | fi |
| 16 | |
| 17 | # ============================================================================= |
| 18 | # UNIVERSAL GIT ALIASES (Fixed: Removed broken "$@" from aliases) |
| 19 | # ============================================================================= |
| 20 | alias gph='git push' |
| 21 | alias gco='git checkout' |
| 22 | alias gbh='git branch' |
| 23 | alias gmt='git commit' |
| 24 | alias gpl='git pull' |
| 25 | alias grb='git rebase' |
| 26 | alias grt='git reset' |
| 27 | alias gst='git status' |
| 28 | alias grmrf='git checkout -- . && git clean -fd' |
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.
| 1 | #!/bin/bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | # Configuration |
| 5 | GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD" |
| 6 | CONFIG_FILES=( |
| 7 | ".alias" |
| 8 | ".func" |
| 9 | ".pathrc" |
| 10 | ".sourcerc" |
| 11 | ".vimrc" |
| 12 | ".zshrc" |
| 13 | ".config/starship.toml" |
| 14 | ) |
| 15 | PLAN_BUILD_SKILL_TARGET="$HOME/.claude/skills/plan-build/SKILL.md" |
| 16 | |
| 17 | echo "Starting configuration download..." |
| 18 | download_failed=0 |
| 19 | |
| 20 | for f in "${CONFIG_FILES[@]}"; do |
| 21 | # Remove the leading dot for the URL path |
| 22 | remote_name="${f#.}" |
| 23 | [[ "$f" == ".config/starship.toml" ]] && remote_name="starship.toml" |
| 24 | url="$GIST_RAW_BASE/$remote_name" |
| 25 | target="$HOME/$f" |
| 26 | tmp="${target}.tmp.$$" |
| 27 | |
| 28 | echo "Downloading $f..." |
| 29 | mkdir -p "$(dirname "$target")" |
| 30 | |
| 31 | # Use -f to fail silently on server errors, -s for silent, -L to follow redirects |
| 32 | if curl -fsSL "$url" -o "$tmp" && mv "$tmp" "$target"; then |
| 33 | echo "Successfully updated $target" |
| 34 | else |
| 35 | rm -f "$tmp" |
| 36 | echo "Error: Failed to download $f from $url" >&2 |
| 37 | download_failed=1 |
| 38 | fi |
| 39 | done |
| 40 | |
| 41 | echo "Downloading Claude Code plan-build workflow..." |
| 42 | mkdir -p "$(dirname "$PLAN_BUILD_SKILL_TARGET")" |
| 43 | skill_tmp="${PLAN_BUILD_SKILL_TARGET}.tmp.$$" |
| 44 | if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp" && mv "$skill_tmp" "$PLAN_BUILD_SKILL_TARGET"; then |
| 45 | echo "Successfully updated $PLAN_BUILD_SKILL_TARGET" |
| 46 | else |
| 47 | rm -f "$skill_tmp" |
| 48 | echo "Error: Failed to download the plan-build workflow" >&2 |
| 49 | download_failed=1 |
| 50 | fi |
| 51 | |
| 52 | if [ "$download_failed" -ne 0 ]; then |
| 53 | echo "Configuration download completed with errors." >&2 |
| 54 | exit 1 |
| 55 | fi |
| 56 | |
| 57 | echo "Done! All configuration files have been replaced." |
| 58 |
| 1 | # ============================================================================= |
| 2 | # CUSTOM FUNCTIONS |
| 3 | # ============================================================================= |
| 4 | |
| 5 | # ----------------------------------------------------------------------------- |
| 6 | # Function: funcs (List available .func functions) |
| 7 | # ----------------------------------------------------------------------------- |
| 8 | funcs() { |
| 9 | local func_file="${1:-$HOME/.func}" |
| 10 | |
| 11 | if [ ! -f "$func_file" ]; then |
| 12 | printf "Error: Function file '%s' not found.\n" "$func_file" >&2 |
| 13 | return 1 |
| 14 | fi |
| 15 | |
| 16 | printf "Available functions in %s:\n" "$func_file" |
| 17 | awk ' |
| 18 | /^# Function: / { |
| 19 | line = $0 |
| 20 | sub(/^# Function: /, "", line) |
| 21 | |
| 22 | name = line |
| 23 | sub(/ .*/, "", name) |
| 24 | |
| 25 | desc = "" |
| 26 | if (line ~ / \(.+\)$/) { |
| 27 | desc = line |
| 28 | sub(/^[^ ]+ \(/, "", desc) |
| 29 | sub(/\)$/, "", desc) |
| 30 | } |
| 31 | |
| 32 | if (desc != "") { |
| 33 | printf " %-22s %s\n", name, desc |
| 34 | } else { |
| 35 | printf " %s\n", name |
| 36 | } |
| 37 | } |
| 38 | ' "$func_file" |
| 39 | } |
| 40 | |
| 41 | # ----------------------------------------------------------------------------- |
| 42 | # Function: clip (Cross-platform clipboard) |
| 43 | # ----------------------------------------------------------------------------- |
| 44 | clip() { |
| 45 | local cmd |
| 46 | local args=() |
| 47 | |
| 48 | if command -v pbcopy >/dev/null 2>&1; then |
| 49 | cmd="pbcopy" # macOS |
| 50 | elif grep -qi "microsoft" /proc/version 2>/dev/null && command -v clip.exe >/dev/null 2>&1; then |
| 51 | cmd="clip.exe" # WSL |
| 52 | elif [ "$XDG_SESSION_TYPE" = "wayland" ] && command -v wl-copy >/dev/null 2>&1; then |
| 53 | cmd="wl-copy" # Linux Wayland |
| 54 | elif command -v xclip >/dev/null 2>&1; then |
| 55 | cmd="xclip" # Linux X11 (Fallback 1) |
| 56 | args=("-selection" "clipboard") |
| 57 | elif command -v xsel >/dev/null 2>&1; then |
| 58 | cmd="xsel" # Linux X11 (Fallback 2) |
| 59 | args=("--clipboard" "--input") |
| 60 | else |
| 61 | printf "Error: No supported clipboard utility found.\n" >&2 |
| 62 | return 1 |
| 63 | fi |
| 64 | |
| 65 | if [ $# -gt 0 ]; then |
| 66 | if [ -f "$1" ]; then |
| 67 | "$cmd" "${args[@]}" < "$1" |
| 68 | echo "Copied contents of '$1' to clipboard." |
| 69 | else |
| 70 | printf "Error: File '%s' not found.\n" "$1" >&2 |
| 71 | return 1 |
| 72 | fi |
| 73 | else |
| 74 | "$cmd" "${args[@]}" |
| 75 | fi |
| 76 | } |
| 77 | |
| 78 | # ----------------------------------------------------------------------------- |
| 79 | # Function: open_file (Cross-platform file/directory opener) |
| 80 | # ----------------------------------------------------------------------------- |
| 81 | open_file() { |
| 82 | local target="${1:-.}" |
| 83 | |
| 84 | if [[ "$OSTYPE" == "darwin"* ]]; then |
| 85 | command open "$target" |
| 86 | elif grep -qi "microsoft" /proc/version 2>/dev/null; then |
| 87 | if command -v wslpath >/dev/null 2>&1 && command -v explorer.exe >/dev/null 2>&1; then |
| 88 | explorer.exe "$(wslpath -w "$target")" |
| 89 | else |
| 90 | printf "Error: 'wslpath' or 'explorer.exe' not found.\n" >&2 |
| 91 | return 1 |
| 92 | fi |
| 93 | elif [[ "$OSTYPE" == "linux-gnu"* ]]; then |
| 94 | if command -v xdg-open >/dev/null 2>&1; then |
| 95 | xdg-open "$target" |
| 96 | else |
| 97 | printf "Error: 'xdg-open' not found.\n" >&2 |
| 98 | return 1 |
| 99 | fi |
| 100 | fi |
| 101 | } |
| 102 | alias open='open_file' |
| 103 | |
| 104 | # ----------------------------------------------------------------------------- |
| 105 | # Function: clear_history (Supports shell, Claude, Codex, and OpenCode) |
| 106 | # ----------------------------------------------------------------------------- |
| 107 | clear_history() { |
| 108 | case "${1:-}" in |
| 109 | claude) |
| 110 | if [ -d "$HOME/.claude/projects" ]; then |
| 111 | # Use 'yes' to skip prompts and -f to ignore non-existent files |
| 112 | yes | rm -rf "$HOME/.claude/projects"/* |
| 113 | echo "Claude project history/cache cleared." |
| 114 | fi |
| 115 | ;; |
| 116 | |
| 117 | codex) |
| 118 | local codex_home="${CODEX_HOME:-$HOME/.codex}" |
| 119 | local codex_session_file |
| 120 | local codex_session_id |
| 121 | |
| 122 | mkdir -p "$codex_home" |
| 123 | : > "$codex_home/history.jsonl" |
| 124 | |
| 125 | if command -v codex >/dev/null 2>&1; then |
| 126 | find "$codex_home/sessions" "$codex_home/archived_sessions" -type f -name '*.jsonl' -print 2>/dev/null | while IFS= read -r codex_session_file; do |
| 127 | codex_session_id="$(sed -n 's/.*"session_id":"\([^"]*\)".*/\1/p; q' "$codex_session_file")" |
| 128 | if [ -n "$codex_session_id" ]; then |
| 129 | codex delete --force "$codex_session_id" >/dev/null 2>&1 || true |
| 130 | fi |
| 131 | done |
| 132 | fi |
| 133 | |
| 134 | rm -rf "$codex_home/sessions" "$codex_home/archived_sessions" "$codex_home/shell_snapshots" |
| 135 | mkdir -p "$codex_home/sessions" "$codex_home/archived_sessions" "$codex_home/shell_snapshots" |
| 136 | |
| 137 | echo "Codex history cleared." |
| 138 | ;; |
| 139 | |
| 140 | opencode) |
| 141 | local opencode_state_home="${XDG_STATE_HOME:-$HOME/.local/state}/opencode" |
| 142 | local opencode_data_home="${XDG_DATA_HOME:-$HOME/.local/share}/opencode" |
| 143 | |
| 144 | mkdir -p "$opencode_state_home" "$opencode_data_home" |
| 145 | : > "$opencode_state_home/prompt-history.jsonl" |
| 146 | rm -f \ |
| 147 | "$opencode_data_home/opencode.db" \ |
| 148 | "$opencode_data_home/opencode.db-shm" \ |
| 149 | "$opencode_data_home/opencode.db-wal" |
| 150 | rm -rf "$opencode_data_home/repos" "$opencode_data_home/log" |
| 151 | mkdir -p "$opencode_data_home/repos" "$opencode_data_home/log" |
| 152 | |
| 153 | echo "OpenCode history cleared." |
| 154 | ;; |
| 155 | |
| 156 | *) |
| 157 | # 1. Truncate the file |
| 158 | : > "$HISTFILE" |
| 159 | |
| 160 | # 2. Clear RAM by briefly setting history size to 0 |
| 161 | local old_histsize=$HISTSIZE |
| 162 | HISTSIZE=0 |
| 163 | HISTSIZE=$old_histsize |
| 164 | |
| 165 | echo "Shell history cleared." |
| 166 | ;; |
| 167 | esac |
| 168 | } |
| 169 | |
| 170 | # ----------------------------------------------------------------------------- |
| 171 | # Function: require_cli (Check external CLI availability) |
| 172 | # ----------------------------------------------------------------------------- |
| 173 | require_cli() { |
| 174 | local binary="$1" |
| 175 | local label="$2" |
| 176 | local resolved |
| 177 | |
| 178 | if command -v whence >/dev/null 2>&1; then |
| 179 | if whence -p "$binary" >/dev/null 2>&1; then |
| 180 | return 0 |
| 181 | fi |
| 182 | else |
| 183 | resolved="$(command -v "$binary" 2>/dev/null)" || resolved="" |
| 184 | if [ -n "$resolved" ] && [ -x "$resolved" ]; then |
| 185 | return 0 |
| 186 | fi |
| 187 | fi |
| 188 | |
| 189 | printf "Error: %s ('%s') is not installed or not in your PATH.\n" "$label" "$binary" >&2 |
| 190 | return 1 |
| 191 | } |
| 192 | |
| 193 | # ----------------------------------------------------------------------------- |
| 194 | # Function: claude (Includes --yolo and clear shortcuts) |
| 195 | # ----------------------------------------------------------------------------- |
| 196 | claude() { |
| 197 | # Shortcut for clearing cache |
| 198 | if [[ "$1" == "clear" ]]; then |
| 199 | clear_history claude |
| 200 | return 0 |
| 201 | fi |
| 202 | |
| 203 | # Check if binary exists |
| 204 | require_cli claude "Claude Code CLI" || return 1 |
| 205 | |
| 206 | # Handle --yolo mode |
| 207 | if [[ "$1" == "--yolo" ]]; then |
| 208 | shift |
| 209 | command claude --dangerously-skip-permissions "$@" |
| 210 | return $? |
| 211 | fi |
| 212 | |
| 213 | command claude "$@" |
| 214 | } |
| 215 | |
| 216 | # ----------------------------------------------------------------------------- |
| 217 | # Internal: _prompt_confirm_indexing (Confirm project indexing via the terminal) |
| 218 | # ----------------------------------------------------------------------------- |
| 219 | _prompt_confirm_indexing() { |
| 220 | local project_root="$1" |
| 221 | local reply |
| 222 | |
| 223 | if ! (: </dev/tty) 2>/dev/null; then |
| 224 | printf "Notice: No interactive terminal available; enhancing without project indexing.\n" >&2 |
| 225 | return 1 |
| 226 | fi |
| 227 | |
| 228 | printf "Allow Auggie to index and use project context from '%s'? (y/N) " "$project_root" >/dev/tty |
| 229 | if ! IFS= read -r reply </dev/tty; then |
| 230 | printf "\nNotice: Unable to read confirmation; enhancing without project indexing.\n" >&2 |
| 231 | return 1 |
| 232 | fi |
| 233 | |
| 234 | case "$reply" in |
| 235 | y|Y|yes|YES|Yes) |
| 236 | return 0 |
| 237 | ;; |
| 238 | *) |
| 239 | return 1 |
| 240 | ;; |
| 241 | esac |
| 242 | } |
| 243 | |
| 244 | # ----------------------------------------------------------------------------- |
| 245 | # Internal: _enhance_prompt (Run Auggie and write only the enhanced prompt) |
| 246 | # ----------------------------------------------------------------------------- |
| 247 | _enhance_prompt() { |
| 248 | local prompt="$1" |
| 249 | local output_file="$2" |
| 250 | local run_dir |
| 251 | local prompt_file |
| 252 | local workspace |
| 253 | local cache_dir |
| 254 | local auth_file |
| 255 | local log_file |
| 256 | local parsed_file |
| 257 | local project_root |
| 258 | local use_project_context |
| 259 | local script_pid |
| 260 | local waited |
| 261 | local timeout_seconds |
| 262 | local monitor_status |
| 263 | |
| 264 | if [ -z "$prompt" ]; then |
| 265 | printf "Error: Prompt was empty.\n" >&2 |
| 266 | return 1 |
| 267 | fi |
| 268 | |
| 269 | if [ ! -s "$HOME/.augment/session.json" ]; then |
| 270 | printf "Error: Auggie session file not found. Run 'auggie login' first.\n" >&2 |
| 271 | return 1 |
| 272 | fi |
| 273 | |
| 274 | run_dir="$(mktemp -d "${TMPDIR:-/tmp}/auggie-enhance.XXXXXX")" || return 1 |
| 275 | prompt_file="$run_dir/prompt.txt" |
| 276 | workspace="$run_dir/workspace" |
| 277 | cache_dir="$run_dir/cache" |
| 278 | auth_file="$HOME/.augment/session.json" |
| 279 | log_file="$run_dir/auggie.log" |
| 280 | parsed_file="$run_dir/enhanced.txt" |
| 281 | mkdir -p "$workspace" "$cache_dir" |
| 282 | printf "%s\n" "$prompt" > "$prompt_file" |
| 283 | |
| 284 | project_root="$(git rev-parse --show-toplevel 2>/dev/null)" || project_root="$PWD" |
| 285 | use_project_context=0 |
| 286 | timeout_seconds=90 |
| 287 | |
| 288 | if _prompt_confirm_indexing "$project_root"; then |
| 289 | use_project_context=1 |
| 290 | timeout_seconds=300 |
| 291 | workspace="$project_root" |
| 292 | cache_dir="$HOME/.augment" |
| 293 | printf "Indexing approved; enhancing with project context from '%s'.\n" "$project_root" >&2 |
| 294 | else |
| 295 | printf "Enhancing without project indexing.\n" >&2 |
| 296 | fi |
| 297 | |
| 298 | ( |
| 299 | if [ "$use_project_context" -eq 1 ]; then |
| 300 | AUGGIE_PROMPT_FILE="$prompt_file" \ |
| 301 | AUGGIE_WORKSPACE="$workspace" \ |
| 302 | AUGGIE_CACHE_DIR="$cache_dir" \ |
| 303 | AUGGIE_AUTH_FILE="$auth_file" \ |
| 304 | script -q -f -O "$log_file" -c '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"' </dev/null >/dev/null 2>&1 & |
| 305 | else |
| 306 | AUGGIE_PROMPT_FILE="$prompt_file" \ |
| 307 | AUGGIE_WORKSPACE="$workspace" \ |
| 308 | AUGGIE_CACHE_DIR="$cache_dir" \ |
| 309 | AUGGIE_AUTH_FILE="$auth_file" \ |
| 310 | script -q -f -O "$log_file" -c '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"' </dev/null >/dev/null 2>&1 & |
| 311 | fi |
| 312 | |
| 313 | script_pid=$! |
| 314 | waited=0 |
| 315 | |
| 316 | while kill -0 "$script_pid" >/dev/null 2>&1; do |
| 317 | if grep -aq "🤖" "$log_file" 2>/dev/null || grep -aq "Tool call:" "$log_file" 2>/dev/null; then |
| 318 | kill "$script_pid" >/dev/null 2>&1 || true |
| 319 | wait "$script_pid" >/dev/null 2>&1 || true |
| 320 | exit 0 |
| 321 | fi |
| 322 | |
| 323 | if [ "$waited" -ge "$timeout_seconds" ]; then |
| 324 | kill "$script_pid" >/dev/null 2>&1 || true |
| 325 | wait "$script_pid" >/dev/null 2>&1 || true |
| 326 | exit 124 |
| 327 | fi |
| 328 | |
| 329 | sleep 1 |
| 330 | waited=$((waited + 1)) |
| 331 | done |
| 332 | |
| 333 | wait "$script_pid" >/dev/null 2>&1 || true |
| 334 | exit 0 |
| 335 | ) |
| 336 | monitor_status=$? |
| 337 | |
| 338 | if [ "$monitor_status" -eq 124 ]; then |
| 339 | printf "Error: Timed out waiting for Auggie to enhance the prompt.\n" >&2 |
| 340 | rm -rf "$run_dir" |
| 341 | return 124 |
| 342 | fi |
| 343 | |
| 344 | perl -ne ' |
| 345 | # Auggie redraws streamed terminal lines with carriage returns. Keep only |
| 346 | # the final rendered segment so stale text cannot corrupt the prompt. |
| 347 | s/\r$//; |
| 348 | s/.*\r//; |
| 349 | 1 while s/[^\x08]\x08//g; |
| 350 | s/\x08//g; |
| 351 | s/\e\][^\a]*(?:\a|\e\\)//g; |
| 352 | s/\e\[[0-?]*[ -\/]*[@-~]//g; |
| 353 | next if /Script started on/ || /Script done on/; |
| 354 | if (/^(?:✨\s*)?Enhanced prompt:\s*(.*)$/) { |
| 355 | $capturing = 1; |
| 356 | $output .= "$1\n" if length $1; |
| 357 | next; |
| 358 | } |
| 359 | next unless $capturing; |
| 360 | exit if /^🤖/ || /Tool call:/ || /Session terminated/; |
| 361 | $output .= $_; |
| 362 | END { |
| 363 | $output =~ s/^\s*\n//; |
| 364 | $output =~ s/\s+\z//; |
| 365 | print "$output\n" if length $output; |
| 366 | } |
| 367 | ' "$log_file" > "$parsed_file" |
| 368 | |
| 369 | if [ ! -s "$parsed_file" ]; then |
| 370 | printf "Error: Auggie did not return an enhanced prompt.\n" >&2 |
| 371 | rm -rf "$run_dir" |
| 372 | return 1 |
| 373 | fi |
| 374 | |
| 375 | if ! command cp "$parsed_file" "$output_file"; then |
| 376 | printf "Error: Unable to save the enhanced prompt.\n" >&2 |
| 377 | rm -rf "$run_dir" |
| 378 | return 1 |
| 379 | fi |
| 380 | rm -rf "$run_dir" |
| 381 | } |
| 382 | |
| 383 | # ----------------------------------------------------------------------------- |
| 384 | # Function: prompt (Enhance a prompt with Auggie and optional project context) |
| 385 | # ----------------------------------------------------------------------------- |
| 386 | prompt() { |
| 387 | local prompt |
| 388 | local line |
| 389 | local output_file |
| 390 | |
| 391 | require_cli auggie "Auggie CLI" || return 1 |
| 392 | require_cli script "script utility" || return 1 |
| 393 | |
| 394 | if [ $# -gt 0 ]; then |
| 395 | prompt="$*" |
| 396 | elif [ ! -t 0 ]; then |
| 397 | prompt="$(cat)" |
| 398 | else |
| 399 | echo "Reading prompt... Type 'EOF' on a new line and press Enter when finished." |
| 400 | prompt="" |
| 401 | |
| 402 | while IFS= read -r line; do |
| 403 | [ "$line" = "EOF" ] && break |
| 404 | if [ -z "$prompt" ]; then |
| 405 | prompt="$line" |
| 406 | else |
| 407 | prompt="${prompt}"$'\n'"${line}" |
| 408 | fi |
| 409 | done |
| 410 | fi |
| 411 | |
| 412 | if [ -z "$prompt" ]; then |
| 413 | printf "Error: Prompt was empty.\n" >&2 |
| 414 | return 1 |
| 415 | fi |
| 416 | |
| 417 | output_file="$(mktemp "${TMPDIR:-/tmp}/auggie-enhanced-prompt.XXXXXX")" || return 1 |
| 418 | if ! _enhance_prompt "$prompt" "$output_file"; then |
| 419 | rm -f "$output_file" |
| 420 | return 1 |
| 421 | fi |
| 422 | |
| 423 | printf "Enhanced prompt:\n" |
| 424 | command cat "$output_file" |
| 425 | rm -f "$output_file" |
| 426 | |
| 427 | command auggie account status |
| 428 | } |
| 429 | |
| 430 | # ----------------------------------------------------------------------------- |
| 431 | # Function: plan_build (Claude Code multi-agent workflow orchestrator) |
| 432 | # ----------------------------------------------------------------------------- |
| 433 | _plan_build_superpowers_state() { |
| 434 | awk ' |
| 435 | BEGIN { |
| 436 | RS = "}" |
| 437 | state = "missing" |
| 438 | printed = 0 |
| 439 | } |
| 440 | /"id"[[:space:]]*:[[:space:]]*"superpowers@claude-plugins-official"/ { |
| 441 | state = "installed" |
| 442 | if ($0 ~ /"enabled"[[:space:]]*:[[:space:]]*true/) { |
| 443 | state = "enabled" |
| 444 | } else if ($0 ~ /"enabled"[[:space:]]*:[[:space:]]*false/) { |
| 445 | state = "disabled" |
| 446 | } |
| 447 | print state |
| 448 | printed = 1 |
| 449 | exit |
| 450 | } |
| 451 | END { |
| 452 | if (!printed) { |
| 453 | print state |
| 454 | } |
| 455 | } |
| 456 | ' |
| 457 | } |
| 458 | |
| 459 | _plan_build_superpowers_preflight() { |
| 460 | local plugin_json |
| 461 | local plugin_state |
| 462 | |
| 463 | case "${CLAUDE_CODE_SAFE_MODE:-}" in |
| 464 | 1|true|TRUE|yes|YES|on|ON) |
| 465 | echo "❌ Error: Claude Code safe mode disables Superpowers." |
| 466 | echo "Unset CLAUDE_CODE_SAFE_MODE before using --brainstorm or --writing-plan." |
| 467 | return 1 |
| 468 | ;; |
| 469 | esac |
| 470 | |
| 471 | if ! plugin_json="$(command claude plugin list --json 2>/dev/null)"; then |
| 472 | echo "❌ Error: Unable to inspect Claude Code plugins." |
| 473 | echo "Run 'claude plugin list' to diagnose the problem." |
| 474 | return 1 |
| 475 | fi |
| 476 | |
| 477 | plugin_state="$(printf '%s\n' "$plugin_json" | _plan_build_superpowers_state)" |
| 478 | |
| 479 | case "$plugin_state" in |
| 480 | enabled) |
| 481 | return 0 |
| 482 | ;; |
| 483 | disabled) |
| 484 | echo "❌ Error: Claude Code Superpowers is installed but disabled." |
| 485 | echo "Enable it with: claude plugin enable superpowers@claude-plugins-official" |
| 486 | ;; |
| 487 | missing) |
| 488 | echo "❌ Error: Claude Code Superpowers is required for --brainstorm and --writing-plan." |
| 489 | echo "Install it in Claude Code with: /plugin install superpowers@claude-plugins-official" |
| 490 | ;; |
| 491 | *) |
| 492 | echo "❌ Error: Unable to determine Claude Code Superpowers status." |
| 493 | echo "Run 'claude plugin list' to diagnose the problem." |
| 494 | ;; |
| 495 | esac |
| 496 | |
| 497 | return 1 |
| 498 | } |
| 499 | |
| 500 | _plan_build_is_worktree() { |
| 501 | local inside_work_tree |
| 502 | |
| 503 | inside_work_tree="$(command git rev-parse --is-inside-work-tree 2>/dev/null)" || return 1 |
| 504 | [ "$inside_work_tree" = "true" ] |
| 505 | } |
| 506 | |
| 507 | _plan_build_architect_preflight() { |
| 508 | local skill_path="$HOME/.claude/skills/plan-build-architect/SKILL.md" |
| 509 | |
| 510 | if ! (: </dev/tty) 2>/dev/null; then |
| 511 | echo "❌ Error: Architect mode requires an interactive terminal." |
| 512 | echo "Run plan_build --architect from an interactive terminal." |
| 513 | return 1 |
| 514 | fi |
| 515 | |
| 516 | require_cli claude "Claude Code CLI" || return 1 |
| 517 | require_cli codex "Codex CLI" || return 1 |
| 518 | require_cli coderabbit "CodeRabbit CLI" || return 1 |
| 519 | require_cli git "Git CLI" || return 1 |
| 520 | |
| 521 | if ! _plan_build_is_worktree; then |
| 522 | echo "❌ Error: Architect mode must run inside a Git worktree." |
| 523 | echo "Change to a Git worktree, then retry plan_build --architect." |
| 524 | return 1 |
| 525 | fi |
| 526 | |
| 527 | if [ ! -r "$skill_path" ] || [ ! -s "$skill_path" ]; then |
| 528 | echo "❌ Error: Architect skill is missing, unreadable, or empty: $skill_path" |
| 529 | echo "Install a readable, non-empty plan-build-architect skill at that path." |
| 530 | return 1 |
| 531 | fi |
| 532 | } |
| 533 | |
| 534 | plan_build() { |
| 535 | local claude_args=() |
| 536 | local use_yolo=0 |
| 537 | local use_architect=0 |
| 538 | local use_new=0 |
| 539 | local enhance_payload=0 |
| 540 | local planning_mode="standard" |
| 541 | local usage="Usage: plan_build [--yolo] [--prompt] [--brainstorm | --writing-plan] |
| 542 | plan_build --architect [--new] [--yolo]" |
| 543 | |
| 544 | while [ $# -gt 0 ]; do |
| 545 | case "$1" in |
| 546 | --yolo) |
| 547 | if [ "$use_yolo" -eq 1 ]; then |
| 548 | echo "Error: Duplicate argument: --yolo" |
| 549 | echo "$usage" |
| 550 | return 1 |
| 551 | fi |
| 552 | use_yolo=1 |
| 553 | claude_args=(--yolo) |
| 554 | ;; |
| 555 | --architect) |
| 556 | if [ "$use_architect" -eq 1 ]; then |
| 557 | echo "Error: Duplicate argument: --architect" |
| 558 | echo "$usage" |
| 559 | return 1 |
| 560 | fi |
| 561 | use_architect=1 |
| 562 | ;; |
| 563 | --new) |
| 564 | if [ "$use_new" -eq 1 ]; then |
| 565 | echo "Error: Duplicate argument: --new" |
| 566 | echo "$usage" |
| 567 | return 1 |
| 568 | fi |
| 569 | use_new=1 |
| 570 | ;; |
| 571 | --prompt) |
| 572 | if [ "$enhance_payload" -eq 1 ]; then |
| 573 | echo "Error: Duplicate argument: --prompt" |
| 574 | echo "$usage" |
| 575 | return 1 |
| 576 | fi |
| 577 | enhance_payload=1 |
| 578 | ;; |
| 579 | --brainstorm) |
| 580 | if [ "$planning_mode" = "brainstorm" ]; then |
| 581 | echo "Error: Duplicate argument: --brainstorm" |
| 582 | echo "$usage" |
| 583 | return 1 |
| 584 | fi |
| 585 | if [ "$planning_mode" != "standard" ]; then |
| 586 | echo "Error: --brainstorm and --writing-plan are mutually exclusive." |
| 587 | echo "$usage" |
| 588 | return 1 |
| 589 | fi |
| 590 | planning_mode="brainstorm" |
| 591 | ;; |
| 592 | --writing-plan) |
| 593 | if [ "$planning_mode" = "writing-plan" ]; then |
| 594 | echo "Error: Duplicate argument: --writing-plan" |
| 595 | echo "$usage" |
| 596 | return 1 |
| 597 | fi |
| 598 | if [ "$planning_mode" != "standard" ]; then |
| 599 | echo "Error: --brainstorm and --writing-plan are mutually exclusive." |
| 600 | echo "$usage" |
| 601 | return 1 |
| 602 | fi |
| 603 | planning_mode="writing-plan" |
| 604 | ;; |
| 605 | *) |
| 606 | echo "Error: Unknown argument: $1" |
| 607 | echo "$usage" |
| 608 | return 1 |
| 609 | ;; |
| 610 | esac |
| 611 | shift |
| 612 | done |
| 613 | |
| 614 | if [ "$use_new" -eq 1 ] && [ "$use_architect" -ne 1 ]; then |
| 615 | echo "Error: --new requires --architect." |
| 616 | echo "$usage" |
| 617 | return 1 |
| 618 | fi |
| 619 | |
| 620 | if [ "$use_architect" -eq 1 ] && |
| 621 | { [ "$enhance_payload" -eq 1 ] || [ "$planning_mode" != "standard" ]; }; then |
| 622 | echo "Error: --architect may combine only with --new and --yolo." |
| 623 | echo "$usage" |
| 624 | return 1 |
| 625 | fi |
| 626 | |
| 627 | if [ "$use_architect" -eq 1 ]; then |
| 628 | _plan_build_architect_preflight || return 1 |
| 629 | else |
| 630 | require_cli claude "Claude Code CLI" || return 1 |
| 631 | if [ "$planning_mode" != "standard" ]; then |
| 632 | _plan_build_superpowers_preflight || return 1 |
| 633 | fi |
| 634 | require_cli codex "Codex CLI" || return 1 |
| 635 | require_cli coderabbit "CodeRabbit CLI" || return 1 |
| 636 | if [ "$enhance_payload" -eq 1 ]; then |
| 637 | require_cli auggie "Auggie CLI" || return 1 |
| 638 | require_cli script "script utility" || return 1 |
| 639 | fi |
| 640 | fi |
| 641 | |
| 642 | echo "📥 Reading payload... Type 'EOF' on a new line and press Enter when finished." |
| 643 | |
| 644 | local payload |
| 645 | local line |
| 646 | payload="" |
| 647 | |
| 648 | while IFS= read -r line; do |
| 649 | [ "$line" = "EOF" ] && break |
| 650 | if [ -z "$payload" ]; then |
| 651 | payload="$line" |
| 652 | else |
| 653 | payload="${payload}"$'\n'"${line}" |
| 654 | fi |
| 655 | done |
| 656 | |
| 657 | if [ -z "$payload" ]; then |
| 658 | echo "❌ Error: Payload was empty." |
| 659 | return 1 |
| 660 | fi |
| 661 | |
| 662 | if [ "$enhance_payload" -eq 1 ]; then |
| 663 | local enhanced_file |
| 664 | local review_reply |
| 665 | enhanced_file="$(mktemp "${TMPDIR:-/tmp}/plan-build-enhanced.XXXXXX")" || return 1 |
| 666 | |
| 667 | echo "✨ Enhancing payload with Auggie..." |
| 668 | if ! _enhance_prompt "$payload" "$enhanced_file"; then |
| 669 | rm -f "$enhanced_file" |
| 670 | echo "❌ Error: Prompt enhancement failed; Claude was not launched." |
| 671 | return 1 |
| 672 | fi |
| 673 | |
| 674 | payload="$(cat "$enhanced_file")" |
| 675 | rm -f "$enhanced_file" |
| 676 | |
| 677 | if [ -z "$payload" ]; then |
| 678 | echo "❌ Error: Enhanced payload was empty; Claude was not launched." |
| 679 | return 1 |
| 680 | fi |
| 681 | |
| 682 | printf '\n%s\n' "━━━━━━━━━━━━━━━━ Auggie enhanced prompt ━━━━━━━━━━━━━━━━" |
| 683 | printf '%s\n' "$payload" |
| 684 | printf '%s\n\n' "━━━━━━━━━━━━━━━━ End enhanced prompt ━━━━━━━━━━━━━━━━━" |
| 685 | |
| 686 | if ! (: </dev/tty) 2>/dev/null; then |
| 687 | echo "❌ Error: Cannot review the enhanced prompt without an interactive terminal; Claude was not launched." |
| 688 | return 1 |
| 689 | fi |
| 690 | |
| 691 | printf "Proceed with this enhanced prompt? (y/N) " >/dev/tty |
| 692 | if ! IFS= read -r review_reply </dev/tty; then |
| 693 | printf '\n' >/dev/tty |
| 694 | echo "🛑 Review cancelled; Claude was not launched." |
| 695 | return 1 |
| 696 | fi |
| 697 | |
| 698 | case "$review_reply" in |
| 699 | y|Y|yes|YES|Yes) |
| 700 | echo "✅ Enhanced prompt approved." |
| 701 | ;; |
| 702 | *) |
| 703 | echo "🛑 Enhanced prompt not approved; Claude was not launched." |
| 704 | return 0 |
| 705 | ;; |
| 706 | esac |
| 707 | fi |
| 708 | |
| 709 | if [ "$use_architect" -eq 1 ]; then |
| 710 | local architect_start_mode |
| 711 | if [ "$use_new" -eq 1 ]; then |
| 712 | 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." |
| 713 | else |
| 714 | 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." |
| 715 | fi |
| 716 | |
| 717 | echo "🚀 Launching Claude Code in architect mode..." |
| 718 | |
| 719 | claude "${claude_args[@]}" "Read \`~/.claude/skills/plan-build-architect/SKILL.md\` and follow it strictly, including every gate even when --yolo is active. Stay in the documentation-first architect/orchestrator role: produce and coordinate documentation and planning only, and do not implement the requirement. |
| 720 | |
| 721 | $architect_start_mode |
| 722 | |
| 723 | Use the following payload as the initial requirement: |
| 724 | |
| 725 | $payload" |
| 726 | return $? |
| 727 | fi |
| 728 | |
| 729 | local planning_instruction |
| 730 | case "$planning_mode" in |
| 731 | brainstorm) |
| 732 | 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." |
| 733 | ;; |
| 734 | writing-plan) |
| 735 | 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." |
| 736 | ;; |
| 737 | *) |
| 738 | planning_instruction="Planning mode: standard. Create the workflow's normal short implementation plan." |
| 739 | ;; |
| 740 | esac |
| 741 | |
| 742 | echo "🚀 Launching Claude Code with your multi-agent workflow ($planning_mode planning)..." |
| 743 | |
| 744 | claude "${claude_args[@]}" "Please read \`~/.claude/skills/plan-build/SKILL.md\` and strictly follow the 8-step multi-agent workflow to implement the following task. |
| 745 | |
| 746 | $planning_instruction |
| 747 | |
| 748 | $payload" |
| 749 | } |
| 750 | |
| 751 | # ----------------------------------------------------------------------------- |
| 752 | # Function: aicommit (Automated Conventional Commit Engine Wrapper) |
| 753 | # ----------------------------------------------------------------------------- |
| 754 | aicommit() { |
| 755 | local diff engine prompt |
| 756 | |
| 757 | # 1. Get diff safely: exclude lock files/minified files and cap at ~100k characters to prevent CLI crashes |
| 758 | diff=$(git diff --cached -- . ":(exclude)*.lock" ":(exclude)*-lock.json" ":(exclude)*.min.js" | head -c 100000) |
| 759 | |
| 760 | if [ -z "$diff" ]; then |
| 761 | printf "❌ Error: Nothing staged to commit.\n" >&2 |
| 762 | return 1 |
| 763 | fi |
| 764 | |
| 765 | if [ ! -f "$HOME/.config/ai-commit-prompt.txt" ]; then |
| 766 | printf "❌ Error: Configuration file not found at ~/.config/ai-commit-prompt.txt\n" >&2 |
| 767 | printf "Please create it and paste your system prompt inside.\n" >&2 |
| 768 | return 1 |
| 769 | fi |
| 770 | |
| 771 | engine=${1:-claude} |
| 772 | prompt=$(cat "$HOME/.config/ai-commit-prompt.txt") |
| 773 | |
| 774 | case "$engine" in |
| 775 | claude) |
| 776 | echo "🤖 Claude is analyzing staged changes..." |
| 777 | cat "$HOME/.config/ai-commit-prompt.txt" <(echo -e "\n\nHere is the git diff:\n$diff") \ |
| 778 | | command claude --allowedTools 'Bash(git commit *)' --permission-mode dontAsk -p \ |
| 779 | "Read the provided instructions and diff, then generate and execute the commit." |
| 780 | ;; |
| 781 | |
| 782 | codex) |
| 783 | echo "🤖 Codex is analyzing staged changes..." |
| 784 | cat "$HOME/.config/ai-commit-prompt.txt" <(echo -e "\n\nHere is the git diff:\n$diff") \ |
| 785 | | command codex --ask-for-approval never exec --sandbox workspace-write --ephemeral \ |
| 786 | "Read the provided instructions and diff, then generate and execute the commit." |
| 787 | ;; |
| 788 | |
| 789 | copilot) |
| 790 | echo "🤖 Copilot is analyzing staged changes..." |
| 791 | copilot -p "$prompt |
| 792 | |
| 793 | Here is the git diff: |
| 794 | $diff" -s --no-ask-user --allow-tool='shell(git:*)' |
| 795 | ;; |
| 796 | |
| 797 | opencode) |
| 798 | echo "🤖 OpenCode is analyzing staged changes..." |
| 799 | cat "$HOME/.config/ai-commit-prompt.txt" <(echo -e "\n\nHere is the git diff:\n$diff") | opencode run |
| 800 | ;; |
| 801 | |
| 802 | *) |
| 803 | printf "❌ Error: Invalid engine '%s'\n" "$engine" >&2 |
| 804 | printf "Usage: aicommit [claude | codex | copilot | opencode]\n" >&2 |
| 805 | return 1 |
| 806 | ;; |
| 807 | esac |
| 808 | } |
| 809 | |
| 810 | # ----------------------------------------------------------------------------- |
| 811 | # Function: create (Efficiently create file + parent dirs) |
| 812 | # ----------------------------------------------------------------------------- |
| 813 | create() { |
| 814 | if ! command -v install >/dev/null 2>&1; then |
| 815 | printf "Error: 'install' coreutil is not available.\n" >&2 |
| 816 | return 1 |
| 817 | fi |
| 818 | |
| 819 | if [ $# -eq 0 ]; then |
| 820 | printf "Usage: create <file> [file ...]\n" >&2 |
| 821 | return 2 |
| 822 | fi |
| 823 | |
| 824 | for p in "$@"; do |
| 825 | # Expand tilde manually if shell doesn't |
| 826 | [[ "$p" == "~/"* ]] && p="${HOME}/${p#\~/}" |
| 827 | |
| 828 | if [ -z "$p" ] || [ "$p" = "/" ]; then |
| 829 | printf "create: refusing to operate on '%s'\n" "$p" >&2 |
| 830 | continue |
| 831 | fi |
| 832 | |
| 833 | if [ -e "$p" ]; then |
| 834 | printf "create: '%s' already exists. Skipping.\n" "$p" >&2 |
| 835 | continue |
| 836 | fi |
| 837 | |
| 838 | if install -D /dev/null "$p"; then |
| 839 | echo "Created '$p'" |
| 840 | fi |
| 841 | done |
| 842 | } |
| 843 | |
| 844 | # ----------------------------------------------------------------------------- |
| 845 | # Function: kill_port (Kill or check the process using a port) |
| 846 | # ----------------------------------------------------------------------------- |
| 847 | kill_port() { |
| 848 | local action="kill" |
| 849 | local signal="TERM" |
| 850 | local port |
| 851 | local pids |
| 852 | local pid |
| 853 | |
| 854 | case "${1:-}" in |
| 855 | -c|--check) |
| 856 | action="check" |
| 857 | shift |
| 858 | ;; |
| 859 | -k|--kill) |
| 860 | action="kill" |
| 861 | shift |
| 862 | ;; |
| 863 | -f|--force) |
| 864 | action="kill" |
| 865 | signal="KILL" |
| 866 | shift |
| 867 | ;; |
| 868 | -h|--help|"") |
| 869 | printf "Usage: kill_port [--check|-c|--kill|-k|--force|-f] <port_number>\n" >&2 |
| 870 | return 1 |
| 871 | ;; |
| 872 | esac |
| 873 | |
| 874 | port="${1:-}" |
| 875 | if [[ ! "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then |
| 876 | printf "Error: port must be a number between 1 and 65535.\n" >&2 |
| 877 | printf "Usage: kill_port [--check|-c|--kill|-k|--force|-f] <port_number>\n" >&2 |
| 878 | return 1 |
| 879 | fi |
| 880 | |
| 881 | if [[ "$OSTYPE" == "darwin"* ]]; then |
| 882 | pids="$( |
| 883 | { |
| 884 | lsof -nP -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null |
| 885 | lsof -nP -tiUDP:"$port" 2>/dev/null |
| 886 | } | sort -u |
| 887 | )" |
| 888 | |
| 889 | if [ -z "$pids" ]; then |
| 890 | printf "Port %s is FREE\n" "$port" |
| 891 | return 0 |
| 892 | fi |
| 893 | |
| 894 | printf "Port %s is in use:\n" "$port" |
| 895 | lsof -nP -iTCP:"$port" -sTCP:LISTEN -iUDP:"$port" 2>/dev/null |
| 896 | else |
| 897 | local port_hex |
| 898 | local inodes |
| 899 | |
| 900 | port_hex="$(printf "%04X" "$port")" |
| 901 | inodes="$( |
| 902 | awk -v port="$port_hex" ' |
| 903 | NR > 1 { |
| 904 | split($2, local_addr, ":") |
| 905 | if (toupper(local_addr[length(local_addr)]) == port && ($4 == "0A" || FILENAME ~ /udp/)) { |
| 906 | print $10 |
| 907 | } |
| 908 | } |
| 909 | ' /proc/net/tcp /proc/net/tcp6 /proc/net/udp /proc/net/udp6 2>/dev/null | sort -u |
| 910 | )" |
| 911 | |
| 912 | if [ -z "$inodes" ]; then |
| 913 | printf "Port %s is FREE\n" "$port" |
| 914 | return 0 |
| 915 | fi |
| 916 | |
| 917 | pids="" |
| 918 | pids="$( |
| 919 | find /proc/[0-9]*/fd -maxdepth 1 -type l -printf "%p %l\n" 2>/dev/null | |
| 920 | awk -v inodes="$inodes" ' |
| 921 | BEGIN { |
| 922 | split(inodes, inode_list, " ") |
| 923 | for (i in inode_list) { |
| 924 | wanted["socket:[" inode_list[i] "]"] = 1 |
| 925 | } |
| 926 | } |
| 927 | $2 in wanted { |
| 928 | split($1, path, "/") |
| 929 | print path[3] |
| 930 | } |
| 931 | ' | sort -u |
| 932 | )" |
| 933 | |
| 934 | if [ -z "$pids" ]; then |
| 935 | printf "Port %s is in use, but no readable process owner was found.\n" "$port" >&2 |
| 936 | return 1 |
| 937 | fi |
| 938 | |
| 939 | printf "Port %s is in use:\n" "$port" |
| 940 | for pid in $pids; do |
| 941 | if [ -r "/proc/$pid/cmdline" ]; then |
| 942 | printf " PID %-8s %s\n" "$pid" "$(tr '\0' ' ' < "/proc/$pid/cmdline")" |
| 943 | else |
| 944 | printf " PID %s\n" "$pid" |
| 945 | fi |
| 946 | done |
| 947 | fi |
| 948 | |
| 949 | if [ "$action" = "kill" ]; then |
| 950 | for pid in $pids; do |
| 951 | if kill "-$signal" "$pid" 2>/dev/null; then |
| 952 | printf "Killed PID %s with SIG%s.\n" "$pid" "$signal" |
| 953 | else |
| 954 | printf "Error: failed to kill PID %s. Try with sudo or --force.\n" "$pid" >&2 |
| 955 | return 1 |
| 956 | fi |
| 957 | done |
| 958 | fi |
| 959 | } |
| 960 | |
| 961 | # ----------------------------------------------------------------------------- |
| 962 | # Function: append_path (Adds to session PATH if exists) |
| 963 | # ----------------------------------------------------------------------------- |
| 964 | append_path() { |
| 965 | local dir="$1" |
| 966 | if [ -d "$dir" ]; then |
| 967 | case ":$PATH:" in |
| 968 | *":$dir:"*) ;; |
| 969 | *) PATH="$PATH:$dir" ;; |
| 970 | esac |
| 971 | elif [ "${SUPPRESS_WARNINGS:-0}" -ne 1 ]; then |
| 972 | echo "Warning: $dir does not exist." >&2 |
| 973 | fi |
| 974 | } |
| 975 | |
| 976 | # ----------------------------------------------------------------------------- |
| 977 | # Function: add_path_to_config (Fixed awk/sed injection) |
| 978 | # ----------------------------------------------------------------------------- |
| 979 | add_path_to_config() { |
| 980 | local new_path="$1" |
| 981 | local config_file="$HOME/.bashrc" |
| 982 | [[ "$SHELL" == *"zsh"* ]] && config_file="$HOME/.zshrc" |
| 983 | |
| 984 | if [ -z "$new_path" ]; then |
| 985 | printf "Usage: add_path_to_config <directory>\n" >&2 |
| 986 | return 1 |
| 987 | fi |
| 988 | |
| 989 | new_path="${new_path/#\~/$HOME}" |
| 990 | |
| 991 | if [ ! -d "$new_path" ]; then |
| 992 | printf "Warning: '%s' does not exist. Add anyway? (y/N) " "$new_path" |
| 993 | read -r REPLY |
| 994 | [[ ! $REPLY =~ ^[Yy]$ ]] && return 1 |
| 995 | fi |
| 996 | |
| 997 | # Check if already exists in file |
| 998 | if grep -Fq "append_path \"$new_path\"" "$config_file"; then |
| 999 | printf "Notice: Already in %s.\n" "$config_file" |
| 1000 | return 0 |
| 1001 | fi |
| 1002 | |
| 1003 | # Robust Injection: Use sed to insert before export PATH or just append |
| 1004 | if grep -q "^export PATH" "$config_file"; then |
| 1005 | sed -i.bak "/^export PATH/i append_path \"$new_path\"" "$config_file" |
| 1006 | else |
| 1007 | echo "append_path \"$new_path\"" >> "$config_file" |
| 1008 | echo "export PATH" >> "$config_file" |
| 1009 | fi |
| 1010 | |
| 1011 | printf "✅ Success: Added to %s\n" "$config_file" |
| 1012 | append_path "$new_path" && export PATH |
| 1013 | } |
| 1014 |
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. When plan_build supplies "Distribution mode: on", Codex and Kimi Code also become the implementers (backend and frontend respectively) via the Distributed Implementation variant of step 5.
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.grill: Invokemattpocock-skills:grillingto relentlessly stress-test the payload and your intended approach with the user. After grilling concludes, write the workflow's normal short implementation plan incorporating what survived.
For either Superpowers mode, save the artifacts at the paths selected by the skills. In grill mode, the short implementation plan is the planning artifact. 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, writing-plan, and grill 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
If plan_build supplied "Distribution mode: on", skip this step and step 6
and follow the Distributed Implementation variant (steps 5a–5c) below
instead, then continue at step 7.
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.
Distributed Implementation (distribution mode: on)
In this variant you orchestrate and review only — you never implement, not even leftovers. Codex implements backend items; Kimi Code implements frontend items.
5a. Split And Confirm
Tag every work item in the plan as BE, FE, or unclear. Present the
full table to the user (AskUserQuestion) and have them confirm or
reassign each item; unclear items must be assigned by the user to BE
or FE. Dispatch nothing until every item is confirmed.
5b. Backend Phase (Codex)
Check git status first so pre-existing work is never mixed in or
reverted. Compose a self-contained brief: the task, constraints, the
exact confirmed BE item list with file paths, project conventions, and
validation commands. Dispatch:
codex exec --full-auto "<brief>"
Then review the phase diff (git diff) yourself for correctness, scope,
and convention fit. Send findings back to Codex as a new brief (findings
plus the original context) — do not fix them yourself. Maximum 2 retry
rounds; if valid findings remain after that, stop and report with the
partial diff intact.
An empty diff from a delegate counts as a failure: retry once with a sharpened brief, within the same 2-round bound. If the delegate CLI crashes or hangs, stop and report, preserving the partial diff.
5c. Frontend Phase (Kimi Code)
Check git status again, then compose the FE brief the same way, and
additionally include the now-implemented backend API surface (routes,
types, request/response contracts) so the frontend builds against the
real backend. Dispatch:
kimi -p "<brief>"
(-p runs one prompt non-interactively and manages permissions itself;
it cannot be combined with --auto or --yolo.)
Review the phase diff and route findings back to Kimi under the same 2-round retry bound and failure rules as the backend phase.
After both phases pass review, skip step 6 (the per-phase reviews above replace it — Codex cannot impartially review the half it wrote) and continue at step 7 with the combined diff.
6. Codex Code Review Pass
Skip this step in distribution mode; the per-phase reviews in steps 5b–5c replace it.
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.
| 1 | # ============================================================================= |
| 2 | # ENVIRONMENT & PATH CONFIGURATION |
| 3 | # ============================================================================= |
| 4 | |
| 5 | # 1. Source functions first |
| 6 | if [[ -f "$HOME/.func" && -r "$HOME/.func" ]]; then |
| 7 | source "$HOME/.func" |
| 8 | fi |
| 9 | |
| 10 | # 2. Define Root Variables |
| 11 | export DOTNET_ROOT="$HOME/.dotnet" |
| 12 | |
| 13 | # 3. PATH INITIALIZATION |
| 14 | # ----------------------------------------------------------------------------- |
| 15 | append_path "$HOME/.local/bin" |
| 16 | append_path "$DOTNET_ROOT" |
| 17 | append_path "$DOTNET_ROOT/tools" |
| 18 | append_path "$HOME/.opencode/bin" |
| 19 | append_path "$HOME/Flutter/bin" |
| 20 | |
| 21 | # macOS specific paths |
| 22 | if [[ "$OSTYPE" == "darwin"* ]]; then |
| 23 | append_path "/Applications/Espanso.app/Contents/MacOS" |
| 24 | fi |
| 25 | |
| 26 | # Finalize PATH |
| 27 | export PATH |
| 1 | # ============================================================================= |
| 2 | # FILE: ~/.sourcerc |
| 3 | # Description: Initializes third-party package managers and external tools. |
| 4 | # ============================================================================= |
| 5 | |
| 6 | # ============================================================================= |
| 7 | # GENERAL ENV |
| 8 | # ============================================================================= |
| 9 | if [[ -f "$HOME/.local/bin/env" && -r "$HOME/.local/bin/env" ]]; then |
| 10 | source "$HOME/.local/bin/env" |
| 11 | fi |
| 12 | |
| 13 | # ============================================================================= |
| 14 | # CLAUDE CODE / AI GATEWAY |
| 15 | # ============================================================================= |
| 16 | export ANTHROPIC_BASE_URL="https://gateway.ai.cloudflare.com/v1/9a71825e3842e918e0dff9ad84f50484/claude-code-gateway/anthropic" |
| 17 | |
| 18 | # ============================================================================= |
| 19 | # SDKMAN (Java/Kotlin/Scala Version Manager) |
| 20 | # ============================================================================= |
| 21 | export SDKMAN_DIR="$HOME/.sdkman" |
| 22 | if [[ -f "$SDKMAN_DIR/bin/sdkman-init.sh" && -r "$SDKMAN_DIR/bin/sdkman-init.sh" ]]; then |
| 23 | source "$SDKMAN_DIR/bin/sdkman-init.sh" |
| 24 | fi |
| 25 | |
| 26 | # ============================================================================= |
| 27 | # NVM (Node Version Manager) |
| 28 | # ============================================================================= |
| 29 | export NVM_DIR="$HOME/.nvm" |
| 30 | if [[ -f "$NVM_DIR/nvm.sh" && -r "$NVM_DIR/nvm.sh" ]]; then |
| 31 | source "$NVM_DIR/nvm.sh" |
| 32 | fi |
| 33 | if [[ -f "$NVM_DIR/bash_completion" && -r "$NVM_DIR/bash_completion" ]]; then |
| 34 | source "$NVM_DIR/bash_completion" |
| 35 | fi |
| 36 | |
| 37 | # ============================================================================= |
| 38 | # GOOGLE CLOUD SDK |
| 39 | # ============================================================================= |
| 40 | if [[ -f "/opt/google-cloud-sdk/path.zsh.inc" ]]; then |
| 41 | source "/opt/google-cloud-sdk/path.zsh.inc" |
| 42 | fi |
| 43 | |
| 44 | if [[ -f "/opt/google-cloud-sdk/completion.zsh.inc" ]]; then |
| 45 | source "/opt/google-cloud-sdk/completion.zsh.inc" |
| 46 | fi |
| 47 |
| 1 | "$schema" = 'https://starship.rs/config-schema.json' |
| 2 | |
| 3 | format = """ |
| 4 | [](red)\ |
| 5 | $os\ |
| 6 | $username\ |
| 7 | [](bg:peach fg:red)\ |
| 8 | $directory\ |
| 9 | [](bg:yellow fg:peach)\ |
| 10 | $git_branch\ |
| 11 | $git_status\ |
| 12 | [](fg:yellow bg:green)\ |
| 13 | $c\ |
| 14 | $rust\ |
| 15 | $golang\ |
| 16 | $nodejs\ |
| 17 | $bun\ |
| 18 | $php\ |
| 19 | $java\ |
| 20 | $kotlin\ |
| 21 | $haskell\ |
| 22 | $python\ |
| 23 | [](fg:green bg:sapphire)\ |
| 24 | $conda\ |
| 25 | [](fg:sapphire bg:lavender)\ |
| 26 | $time\ |
| 27 | [ ](fg:lavender)\ |
| 28 | $cmd_duration\ |
| 29 | $line_break\ |
| 30 | $character""" |
| 31 | |
| 32 | palette = 'catppuccin_mocha' |
| 33 | |
| 34 | [os] |
| 35 | disabled = false |
| 36 | style = "bg:red fg:crust" |
| 37 | |
| 38 | [os.symbols] |
| 39 | Windows = "" |
| 40 | Ubuntu = "" |
| 41 | SUSE = "" |
| 42 | Raspbian = "" |
| 43 | Mint = "" |
| 44 | Macos = "" |
| 45 | Manjaro = "" |
| 46 | Linux = "" |
| 47 | Gentoo = "" |
| 48 | Fedora = "" |
| 49 | Alpine = "" |
| 50 | Amazon = "" |
| 51 | Android = "" |
| 52 | AOSC = "" |
| 53 | Arch = "" |
| 54 | Artix = "" |
| 55 | CentOS = "" |
| 56 | Debian = "" |
| 57 | Redhat = "" |
| 58 | RedHatEnterprise = "" |
| 59 | |
| 60 | [username] |
| 61 | show_always = true |
| 62 | style_user = "bg:red fg:crust" |
| 63 | style_root = "bg:red fg:crust" |
| 64 | format = '[ $user]($style)' |
| 65 | |
| 66 | [directory] |
| 67 | style = "bg:peach fg:crust" |
| 68 | format = "[ $path ]($style)" |
| 69 | truncation_length = 3 |
| 70 | truncation_symbol = "…/" |
| 71 | |
| 72 | [directory.substitutions] |
| 73 | "Documents" = " " |
| 74 | "Downloads" = " " |
| 75 | "Music" = " " |
| 76 | "Pictures" = " " |
| 77 | "Developer" = " " |
| 78 | |
| 79 | [git_branch] |
| 80 | symbol = "" |
| 81 | style = "bg:yellow" |
| 82 | format = '[[ $symbol $branch ](fg:crust bg:yellow)]($style)' |
| 83 | |
| 84 | [git_status] |
| 85 | style = "bg:yellow" |
| 86 | format = '[[($all_status$ahead_behind )](fg:crust bg:yellow)]($style)' |
| 87 | |
| 88 | [nodejs] |
| 89 | symbol = "" |
| 90 | style = "bg:green" |
| 91 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 92 | |
| 93 | [bun] |
| 94 | symbol = "" |
| 95 | style = "bg:green" |
| 96 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 97 | |
| 98 | [c] |
| 99 | symbol = " " |
| 100 | style = "bg:green" |
| 101 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 102 | |
| 103 | [rust] |
| 104 | symbol = "" |
| 105 | style = "bg:green" |
| 106 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 107 | |
| 108 | [golang] |
| 109 | symbol = "" |
| 110 | style = "bg:green" |
| 111 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 112 | |
| 113 | [php] |
| 114 | symbol = "" |
| 115 | style = "bg:green" |
| 116 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 117 | |
| 118 | [java] |
| 119 | symbol = " " |
| 120 | style = "bg:green" |
| 121 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 122 | |
| 123 | [kotlin] |
| 124 | symbol = "" |
| 125 | style = "bg:green" |
| 126 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 127 | |
| 128 | [haskell] |
| 129 | symbol = "" |
| 130 | style = "bg:green" |
| 131 | format = '[[ $symbol( $version) ](fg:crust bg:green)]($style)' |
| 132 | |
| 133 | [python] |
| 134 | symbol = "" |
| 135 | style = "bg:green" |
| 136 | format = '[[ $symbol( $version)(\(#$virtualenv\)) ](fg:crust bg:green)]($style)' |
| 137 | |
| 138 | [docker_context] |
| 139 | symbol = "" |
| 140 | style = "bg:sapphire" |
| 141 | format = '[[ $symbol( $context) ](fg:crust bg:sapphire)]($style)' |
| 142 | |
| 143 | [conda] |
| 144 | symbol = " " |
| 145 | style = "fg:crust bg:sapphire" |
| 146 | format = '[$symbol$environment ]($style)' |
| 147 | ignore_base = false |
| 148 | |
| 149 | [time] |
| 150 | disabled = false |
| 151 | time_format = "%R" |
| 152 | style = "bg:lavender" |
| 153 | format = '[[ $time ](fg:crust bg:lavender)]($style)' |
| 154 | |
| 155 | [line_break] |
| 156 | disabled = false |
| 157 | |
| 158 | [character] |
| 159 | disabled = false |
| 160 | success_symbol = '[❯](bold fg:green)' |
| 161 | error_symbol = '[❯](bold fg:red)' |
| 162 | vimcmd_symbol = '[❮](bold fg:green)' |
| 163 | vimcmd_replace_one_symbol = '[❮](bold fg:lavender)' |
| 164 | vimcmd_replace_symbol = '[❮](bold fg:lavender)' |
| 165 | vimcmd_visual_symbol = '[❮](bold fg:yellow)' |
| 166 | |
| 167 | [cmd_duration] |
| 168 | show_milliseconds = true |
| 169 | format = " in $duration " |
| 170 | style = "bg:lavender" |
| 171 | disabled = false |
| 172 | show_notifications = true |
| 173 | min_time_to_notify = 45000 |
| 174 | |
| 175 | [palettes.catppuccin_mocha] |
| 176 | rosewater = "#f5e0dc" |
| 177 | flamingo = "#f2cdcd" |
| 178 | pink = "#f5c2e7" |
| 179 | mauve = "#cba6f7" |
| 180 | red = "#f38ba8" |
| 181 | maroon = "#eba0ac" |
| 182 | peach = "#fab387" |
| 183 | yellow = "#f9e2af" |
| 184 | green = "#a6e3a1" |
| 185 | teal = "#94e2d5" |
| 186 | sky = "#89dceb" |
| 187 | sapphire = "#74c7ec" |
| 188 | blue = "#89b4fa" |
| 189 | lavender = "#b4befe" |
| 190 | text = "#cdd6f4" |
| 191 | subtext1 = "#bac2de" |
| 192 | subtext0 = "#a6adc8" |
| 193 | overlay2 = "#9399b2" |
| 194 | overlay1 = "#7f849c" |
| 195 | overlay0 = "#6c7086" |
| 196 | surface2 = "#585b70" |
| 197 | surface1 = "#45475a" |
| 198 | surface0 = "#313244" |
| 199 | base = "#1e1e2e" |
| 200 | mantle = "#181825" |
| 201 | crust = "#11111b" |
| 202 | |
| 203 | [palettes.catppuccin_frappe] |
| 204 | rosewater = "#f2d5cf" |
| 205 | flamingo = "#eebebe" |
| 206 | pink = "#f4b8e4" |
| 207 | mauve = "#ca9ee6" |
| 208 | red = "#e78284" |
| 209 | maroon = "#ea999c" |
| 210 | peach = "#ef9f76" |
| 211 | yellow = "#e5c890" |
| 212 | green = "#a6d189" |
| 213 | teal = "#81c8be" |
| 214 | sky = "#99d1db" |
| 215 | sapphire = "#85c1dc" |
| 216 | blue = "#8caaee" |
| 217 | lavender = "#babbf1" |
| 218 | text = "#c6d0f5" |
| 219 | subtext1 = "#b5bfe2" |
| 220 | subtext0 = "#a5adce" |
| 221 | overlay2 = "#949cbb" |
| 222 | overlay1 = "#838ba7" |
| 223 | overlay0 = "#737994" |
| 224 | surface2 = "#626880" |
| 225 | surface1 = "#51576d" |
| 226 | surface0 = "#414559" |
| 227 | base = "#303446" |
| 228 | mantle = "#292c3c" |
| 229 | crust = "#232634" |
| 230 | |
| 231 | [palettes.catppuccin_latte] |
| 232 | rosewater = "#dc8a78" |
| 233 | flamingo = "#dd7878" |
| 234 | pink = "#ea76cb" |
| 235 | mauve = "#8839ef" |
| 236 | red = "#d20f39" |
| 237 | maroon = "#e64553" |
| 238 | peach = "#fe640b" |
| 239 | yellow = "#df8e1d" |
| 240 | green = "#40a02b" |
| 241 | teal = "#179299" |
| 242 | sky = "#04a5e5" |
| 243 | sapphire = "#209fb5" |
| 244 | blue = "#1e66f5" |
| 245 | lavender = "#7287fd" |
| 246 | text = "#4c4f69" |
| 247 | subtext1 = "#5c5f77" |
| 248 | subtext0 = "#6c6f85" |
| 249 | overlay2 = "#7c7f93" |
| 250 | overlay1 = "#8c8fa1" |
| 251 | overlay0 = "#9ca0b0" |
| 252 | surface2 = "#acb0be" |
| 253 | surface1 = "#bcc0cc" |
| 254 | surface0 = "#ccd0da" |
| 255 | base = "#eff1f5" |
| 256 | mantle = "#e6e9ef" |
| 257 | crust = "#dce0e8" |
| 258 | |
| 259 | [palettes.catppuccin_macchiato] |
| 260 | rosewater = "#f4dbd6" |
| 261 | flamingo = "#f0c6c6" |
| 262 | pink = "#f5bde6" |
| 263 | mauve = "#c6a0f6" |
| 264 | red = "#ed8796" |
| 265 | maroon = "#ee99a0" |
| 266 | peach = "#f5a97f" |
| 267 | yellow = "#eed49f" |
| 268 | green = "#a6da95" |
| 269 | teal = "#8bd5ca" |
| 270 | sky = "#91d7e3" |
| 271 | sapphire = "#7dc4e4" |
| 272 | blue = "#8aadf4" |
| 273 | lavender = "#b7bdf8" |
| 274 | text = "#cad3f5" |
| 275 | subtext1 = "#b8c0e0" |
| 276 | subtext0 = "#a5adcb" |
| 277 | overlay2 = "#939ab7" |
| 278 | overlay1 = "#8087a2" |
| 279 | overlay0 = "#6e738d" |
| 280 | surface2 = "#5b6078" |
| 281 | surface1 = "#494d64" |
| 282 | surface0 = "#363a4f" |
| 283 | base = "#24273a" |
| 284 | mantle = "#1e2030" |
| 285 | crust = "#181926" |
| 286 |
| 1 | #!/usr/bin/env zsh |
| 2 | |
| 3 | set -eu |
| 4 | |
| 5 | repo_dir="${0:A:h}" |
| 6 | source "$repo_dir/func" |
| 7 | |
| 8 | failures=0 |
| 9 | |
| 10 | assert_equal() { |
| 11 | local expected="$1" |
| 12 | local actual="$2" |
| 13 | local label="$3" |
| 14 | |
| 15 | if [ "$actual" != "$expected" ]; then |
| 16 | print -u2 -- "FAIL: $label (expected '$expected', got '$actual')" |
| 17 | failures=$((failures + 1)) |
| 18 | fi |
| 19 | } |
| 20 | |
| 21 | assert_contains() { |
| 22 | local output="$1" |
| 23 | local expected="$2" |
| 24 | local label="$3" |
| 25 | |
| 26 | if [[ "$output" != *"$expected"* ]]; then |
| 27 | print -u2 -- "FAIL: $label (missing '$expected')" |
| 28 | failures=$((failures + 1)) |
| 29 | fi |
| 30 | } |
| 31 | |
| 32 | assert_not_contains() { |
| 33 | local output="$1" |
| 34 | local unexpected="$2" |
| 35 | local label="$3" |
| 36 | |
| 37 | if [[ "$output" == *"$unexpected"* ]]; then |
| 38 | print -u2 -- "FAIL: $label (unexpected '$unexpected')" |
| 39 | failures=$((failures + 1)) |
| 40 | fi |
| 41 | } |
| 42 | |
| 43 | preflight_fixture_dir="$(mktemp -d "${TMPDIR:-/tmp}/plan-build-preflight.XXXXXX")" |
| 44 | trap 'rm -rf -- "$preflight_fixture_dir"' EXIT |
| 45 | command git init -q "$preflight_fixture_dir/worktree" |
| 46 | command git init -q --bare "$preflight_fixture_dir/bare.git" |
| 47 | |
| 48 | (cd "$preflight_fixture_dir/worktree" && _plan_build_is_worktree) && rc=0 || rc=$? |
| 49 | assert_equal "0" "$rc" "architect preflight accepts normal worktree" |
| 50 | |
| 51 | (cd "$preflight_fixture_dir/bare.git" && _plan_build_is_worktree) && rc=0 || rc=$? |
| 52 | assert_equal "1" "$rc" "architect preflight rejects bare repository" |
| 53 | |
| 54 | plugin_state() { |
| 55 | printf '%s\n' "$1" | _plan_build_superpowers_state |
| 56 | } |
| 57 | |
| 58 | assert_equal "enabled" "$(plugin_state '[{"id":"superpowers@claude-plugins-official","enabled":true}]')" "compact enabled JSON" |
| 59 | assert_equal "disabled" "$(plugin_state $'[\n {\n "enabled": false,\n "id": "superpowers@claude-plugins-official"\n }\n]')" "reordered disabled JSON" |
| 60 | assert_equal "enabled" "$(plugin_state $'[\n {"enabled": false, "id": "other@market"},\n {"enabled": true, "id": "superpowers@claude-plugins-official"}\n]')" "multiple plugin JSON" |
| 61 | assert_equal "missing" "$(plugin_state '[{"id":"coderabbit@claude-plugins-official","enabled":true}]')" "missing plugin JSON" |
| 62 | assert_equal "installed" "$(plugin_state '[{"id":"superpowers@claude-plugins-official"}]')" "malformed plugin JSON" |
| 63 | |
| 64 | output="$(plan_build --brainstorm --writing-plan 2>&1)" && rc=0 || rc=$? |
| 65 | assert_equal "1" "$rc" "mutually exclusive flags status" |
| 66 | assert_contains "$output" "mutually exclusive" "mutually exclusive flags message" |
| 67 | |
| 68 | output="$(plan_build --brainstorm --brainstorm 2>&1)" && rc=0 || rc=$? |
| 69 | assert_equal "1" "$rc" "duplicate brainstorm status" |
| 70 | assert_contains "$output" "Duplicate argument: --brainstorm" "duplicate brainstorm message" |
| 71 | |
| 72 | output="$(plan_build --writing-plan --writing-plan 2>&1)" && rc=0 || rc=$? |
| 73 | assert_equal "1" "$rc" "duplicate writing-plan status" |
| 74 | assert_contains "$output" "Duplicate argument: --writing-plan" "duplicate writing-plan message" |
| 75 | |
| 76 | output="$(plan_build --v2 2>&1)" && rc=0 || rc=$? |
| 77 | assert_equal "1" "$rc" "v2 unknown argument status" |
| 78 | assert_contains "$output" "Unknown argument: --v2" "v2 unknown argument message" |
| 79 | |
| 80 | for args expected label in \ |
| 81 | "--architect --architect" "--architect" "duplicate architect" \ |
| 82 | "--architect --new --new" "--new" "duplicate new"; do |
| 83 | output="$(plan_build ${(z)args} 2>&1)" && rc=0 || rc=$? |
| 84 | assert_equal "1" "$rc" "$label status" |
| 85 | assert_contains "$output" "Duplicate argument: $expected" "$label message" |
| 86 | assert_contains "$output" "plan_build --architect [--new] [--yolo]" "$label usage" |
| 87 | done |
| 88 | |
| 89 | for args in \ |
| 90 | "--architect --prompt" \ |
| 91 | "--architect --brainstorm" \ |
| 92 | "--architect --writing-plan"; do |
| 93 | output="$(plan_build ${(z)args} 2>&1)" && rc=0 || rc=$? |
| 94 | assert_equal "1" "$rc" "$args incompatible status" |
| 95 | assert_contains "$output" "may combine only with --new and --yolo" "$args incompatible message" |
| 96 | done |
| 97 | |
| 98 | output="$(plan_build --new 2>&1)" && rc=0 || rc=$? |
| 99 | assert_equal "1" "$rc" "orphan new status" |
| 100 | assert_contains "$output" "--new requires --architect" "orphan new message" |
| 101 | |
| 102 | export CLAUDE_CODE_SAFE_MODE=1 |
| 103 | output="$(_plan_build_superpowers_preflight 2>&1)" && rc=0 || rc=$? |
| 104 | unset CLAUDE_CODE_SAFE_MODE |
| 105 | assert_equal "1" "$rc" "safe mode status" |
| 106 | assert_contains "$output" "safe mode disables Superpowers" "safe mode message" |
| 107 | |
| 108 | require_cli() { return 0; } |
| 109 | _plan_build_superpowers_preflight() { return 0; } |
| 110 | architect_preflight_calls=0 |
| 111 | _plan_build_architect_preflight() { |
| 112 | architect_preflight_calls=$((architect_preflight_calls + 1)) |
| 113 | return 0 |
| 114 | } |
| 115 | claude() { |
| 116 | printf 'CLAUDE_ARGS:' |
| 117 | printf ' <%s>' "$@" |
| 118 | printf '\n' |
| 119 | } |
| 120 | |
| 121 | output="$(printf 'build feature\nEOF\n' | plan_build --writing-plan --yolo 2>&1)" && rc=0 || rc=$? |
| 122 | assert_equal "0" "$rc" "writing-plan launch status" |
| 123 | assert_contains "$output" "Planning mode: writing-plan" "writing-plan prompt" |
| 124 | assert_contains "$output" "<--yolo>" "yolo forwarding" |
| 125 | |
| 126 | output="$(printf 'build feature\nEOF\n' | plan_build --brainstorm 2>&1)" && rc=0 || rc=$? |
| 127 | assert_equal "0" "$rc" "brainstorm launch status" |
| 128 | assert_contains "$output" "Planning mode: brainstorm" "brainstorm prompt" |
| 129 | |
| 130 | output="$(printf 'build feature\nEOF\n' | plan_build 2>&1)" && rc=0 || rc=$? |
| 131 | assert_equal "0" "$rc" "standard launch status" |
| 132 | assert_contains "$output" "Planning mode: standard" "standard prompt" |
| 133 | |
| 134 | output="$(printf 'design feature\nEOF\n' | plan_build --architect 2>&1)" && rc=0 || rc=$? |
| 135 | assert_equal "0" "$rc" "architect launch status" |
| 136 | assert_contains "$output" "~/.claude/skills/plan-build-architect/SKILL.md" "architect skill path" |
| 137 | assert_contains "$output" "documentation-first architect/orchestrator role" "architect role boundary" |
| 138 | assert_contains "$output" "design feature" "architect payload" |
| 139 | assert_contains "$output" "Start mode: safe-resume-detection" "architect resume mode" |
| 140 | |
| 141 | output="$(printf 'fresh design\nEOF\n' | plan_build --architect --new 2>&1)" && rc=0 || rc=$? |
| 142 | assert_equal "0" "$rc" "architect new launch status" |
| 143 | assert_contains "$output" "Start mode: archive-and-start-new" "architect new mode" |
| 144 | assert_contains "$output" "fresh design" "architect new payload" |
| 145 | |
| 146 | output="$(printf 'fast design\nEOF\n' | plan_build --architect --yolo 2>&1)" && rc=0 || rc=$? |
| 147 | assert_equal "0" "$rc" "architect yolo launch status" |
| 148 | assert_contains "$output" "<--yolo>" "architect yolo forwarding" |
| 149 | assert_contains "$output" "every gate even when --yolo is active" "architect yolo gates" |
| 150 | |
| 151 | output="$(printf 'EOF\n' | plan_build --architect 2>&1)" && rc=0 || rc=$? |
| 152 | assert_equal "1" "$rc" "architect empty payload status" |
| 153 | assert_contains "$output" "Payload was empty" "architect empty payload message" |
| 154 | |
| 155 | _plan_build_architect_preflight() { |
| 156 | echo "architect preflight failed" |
| 157 | return 1 |
| 158 | } |
| 159 | output="$(printf 'must not be read\nEOF\n' | plan_build --architect 2>&1)" && rc=0 || rc=$? |
| 160 | assert_equal "1" "$rc" "architect preflight failure status" |
| 161 | assert_contains "$output" "architect preflight failed" "architect preflight failure message" |
| 162 | assert_not_contains "$output" "Reading payload" "architect preflight before payload" |
| 163 | assert_not_contains "$output" "CLAUDE_ARGS" "architect preflight prevents launch" |
| 164 | |
| 165 | if [ "$failures" -ne 0 ]; then |
| 166 | exit 1 |
| 167 | fi |
| 168 | |
| 169 | print -- "PASS: plan_build tests" |
| 170 |
| 1 | " Enable line numbers |
| 2 | set number |
| 3 | |
| 4 | " Enable relative line numbers |
| 5 | set relativenumber |
| 6 | |
| 7 | " Enable syntax highlighting |
| 8 | syntax on |
| 9 | |
| 10 | " Set colorscheme |
| 11 | colorscheme slate |
| 12 | |
| 13 | " Enable file type detection and plugins |
| 14 | filetype plugin indent on |
| 15 | |
| 16 | " Set the tab width to 4 spaces |
| 17 | set tabstop=4 |
| 18 | set shiftwidth=4 |
| 19 | set expandtab |
| 20 | |
| 21 | " Enable auto-indentation |
| 22 | set autoindent |
| 23 | set smartindent |
| 24 | |
| 25 | " Highlight current line |
| 26 | set cursorline |
| 27 | |
| 28 | " Show matching parentheses |
| 29 | set showmatch |
| 30 | |
| 31 | " Enable line wrapping |
| 32 | set wrap |
| 33 | |
| 34 | " Enable mouse support |
| 35 | set mouse=a |
| 36 | |
| 37 | " Use Wayland's clipboard tools when this Vim lacks native clipboard support |
| 38 | if !has('clipboard') && exists('v:clipproviders') |
| 39 | \ && executable('wl-copy') && executable('wl-paste') |
| 40 | function! s:WaylandCopy(register, type, lines) abort |
| 41 | let l:text = join(a:lines, "\n") |
| 42 | if a:type ==# 'V' |
| 43 | let l:text ..= "\n" |
| 44 | endif |
| 45 | call system('wl-copy', l:text) |
| 46 | endfunction |
| 47 | |
| 48 | function! s:WaylandPaste(register) abort |
| 49 | return ['', systemlist('wl-paste --no-newline')] |
| 50 | endfunction |
| 51 | |
| 52 | let v:clipproviders['wltools'] = { |
| 53 | \ 'copy': { |
| 54 | \ '+': function('s:WaylandCopy'), |
| 55 | \ '*': function('s:WaylandCopy') |
| 56 | \ }, |
| 57 | \ 'paste': { |
| 58 | \ '+': function('s:WaylandPaste'), |
| 59 | \ '*': function('s:WaylandPaste') |
| 60 | \ } |
| 61 | \ } |
| 62 | set clipmethod^=wltools |
| 63 | endif |
| 64 | set clipboard=unnamedplus |
| 65 | |
| 66 | " Disable swap file |
| 67 | set noswapfile |
| 68 | |
| 69 | " Enable incremental search |
| 70 | set incsearch |
| 71 | |
| 72 | " Ignore case in search |
| 73 | set ignorecase |
| 74 | |
| 75 | " Override ignorecase if search contains capital letters |
| 76 | set smartcase |
| 77 | |
| 78 | " Display line and column number of the cursor position |
| 79 | set ruler |
| 80 | |
| 81 | " Set the status line at the bottom |
| 82 | set laststatus=2 |
| 83 | |
| 84 | " Show command in bottom bar |
| 85 | set showcmd |
| 86 | |
| 87 | " Set command height |
| 88 | set cmdheight=2 |
| 89 | |
| 90 | " Set history lines |
| 91 | set history=1000 |
| 92 | |
| 93 | " Disable backup file |
| 94 | set nobackup |
| 95 | |
| 96 | " Enable persistent undo |
| 97 | set undofile |
| 98 | |
| 99 | " Set maximum number of undo levels |
| 100 | set undolevels=1000 |
| 101 | |
| 102 | " Set undo directory |
| 103 | if has("persistent_undo") |
| 104 | silent !mkdir ~/.vim/undodir > /dev/null 2>&1 |
| 105 | set undodir=~/.vim/undodir |
| 106 | endif |
| 107 | |
| 108 | " Set search highlighting |
| 109 | set hlsearch |
| 110 | |
| 111 | " Enable visual bell |
| 112 | set visualbell |
| 113 | |
| 114 | " Set default file encoding |
| 115 | set encoding=utf-8 |
| 116 | |
| 117 | " Set the leader key to space |
| 118 | let mapleader = " " |
| 119 | |
| 120 | " Map <Leader>w to save the file |
| 121 | nnoremap <Leader>w :w<CR> |
| 122 | |
| 123 | " Map <Leader>q to quit |
| 124 | nnoremap <Leader>q :q<CR> |
| 125 | |
| 126 | " Map <Leader>x to save and quit |
| 127 | nnoremap <Leader>x :wq<CR> |
| 128 | |
| 129 | " Enable folding |
| 130 | set foldmethod=syntax |
| 131 | set foldlevelstart=99 |
| 132 | |
| 133 | " Enable line wrapping at 80 characters |
| 134 | set textwidth=80 |
| 135 | set colorcolumn=80 |
| 136 | |
| 137 | " Add some basic key mappings |
| 138 | " Map jj to escape insert mode |
| 139 | inoremap jj <Esc> |
| 140 | |
| 141 | " Map <Leader>n to toggle line numbers |
| 142 | nnoremap <Leader>n :set number!<CR> |
| 143 | |
| 144 | " Map <Leader>r to toggle relative line numbers |
| 145 | nnoremap <Leader>r :set relativenumber!<CR> |
| 146 | |
| 147 | " Configure plugins (if you use a plugin manager like vim-plug) |
| 148 | " Example with vim-plug: |
| 149 | " call plug#begin('~/.vim/plugged') |
| 150 | " Plug 'tpope/vim-sensible' |
| 151 | " Plug 'preservim/nerdtree' |
| 152 | " Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } |
| 153 | " Plug 'airblade/vim-gitgutter' |
| 154 | " call plug#end() |
| 155 | |
| 156 | " NERDTree key mappings |
| 157 | " nnoremap <C-n> :NERDTreeToggle<CR> |
| 158 | |
| 159 | " Enable automatic hard wrapping at textwidth (80 chars) |
| 160 | set formatoptions+=t " Auto-wrap text using textwidth |
| 161 | set formatoptions+=c " Auto-wrap comments using textwidth |
| 162 | set formatoptions+=r " Continue comments when pressing Enter |
| 163 | set formatoptions+=o " Continue comments when using 'o' or 'O' |
| 164 | set formatoptions+=q " Allow formatting of comments with 'gq' |
| 165 | set formatoptions+=n " Recognize numbered lists |
| 166 | set formatoptions+=l " Don't break lines that were already long |
| 167 | |
| 168 | " Enable syntax highlighting |
| 169 | syntax on |
| 170 | |
| 171 | " Increase memory limit for complex syntax parsing (Prevents E363) |
| 172 | set maxmempattern=20000 |
| 173 |
| 1 | #!/bin/bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | # ============================= |
| 5 | # COLORS & LOGGING |
| 6 | # ============================= |
| 7 | RED='\033[0;31m' |
| 8 | GREEN='\033[0;32m' |
| 9 | YELLOW='\033[1;33m' |
| 10 | BLUE='\033[0;34m' |
| 11 | NC='\033[0m' |
| 12 | |
| 13 | log() { echo -e "${BLUE}[INFO]${NC} $*"; } |
| 14 | ok() { echo -e "${GREEN}[OK]${NC} $*"; } |
| 15 | warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } |
| 16 | err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } |
| 17 | |
| 18 | # ============================= |
| 19 | # CONFIG |
| 20 | # ============================= |
| 21 | GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD" |
| 22 | |
| 23 | CONFIG_FILES=( |
| 24 | ".alias" |
| 25 | ".func" |
| 26 | ".pathrc" |
| 27 | ".sourcerc" |
| 28 | ".vimrc" |
| 29 | ".zshrc" |
| 30 | ".config/starship.toml" |
| 31 | ) |
| 32 | |
| 33 | # ============================= |
| 34 | # REQUIREMENTS |
| 35 | # ============================= |
| 36 | check_requirements() { |
| 37 | if [[ $EUID -eq 0 ]]; then |
| 38 | err "Do not run as root on macOS" |
| 39 | exit 1 |
| 40 | fi |
| 41 | } |
| 42 | |
| 43 | # ============================= |
| 44 | # INSTALLATION FUNCTIONS |
| 45 | # ============================= |
| 46 | install_oh_my_zsh() { |
| 47 | log "Installing Oh My Zsh and plugins..." |
| 48 | export RUNZSH=no |
| 49 | export CHSH=no |
| 50 | export KEEP_ZSHRC=yes |
| 51 | |
| 52 | if [[ ! -d "$HOME/.oh-my-zsh" ]]; then |
| 53 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended |
| 54 | else |
| 55 | ok "Oh My Zsh already installed" |
| 56 | fi |
| 57 | |
| 58 | local custom="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}" |
| 59 | local plugin_dir="$custom/plugins" |
| 60 | mkdir -p "$plugin_dir" |
| 61 | |
| 62 | [[ -d "$plugin_dir/zsh-autosuggestions" ]] || \ |
| 63 | git clone https://github.com/zsh-users/zsh-autosuggestions "$plugin_dir/zsh-autosuggestions" |
| 64 | |
| 65 | [[ -d "$plugin_dir/zsh-syntax-highlighting" ]] || \ |
| 66 | git clone https://github.com/zsh-users/zsh-syntax-highlighting "$plugin_dir/zsh-syntax-highlighting" |
| 67 | |
| 68 | ok "Oh My Zsh plugins installed to $plugin_dir" |
| 69 | } |
| 70 | |
| 71 | install_starship() { |
| 72 | if command -v starship >/dev/null 2>&1 || [[ -f "$HOME/.local/bin/starship" ]]; then |
| 73 | ok "Starship already installed" |
| 74 | elif command -v brew >/dev/null 2>&1; then |
| 75 | log "Installing Starship with Homebrew..." |
| 76 | brew install starship |
| 77 | ok "Starship installed" |
| 78 | else |
| 79 | log "Installing Starship..." |
| 80 | mkdir -p "$HOME/.local/bin" |
| 81 | curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin" |
| 82 | ok "Starship installed" |
| 83 | fi |
| 84 | } |
| 85 | |
| 86 | download_configs() { |
| 87 | log "Downloading custom config files..." |
| 88 | local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)" |
| 89 | mkdir -p "$backup" |
| 90 | |
| 91 | for f in "${CONFIG_FILES[@]}"; do |
| 92 | # Strip the leading dot for the download URL |
| 93 | local remote_file="${f#.}" |
| 94 | [[ "$f" == ".config/starship.toml" ]] && remote_file="starship.toml" |
| 95 | local url="$GIST_RAW_BASE/$remote_file" |
| 96 | local target="$HOME/$f" |
| 97 | local tmp="${target}.tmp.$$" |
| 98 | |
| 99 | mkdir -p "$(dirname "$target")" |
| 100 | |
| 101 | if [[ -f "$target" ]]; then |
| 102 | cp "$target" "$backup/" |
| 103 | fi |
| 104 | |
| 105 | log "Fetching $remote_file -> $f ..." |
| 106 | if curl -fsSL "$url" -o "$tmp"; then |
| 107 | mv "$tmp" "$target" |
| 108 | else |
| 109 | rm -f "$tmp" |
| 110 | warn "Failed to download $remote_file" |
| 111 | fi |
| 112 | done |
| 113 | |
| 114 | local skill_target="$HOME/.claude/skills/plan-build/SKILL.md" |
| 115 | local skill_tmp="${skill_target}.tmp.$$" |
| 116 | mkdir -p "$(dirname "$skill_target")" |
| 117 | log "Fetching orchestrate-loop.md -> .claude/skills/plan-build/SKILL.md ..." |
| 118 | if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp"; then |
| 119 | mv "$skill_tmp" "$skill_target" |
| 120 | else |
| 121 | rm -f "$skill_tmp" |
| 122 | warn "Failed to download orchestrate-loop.md" |
| 123 | fi |
| 124 | |
| 125 | ok "Configs downloaded (Backup at $backup)" |
| 126 | } |
| 127 | |
| 128 | configure_zshrc() { |
| 129 | local zshrc="$HOME/.zshrc" |
| 130 | log "Configuring .zshrc for Oh My Zsh and Starship..." |
| 131 | |
| 132 | touch "$zshrc" |
| 133 | cp "$zshrc" "$HOME/.zshrc.backup_$(date +%Y%m%d_%H%M%S)" |
| 134 | |
| 135 | cat > "$zshrc" <<'EOF' |
| 136 | # ============================================================================= |
| 137 | # 1. HELPER FUNCTIONS & PATH |
| 138 | # ============================================================================= |
| 139 | source_if_readable() { |
| 140 | local file="$1" |
| 141 | if [[ -f "$file" && -r "$file" ]]; then |
| 142 | source "$file" |
| 143 | fi |
| 144 | } |
| 145 | |
| 146 | export PATH="$HOME/.local/bin:$PATH" |
| 147 | |
| 148 | # ============================================================================= |
| 149 | # 2. OH MY ZSH FRAMEWORK |
| 150 | # ============================================================================= |
| 151 | export ZSH="${ZSH:-$HOME/.oh-my-zsh}" |
| 152 | ZSH_THEME="" |
| 153 | |
| 154 | plugins=( |
| 155 | git |
| 156 | zsh-autosuggestions |
| 157 | zsh-syntax-highlighting |
| 158 | ) |
| 159 | |
| 160 | source_if_readable "$ZSH/oh-my-zsh.sh" |
| 161 | |
| 162 | # ============================================================================= |
| 163 | # 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS |
| 164 | # ============================================================================= |
| 165 | source_if_readable "$HOME/.sourcerc" |
| 166 | source_if_readable "$HOME/.func" |
| 167 | source_if_readable "$HOME/.pathrc" |
| 168 | source_if_readable "$HOME/.alias" |
| 169 | |
| 170 | # ============================================================================= |
| 171 | # 4. STARSHIP PROMPT |
| 172 | # ============================================================================= |
| 173 | if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then |
| 174 | export STARSHIP_CONFIG="$HOME/.config/starship.toml" |
| 175 | fi |
| 176 | |
| 177 | if command -v starship >/dev/null 2>&1; then |
| 178 | eval "$(starship init zsh)" |
| 179 | elif [[ -x "$HOME/.local/bin/starship" ]]; then |
| 180 | eval "$("$HOME/.local/bin/starship" init zsh)" |
| 181 | fi |
| 182 | EOF |
| 183 | |
| 184 | ok ".zshrc configured for Oh My Zsh framework with Starship prompt" |
| 185 | } |
| 186 | |
| 187 | switch_shell() { |
| 188 | log "Starting Zsh session..." |
| 189 | echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}" |
| 190 | echo "----------------------------------------" |
| 191 | zsh -l |
| 192 | echo "----------------------------------------" |
| 193 | ok "Returned from Zsh session" |
| 194 | } |
| 195 | |
| 196 | # ============================= |
| 197 | # INTERACTIVE MENU |
| 198 | # ============================= |
| 199 | show_menu() { |
| 200 | echo "===========================================" |
| 201 | echo "macOS Minimal Zsh Setup - Choose what to do" |
| 202 | echo "===========================================" |
| 203 | echo " 0) Run ALL steps (1-5)" |
| 204 | echo " 1) Install Oh My Zsh + plugins" |
| 205 | echo " 2) Install Starship prompt" |
| 206 | echo " 3) Download custom configs (~/.alias, .func, .vimrc, etc.)" |
| 207 | echo " 4) Configure ~/.zshrc (Oh My Zsh + Starship)" |
| 208 | echo " 5) Switch to Zsh (Temporary Sub-shell)" |
| 209 | echo " 6) Quit" |
| 210 | echo "===========================================" |
| 211 | } |
| 212 | |
| 213 | run_choices() { |
| 214 | local input |
| 215 | read -p "Select: " input |
| 216 | input="${input//,/ }" |
| 217 | |
| 218 | local -a to_run=() |
| 219 | local -a to_exclude=() |
| 220 | |
| 221 | for item in $input; do |
| 222 | if [[ "$item" == !* ]]; then |
| 223 | to_exclude+=("${item:1}") |
| 224 | elif [[ "$item" == "0" ]]; then |
| 225 | to_run+=(1 2 3 4 5) |
| 226 | else |
| 227 | to_run+=("$item") |
| 228 | fi |
| 229 | done |
| 230 | |
| 231 | if [[ ${#to_run[@]} -gt 0 ]]; then |
| 232 | for choice in "${to_run[@]}"; do |
| 233 | local skip=false |
| 234 | |
| 235 | if [[ ${#to_exclude[@]} -gt 0 ]]; then |
| 236 | for ex in "${to_exclude[@]}"; do |
| 237 | if [[ "$choice" == "$ex" ]]; then |
| 238 | skip=true |
| 239 | break |
| 240 | fi |
| 241 | done |
| 242 | fi |
| 243 | |
| 244 | $skip && continue |
| 245 | |
| 246 | case "$choice" in |
| 247 | 1) install_oh_my_zsh ;; |
| 248 | 2) install_starship ;; |
| 249 | 3) download_configs ;; |
| 250 | 4) configure_zshrc ;; |
| 251 | 5) switch_shell ;; |
| 252 | 6) exit 0 ;; |
| 253 | *) warn "Skipping invalid option: $choice" ;; |
| 254 | esac |
| 255 | echo |
| 256 | done |
| 257 | fi |
| 258 | } |
| 259 | |
| 260 | # ============================= |
| 261 | # MAIN |
| 262 | # ============================= |
| 263 | main() { |
| 264 | check_requirements |
| 265 | while true; do |
| 266 | show_menu |
| 267 | run_choices |
| 268 | read -p "Do you want to run more options? (y/n): " again |
| 269 | [[ "$again" =~ ^[Yy]$ ]] || break |
| 270 | done |
| 271 | ok "macOS minimal Zsh configuration complete!" |
| 272 | } |
| 273 | |
| 274 | main "$@" |
| 275 |
| 1 | #!/bin/bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | # ============================= |
| 5 | # COLORS & LOGGING |
| 6 | # ============================= |
| 7 | RED='\033[0;31m' |
| 8 | GREEN='\033[0;32m' |
| 9 | YELLOW='\033[1;33m' |
| 10 | BLUE='\033[0;34m' |
| 11 | NC='\033[0m' |
| 12 | |
| 13 | log() { echo -e "${BLUE}[INFO]${NC} $*"; } |
| 14 | ok() { echo -e "${GREEN}[OK]${NC} $*"; } |
| 15 | warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } |
| 16 | err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } |
| 17 | |
| 18 | # ============================= |
| 19 | # FLAGS & CONFIG |
| 20 | # ============================= |
| 21 | SKIP_PACKAGES=false |
| 22 | SKIP_SHELL_CHANGE=false |
| 23 | INSTALL_HOMEBREW=false |
| 24 | |
| 25 | GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD" |
| 26 | CONFIG_FILES=( |
| 27 | ".alias" |
| 28 | ".func" |
| 29 | ".pathrc" |
| 30 | ".sourcerc" |
| 31 | ".vimrc" |
| 32 | ".zshrc" |
| 33 | ".config/starship.toml" |
| 34 | ) |
| 35 | |
| 36 | # ============================= |
| 37 | # OS DETECTION |
| 38 | # ============================= |
| 39 | detect_os() { |
| 40 | if [[ "$OSTYPE" == "darwin"* ]]; then |
| 41 | echo "macos" |
| 42 | elif [ -f /etc/os-release ]; then |
| 43 | . /etc/os-release |
| 44 | echo "$ID" |
| 45 | else |
| 46 | echo "unknown" |
| 47 | fi |
| 48 | } |
| 49 | |
| 50 | # ============================= |
| 51 | # REQUIREMENTS |
| 52 | # ============================= |
| 53 | check_requirements() { |
| 54 | if [[ $EUID -eq 0 ]]; then |
| 55 | err "Do not run as root. The script will request sudo when necessary." |
| 56 | exit 1 |
| 57 | fi |
| 58 | if [[ "$(detect_os)" != "macos" ]] && ! command -v sudo >/dev/null 2>&1; then |
| 59 | err "sudo required on Linux" |
| 60 | exit 1 |
| 61 | fi |
| 62 | } |
| 63 | |
| 64 | # ============================= |
| 65 | # INSTALLATION FUNCTIONS |
| 66 | # ============================= |
| 67 | update_system() { |
| 68 | $SKIP_PACKAGES && return |
| 69 | log "Updating system..." |
| 70 | local os=$(detect_os) |
| 71 | |
| 72 | case "$os" in |
| 73 | macos) |
| 74 | brew update || warn "Homebrew update failed; continuing installer" |
| 75 | ;; |
| 76 | ubuntu|debian) |
| 77 | sudo apt-get update -y |
| 78 | if ! sudo apt-get upgrade -y; then |
| 79 | warn "System upgrade did not complete. This can happen when apt wants to downgrade a package." |
| 80 | warn "Continuing because the Zsh setup does not require OS package upgrades to finish." |
| 81 | fi |
| 82 | ;; |
| 83 | fedora) |
| 84 | sudo dnf upgrade -y || warn "System upgrade failed; continuing installer" |
| 85 | ;; |
| 86 | arch) |
| 87 | sudo pacman -Syu --noconfirm || warn "System upgrade failed; continuing installer" |
| 88 | ;; |
| 89 | *) warn "Auto-update not supported for OS: $os" ;; |
| 90 | esac |
| 91 | ok "System update step finished" |
| 92 | } |
| 93 | |
| 94 | install_packages() { |
| 95 | $SKIP_PACKAGES && return |
| 96 | log "Installing core packages..." |
| 97 | local os=$(detect_os) |
| 98 | |
| 99 | case "$os" in |
| 100 | macos) brew install zsh git vim curl wget unzip xz ;; |
| 101 | ubuntu|debian) sudo apt-get install -y zsh git vim curl wget unzip zip build-essential xz-utils ;; |
| 102 | fedora) sudo dnf install -y zsh git vim curl wget unzip zip @development-tools xz ;; |
| 103 | arch) sudo pacman -S --noconfirm zsh git vim curl wget unzip zip base-devel xz ;; |
| 104 | *) warn "Auto-install not supported for OS: $os. Please install zsh, git, vim, curl manually." ;; |
| 105 | esac |
| 106 | ok "Packages installed" |
| 107 | } |
| 108 | |
| 109 | set_timezone() { |
| 110 | log "Setting timezone to Asia/Singapore..." |
| 111 | local os=$(detect_os) |
| 112 | if [[ "$os" == "macos" ]]; then |
| 113 | sudo systemsetup -settimezone Asia/Singapore >/dev/null |
| 114 | else |
| 115 | sudo timedatectl set-timezone Asia/Singapore |
| 116 | fi |
| 117 | ok "Timezone set to Asia/Singapore" |
| 118 | } |
| 119 | |
| 120 | install_homebrew() { |
| 121 | ! $INSTALL_HOMEBREW && return |
| 122 | command -v brew >/dev/null 2>&1 && ok "Homebrew already installed" && return |
| 123 | log "Installing Homebrew..." |
| 124 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" |
| 125 | ok "Homebrew installed" |
| 126 | } |
| 127 | |
| 128 | configure_shell() { |
| 129 | $SKIP_SHELL_CHANGE && return |
| 130 | log "Changing default shell to zsh..." |
| 131 | local zsh_path |
| 132 | zsh_path="$(command -v zsh)" |
| 133 | |
| 134 | if ! grep -qx "$zsh_path" /etc/shells; then |
| 135 | echo "$zsh_path" | sudo tee -a /etc/shells >/dev/null |
| 136 | fi |
| 137 | chsh -s "$zsh_path" |
| 138 | ok "Shell changed (requires logout/login to take effect)" |
| 139 | } |
| 140 | |
| 141 | install_oh_my_zsh() { |
| 142 | log "Installing Oh My Zsh and plugins..." |
| 143 | export RUNZSH=no |
| 144 | export CHSH=no |
| 145 | export KEEP_ZSHRC=yes |
| 146 | |
| 147 | if [[ ! -d "$HOME/.oh-my-zsh" ]]; then |
| 148 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended |
| 149 | else |
| 150 | ok "Oh My Zsh already installed" |
| 151 | fi |
| 152 | |
| 153 | local custom="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}" |
| 154 | local plugin_dir="$custom/plugins" |
| 155 | mkdir -p "$plugin_dir" |
| 156 | |
| 157 | [[ -d "$plugin_dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$plugin_dir/zsh-autosuggestions" |
| 158 | [[ -d "$plugin_dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$plugin_dir/zsh-syntax-highlighting" |
| 159 | |
| 160 | ok "Oh My Zsh plugins installed to $plugin_dir" |
| 161 | } |
| 162 | |
| 163 | install_starship() { |
| 164 | if command -v starship >/dev/null 2>&1 || [[ -f "$HOME/.local/bin/starship" ]]; then |
| 165 | ok "Starship already installed" |
| 166 | else |
| 167 | log "Installing Starship prompt..." |
| 168 | if command -v brew >/dev/null 2>&1; then |
| 169 | brew install starship |
| 170 | else |
| 171 | mkdir -p "$HOME/.local/bin" |
| 172 | curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin" |
| 173 | fi |
| 174 | ok "Starship installed" |
| 175 | fi |
| 176 | } |
| 177 | |
| 178 | download_configs() { |
| 179 | log "Downloading custom config files from OpenGist..." |
| 180 | local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)" |
| 181 | mkdir -p "$backup" |
| 182 | |
| 183 | for f in "${CONFIG_FILES[@]}"; do |
| 184 | local remote_file="${f#.}" |
| 185 | [[ "$f" == ".config/starship.toml" ]] && remote_file="starship.toml" |
| 186 | local url="$GIST_RAW_BASE/$remote_file" |
| 187 | local target="$HOME/$f" |
| 188 | local tmp="${target}.tmp.$$" |
| 189 | |
| 190 | mkdir -p "$(dirname "$target")" |
| 191 | |
| 192 | if [[ -f "$target" ]]; then |
| 193 | cp "$target" "$backup/" |
| 194 | fi |
| 195 | |
| 196 | log "Fetching $remote_file -> $f ..." |
| 197 | if curl -fsSL "$url" -o "$tmp"; then |
| 198 | mv "$tmp" "$target" |
| 199 | else |
| 200 | rm -f "$tmp" |
| 201 | warn "Failed to download $remote_file" |
| 202 | fi |
| 203 | done |
| 204 | |
| 205 | local skill_target="$HOME/.claude/skills/plan-build/SKILL.md" |
| 206 | local skill_tmp="${skill_target}.tmp.$$" |
| 207 | mkdir -p "$(dirname "$skill_target")" |
| 208 | log "Fetching orchestrate-loop.md -> .claude/skills/plan-build/SKILL.md ..." |
| 209 | if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp"; then |
| 210 | mv "$skill_tmp" "$skill_target" |
| 211 | else |
| 212 | rm -f "$skill_tmp" |
| 213 | warn "Failed to download orchestrate-loop.md" |
| 214 | fi |
| 215 | |
| 216 | ok "Configs downloaded (Backup at $backup)" |
| 217 | } |
| 218 | |
| 219 | configure_zshrc() { |
| 220 | local zshrc="$HOME/.zshrc" |
| 221 | log "Configuring .zshrc for Oh My Zsh and Starship..." |
| 222 | |
| 223 | touch "$zshrc" |
| 224 | cp "$zshrc" "$HOME/.zshrc.backup_$(date +%Y%m%d_%H%M%S)" |
| 225 | |
| 226 | cat > "$zshrc" <<'EOF' |
| 227 | # ============================================================================= |
| 228 | # 1. HELPER FUNCTIONS & PATH |
| 229 | # ============================================================================= |
| 230 | source_if_readable() { |
| 231 | local file="$1" |
| 232 | if [[ -f "$file" && -r "$file" ]]; then |
| 233 | source "$file" |
| 234 | fi |
| 235 | } |
| 236 | |
| 237 | export PATH="$HOME/.local/bin:$PATH" |
| 238 | |
| 239 | # ============================================================================= |
| 240 | # 2. OH MY ZSH FRAMEWORK |
| 241 | # ============================================================================= |
| 242 | export ZSH="${ZSH:-$HOME/.oh-my-zsh}" |
| 243 | ZSH_THEME="" |
| 244 | |
| 245 | plugins=( |
| 246 | git |
| 247 | zsh-autosuggestions |
| 248 | zsh-syntax-highlighting |
| 249 | ) |
| 250 | |
| 251 | source_if_readable "$ZSH/oh-my-zsh.sh" |
| 252 | |
| 253 | # ============================================================================= |
| 254 | # 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS |
| 255 | # ============================================================================= |
| 256 | source_if_readable "$HOME/.sourcerc" |
| 257 | source_if_readable "$HOME/.func" |
| 258 | source_if_readable "$HOME/.pathrc" |
| 259 | source_if_readable "$HOME/.alias" |
| 260 | |
| 261 | # ============================================================================= |
| 262 | # 4. STARSHIP PROMPT |
| 263 | # ============================================================================= |
| 264 | if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then |
| 265 | export STARSHIP_CONFIG="$HOME/.config/starship.toml" |
| 266 | fi |
| 267 | |
| 268 | if command -v starship >/dev/null 2>&1; then |
| 269 | eval "$(starship init zsh)" |
| 270 | elif [[ -x "$HOME/.local/bin/starship" ]]; then |
| 271 | eval "$("$HOME/.local/bin/starship" init zsh)" |
| 272 | fi |
| 273 | EOF |
| 274 | |
| 275 | ok ".zshrc configured for Oh My Zsh framework with Starship prompt" |
| 276 | } |
| 277 | |
| 278 | switch_shell() { |
| 279 | log "Starting Zsh session..." |
| 280 | echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}" |
| 281 | echo "----------------------------------------" |
| 282 | zsh -l |
| 283 | echo "----------------------------------------" |
| 284 | ok "Returned from Zsh session" |
| 285 | } |
| 286 | |
| 287 | # ============================= |
| 288 | # INTERACTIVE MENU |
| 289 | # ============================= |
| 290 | show_menu() { |
| 291 | echo "===========================================" |
| 292 | echo "Minimal Zsh Installer - Choose what to do" |
| 293 | echo "===========================================" |
| 294 | echo " 0) Run ALL steps (1-10)" |
| 295 | echo " 1) Update system packages" |
| 296 | echo " 2) Install core packages (zsh, git, vim, etc.)" |
| 297 | echo " 3) Set Timezone (Asia/Singapore)" |
| 298 | echo " 4) Install Homebrew" |
| 299 | echo " 5) Configure shell (chsh - sets default shell)" |
| 300 | echo " 6) Install Oh My Zsh + plugins" |
| 301 | echo " 7) Install Starship prompt" |
| 302 | echo " 8) Download custom configs (from OpenGist)" |
| 303 | echo " 9) Configure ~/.zshrc (Oh My Zsh + Starship)" |
| 304 | echo "10) Switch to Zsh (Temporary Sub-shell)" |
| 305 | echo "11) Quit" |
| 306 | echo "===========================================" |
| 307 | } |
| 308 | |
| 309 | run_choices() { |
| 310 | local input |
| 311 | read -p "Select: " input |
| 312 | input="${input//,/ }" |
| 313 | |
| 314 | local -a to_run=() |
| 315 | local -a to_exclude=() |
| 316 | |
| 317 | for item in $input; do |
| 318 | if [[ "$item" == !* ]]; then |
| 319 | to_exclude+=("${item:1}") |
| 320 | elif [[ "$item" == "0" ]]; then |
| 321 | to_run+=(1 2 3 4 5 6 7 8 9 10) |
| 322 | else |
| 323 | to_run+=("$item") |
| 324 | fi |
| 325 | done |
| 326 | |
| 327 | for choice in "${to_run[@]}"; do |
| 328 | local skip=false |
| 329 | |
| 330 | for ex in "${to_exclude[@]}"; do |
| 331 | if [[ "$choice" == "$ex" ]]; then |
| 332 | skip=true |
| 333 | break |
| 334 | fi |
| 335 | done |
| 336 | |
| 337 | $skip && continue |
| 338 | |
| 339 | case "$choice" in |
| 340 | 1) update_system ;; |
| 341 | 2) install_packages ;; |
| 342 | 3) set_timezone ;; |
| 343 | 4) install_homebrew ;; |
| 344 | 5) configure_shell ;; |
| 345 | 6) install_oh_my_zsh ;; |
| 346 | 7) install_starship ;; |
| 347 | 8) download_configs ;; |
| 348 | 9) configure_zshrc ;; |
| 349 | 10) switch_shell ;; |
| 350 | 11) log "Exiting..."; exit 0 ;; |
| 351 | *) warn "Skipping invalid option: $choice" ;; |
| 352 | esac |
| 353 | echo |
| 354 | done |
| 355 | } |
| 356 | |
| 357 | # ============================= |
| 358 | # MAIN |
| 359 | # ============================= |
| 360 | main() { |
| 361 | check_requirements |
| 362 | while true; do |
| 363 | show_menu |
| 364 | run_choices |
| 365 | read -p "Do you want to run more options? (y/n): " again |
| 366 | [[ "$again" =~ ^[Yy]$ ]] || break |
| 367 | done |
| 368 | ok "Zsh installation/configuration complete!" |
| 369 | } |
| 370 | |
| 371 | main "$@" |
| 372 |
| 1 | #!/bin/bash |
| 2 | set -euo pipefail |
| 3 | |
| 4 | # ============================= |
| 5 | # COLORS & LOGGING |
| 6 | # ============================= |
| 7 | RED='\033[0;31m' |
| 8 | GREEN='\033[0;32m' |
| 9 | YELLOW='\033[1;33m' |
| 10 | BLUE='\033[0;34m' |
| 11 | NC='\033[0m' |
| 12 | |
| 13 | log() { echo -e "${BLUE}[INFO]${NC} $*"; } |
| 14 | ok() { echo -e "${GREEN}[OK]${NC} $*"; } |
| 15 | warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } |
| 16 | err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } |
| 17 | |
| 18 | # ============================= |
| 19 | # FLAGS & CONFIG |
| 20 | # ============================= |
| 21 | SKIP_PACKAGES=false |
| 22 | SKIP_SHELL_CHANGE=false |
| 23 | INSTALL_HOMEBREW=false |
| 24 | |
| 25 | GIST_RAW_BASE="https://opengist.resetrix.work/weehong/f0d940c3c1214bf5b7996195199fdc09/raw/HEAD" |
| 26 | CONFIG_FILES=( |
| 27 | ".alias" |
| 28 | ".func" |
| 29 | ".pathrc" |
| 30 | ".sourcerc" |
| 31 | ".vimrc" |
| 32 | ".zshrc" |
| 33 | ".config/starship.toml" |
| 34 | ) |
| 35 | |
| 36 | # ============================= |
| 37 | # PLATFORM DETECTION |
| 38 | # ============================= |
| 39 | detect_os() { |
| 40 | if [ -f /etc/os-release ]; then |
| 41 | . /etc/os-release |
| 42 | echo "$ID" |
| 43 | else |
| 44 | echo "unknown" |
| 45 | fi |
| 46 | } |
| 47 | |
| 48 | is_wsl() { |
| 49 | grep -qi "microsoft" /proc/version 2>/dev/null |
| 50 | } |
| 51 | |
| 52 | # ============================= |
| 53 | # REQUIREMENTS |
| 54 | # ============================= |
| 55 | check_requirements() { |
| 56 | if [[ $EUID -eq 0 ]]; then |
| 57 | err "Do not run as root. The script will request sudo when necessary." |
| 58 | exit 1 |
| 59 | fi |
| 60 | if ! command -v sudo >/dev/null 2>&1; then |
| 61 | err "sudo required" |
| 62 | exit 1 |
| 63 | fi |
| 64 | if ! is_wsl; then |
| 65 | err "This installer is intended for WSL" |
| 66 | exit 1 |
| 67 | fi |
| 68 | } |
| 69 | |
| 70 | # ============================= |
| 71 | # INSTALLATION FUNCTIONS |
| 72 | # ============================= |
| 73 | update_system() { |
| 74 | $SKIP_PACKAGES && return |
| 75 | log "Updating system..." |
| 76 | local os=$(detect_os) |
| 77 | |
| 78 | case "$os" in |
| 79 | ubuntu|debian) |
| 80 | sudo apt-get update -y |
| 81 | if ! sudo apt-get upgrade -y; then |
| 82 | warn "System upgrade did not complete. This can happen when apt wants to downgrade a package." |
| 83 | warn "Continuing because the Zsh setup does not require OS package upgrades to finish." |
| 84 | fi |
| 85 | ;; |
| 86 | fedora) |
| 87 | sudo dnf upgrade -y || warn "System upgrade failed; continuing installer" |
| 88 | ;; |
| 89 | arch) |
| 90 | sudo pacman -Syu --noconfirm || warn "System upgrade failed; continuing installer" |
| 91 | ;; |
| 92 | *) warn "Auto-update not supported for OS: $os" ;; |
| 93 | esac |
| 94 | ok "System update step finished" |
| 95 | } |
| 96 | |
| 97 | install_packages() { |
| 98 | $SKIP_PACKAGES && return |
| 99 | log "Installing core packages..." |
| 100 | local os=$(detect_os) |
| 101 | |
| 102 | case "$os" in |
| 103 | ubuntu|debian) sudo apt-get install -y zsh git vim curl wget unzip zip build-essential xz-utils ;; |
| 104 | fedora) sudo dnf install -y zsh git vim curl wget unzip zip @development-tools xz ;; |
| 105 | arch) sudo pacman -S --noconfirm zsh git vim curl wget unzip zip base-devel xz ;; |
| 106 | *) warn "Auto-install not supported for OS: $os. Please install zsh, git, vim, curl manually." ;; |
| 107 | esac |
| 108 | ok "Packages installed" |
| 109 | } |
| 110 | |
| 111 | set_timezone() { |
| 112 | log "Checking timezone configuration..." |
| 113 | if command -v timedatectl >/dev/null 2>&1 && timedatectl status >/dev/null 2>&1; then |
| 114 | sudo timedatectl set-timezone Asia/Singapore |
| 115 | ok "Timezone set to Asia/Singapore" |
| 116 | return |
| 117 | fi |
| 118 | |
| 119 | warn "Skipping timezone change: timedatectl is not available in this WSL environment" |
| 120 | } |
| 121 | |
| 122 | install_homebrew() { |
| 123 | ! $INSTALL_HOMEBREW && return |
| 124 | command -v brew >/dev/null 2>&1 && ok "Homebrew already installed" && return |
| 125 | log "Installing Homebrew..." |
| 126 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" |
| 127 | ok "Homebrew installed" |
| 128 | } |
| 129 | |
| 130 | configure_shell() { |
| 131 | $SKIP_SHELL_CHANGE && return |
| 132 | log "Changing default shell to zsh..." |
| 133 | local zsh_path |
| 134 | zsh_path="$(command -v zsh)" |
| 135 | |
| 136 | if ! grep -qx "$zsh_path" /etc/shells; then |
| 137 | echo "$zsh_path" | sudo tee -a /etc/shells >/dev/null |
| 138 | fi |
| 139 | chsh -s "$zsh_path" |
| 140 | ok "Shell changed (open a new WSL session for it to take effect)" |
| 141 | } |
| 142 | |
| 143 | install_oh_my_zsh() { |
| 144 | log "Installing Oh My Zsh and plugins..." |
| 145 | export RUNZSH=no |
| 146 | export CHSH=no |
| 147 | export KEEP_ZSHRC=yes |
| 148 | |
| 149 | if [[ ! -d "$HOME/.oh-my-zsh" ]]; then |
| 150 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" --unattended |
| 151 | else |
| 152 | ok "Oh My Zsh already installed" |
| 153 | fi |
| 154 | |
| 155 | local custom="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}" |
| 156 | local plugin_dir="$custom/plugins" |
| 157 | mkdir -p "$plugin_dir" |
| 158 | |
| 159 | [[ -d "$plugin_dir/zsh-autosuggestions" ]] || git clone https://github.com/zsh-users/zsh-autosuggestions "$plugin_dir/zsh-autosuggestions" |
| 160 | [[ -d "$plugin_dir/zsh-syntax-highlighting" ]] || git clone https://github.com/zsh-users/zsh-syntax-highlighting "$plugin_dir/zsh-syntax-highlighting" |
| 161 | |
| 162 | ok "Oh My Zsh plugins installed to $plugin_dir" |
| 163 | } |
| 164 | |
| 165 | install_starship() { |
| 166 | if command -v starship >/dev/null 2>&1 || [[ -f "$HOME/.local/bin/starship" ]]; then |
| 167 | ok "Starship already installed" |
| 168 | else |
| 169 | log "Installing Starship prompt..." |
| 170 | if command -v brew >/dev/null 2>&1; then |
| 171 | brew install starship |
| 172 | else |
| 173 | mkdir -p "$HOME/.local/bin" |
| 174 | curl -fsSL https://starship.rs/install.sh | sh -s -- -y -b "$HOME/.local/bin" |
| 175 | fi |
| 176 | ok "Starship installed" |
| 177 | fi |
| 178 | } |
| 179 | |
| 180 | download_configs() { |
| 181 | log "Downloading custom config files from OpenGist..." |
| 182 | local backup="$HOME/.config_backup_$(date +%Y%m%d_%H%M%S)" |
| 183 | mkdir -p "$backup" |
| 184 | |
| 185 | for f in "${CONFIG_FILES[@]}"; do |
| 186 | local remote_file="${f#.}" |
| 187 | [[ "$f" == ".config/starship.toml" ]] && remote_file="starship.toml" |
| 188 | local url="$GIST_RAW_BASE/$remote_file" |
| 189 | local target="$HOME/$f" |
| 190 | local tmp="${target}.tmp.$$" |
| 191 | |
| 192 | mkdir -p "$(dirname "$target")" |
| 193 | |
| 194 | if [[ -f "$target" ]]; then |
| 195 | cp "$target" "$backup/" |
| 196 | fi |
| 197 | |
| 198 | log "Fetching $remote_file -> $f ..." |
| 199 | if curl -fsSL "$url" -o "$tmp"; then |
| 200 | mv "$tmp" "$target" |
| 201 | else |
| 202 | rm -f "$tmp" |
| 203 | warn "Failed to download $remote_file" |
| 204 | fi |
| 205 | done |
| 206 | |
| 207 | local skill_target="$HOME/.claude/skills/plan-build/SKILL.md" |
| 208 | local skill_tmp="${skill_target}.tmp.$$" |
| 209 | mkdir -p "$(dirname "$skill_target")" |
| 210 | log "Fetching orchestrate-loop.md -> .claude/skills/plan-build/SKILL.md ..." |
| 211 | if curl -fsSL "$GIST_RAW_BASE/orchestrate-loop.md" -o "$skill_tmp"; then |
| 212 | mv "$skill_tmp" "$skill_target" |
| 213 | else |
| 214 | rm -f "$skill_tmp" |
| 215 | warn "Failed to download orchestrate-loop.md" |
| 216 | fi |
| 217 | |
| 218 | ok "Configs downloaded (Backup at $backup)" |
| 219 | } |
| 220 | |
| 221 | configure_zshrc() { |
| 222 | local zshrc="$HOME/.zshrc" |
| 223 | log "Configuring .zshrc for Oh My Zsh and Starship..." |
| 224 | |
| 225 | touch "$zshrc" |
| 226 | cp "$zshrc" "$HOME/.zshrc.backup_$(date +%Y%m%d_%H%M%S)" |
| 227 | |
| 228 | cat > "$zshrc" <<'EOF' |
| 229 | # ============================================================================= |
| 230 | # 1. HELPER FUNCTIONS & PATH |
| 231 | # ============================================================================= |
| 232 | source_if_readable() { |
| 233 | local file="$1" |
| 234 | if [[ -f "$file" && -r "$file" ]]; then |
| 235 | source "$file" |
| 236 | fi |
| 237 | } |
| 238 | |
| 239 | export PATH="$HOME/.local/bin:$PATH" |
| 240 | |
| 241 | # ============================================================================= |
| 242 | # 2. OH MY ZSH FRAMEWORK |
| 243 | # ============================================================================= |
| 244 | export ZSH="${ZSH:-$HOME/.oh-my-zsh}" |
| 245 | ZSH_THEME="" |
| 246 | |
| 247 | plugins=( |
| 248 | git |
| 249 | zsh-autosuggestions |
| 250 | zsh-syntax-highlighting |
| 251 | ) |
| 252 | |
| 253 | source_if_readable "$ZSH/oh-my-zsh.sh" |
| 254 | |
| 255 | # ============================================================================= |
| 256 | # 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS |
| 257 | # ============================================================================= |
| 258 | source_if_readable "$HOME/.sourcerc" |
| 259 | source_if_readable "$HOME/.func" |
| 260 | source_if_readable "$HOME/.pathrc" |
| 261 | source_if_readable "$HOME/.alias" |
| 262 | |
| 263 | # ============================================================================= |
| 264 | # 4. STARSHIP PROMPT |
| 265 | # ============================================================================= |
| 266 | if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then |
| 267 | export STARSHIP_CONFIG="$HOME/.config/starship.toml" |
| 268 | fi |
| 269 | |
| 270 | if command -v starship >/dev/null 2>&1; then |
| 271 | eval "$(starship init zsh)" |
| 272 | elif [[ -x "$HOME/.local/bin/starship" ]]; then |
| 273 | eval "$("$HOME/.local/bin/starship" init zsh)" |
| 274 | fi |
| 275 | EOF |
| 276 | |
| 277 | ok ".zshrc configured for Oh My Zsh framework with Starship prompt" |
| 278 | } |
| 279 | |
| 280 | switch_shell() { |
| 281 | log "Starting Zsh session..." |
| 282 | echo -e "${YELLOW}Type 'exit' to return to this installer menu.${NC}" |
| 283 | echo "----------------------------------------" |
| 284 | zsh -l |
| 285 | echo "----------------------------------------" |
| 286 | ok "Returned from Zsh session" |
| 287 | } |
| 288 | |
| 289 | # ============================= |
| 290 | # INTERACTIVE MENU |
| 291 | # ============================= |
| 292 | show_menu() { |
| 293 | echo "===========================================" |
| 294 | echo "WSL Minimal Zsh Installer - Choose what to do" |
| 295 | echo "===========================================" |
| 296 | echo " 0) Run ALL steps (1-10)" |
| 297 | echo " 1) Update system packages" |
| 298 | echo " 2) Install core packages (zsh, git, vim, etc.)" |
| 299 | echo " 3) Set Timezone (best effort)" |
| 300 | echo " 4) Install Homebrew" |
| 301 | echo " 5) Configure shell (chsh - sets default shell)" |
| 302 | echo " 6) Install Oh My Zsh + plugins" |
| 303 | echo " 7) Install Starship prompt" |
| 304 | echo " 8) Download custom configs (from OpenGist)" |
| 305 | echo " 9) Configure ~/.zshrc (Oh My Zsh + Starship)" |
| 306 | echo "10) Switch to Zsh (Temporary Sub-shell)" |
| 307 | echo "11) Quit" |
| 308 | echo "===========================================" |
| 309 | } |
| 310 | |
| 311 | run_choices() { |
| 312 | local input |
| 313 | read -p "Select: " input |
| 314 | input="${input//,/ }" |
| 315 | |
| 316 | local -a to_run=() |
| 317 | local -a to_exclude=() |
| 318 | |
| 319 | for item in $input; do |
| 320 | if [[ "$item" == !* ]]; then |
| 321 | to_exclude+=("${item:1}") |
| 322 | elif [[ "$item" == "0" ]]; then |
| 323 | to_run+=(1 2 3 4 5 6 7 8 9 10) |
| 324 | else |
| 325 | to_run+=("$item") |
| 326 | fi |
| 327 | done |
| 328 | |
| 329 | for choice in "${to_run[@]}"; do |
| 330 | local skip=false |
| 331 | |
| 332 | for ex in "${to_exclude[@]}"; do |
| 333 | if [[ "$choice" == "$ex" ]]; then |
| 334 | skip=true |
| 335 | break |
| 336 | fi |
| 337 | done |
| 338 | |
| 339 | $skip && continue |
| 340 | |
| 341 | case "$choice" in |
| 342 | 1) update_system ;; |
| 343 | 2) install_packages ;; |
| 344 | 3) set_timezone ;; |
| 345 | 4) install_homebrew ;; |
| 346 | 5) configure_shell ;; |
| 347 | 6) install_oh_my_zsh ;; |
| 348 | 7) install_starship ;; |
| 349 | 8) download_configs ;; |
| 350 | 9) configure_zshrc ;; |
| 351 | 10) switch_shell ;; |
| 352 | 11) log "Exiting..."; exit 0 ;; |
| 353 | *) warn "Skipping invalid option: $choice" ;; |
| 354 | esac |
| 355 | echo |
| 356 | done |
| 357 | } |
| 358 | |
| 359 | # ============================= |
| 360 | # MAIN |
| 361 | # ============================= |
| 362 | main() { |
| 363 | check_requirements |
| 364 | while true; do |
| 365 | show_menu |
| 366 | run_choices |
| 367 | read -p "Do you want to run more options? (y/n): " again |
| 368 | [[ "$again" =~ ^[Yy]$ ]] || break |
| 369 | done |
| 370 | ok "WSL Zsh installation/configuration complete!" |
| 371 | } |
| 372 | |
| 373 | main "$@" |
| 374 |
| 1 | # ============================================================================= |
| 2 | # 1. HELPER FUNCTIONS & PATH |
| 3 | # ============================================================================= |
| 4 | source_if_readable() { |
| 5 | local file="$1" |
| 6 | if [[ -f "$file" && -r "$file" ]]; then |
| 7 | source "$file" |
| 8 | fi |
| 9 | } |
| 10 | |
| 11 | # Ensure local bin is in PATH early (catches manual Starship installations) |
| 12 | export PATH="$HOME/.local/bin:$PATH" |
| 13 | |
| 14 | # ============================================================================= |
| 15 | # 2. OH MY ZSH FRAMEWORK |
| 16 | # ============================================================================= |
| 17 | export ZSH="${ZSH:-$HOME/.oh-my-zsh}" |
| 18 | ZSH_THEME="" |
| 19 | |
| 20 | plugins=( |
| 21 | git |
| 22 | zsh-autosuggestions |
| 23 | zsh-syntax-highlighting |
| 24 | ) |
| 25 | |
| 26 | source_if_readable "$ZSH/oh-my-zsh.sh" |
| 27 | |
| 28 | # ============================================================================= |
| 29 | # 3. THIRD-PARTY INITIALIZATION & CUSTOM CONFIGS |
| 30 | # ============================================================================= |
| 31 | # Load external initializers (SDKMAN, NVM, etc.) |
| 32 | source_if_readable "$HOME/.sourcerc" |
| 33 | |
| 34 | # Custom Functions (must load before Path Management) |
| 35 | source_if_readable "$HOME/.func" |
| 36 | |
| 37 | # Path Management (Relies on append_path from .func) |
| 38 | source_if_readable "$HOME/.pathrc" |
| 39 | |
| 40 | # Aliases (Loaded late so they override framework/system defaults) |
| 41 | source_if_readable "$HOME/.alias" |
| 42 | |
| 43 | # ============================================================================= |
| 44 | # 4. STARSHIP PROMPT |
| 45 | # ============================================================================= |
| 46 | if [[ -z "${STARSHIP_CONFIG:-}" && -f "$HOME/.config/starship.toml" ]]; then |
| 47 | export STARSHIP_CONFIG="$HOME/.config/starship.toml" |
| 48 | fi |
| 49 | |
| 50 | # Starship owns the prompt. It must initialize after Oh My Zsh. |
| 51 | if command -v starship >/dev/null 2>&1; then |
| 52 | eval "$(starship init zsh)" |
| 53 | elif [[ -x "$HOME/.local/bin/starship" ]]; then |
| 54 | eval "$("$HOME/.local/bin/starship" init zsh)" |
| 55 | fi |
| 56 |