--- name: ship description: Ship a Hamster Studio brief. Merge base, implement in parallel waves, test, review, create bisectable commits, and optionally PR. Use when the user wants to execute or ship a brief. --- # Ship Brief Executes a Hamster Studio brief. **Execution-only**: the plan — parent tasks, subtasks, context — was generated upstream in Hamster Studio and synced into `.hamster/`. This skill schedules those existing tasks into parallel waves and executes them. It never generates, splits, or replans tasks on its own initiative — but it doesn't execute blindly either. Executors adapt to mechanical drift (documented as deviations) and escalate genuine plan defects as **PLAN_ISSUE** instead of implementing something known to be wrong; the orchestrator (and for scope changes, the user) decides how to proceed. Project skills ARE loaded during execution: task-executors read `.claude/skills/hamster-project-context/SKILL.md` when it exists (generated by `hamster sync`; clients that discover `.claude/skills/` expose it as the `hamster-project-context` skill), relevant project skills, and `.hamster/` blueprints/methods. Execution-only restricts *planning*, not *context*. **Argument**: "$ARGUMENTS" --- ## Readiness Before assuming a Hamster CLI, run this skill's bundled gate. Set `SKILL_DIR` to the absolute path of the directory holding the SKILL.md you just read — the shell's working directory is the user's project, not this skill — and keep the whole command on one line with its statements `;`-separated, because some clients flatten a fenced block before running it. The `else` branch is the same gate inline, for clients that expose no readable path to the installed package. Unix: ```bash SKILL_DIR=""; if [ -f "$SKILL_DIR/scripts/ensure-ready.sh" ]; then bash "$SKILL_DIR/scripts/ensure-ready.sh"; else export PATH="$HOME/.hamster/bin:$PATH"; if command -v hamster >/dev/null 2>&1 && { status_err="$(hamster --no-tui status 2>&1)"; printf '%s' "$status_err" | grep -q "Logged in" || { printf '%s\n' "$status_err" >&2; false; }; } && sync_err="$(hamster sync 2>&1)"; then echo READY; else [ -n "${sync_err:-}" ] && printf '%s\n' "$sync_err" >&2; echo SETUP_NEEDED; exit 1; fi; fi ``` Windows: ```powershell $SkillDir = ""; if (Test-Path "$SkillDir\scripts\ensure-ready.ps1") { pwsh "$SkillDir\scripts\ensure-ready.ps1" } else { $env:PATH = "$env:USERPROFILE\.hamster\bin;" + $env:PATH; if (Get-Command hamster -ErrorAction SilentlyContinue) { $status = & hamster --no-tui status 2>&1 | Out-String; if ($LASTEXITCODE -eq 0 -and $status -cmatch "Logged in") { $syncOut = & hamster sync 2>&1; if ($LASTEXITCODE -eq 0) { "READY" } else { [Console]::Error.WriteLine(($syncOut | Out-String).TrimEnd()); "SETUP_NEEDED"; exit 1 } } else { [Console]::Error.WriteLine($status.TrimEnd()); "SETUP_NEEDED"; exit 1 } } else { "SETUP_NEEDED"; exit 1 } } ``` If it prints `SETUP_NEEDED` in Codex on macOS and `hamster` is already on `PATH`, re-run only this readiness command with elevated permission. The default workspace sandbox cannot read Keychain credentials, and an extra directory grant does not change that. If the elevated check prints `READY`, continue. Only if it still prints `SETUP_NEEDED`, follow the setup skill or https://tryhamster.com/plugin/install, then re-run the check. Do not continue until it prints `READY`. ## Setup Read [brief-selection](references/brief-selection.md) and run **Account Resolution** first. On `ACCOUNT_UNRESOLVED`, follow its team-selection/re-sync guidance and stop. Then run prerequisites and live sync in ONE bash call, assigning `account` to the resolved filesystem slug (shell-quoted), not `HAMSTER_ACCOUNT_ID`: ```bash export PATH="$HOME/.hamster/bin:$PATH" errors="" command -v hamster >/dev/null 2>&1 || errors="${errors}hamster CLI not found. Install from https://tryhamster.com/plugin/install\n" [ -d ".hamster" ] || errors="${errors}.hamster/ directory not found. Run 'hamster sync' first.\n" command -v gh >/dev/null 2>&1 || errors="${errors}gh CLI not found. Install from https://cli.github.com\n" dirty=$(git status --porcelain 2>/dev/null | head -5) [ -n "$dirty" ] && errors="${errors}Uncommitted changes:\n${dirty}\n" if [ -n "$errors" ]; then printf "PREREQ_FAIL:\n$errors"; exit 1; fi account="" repo=$(git rev-parse --show-toplevel 2>/dev/null) watch="" for pid in $(pgrep -f "hamster sync .*--watch" 2>/dev/null); do cwd=$(lsof -a -d cwd -p "$pid" -Fn 2>/dev/null | sed -n 's/^n//p' | head -1) [ -z "$cwd" ] && cwd=$(readlink "/proc/$pid/cwd" 2>/dev/null) [ "$cwd" = "$repo" ] && { watch="existing"; break; } [ -z "$cwd" ] && watch="unknown" done if [ -n "$watch" ]; then echo "PREREQ_OK account=${account} sync_pid=${watch}" else hamster sync --watch > /dev/null 2>&1 & echo "PREREQ_OK account=${account} sync_pid=$!"; fi ``` - `PREREQ_FAIL` → show errors and stop (for uncommitted changes only: ask whether to proceed or stash) - `sync_pid=existing` → a watcher whose working directory is this repo is already running (an interrupted session, or the user's own). Reuse it; do NOT kill it at completion - `sync_pid=unknown` → a watcher exists but its working directory could not be read, so it may belong to another repo. Advisory only: reuse it rather than starting a second one, and never kill it - Numeric `sync_pid` → remember the literal number. Each Bash call is a fresh shell, so `$sync_pid` does not survive to later calls — at Completion, substitute it literally (e.g. `kill 12345`) --- ## Brief Selection and Scheduling Follow **Brief Selection** and **Scheduling** in [brief-selection](references/brief-selection.md): argument parsing, the brief picker, the inline frontmatter parse, and wave grouping, ending with the single execute/modify/cancel confirmation. --- ## Branch + Merge Base One call: ```bash lowest_id=$(ls "$tasks_dir"/*.md 2>/dev/null | xargs -I{} basename {} | grep -oE 'ham-[0-9]+' | sed 's/ham-//' | sort -n | head -1) branch="feature/ham-${lowest_id}-${slug}" git checkout -b "$branch" default_branch=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name 2>/dev/null || echo "main") git fetch origin "$default_branch" && git merge "origin/$default_branch" --no-edit echo "branch=$branch base=$default_branch" ``` Merge conflict → **STOP**, report conflicts, do NOT auto-resolve. --- ## Execution and Completion Read [execution-loop](references/execution-loop.md) and follow it for every wave: parallel execution, the validate/test gate, wave review, bisectable per-parent commits, and the progress report — then its Completion section for the watcher shutdown, final validation, optional PR, and brief status update. --- ## Error Recovery | Error | Recovery | |-------|----------| | Prereq failure | Stop with instructions (uncommitted changes: ask stash/proceed) | | Account unresolved | Stop; follow Account Resolution's team-selection/re-sync guidance | | Brief not found | Show partial matches, ask user | | Auth expired | `hamster auth login`; continue without status updates if it fails | | Merge conflict (base or between executors) | Stop, report, never auto-resolve | | Test gate fails | Stop, report, ask fix or skip | | NEEDS_FIXES after 2 rounds | Report to user, ask skip or manual fix | | Worker fails | Report, ask retry or skip | | Executor returns PLAN_ISSUE | Verify, then: local fix → corrected re-launch; scope change → ask user | | Task already done | Skip it | ## Notes - One commit set per parent (subtasks are never committed individually) - Interrupted? `/hamster:resume-hamster` reconstructs state from git + task statuses - Single PR per brief by default; >15 tasks → ask about splitting